]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
snapshot-20010707
authorWietse Venema <wietse@porcupine.org>
Sat, 7 Jul 2001 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:27:21 +0000 (06:27 +0000)
59 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/Makefile.in
postfix/README_QMQP [new file with mode: 0644]
postfix/RELEASE_NOTES
postfix/conf/master.cf
postfix/conf/postfix-script-diff
postfix/conf/postfix-script-sgid [changed mode: 0755->0644]
postfix/conf/sample-qmqpd.cf [new file with mode: 0644]
postfix/conf/sample-smtp.cf
postfix/html/Makefile.in
postfix/html/faq.html
postfix/html/lmtp.8.html
postfix/html/pipe.8.html
postfix/html/qmqpd.8.html [new file with mode: 0644]
postfix/html/smtp.8.html
postfix/man/Makefile.in
postfix/man/man8/lmtp.8
postfix/man/man8/pipe.8
postfix/man/man8/qmqpd.8 [new file with mode: 0644]
postfix/man/man8/smtp.8
postfix/mantools/postlink
postfix/src/global/Makefile.in
postfix/src/global/mail_params.h
postfix/src/global/mail_queue.c
postfix/src/global/mail_version.h
postfix/src/global/qmqp_proto.h [new file with mode: 0644]
postfix/src/lmtp/lmtp.c
postfix/src/lmtp/lmtp_proto.c
postfix/src/local/mailbox.c
postfix/src/pipe/pipe.c
postfix/src/qmqpd/.indent.pro [new symlink]
postfix/src/qmqpd/.printfck [new file with mode: 0644]
postfix/src/qmqpd/Makefile.in [new file with mode: 0644]
postfix/src/qmqpd/qmqpd.c [new file with mode: 0644]
postfix/src/qmqpd/qmqpd.h [new file with mode: 0644]
postfix/src/qmqpd/qmqpd_peer.c [new file with mode: 0644]
postfix/src/qmqpd/qmqpd_state.c [new file with mode: 0644]
postfix/src/smtp/smtp.c
postfix/src/smtp/smtp_addr.c
postfix/src/smtp/smtp_connect.c
postfix/src/smtp/smtp_proto.c
postfix/src/smtp/smtp_sasl_glue.c
postfix/src/smtpd/smtpd.c
postfix/src/smtpd/smtpd_check.c
postfix/src/smtpd/smtpd_peer.c
postfix/src/smtpd/smtpd_sasl_glue.c
postfix/src/smtpstone/Makefile.in
postfix/src/smtpstone/qmqp-sink.c [new file with mode: 0644]
postfix/src/smtpstone/qmqp-source.c [new file with mode: 0644]
postfix/src/smtpstone/smtp-source.c
postfix/src/util/Makefile.in
postfix/src/util/dict_mysql.c
postfix/src/util/mymalloc.c
postfix/src/util/netstring.c [new file with mode: 0644]
postfix/src/util/netstring.h [new file with mode: 0644]
postfix/src/util/vstream.c
postfix/src/util/vstring.c
postfix/src/util/vstring.h

index c35b4962ff877e69d2556ae590744d51a8930f57..3dcb109b1040775ca3effcb70ba40b414f1a32b9 100644 (file)
 -TDICT
 -TDICT_DB
 -TDICT_DBM
+-TDICT_DEBUG
 -TDICT_ENV
 -TDICT_HT
 -TDICT_LDAP
--TDICT_DEBUG
 -TDICT_MYSQL
 -TDICT_NI
 -TDICT_NIS
@@ -93,6 +93,7 @@
 -TQMGR_RECIPIENT
 -TQMGR_SCAN
 -TQMGR_TRANSPORT
+-TQMQPD_STATE
 -TRECIPIENT
 -TRECIPIENT_LIST
 -TREC_TYPE_NAME
index fe46e52a66693e739d0484f92f05bcc5ae9ac8a0..17a0dfc47a31a8aeb7cfe82f2e80f228cea759cb 100644 (file)
@@ -5261,3 +5261,51 @@ Apologies for any names omitted.
 
        Feature: address quoting and case folding flags for the
        pipe(8) mailer.
+
+20010611
+
+       Workaround: some MTAs fall on their face when they receive
+       unexpectedly long lines. From now on, Postfix defaults to
+       breaking long lines at 2048 (like Sendmail so it has got to
+       be right). To get the old, content preserving, behavior
+       specify "smtp_truncate_lines = no". File: smtp/smtp_proto.c.
+
+20010614
+
+       Bugfix: did not really undo 2821 552->452 mapping.
+
+20010628
+
+       Bugfix: postfix-script used a hard-coded maildrop group
+       owner instead of using the install-time specified name
+       stored in /etc/postfix/install.cf. Problem reported by
+       David Terrell @ meat.net.
+
+20010701
+
+       Feature: mail_spool_directory ending in / causes maildir
+       style delivery.
+
+       Bugfix: the FreeBSD kernel parameters kern.ipc.nmbclusters
+       and kern.ipc.maxsockets cannot be set with sysctl commands.
+       File: html/faq.html. Len Conrad @ Go2France.com.
+
+       Cleanup: the virtual delivery agent was poorly integrated
+       so that the SMTP server and queue manager did not reject
+       mail for unknown users. Files: smtpd/smtpd_check.c,
+       *qmgr/qmgr_message.c.
+
+20010705
+
+       Feature: QMQP server for compatibility with the ezmlm list
+       manager. Files:  util/netstring.[hc], qmqpd/qmqpd*.c.
+
+20010706
+
+       Feature: QMQP stress test message generator program.  Files:
+       smtpstone/qmqp-source.c, smtpstone/qmqp-sink.c.
+
+20010708
+
+       Bugfix: with disable_dns=yes, the SMTP client treated all
+       host lookup errors as permanent. File: smtp/smtp_addr.c.
index 7046d405066db4c8146510d4f63938b65a8c9da3..62d3af31d3b0d297c64eb02b272d583a3a69b9bd 100644 (file)
@@ -6,7 +6,7 @@ DIRS    = src/util src/global src/dns src/master src/postfix src/smtpstone \
        src/lmtp src/trivial-rewrite src/qmgr src/smtp src/bounce src/pipe \
        src/showq src/postalias src/postcat src/postconf src/postdrop \
        src/postkick src/postlock src/postlog src/postmap src/postsuper \
-       src/nqmgr src/spawn src/flush src/virtual # proto man html
+       src/nqmgr src/qmqpd src/spawn src/flush src/virtual # proto man html
 
 default: update
 
diff --git a/postfix/README_QMQP b/postfix/README_QMQP
new file mode 100644 (file)
index 0000000..666f79e
--- /dev/null
@@ -0,0 +1,39 @@
+Postfix QMQP server support
+===========================
+
+Postfix has preliminary server support for the QMQP protocol, so
+that Postfix can be used as a backend for the Ezmlm-idx mailing
+list manager. This support includes qmqp-source and qmqp-sink
+programs for protocol stress testing.
+
+Turning on the QMQP service
+===========================
+
+To enable QMQP server support on an existing Postfix system you
+have to add the following line to /etc/postfix/master.cf:
+
+628       inet  n       -       n       -       -       qmqpd
+
+
+QMQP server access control
+==========================
+
+By default, the QMQP server does not accept mail from any client.
+This is because the QMQP server relays mail to any destination
+(the "protocol" has no provision to reject specific recipients).
+
+To authorize QMQP clients, edit /etc/postfix/main.cf and specify
+a list of client patterns.
+
+qmqp_authorized_clients = client, client, ...
+
+A list pattern specifies a host name, a domain name, an internet
+address, or a network/mask pattern, where the mask specifies the
+number of bits in the network part.  When a pattern specifies a
+file name, its contents are substituted for the file name; when a
+pattern is a type:name table specification, table lookup is used
+instead.
+
+Patterns are separated by whitespace and/or commas. In order to
+reverse the result, precede a non-file name pattern with an
+exclamation point (!).
index 7a25fef570b24e847d64cf158ab32611109eb392..71b51f4ac37f1815c0439e23d88dbc8d53ee2f04 100644 (file)
@@ -1,3 +1,28 @@
+Incompatible changes with snapshot-20010707
+===========================================
+
+The SMTP client by default breaks lines > 2048 characters, in order
+to avoid problems with mail delivery to fragile SMTP server software.
+To get the old behavior, specify "smtp_break_lines = no" in the
+Postfix main.cf file.
+
+Major changes with snapshot-20010707
+====================================
+
+QMQP server support, so that Postfix can be used as a backend mailer
+for the Ezmlm-idx mailing list manager. The service is disabled by
+default. To enable, follow instructions in the README_QMQP file.
+
+You can now reject unknown virtual(8) recipients at the SMTP port
+by specifying a "domain.name whatever" entry in the tables specified
+with virtual_mailbox_maps, similar to Postfix virtual(5) domains.
+[virtual(8) is the Postfix virtual delivery agent, virtual(5) is
+the Postfix virtual map. The two implement virtual domains in a
+very different manner.]
+
+Specify "mail_spool_directory = /var/mail/" (note the trailing "/"
+character) to enable maildir format for /var/mail/username.
+
 Incompatible changes with snapshot-20010610
 ===========================================
 
index 75073b0800aeadaa05fa74d34fb786848f4da95c..fc441732383904ed372d8eb352ac0f6c700da89c 100644 (file)
@@ -69,6 +69,7 @@
 #              (yes)   (yes)   (yes)   (never) (50)
 # ==========================================================================
 smtp     inet  n       -       n       -       -       smtpd
+#628     inet  n       -       n       -       -       qmqpd
 pickup   fifo  n       n       n       60      1       pickup
 cleanup          unix  -       -       n       -       0       cleanup
 qmgr     fifo  n       -       n       300     1       qmgr
index 538659fb9c90c7c18d2d247f438e20d93cd82aab..1881b7380b77703cc805ed043b8c61c7400ca4d1 100644 (file)
@@ -1,7 +1,7 @@
-*** postfix-script-nosgid      Wed Mar 24 11:20:49 1999
---- postfix-script-sgid        Wed Mar 24 11:20:53 1999
+*** postfix-script-nosgid      Thu May 24 17:13:59 2001
+--- postfix-script-sgid        Fri Jun 29 10:28:19 2001
 ***************
-*** 174,181 ****
+*** 177,184 ****
        test -d maildrop || {
                $WARN creating missing Postfix maildrop directory
                mkdir maildrop || exit 1
        }
        test -d pid || {
                $WARN creating missing Postfix pid directory
---- 174,182 ----
+--- 177,185 ----
        test -d maildrop || {
                $WARN creating missing Postfix maildrop directory
                mkdir maildrop || exit 1
 !              chmod 1730 maildrop
                chown $mail_owner maildrop
-+              chgrp maildrop maildrop
++              (. $config_directory/install.cf; chgrp $setgid maildrop)
        }
        test -d pid || {
                $WARN creating missing Postfix pid directory
old mode 100755 (executable)
new mode 100644 (file)
index 98207c5..43f5b2b
@@ -179,7 +179,7 @@ check)
                mkdir maildrop || exit 1
                chmod 1730 maildrop
                chown $mail_owner maildrop
-               chgrp maildrop maildrop
+               (. $config_directory/install.cf; chgrp $setgid maildrop)
        }
        test -d pid || {
                $WARN creating missing Postfix pid directory
diff --git a/postfix/conf/sample-qmqpd.cf b/postfix/conf/sample-qmqpd.cf
new file mode 100644 (file)
index 0000000..03d26ee
--- /dev/null
@@ -0,0 +1,40 @@
+# DO NOT EDIT THIS FILE. EDIT THE MAIN.CF FILE INSTEAD. THE STUFF
+# HERE JUST SERVES AS AN EXAMPLE.
+#
+# This file contains example settings of Postfix configuration parameters
+# that control the QMQP server program.
+
+# The qmqpd_authorized_clients parameter specifies what clients are
+# allowed to connect to the QMQP server port.
+# 
+# By default, no client is allowed to use the service. This is
+# because the QMQP server will relay mail to any destination.
+# 
+# Specify a list of client patterns. A list pattern specifies a host
+# name, a domain name, an internet address, or a network/mask pattern,
+# where the mask specifies the number of bits in the network part.
+# When a pattern specifies a file name, its contents are substituted
+# for the file name; when a pattern is a type:name table specification,
+# table lookup is used instead.
+# 
+# Patterns are separated by whitespace and/or commas. In order to
+# reverse the result, precede a non-file name pattern with an
+# exclamation point (!).
+# 
+#qmqpd_authorized_clients = 
+
+# The qmqpd_error_delay parameter specifies how long the QMQP server
+# will pause before sending a negative reply to the client. The
+# purpose is to slow down confused or malicious clients.
+# 
+# By default, the QMQP server pauses for 5 seconds.
+#  
+#qmqpd_error_delay = 5s
+
+# The qmqpd_timeout parameter specifies a time limit for network I/O
+# operations.  If a read or write operation blocks for more than
+# $qmqpd_timeout seconds the QMQP server gives up and disconnects.
+#
+# By default, the QMQP server runs out of patience after 300 seconds.
+# 
+#qmqpd_timeout = 300s
index ccaf5ec64d55462a91ab37551bc1ada207146264..b1ffef7255ca8cf782d29969fdc83f258ec55450 100644 (file)
@@ -65,6 +65,14 @@ smtp_never_send_ehlo = no
 # 
 #smtp_bind_address=111.222.333.444
 
+# The smtp_break_lines parameter controls whether the SMTP client
+# will break lines longer than $line_length_limit characters.
+# 
+# By default, line breaking is turned on, because some fragile SMTP
+# server implementations cannot receive mail with long lines.
+# 
+#smtp_break_lines = yes
+
 # The smtp_skip_4xx_greeting parameter controls what happens when
 # an SMTP server greets us with a 4XX status code (go away, try
 # again later).
index cead447a024935aaa5e105edd111055d252ea6d7..44a59f239f23a0c36ea57ed3eb03a182e98b5287 100644 (file)
@@ -5,7 +5,7 @@ SHELL   = /bin/sh
 DAEMONS        =  bounce.8.html cleanup.8.html defer.8.html error.8.html local.8.html \
        lmtp.8.html master.8.html pickup.8.html pipe.8.html qmgr.8.html \
        showq.8.html smtp.8.html smtpd.8.html trivial-rewrite.8.html \
-       nqmgr.8.html spawn.8.html flush.8.html virtual.8.html
+       nqmgr.8.html spawn.8.html flush.8.html virtual.8.html qmqpd.8.html
 COMMANDS= mailq.1.html newaliases.1.html postalias.1.html postcat.1.html \
        postconf.1.html postfix.1.html postkick.1.html postlock.1.html \
        postlog.1.html postdrop.1.html postmap.1.html sendmail.1.html \
@@ -68,6 +68,9 @@ pipe.8.html: ../src/pipe/pipe.c
 qmgr.8.html: ../src/qmgr/qmgr.c
        srctoman $? | $(AWK) | nroff -man | uniq | man2html | postlink >$@
 
+qmqpd.8.html: ../src/qmqpd/qmqpd.c
+       srctoman $? | $(AWK) | nroff -man | uniq | man2html | postlink >$@
+
 showq.8.html: ../src/showq/showq.c
        srctoman $? | $(AWK) | nroff -man | uniq | man2html | postlink >$@
 
index 355a5a8554481c94b94e05ac2070c041ffb5822a..827439eccbb122b64996795a177d11bfaa8748d6 100644 (file)
@@ -94,6 +94,12 @@ domains with "relay access denied"</a>
 <li><a href="#broken_transport">Mail delivery fails with: "unknown
 mail transport error"</a>
 
+<li><a href="#msql_limit">Too many connections</a>
+
+<li><a href="#reiser_bugs">write queue file: No such file or directory</a>
+
+<li><a href="#reiser_bugs">write queue file: Unknown error 4294967289</a>
+
 </ul>
 
 <p>
@@ -1033,7 +1039,7 @@ Berkeley DB library version.
 
 <hr>
 
-<a name="nosuid"><h1>sendmail has set-uid root file permissions, or is run from a
+<a name="nosuid"><h3>sendmail has set-uid root file permissions, or is run from a
 set-uid root process</h3></a>
 
 Traditionally, the UNIX <b>sendmail</b> command is installed with
@@ -1126,36 +1132,49 @@ run out of file handles; after that, it will run out of sockets.
 
 <p>
 
-To set kernel parameters at boot time, add the following lines to
-the <b>/boot/loader.conf</b> file (this is specific to FreeBSD 4.x):
+To set the following kernel parameters at boot time, add the
+following lines to the <b>/boot/loader.conf</b> file (this is
+specific to FreeBSD 4.x):
 
 <p>
 
 <blockquote>
 <pre>
 kern.ipc.maxsockets="5000"
-kern.maxfiles="16384"
-kern.maxfilesperproc="16384"
 kern.ipc.nmbclusters="65536"
 </pre>
 </blockquote>
 
 <p>
 
-To set kernel parameters at run time execute the following commands
-as root (this is specific to FreeBSD 4.x):
+These parameters cannot be set at run time (verified with FreeBSD
+4.2).
+
+<p>
+
+To set the following kernel parameters at run time execute the
+following commands as root (this is specific to FreeBSD 4.x):
 
 <p>
 
 <blockquote>
 <pre>
-# sysctl -w kern.ipc.maxsockets=5000
 # sysctl -w kern.maxfiles=16384
 # sysctl -w kern.maxfilesperproc=16384
-# sysctl -w kern.ipc.nmbclusters=65536
 </pre>
 </blockquote>
 
+<p>
+
+These parameters cannot be set from <b>/boot/loader.conf</b>
+(verified with FreeBSD 4.2).
+
+<p>
+
+Other kernel parameters such as <b>kern.maxproc</b> can be increased
+only by recompiling the kernel with a different <b>maxusers</b>
+setting in the kernel configuration file (verified with FreeBSD 4.2).
+
 <hr>
 
 <a name="moby-linux"><h3>Running hundreds of Postfix processes on Linux</h3></a>
@@ -3367,6 +3386,27 @@ files, and to mount the Postfix queue file system with the
 
 <hr>
 
+<a name="msql_limit"><h3>Too many connections</h3></a>
+
+This message is produced by the MYQSL server. You need to increase
+the number of connections that it can handle.  Things to bear in
+mind: the <b>virtual</b> and <b>canonical</b> maps are accessed by
+every <b>smtpd</b> and <b>cleanup</b> process.
+
+<hr>
+
+<a name="reiser_bugs"><h3>write queue file: No such file or directory</h3></a>
+
+<h3>write queue file: Unknown error 4294967289</h3>
+
+Reiserfs reports the wrong error code when a message exceeds the
+<b>message_size_limit</b> setting. As a result, the Postfix SMTP
+server reports a "queue file write error" to the SMTP client, rather
+than reporting a "file too large" condition. The client will keep
+sending the same email again and again until the mail is too old.
+
+<hr>
+
 <a href="index.html">Up one level</a> | Postfix FAQ
 
 </body>
index df2d81ed6c5641928a58a9ae26696d71d0cc9a04..a4428fa0be6a4014746d130ee92b2a682af17420 100644 (file)
@@ -108,7 +108,7 @@ LMTP(8)                                                   LMTP(8)
               found in <b>services</b>(4).
 
 <b>Authentication</b> <b>controls</b>
-       <b>lmtp</b><i>_</i><b>enable</b><i>_</i><b>sasl</b><i>_</i><b>auth</b>
+       <b>lmtp</b><i>_</i><b>sasl</b><i>_</i><b>auth</b><i>_</i><b>enable</b>
               Enable  per-session  authentication as per <a href="http://www.faqs.org/rfcs/rfc2554.html">RFC 2554</a>
               (SASL).  By default, Postfix is built without  SASL
               support.
index 9fb5b181ce7ff28a8e69c24078293e1b2c6a4059..372fce1b6f2523785d6e3701a097ba1a1907d17b 100644 (file)
@@ -78,7 +78,7 @@ PIPE(8)                                                   PIPE(8)
 
                      The  <b>q</b>  flag  affects only entire addresses,
                      not the partial address information from the
-                     <b>$user</b>,  <b>extension</b>  or  <b>mailbox</b>  command-line
+                     <b>$user</b>,  <b>$extension</b>  or <b>$mailbox</b> command-line
                      macros.
 
               <b>u</b>      Fold  the  command-line  <b>$recipient</b>  address
diff --git a/postfix/html/qmqpd.8.html b/postfix/html/qmqpd.8.html
new file mode 100644 (file)
index 0000000..7bed710
--- /dev/null
@@ -0,0 +1,122 @@
+<html> <head> </head> <body> <pre>
+
+QMQPD(8)                                                 QMQPD(8)
+
+<b>NAME</b>
+       qmqpd - Postfix QMQP server
+
+<b>SYNOPSIS</b>
+       <b>qmqpd</b> [generic Postfix daemon options]
+
+<b>DESCRIPTION</b>
+       The  Postfix  QMQP server receives one message per connec-
+       tion.  Each message is piped through the  <a href="cleanup.8.html"><b>cleanup</b>(8)</a>  dae-
+       mon,  and  is placed into the <b>incoming</b> queue as one single
+       queue file.  The program expects to be run from  the  <a href="master.8.html"><b>mas-</b></a>
+       <a href="master.8.html"><b>ter</b>(8)</a> process manager.
+
+       The QMQP server implements one access policy: only explic-
+       itly authorized client hosts are allowed to use  the  ser-
+       vice.
+
+<b>SECURITY</b>
+       The QMQP server is moderately security-sensitive. It talks
+       to QMQP clients and to DNS servers  on  the  network.  The
+       QMQP server can be run chrooted at fixed low privilege.
+
+<b>DIAGNOSTICS</b>
+       Problems and transactions are logged to <b>syslogd</b>(8).
+
+<b>BUGS</b>
+       The  QMQP protocol provides only one server reply per mes-
+       sage delivery. It is  therefore  not  possible  to  reject
+       individual recipients.
+
+       The  QMQP  protocol  requires  the  server  to receive the
+       entire message before replying. If a message is malformed,
+       or  if  any netstring component is longer than acceptable,
+       Postfix replies immediately and closes the connection.  It
+       is left up to the client to handle the situation.
+
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+       The  following  <b>main.cf</b> parameters are especially relevant
+       to this program. See the Postfix <b>main.cf</b> file  for  syntax
+       details  and  for  default  values. Use the <b>postfix</b> <b>reload</b>
+       command after a configuration change.
+
+<b>Miscellaneous</b>
+       <b>always</b><i>_</i><b>bcc</b>
+              Address to send a copy of each message that  enters
+              the system.
+
+       <b>debug</b><i>_</i><b>peer</b><i>_</i><b>level</b>
+              Increment  in  verbose  logging level when a remote
+              host  matches  a  pattern  in  the  <b>debug</b><i>_</i><b>peer</b><i>_</i><b>list</b>
+              parameter.
+
+       <b>debug</b><i>_</i><b>peer</b><i>_</i><b>list</b>
+              List  of  domain or network patterns. When a remote
+              host matches a pattern, increase the  verbose  log-
+              ging   level   by   the  amount  specified  in  the
+              <b>debug</b><i>_</i><b>peer</b><i>_</i><b>level</b> parameter.
+
+       <b>hopcount</b><i>_</i><b>limit</b>
+              Limit the number of <b>Received:</b> message headers.
+
+       <b>qmqpd</b><i>_</i><b>authorized</b><i>_</i><b>clients</b>
+              A list of domain or network patterns that specifies
+              what clients are allowed to use the service.
+
+       <b>qmqpd</b><i>_</i><b>timeout</b>
+              Limit  the  time  to  send a server response and to
+              receive a client request.
+
+       <b>soft</b><i>_</i><b>bounce</b>
+              Change hard (D)  reject  responses  into  soft  (Z)
+              reject  responses.   This can be useful for testing
+              purposes.
+
+<b>Content</b> <b>inspection</b> <b>controls</b>
+       <b>content</b><i>_</i><b>filter</b>
+              The name of a mail delivery transport that  filters
+              mail and that either bounces mail or re-injects the
+              result back into Postfix.  This parameter uses  the
+              same  syntax  as  the  right-hand side of a Postfix
+              transport table.
+
+<b>Resource</b> <b>controls</b>
+       <b>line</b><i>_</i><b>length</b><i>_</i><b>limit</b>
+              Limit the amount of memory in bytes  used  for  the
+              handling  of partial input lines, and the length of
+              sender and recipient addresses  that  are  received
+              from client.
+
+       <b>message</b><i>_</i><b>size</b><i>_</i><b>limit</b>
+              Limit the total size in bytes of a message, includ-
+              ing  on-disk  storage  for  sender  and   recipient
+              address information.
+
+<b>Tarpitting</b>
+       <b>qmqpd</b><i>_</i><b>error</b><i>_</i><b>sleep</b><i>_</i><b>time</b>
+              Time to wait in seconds before informing the client
+              of a problem. This slows down run-away errors.
+
+<b>SEE</b> <b>ALSO</b>
+       http://cr.yp.to/proto/qmqp.html, QMQP protocol
+       <a href="cleanup.8.html">cleanup(8)</a> message canonicalization
+       <a href="master.8.html">master(8)</a> process manager
+       syslogd(8) system logging
+
+<b>LICENSE</b>
+       The Secure Mailer license must be  distributed  with  this
+       software.
+
+<b>AUTHOR(S)</b>
+       Wietse Venema
+       IBM T.J. Watson Research
+       P.O. Box 704
+       Yorktown Heights, NY 10598, USA
+
+                                                                1
+
+</pre> </body> </html>
index 4dc5f52761ec2e66c7727cb020931b4723308884..3d06c3a055767a7471cf8e65c6f13e4fb5970f23 100644 (file)
@@ -117,6 +117,11 @@ SMTP(8)                                                   SMTP(8)
        <b>smtp</b><i>_</i><b>never</b><i>_</i><b>send</b><i>_</i><b>ehlo</b>
               Never send EHLO at the start of a connection.
 
+       <b>smtp</b><i>_</i><b>break</b><i>_</i><b>lines</b>
+              Break  lines  &gt;  <b>$line</b><i>_</i><b>length</b><i>_</i><b>limit</b>  into  multiple
+              shorter lines.  Some SMTP servers misbehave on long
+              lines.
+
        <b>smtp</b><i>_</i><b>skip</b><i>_</i><b>4xx</b><i>_</i><b>greeting</b>
               Skip servers that greet us with a 4xx status  code.
 
index 489162160d6d1f4a42ce19b7a3c4fc258044ba38..a59eb1fe96b64ee89dd2815ac8cf704f11fc52ea 100644 (file)
@@ -5,7 +5,7 @@ SHELL   = /bin/sh
 DAEMONS        = man8/bounce.8 man8/defer.8 man8/cleanup.8 man8/error.8 man8/local.8 \
        man8/lmtp.8 man8/master.8 man8/pickup.8 man8/pipe.8 man8/qmgr.8 \
        man8/showq.8 man8/smtp.8 man8/smtpd.8 man8/trivial-rewrite.8 \
-       man8/nqmgr.8 man8/spawn.8 man8/flush.8 man8/virtual.8
+       man8/nqmgr.8 man8/spawn.8 man8/flush.8 man8/virtual.8 man8/qmqpd.8
 COMMANDS= man1/postalias.1 man1/postcat.1 man1/postconf.1 man1/postfix.1 \
        man1/postkick.1 man1/postlock.1 man1/postlog.1 man1/postdrop.1 \
        man1/postmap.1 man1/sendmail.1 man1/mailq.1 man1/newaliases.1 \
@@ -65,6 +65,9 @@ man8/pipe.8: ../src/pipe/pipe.c
 man8/qmgr.8: ../src/qmgr/qmgr.c
        ../mantools/srctoman $? >$@
 
+man8/qmqpd.8: ../src/qmqpd/qmqpd.c
+       ../mantools/srctoman $? >$@
+
 man8/showq.8: ../src/showq/showq.c
        ../mantools/srctoman $? >$@
 
index 51e54e05d785cb6d724a68ede98fa0e4ff6b30a9..b00dc9d3569d177570203dc21d602760fc055ece 100644 (file)
@@ -104,7 +104,7 @@ Do not wait for the server response after sending QUIT.
 The TCP port to be used when connecting to a LMTP server.  Used as
 backup if the \fBlmtp\fR service is not found in \fBservices\fR(4).
 .SH "Authentication controls"
-.IP \fBlmtp_enable_sasl_auth\fR
+.IP \fBlmtp_sasl_auth_enable\fR
 Enable per-session authentication as per RFC 2554 (SASL).
 By default, Postfix is built without SASL support.
 .IP \fBlmtp_sasl_password_maps\fR
index c947ab058bba27cb3088880958a3119c5fda155f..65e566e480c9872dcf3aec7f4b79c29c5a83a217 100644 (file)
@@ -80,8 +80,8 @@ The result is compatible with the address parsing of command-line
 recipients by the Postfix \fBsendmail\fR mail submission command.
 .sp
 The \fBq\fR flag affects only entire addresses, not the partial
-address information from the \fB$user\fR, \fBextension\fR or
-\fBmailbox\fR command-line macros.
+address information from the \fB$user\fR, \fB$extension\fR or
+\fB$mailbox\fR command-line macros.
 .IP \fBu\fR
 Fold the command-line \fB$recipient\fR address localpart (text to
 the left of the right-most \fB@\fR character) to lower case.
diff --git a/postfix/man/man8/qmqpd.8 b/postfix/man/man8/qmqpd.8
new file mode 100644 (file)
index 0000000..f2b0c31
--- /dev/null
@@ -0,0 +1,120 @@
+.TH QMQPD 8 
+.ad
+.fi
+.SH NAME
+qmqpd
+\-
+Postfix QMQP server
+.SH SYNOPSIS
+.na
+.nf
+\fBqmqpd\fR [generic Postfix daemon options]
+.SH DESCRIPTION
+.ad
+.fi
+The Postfix QMQP server receives one message per connection.
+Each message is piped through the \fBcleanup\fR(8)
+daemon, and is placed into the \fBincoming\fR queue as one
+single queue file.  The program expects to be run from the
+\fBmaster\fR(8) process manager.
+
+The QMQP server implements one access policy: only explicitly
+authorized client hosts are allowed to use the service.
+.SH SECURITY
+.na
+.nf
+.ad
+.fi
+The QMQP server is moderately security-sensitive. It talks to QMQP
+clients and to DNS servers on the network. The QMQP server can be
+run chrooted at fixed low privilege.
+.SH DIAGNOSTICS
+.ad
+.fi
+Problems and transactions are logged to \fBsyslogd\fR(8).
+.SH BUGS
+.ad
+.fi
+The QMQP protocol provides only one server reply per message
+delivery. It is therefore not possible to reject individual
+recipients.
+
+The QMQP protocol requires the server to receive the entire
+message before replying. If a message is malformed, or if any
+netstring component is longer than acceptable, Postfix replies
+immediately and closes the connection. It is left up to the
+client to handle the situation.
+.SH CONFIGURATION PARAMETERS
+.na
+.nf
+.ad
+.fi
+The following \fBmain.cf\fR parameters are especially relevant to
+this program. See the Postfix \fBmain.cf\fR file for syntax details
+and for default values. Use the \fBpostfix reload\fR command after
+a configuration change.
+.SH Miscellaneous
+.ad
+.fi
+.IP \fBalways_bcc\fR
+Address to send a copy of each message that enters the system.
+.IP \fBdebug_peer_level\fR
+Increment in verbose logging level when a remote host matches a
+pattern in the \fBdebug_peer_list\fR parameter.
+.IP \fBdebug_peer_list\fR
+List of domain or network patterns. When a remote host matches
+a pattern, increase the verbose logging level by the amount
+specified in the \fBdebug_peer_level\fR parameter.
+.IP \fBhopcount_limit\fR
+Limit the number of \fBReceived:\fR message headers.
+.IP \fBqmqpd_authorized_clients\fR
+A list of domain or network patterns that specifies what
+clients are allowed to use the service.
+.IP \fBqmqpd_timeout\fR
+Limit the time to send a server response and to receive a client
+request.
+.IP \fBsoft_bounce\fR
+Change hard (D) reject responses into soft (Z) reject responses.
+This can be useful for testing purposes.
+.SH "Content inspection controls"
+.IP \fBcontent_filter\fR
+The name of a mail delivery transport that filters mail and that
+either bounces mail or re-injects the result back into Postfix.
+This parameter uses the same syntax as the right-hand side of
+a Postfix transport table.
+.SH "Resource controls"
+.ad
+.fi
+.IP \fBline_length_limit\fR
+Limit the amount of memory in bytes used for the handling of
+partial input lines, and the length of sender and recipient
+addresses that are received from client.
+.IP \fBmessage_size_limit\fR
+Limit the total size in bytes of a message, including on-disk
+storage for sender and recipient address information.
+.SH Tarpitting
+.ad
+.fi
+.IP \fBqmqpd_error_sleep_time\fR
+Time to wait in seconds before informing the client of
+a problem. This slows down run-away errors.
+.SH SEE ALSO
+.na
+.nf
+http://cr.yp.to/proto/qmqp.html, QMQP protocol
+cleanup(8) message canonicalization
+master(8) process manager
+syslogd(8) system logging
+.SH LICENSE
+.na
+.nf
+.ad
+.fi
+The Secure Mailer license must be distributed with this software.
+.SH AUTHOR(S)
+.na
+.nf
+Wietse Venema
+IBM T.J. Watson Research
+P.O. Box 704
+Yorktown Heights, NY 10598, USA
index 3fd07aa090127fe2392baa28842cbb2b96115911..f05a893c1727dd2fa83e8ffa691c558f22f74cd4 100644 (file)
@@ -111,6 +111,9 @@ postmaster with transcripts of SMTP sessions with protocol errors.
 Always send EHLO at the start of a connection.
 .IP \fBsmtp_never_send_ehlo\fR
 Never send EHLO at the start of a connection.
+.IP \fBsmtp_break_lines\fR
+Break lines > \fB$line_length_limit\fR into multiple shorter lines.
+Some SMTP servers misbehave on long lines.
 .IP \fBsmtp_skip_4xx_greeting\fR
 Skip servers that greet us with a 4xx status code.
 .IP \fBsmtp_skip_5xx_greeting\fR
index d9c57a9c15038f0a592fe38edf778d81c4093def..3e228063c327935d3bbccb2e3aa57b580aac4016 100755 (executable)
@@ -19,6 +19,7 @@ exec sed '
        s/[<bB>]*pickup[</bB>]*(8)/<a href="pickup.8.html">&<\/a>/
        s/[<bB>]*pipe[</bB>]*(8)/<a href="pipe.8.html">&<\/a>/
        s/[<bB>]*qmgr[</bB>]*(8)/<a href="qmgr.8.html">&<\/a>/
+       s/[<bB>]*qmqpd[</bB>]*(8)/<a href="qmqpd.8.html">&<\/a>/
        s/[<bB>]*showq[</bB>]*(8)/<a href="showq.8.html">&<\/a>/
        s/[<bB>]*smtp[</bB>]*(8)/<a href="smtp.8.html">&<\/a>/
        s/[<bB>]*smtpd[</bB>]*(8)/<a href="smtpd.8.html">&<\/a>/
index 053ea4e9251b14be94cd63fec06c4b87d8142e44..b14c251a302aacfa883dabd9ac15b822c87aed2a 100644 (file)
@@ -54,7 +54,7 @@ HDRS  = been_here.h bounce.h canon_addr.h cleanup_user.h clnt_stream.h \
        recipient_list.h record.h resolve_clnt.h resolve_local.h \
        rewrite_clnt.h sent.h smtp_stream.h split_addr.h string_list.h \
        sys_exits.h timed_ipc.h tok822.h xtext.h bounce_log.h flush_clnt.h \
-       mbox_conf.h mbox_open.h abounce.h
+       mbox_conf.h mbox_open.h abounce.h qmqp_proto.h
 TESTSRC        = rec2stream.c stream2rec.c recdump.c
 WARN   = -W -Wformat -Wimplicit -Wmissing-prototypes \
        -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
index 452fc443cbfb9f716418f1c16c7f358ebac5d48e..1cfaf8755a8123c9d8bc2b342ecef951c51dcf95 100644 (file)
@@ -714,6 +714,10 @@ extern char *var_smtp_bind_addr;
 #define DEF_SMTP_RAND_ADDR     1
 extern bool var_smtp_rand_addr;
 
+#define VAR_SMTP_BREAK_LINES   "smtp_break_lines"
+#define DEF_SMTP_BREAK_LINES   1
+extern bool var_smtp_break_lines;
+
  /*
   * SMTP server. The soft error limit determines how many errors an SMTP
   * client may make before we start to slow down; the hard error limit
@@ -1252,6 +1256,21 @@ extern char *var_virt_mailbox_lock;
 #define DEF_SYSLOG_NAME                        "postfix"
 extern char *var_syslog_name;
 
+ /*
+  * QMQPD
+  */
+#define VAR_QMQPD_CLIENTS              "qmqpd_authorized_clients"
+#define DEF_QMQPD_CLIENTS              ""
+extern char *var_qmqpd_clients;
+
+#define VAR_QMTPD_TMOUT                        "qmqpd_timeout"
+#define DEF_QMTPD_TMOUT                        "300s"
+extern int var_qmqpd_timeout;
+
+#define VAR_QMTPD_ERR_SLEEP            "qmqpd_error_delay"
+#define DEF_QMTPD_ERR_SLEEP            "5s"
+extern int var_qmqpd_err_sleep;
+
 /* LICENSE
 /* .ad
 /* .fi
index 9e8e6432380b8ba5247b3749e5e3209b29f9fa8c..54916c8b59fd1a2b8ee9477d4ea2221afc4f909c 100644 (file)
@@ -76,7 +76,7 @@
 /*     mail_queue_rename() renames a queue file. A non-zero result
 /*     means the operation failed.
 /*
-/*     mail_queue_remove() renames the named queue file. A non-zero result
+/*     mail_queue_remove() removes the named queue file. A non-zero result
 /*     means the operation failed.
 /*
 /*     mail_queue_name_ok() validates a mail queue name and returns
index eac4c3b73a240f440ef0e3e53c41f312d2f58672..acd597ae939201be2d519a648dbc6c07fd7c7c81 100644 (file)
@@ -15,7 +15,7 @@
   * Version of this program.
   */
 #define VAR_MAIL_VERSION       "mail_version"
-#define DEF_MAIL_VERSION       "Snapshot-20010610"
+#define DEF_MAIL_VERSION       "Snapshot-20010707"
 extern char *var_mail_version;
 
 /* LICENSE
diff --git a/postfix/src/global/qmqp_proto.h b/postfix/src/global/qmqp_proto.h
new file mode 100644 (file)
index 0000000..8ad9e2a
--- /dev/null
@@ -0,0 +1,27 @@
+/*++
+/* NAME
+/*     qmqpd 3h
+/* SUMMARY
+/*     QMQP protocol
+/* SYNOPSIS
+/*     include <qmqpd_proto.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * QMQP protocol status codes.
+  */
+#define QMQP_STAT_OK   'K'             /* success */
+#define QMQP_STAT_RETRY        'Z'             /* recoverable error */
+#define QMQP_STAT_HARD 'D'             /* unrecoverable error */
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
index 0b538f8fbd0fcaa99360a1c053e7b9d3a113e60d..e943fa855c794348a23cfddde69d66eeb97f79e9 100644 (file)
@@ -88,7 +88,7 @@
 /*     The TCP port to be used when connecting to a LMTP server.  Used as
 /*     backup if the \fBlmtp\fR service is not found in \fBservices\fR(4).
 /* .SH "Authentication controls"
-/* .IP \fBlmtp_enable_sasl_auth\fR
+/* .IP \fBlmtp_sasl_auth_enable\fR
 /*     Enable per-session authentication as per RFC 2554 (SASL).
 /*     By default, Postfix is built without SASL support.
 /* .IP \fBlmtp_sasl_password_maps\fR
index f35d8249b17a4caa65e850dafdd2d5a71b70569e..7cf98f77cc697d633d21f3ad5cc7ef001f31592b 100644 (file)
@@ -498,7 +498,7 @@ static int lmtp_loop(LMTP_STATE *state, int send_state, int recv_state)
                     */
                case LMTP_STATE_RCPT:
                    if (!mail_from_rejected) {
-#ifndef notRFC821_SYNTAX
+#ifdef notdef
                        if (resp->code == 552)
                            resp->code = 452;
 #endif
index 854590c111a63e0f32eb4242e420396d022cf993..e49c9137ce15164654150ded543716d2adf18890 100644 (file)
@@ -276,7 +276,7 @@ int     deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
     SET_USER_ATTR(usr_attr, mbox_pwd, state.level);
 
     /*
-     * Deliver to mailbox or to external command.
+     * Deliver to mailbox, maildir or to external command.
      */
 #define LAST_CHAR(s) (s[strlen(s) - 1])
 
@@ -286,6 +286,11 @@ int     deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
        path = concatenate(usr_attr.home, "/", var_home_mailbox, (char *) 0);
        status = deliver_maildir(state, usr_attr, path);
        myfree(path);
+    } else if (*var_mail_spool_dir && LAST_CHAR(var_mail_spool_dir) == '/') {
+       path = concatenate(var_mail_spool_dir, state.msg_attr.user,
+                          "/", (char *) 0);
+       status = deliver_maildir(state, usr_attr, path);
+       myfree(path);
     } else
        status = deliver_mailbox_file(state, usr_attr);
 
index d32777c3d9a6a3707f83a6d82f4c938b05937b2f..6503905db8b512b9e50178580ffaff7b64378634 100644 (file)
@@ -70,8 +70,8 @@
 /*     recipients by the Postfix \fBsendmail\fR mail submission command.
 /* .sp
 /*     The \fBq\fR flag affects only entire addresses, not the partial
-/*     address information from the \fB$user\fR, \fBextension\fR or
-/*     \fBmailbox\fR command-line macros.
+/*     address information from the \fB$user\fR, \fB$extension\fR or
+/*     \fB$mailbox\fR command-line macros.
 /* .IP \fBu\fR
 /*     Fold the command-line \fB$recipient\fR address localpart (text to 
 /*     the left of the right-most \fB@\fR character) to lower case.
diff --git a/postfix/src/qmqpd/.indent.pro b/postfix/src/qmqpd/.indent.pro
new file mode 120000 (symlink)
index 0000000..5c837ec
--- /dev/null
@@ -0,0 +1 @@
+../../.indent.pro
\ No newline at end of file
diff --git a/postfix/src/qmqpd/.printfck b/postfix/src/qmqpd/.printfck
new file mode 100644 (file)
index 0000000..66016ed
--- /dev/null
@@ -0,0 +1,25 @@
+been_here_xt   2       0
+bounce_append  5       0
+cleanup_out_format     1       0
+defer_append   5       0
+mail_command   1       0
+mail_print     1       0
+msg_error      0       0
+msg_fatal      0       0
+msg_info       0       0
+msg_panic      0       0
+msg_warn       0       0
+opened         4       0
+post_mail_fprintf      1       0
+qmgr_message_bounce    2       0
+rec_fprintf    2       0
+sent   4       0
+smtp_cmd       1       0
+smtp_mesg_fail 2       0
+smtp_printf    1       0
+smtp_rcpt_fail 3       0
+smtp_site_fail 2       0
+udp_syslog     1       0
+vstream_fprintf        1       0
+vstream_printf 0       0
+vstring_sprintf        1       0
diff --git a/postfix/src/qmqpd/Makefile.in b/postfix/src/qmqpd/Makefile.in
new file mode 100644 (file)
index 0000000..117cf9e
--- /dev/null
@@ -0,0 +1,113 @@
+SHELL  = /bin/sh
+SRCS   = qmqpd.c qmqpd_state.c qmqpd_peer.c
+OBJS   = qmqpd.o qmqpd_state.o qmqpd_peer.o
+HDRS   =
+TESTSRC        =
+WARN   = -W -Wformat -Wimplicit -Wmissing-prototypes \
+       -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
+       -Wunused
+DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
+CFLAGS = $(DEBUG) $(OPT) $(DEFS)
+TESTPROG= qmqpd_token qmqpd_check
+PROG   = qmqpd
+INC_DIR        = ../../include
+LIBS   = ../../lib/libmaster.a ../../lib/libglobal.a ../../lib/libdns.a ../../lib/libutil.a
+
+.c.o:; $(CC) $(CFLAGS) -c $*.c
+
+$(PROG): $(OBJS) $(LIBS)
+       $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(SYSLIBS)
+
+Makefile: Makefile.in
+       (set -e; echo "# DO NOT EDIT"; $(OPTS) $(SHELL) ../../makedefs && cat $?) >$@
+
+test:  $(TESTPROG)
+
+update: ../../libexec/$(PROG)
+
+../../libexec/$(PROG): $(PROG)
+       cp $(PROG) ../../libexec
+
+SMTPD_CHECK_OBJ = qmqpd_state.o qmqpd_peer.o
+
+qmqpd_token: qmqpd_token.c $(LIBS)
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIBS) $(SYSLIBS)
+
+qmqpd_check: qmqpd_check.c $(SMTPD_CHECK_OBJ) $(LIBS)
+       mv $@.o junk
+       $(CC) $(CFLAGS) -DTEST -o $@ qmqpd_check.c $(SMTPD_CHECK_OBJ) \
+               $(LIBS) $(SYSLIBS)
+       mv junk $@.o
+
+printfck: $(OBJS) $(PROG)
+       rm -rf printfck
+       mkdir printfck
+       cp *.h printfck
+       sed '1,/^# do not edit/!d' Makefile >printfck/Makefile
+       set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done
+       cd printfck; make "INC_DIR=../../../include" `cd ..; ls *.o`
+
+lint:
+       lint $(DEFS) $(SRCS) $(LINTFIX)
+
+clean:
+       rm -f *.o *core $(PROG) $(TESTPROG) junk *.db *.out *.tmp
+       rm -rf printfck
+
+tidy:  clean
+
+depend: $(MAKES)
+       (sed '1,/^# do not edit/!d' Makefile.in; \
+       set -e; for i in [a-z][a-z0-9]*.c; do \
+           $(CC) -E $(DEFS) $(INCL) $$i | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \
+           -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' -e 'p' -e '}'; \
+       done) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in
+       @$(EXPORT) make -f Makefile.in Makefile 1>&2
+
+tests:
+
+# do not edit below this line - it is generated by 'make depend'
+qmqpd.o: qmqpd.c
+qmqpd.o: ../../include/sys_defs.h
+qmqpd.o: ../../include/msg.h
+qmqpd.o: ../../include/mymalloc.h
+qmqpd.o: ../../include/vstring.h
+qmqpd.o: ../../include/vbuf.h
+qmqpd.o: ../../include/vstream.h
+qmqpd.o: ../../include/netstring.h
+qmqpd.o: ../../include/dict.h
+qmqpd.o: ../../include/argv.h
+qmqpd.o: ../../include/mail_params.h
+qmqpd.o: ../../include/record.h
+qmqpd.o: ../../include/rec_type.h
+qmqpd.o: ../../include/mail_proto.h
+qmqpd.o: ../../include/iostuff.h
+qmqpd.o: ../../include/cleanup_user.h
+qmqpd.o: ../../include/mail_date.h
+qmqpd.o: ../../include/mail_conf.h
+qmqpd.o: ../../include/debug_peer.h
+qmqpd.o: ../../include/mail_stream.h
+qmqpd.o: ../../include/namadr_list.h
+qmqpd.o: ../../include/quote_822_local.h
+qmqpd.o: ../../include/mail_server.h
+qmqpd.o: qmqpd.h
+qmqpd_peer.o: qmqpd_peer.c
+qmqpd_peer.o: ../../include/sys_defs.h
+qmqpd_peer.o: ../../include/msg.h
+qmqpd_peer.o: ../../include/mymalloc.h
+qmqpd_peer.o: ../../include/valid_hostname.h
+qmqpd_peer.o: ../../include/stringops.h
+qmqpd_peer.o: ../../include/vstring.h
+qmqpd_peer.o: ../../include/vbuf.h
+qmqpd_peer.o: qmqpd.h
+qmqpd_peer.o: ../../include/vstream.h
+qmqpd_peer.o: ../../include/mail_stream.h
+qmqpd_state.o: qmqpd_state.c
+qmqpd_state.o: ../../include/sys_defs.h
+qmqpd_state.o: ../../include/mymalloc.h
+qmqpd_state.o: ../../include/vstream.h
+qmqpd_state.o: ../../include/vbuf.h
+qmqpd_state.o: ../../include/vstring.h
+qmqpd_state.o: ../../include/mail_stream.h
+qmqpd_state.o: ../../include/cleanup_user.h
+qmqpd_state.o: qmqpd.h
diff --git a/postfix/src/qmqpd/qmqpd.c b/postfix/src/qmqpd/qmqpd.c
new file mode 100644 (file)
index 0000000..281e472
--- /dev/null
@@ -0,0 +1,645 @@
+/*++
+/* NAME
+/*     qmqpd 8
+/* SUMMARY
+/*     Postfix QMQP server
+/* SYNOPSIS
+/*     \fBqmqpd\fR [generic Postfix daemon options]
+/* DESCRIPTION
+/*     The Postfix QMQP server receives one message per connection.
+/*     Each message is piped through the \fBcleanup\fR(8)
+/*     daemon, and is placed into the \fBincoming\fR queue as one
+/*     single queue file.  The program expects to be run from the
+/*     \fBmaster\fR(8) process manager.
+/*
+/*     The QMQP server implements one access policy: only explicitly
+/*     authorized client hosts are allowed to use the service.
+/* SECURITY
+/* .ad
+/* .fi
+/*     The QMQP server is moderately security-sensitive. It talks to QMQP
+/*     clients and to DNS servers on the network. The QMQP server can be
+/*     run chrooted at fixed low privilege.
+/* DIAGNOSTICS
+/*     Problems and transactions are logged to \fBsyslogd\fR(8).
+/* BUGS
+/*     The QMQP protocol provides only one server reply per message
+/*     delivery. It is therefore not possible to reject individual
+/*     recipients.
+/*
+/*     The QMQP protocol requires the server to receive the entire
+/*     message before replying. If a message is malformed, or if any
+/*     netstring component is longer than acceptable, Postfix replies
+/*     immediately and closes the connection. It is left up to the
+/*     client to handle the situation.
+/* CONFIGURATION PARAMETERS
+/* .ad
+/* .fi
+/*     The following \fBmain.cf\fR parameters are especially relevant to
+/*     this program. See the Postfix \fBmain.cf\fR file for syntax details
+/*     and for default values. Use the \fBpostfix reload\fR command after
+/*     a configuration change.
+/* .SH Miscellaneous
+/* .ad
+/* .fi
+/* .IP \fBalways_bcc\fR
+/*     Address to send a copy of each message that enters the system.
+/* .IP \fBdebug_peer_level\fR
+/*     Increment in verbose logging level when a remote host matches a
+/*     pattern in the \fBdebug_peer_list\fR parameter.
+/* .IP \fBdebug_peer_list\fR
+/*     List of domain or network patterns. When a remote host matches
+/*     a pattern, increase the verbose logging level by the amount
+/*     specified in the \fBdebug_peer_level\fR parameter.
+/* .IP \fBhopcount_limit\fR
+/*     Limit the number of \fBReceived:\fR message headers.
+/* .IP \fBqmqpd_authorized_clients\fR
+/*      A list of domain or network patterns that specifies what
+/*     clients are allowed to use the service.
+/* .IP \fBqmqpd_timeout\fR
+/*     Limit the time to send a server response and to receive a client
+/*     request.
+/* .IP \fBsoft_bounce\fR
+/*     Change hard (D) reject responses into soft (Z) reject responses.
+/*     This can be useful for testing purposes.
+/* .SH "Content inspection controls"
+/* .IP \fBcontent_filter\fR
+/*     The name of a mail delivery transport that filters mail and that
+/*     either bounces mail or re-injects the result back into Postfix.
+/*     This parameter uses the same syntax as the right-hand side of
+/*     a Postfix transport table.
+/* .SH "Resource controls"
+/* .ad
+/* .fi
+/* .IP \fBline_length_limit\fR
+/*     Limit the amount of memory in bytes used for the handling of
+/*     partial input lines, and the length of sender and recipient
+/*     addresses that are received from client.
+/* .IP \fBmessage_size_limit\fR
+/*     Limit the total size in bytes of a message, including on-disk
+/*     storage for sender and recipient address information.
+/* .SH Tarpitting
+/* .ad
+/* .fi
+/* .IP \fBqmqpd_error_sleep_time\fR
+/*     Time to wait in seconds before informing the client of
+/*     a problem. This slows down run-away errors.
+/* SEE ALSO
+/*     http://cr.yp.to/proto/qmqp.html, QMQP protocol
+/*     cleanup(8) message canonicalization
+/*     master(8) process manager
+/*     syslogd(8) system logging
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <netstring.h>
+#include <dict.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <record.h>
+#include <rec_type.h>
+#include <mail_proto.h>
+#include <cleanup_user.h>
+#include <mail_date.h>
+#include <mail_conf.h>
+#include <debug_peer.h>
+#include <mail_stream.h>
+#include <namadr_list.h>
+#include <quote_822_local.h>
+
+/* Single-threaded server skeleton. */
+
+#include <mail_server.h>
+
+/* Application-specific */
+
+#include <qmqpd.h>
+
+ /*
+  * Tunable parameters. Make sure that there is some bound on the length of a
+  * netstring, so that the mail system stays in control even when a malicious
+  * client sends netstrings of unreasonable length. The recipient count limit
+  * is enforced by the message size limit.
+  */
+int     var_qmqpd_timeout;
+int     var_qmqpd_err_sleep;
+char   *var_always_bcc;
+char   *var_filter_xport;
+char   *var_qmqpd_clients;
+
+ /*
+  * Silly little macros.
+  */
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+#define DO_LOG         1
+#define DONT_LOG       0
+
+ /*
+  * Access control. This service should be exposed only to explicitly
+  * authorized clients. There is no default authorization.
+  */
+static NAMADR_LIST *qmqpd_clients;
+
+/* qmqpd_open_file - open a queue file */
+
+static void qmqpd_open_file(QMQPD_STATE *state)
+{
+
+    /*
+     * Connect to the cleanup server. Log client name/address with queue ID.
+     */
+    state->dest = mail_stream_service(MAIL_CLASS_PRIVATE, MAIL_SERVICE_CLEANUP);
+    if (state->dest == 0
+       || mail_print(state->dest->stream, "%d", CLEANUP_FLAG_FILTER) != 0)
+       msg_fatal("unable to connect to the %s %s service",
+                 MAIL_CLASS_PRIVATE, MAIL_SERVICE_CLEANUP);
+    state->cleanup = state->dest->stream;
+    state->queue_id = mystrdup(state->dest->id);
+    msg_info("%s: client=%s", state->queue_id, state->namaddr);
+
+    /*
+     * Record the time of arrival. Optionally, enable content filtering (not
+     * bloody likely, but present for the sake of consistency with all other
+     * Postfix points of entrance).
+     */
+    rec_fprintf(state->cleanup, REC_TYPE_TIME, "%ld", state->time);
+    if (*var_filter_xport)
+       rec_fprintf(state->cleanup, REC_TYPE_FILT, "%s", var_filter_xport);
+}
+
+/* qmqpd_read_content - receive message content */
+
+static void qmqpd_read_content(QMQPD_STATE *state)
+{
+    state->where = "receiving message content";
+    netstring_get(state->client, state->message, var_message_limit);
+}
+
+/* qmqpd_copy_sender - copy envelope sender */
+
+static void qmqpd_copy_sender(QMQPD_STATE *state)
+{
+    state->where = "receiving sender address";
+    netstring_get(state->client, state->buf, var_line_limit);
+    if (state->err == CLEANUP_STAT_OK
+       && REC_PUT_BUF(state->cleanup, REC_TYPE_FROM, state->buf) < 0)
+       state->err = CLEANUP_STAT_WRITE;
+    state->sender = mystrndup(STR(state->buf), LEN(state->buf));
+}
+
+/* qmqpd_copy_recipients - copy message recipients */
+
+static void qmqpd_copy_recipients(QMQPD_STATE *state)
+{
+    int     ch;
+
+    /*
+     * Remember the first recipient. We are done when we read the over-all
+     * netstring terminator.
+     * 
+     * XXX This approach violates abstractions, but it is a heck of a lot more
+     * convenient than counting the over-all byte count down to zero, like
+     * qmail does.
+     */
+    state->where = "receiving recipient address";
+    while ((ch = VSTREAM_GETC(state->client)) != ',') {
+       vstream_ungetc(state->client, ch);
+       netstring_get(state->client, state->buf, var_line_limit);
+       if (state->err == CLEANUP_STAT_OK
+           && REC_PUT_BUF(state->cleanup, REC_TYPE_RCPT, state->buf) < 0)
+           state->err = CLEANUP_STAT_WRITE;
+       state->rcpt_count++;
+       if (state->recipient == 0)
+           state->recipient = mystrndup(STR(state->buf), LEN(state->buf));
+    }
+
+    /*
+     * Append the optional recipient who is copied on all mail.
+     */
+    if (*var_always_bcc)
+       rec_fputs(state->cleanup, REC_TYPE_RCPT, var_always_bcc);
+}
+
+/* qmqpd_next_line - get line from buffer, return last char, newline, or -1 */
+
+static int qmqpd_next_line(VSTRING *message, char **start, int *len,
+                                  char **next)
+{
+    char   *beyond = STR(message) + LEN(message);
+    char   *enough = *next + var_line_limit;
+    char   *cp;
+
+    /*
+     * Stop at newline or at some limit. Don't look beyond the end of the
+     * buffer.
+     */
+#define UCHARPTR(x) ((unsigned char *) (x))
+
+    for (cp = *start = *next; /* void */ ; cp++) {
+       if (cp >= beyond)
+           return ((*len = (*next = cp) - *start) > 0 ? UCHARPTR(cp)[-1] : -1);
+       if (*cp == '\n')
+           return ((*len = cp - *start), (*next = cp + 1), '\n');
+       if (cp >= enough)
+           return ((*len = cp - *start), (*next = cp), UCHARPTR(cp)[-1]);
+    }
+}
+
+/* qmqpd_write_content - write the message content segment */
+
+static void qmqpd_write_content(QMQPD_STATE *state)
+{
+    char   *start;
+    char   *next;
+    int     len;
+    int     rec_type;
+    int     first = 1;
+    int     ch;
+
+    /*
+     * Start the message content segment. Prepend our own Received: header to
+     * the message content. List the recipient only when a message has one
+     * recipient. Otherwise, don't list the recipient to avoid revealing Bcc:
+     * recipients that are supposed to be invisible.
+     */
+    rec_fputs(state->cleanup, REC_TYPE_MESG, "");
+    rec_fprintf(state->cleanup, REC_TYPE_NORM, "Received: from %s (%s [%s])",
+               state->name, state->name, state->addr);
+    if (state->rcpt_count == 1 && state->recipient) {
+       rec_fprintf(state->cleanup, REC_TYPE_NORM,
+                   "\tby %s (%s) with %s id %s",
+                   var_myhostname, var_mail_name,
+                   state->protocol, state->queue_id);
+       quote_822_local(state->buf, state->recipient);
+       rec_fprintf(state->cleanup, REC_TYPE_NORM,
+                "\tfor <%s>; %s", STR(state->buf), mail_date(state->time));
+    } else {
+       rec_fprintf(state->cleanup, REC_TYPE_NORM,
+                   "\tby %s (%s) with %s",
+                   var_myhostname, var_mail_name, state->protocol);
+       rec_fprintf(state->cleanup, REC_TYPE_NORM,
+                   "\tid %s; %s", state->queue_id, mail_date(state->time));
+    }
+#ifdef RECEIVED_ENVELOPE_FROM
+    quote_822_local(state->buf, state->sender);
+    rec_fprintf(state->cleanup, REC_TYPE_NORM,
+               "\t(envelope-from <%s>)", STR(state->buf));
+#endif
+
+    /*
+     * Write the message content.
+     * 
+     * XXX Force an empty record when the queue file content begins with
+     * whitespace, so that it won't be considered as being part of our own
+     * Received: header. What an ugly Kluge.
+     * 
+     * XXX Deal with UNIX-style From_ lines at the start of message content just
+     * in case.
+     */
+    for (next = STR(state->message); /* void */ ; /* void */ ) {
+       if ((ch = qmqpd_next_line(state->message, &start, &len, &next)) < 0)
+           break;
+       if (ch == '\n')
+           rec_type = REC_TYPE_NORM;
+       else
+           rec_type = REC_TYPE_CONT;
+       if (first) {
+           if (strncmp(start + strspn(start, ">"), "From ", 5) == 0) {
+               rec_fprintf(state->cleanup, rec_type,
+                           "Mailbox-Line: %*s", len, start);
+               continue;
+           }
+           first = 0;
+           if (len > 0 && ISSPACE(start[0]))
+               rec_put(state->cleanup, REC_TYPE_NORM, "", 0);
+       }
+       if (rec_put(state->cleanup, rec_type, start, len) < 0) {
+           state->err = CLEANUP_STAT_WRITE;
+           return;
+       }
+    }
+}
+
+/* qmqpd_close_file - close queue file */
+
+static void qmqpd_close_file(QMQPD_STATE *state)
+{
+
+    /*
+     * Send the end-of-segment markers.
+     */
+    if (state->err == CLEANUP_STAT_OK)
+       if (rec_fputs(state->cleanup, REC_TYPE_XTRA, "") < 0
+           || rec_fputs(state->cleanup, REC_TYPE_END, "") < 0
+           || vstream_fflush(state->cleanup))
+           state->err = CLEANUP_STAT_WRITE;
+
+    /*
+     * Finish the queue file or finish the cleanup conversation.
+     */
+    if (state->err == 0)
+       state->err = mail_stream_finish(state->dest);
+    else
+       mail_stream_cleanup(state->dest);
+    state->dest = 0;
+}
+
+/* qmqpd_reply - send status to client and optionally log message */
+
+static void qmqpd_reply(QMQPD_STATE *state, int log_message,
+                               int status_code, const char *fmt,...)
+{
+    va_list ap;
+
+    /*
+     * Optionally change hard errors into retryable ones. Send the reply and
+     * optionally log it. Always insert a delay before reporting a problem.
+     * This slows down software run-away conditions.
+     */
+    if (status_code == QMQPD_STAT_HARD && var_soft_bounce)
+       status_code = QMQPD_STAT_RETRY;
+    VSTRING_RESET(state->buf);
+    VSTRING_ADDCH(state->buf, status_code);
+    va_start(ap, fmt);
+    vstring_vsprintf_append(state->buf, fmt, ap);
+    va_end(ap);
+    NETSTRING_PUT_BUF(state->client, state->buf);
+    if (log_message)
+       (status_code == QMQPD_STAT_OK ? msg_info : msg_warn) ("%s: %s: %s",
+                     state->queue_id, state->namaddr, STR(state->buf) + 1);
+    if (status_code != QMQPD_STAT_OK)
+       sleep(var_qmqpd_err_sleep);
+    netstring_fflush(state->client);
+}
+
+/* qmqpd_send_status - send mail transaction completion status */
+
+static int qmqpd_send_status(QMQPD_STATE *state)
+{
+
+    /*
+     * One message may suffer from multiple errors, so complain only about
+     * the most severe error.
+     */
+    state->where = "sending completion status";
+
+    if (state->err == CLEANUP_STAT_OK) {
+       qmqpd_reply(state, DONT_LOG, QMQPD_STAT_OK,
+                   "Ok: queued as %s", state->queue_id);
+    } else if ((state->err & CLEANUP_STAT_BAD) != 0) {
+       qmqpd_reply(state, DO_LOG, QMQPD_STAT_RETRY,
+                   "Error: internal error %d", state->err);
+    } else if ((state->err & CLEANUP_STAT_SIZE) != 0) {
+       qmqpd_reply(state, DO_LOG, QMQPD_STAT_HARD,
+                   "Error: message too large");
+    } else if ((state->err & CLEANUP_STAT_HOPS) != 0) {
+       qmqpd_reply(state, DO_LOG, QMQPD_STAT_HARD,
+                   "Error: too many hops");
+    } else if ((state->err & CLEANUP_STAT_CONT) != 0) {
+       qmqpd_reply(state, DO_LOG, QMQPD_STAT_HARD,
+                   "Error: content rejected");
+    } else if ((state->err & CLEANUP_STAT_WRITE) != 0) {
+       qmqpd_reply(state, DO_LOG, QMQPD_STAT_RETRY,
+                   "Error: queue file write error");
+    } else if ((state->err & CLEANUP_STAT_RCPT) != 0) {
+       qmqpd_reply(state, DO_LOG, QMQPD_STAT_HARD,
+                   "Error: no recipients specified");
+    } else {
+       msg_panic("qmqpd_send_status: unknown status %d", state->err);
+    }
+}
+
+/* qmqpd_receive - receive QMQP message+sender+recipients */
+
+static int qmqpd_receive(QMQPD_STATE *state)
+{
+
+    /*
+     * Open a queue file. This must be first so that we can simplify the
+     * error logging and always include the queue ID information.
+     */
+    qmqpd_open_file(state);
+
+    /*
+     * Read and ignore the over-all netstring length indicator.
+     */
+    state->where = "receiving QMQP packet header";
+    (void) netstring_get_length(state->client);
+
+    /*
+     * XXX Read the message content into memory, because Postfix expects to
+     * store the sender before storing the message content. Fixing that
+     * requires changes to pickup, cleanup, qmgr, and perhaps elsewhere, so
+     * that will have to happen later when I have more time. However, QMQP is
+     * used for mailing list distribution, so the bulk of the volume is
+     * expected to be not message content but recipients, and recipients are
+     * not accumulated in memory.
+     */
+    qmqpd_read_content(state);
+
+    /*
+     * Read and write the envelope sender.
+     */
+    qmqpd_copy_sender(state);
+
+    /*
+     * Read and write the envelope recipients, including the optional big
+     * brother recipient.
+     */
+    qmqpd_copy_recipients(state);
+
+    /*
+     * Start the message content segment, prepend our own Received: header,
+     * and write the message content.
+     */
+    qmqpd_write_content(state);
+
+    /*
+     * Close the queue file.
+     */
+    qmqpd_close_file(state);
+
+    /*
+     * Report the completion status to the client.
+     */
+    qmqpd_send_status(state);
+}
+
+/* qmqpd_proto - speak the QMQP "protocol" */
+
+static void qmqpd_proto(QMQPD_STATE *state)
+{
+    int     status;
+
+    netstring_setup(state->client, var_qmqpd_timeout);
+
+    switch (status = vstream_setjmp(state->client)) {
+
+    default:
+       msg_panic("qmqpd_proto: unknown status %d", status);
+
+    case NETSTRING_ERR_EOF:
+       state->reason = "lost connection";
+       break;
+
+    case NETSTRING_ERR_TIME:
+       state->reason = "read/write timeout";
+       break;
+
+    case NETSTRING_ERR_FORMAT:
+       state->reason = "netstring format error";
+       if (vstream_setjmp(state->client) == 0)
+           if (state->reason && state->where)
+               qmqpd_reply(state, DONT_LOG, QMQPD_STAT_HARD, "%s while %s",
+                           state->reason, state->where);
+       break;
+
+    case NETSTRING_ERR_SIZE:
+       state->reason = "netstring length exceeds storage limit";
+       if (vstream_setjmp(state->client) == 0)
+           if (state->reason && state->where)
+               qmqpd_reply(state, DONT_LOG, QMQPD_STAT_HARD, "%s while %s",
+                           state->reason, state->where);
+       break;
+
+    case 0:
+       qmqpd_receive(state);
+       break;
+    }
+
+    /*
+     * Log abnormal session termination. Indicate the last recognized state
+     * before things went wrong.
+     */
+    if (state->reason && state->where)
+       msg_info("%s: %s: %s while %s",
+             state->queue_id, state->namaddr, state->reason, state->where);
+}
+
+/* qmqpd_service - service one client */
+
+static void qmqpd_service(VSTREAM *stream, char *unused_service, char **argv)
+{
+    QMQPD_STATE *state;
+
+    /*
+     * Sanity check. This service takes no command-line arguments.
+     */
+    if (argv[0])
+       msg_fatal("unexpected command-line argument: %s", argv[0]);
+
+    /*
+     * This routine runs when a client has connected to our network port.
+     * Look up and sanitize the peer name and initialize some connection-
+     * specific state.
+     */
+    state = qmqpd_state_alloc(stream);
+
+    /*
+     * See if we need to turn on verbose logging for this client.
+     */
+    debug_peer_check(state->name, state->addr);
+
+    /*
+     * See if we want to talk to this client at all. In all cases, log the
+     * connection event.
+     */
+    if (namadr_list_match(qmqpd_clients, state->name, state->addr) == 0) {
+       msg_info("refused connect from %s", state->namaddr);
+       qmqpd_reply(state, DONT_LOG, QMQPD_STAT_HARD,
+                   "Error: %s is not authorized to use this service",
+                   state->namaddr);
+    }
+
+    /*
+     * Provide the QMQP service.
+     */
+    else {
+       msg_info("connect from %s", state->namaddr);
+       qmqpd_proto(state);
+       msg_info("disconnect from %s", state->namaddr);
+    }
+
+    /*
+     * After the client has gone away, clean up whatever we have set up at
+     * connection time.
+     */
+    debug_peer_restore();
+    qmqpd_state_free(state);
+}
+
+/* pre_accept - see if tables have changed */
+
+static void pre_accept(char *unused_name, char **unused_argv)
+{
+    if (dict_changed()) {
+       msg_info("lookup table has changed -- exiting");
+       exit(0);
+    }
+}
+
+/* pre_jail_init - pre-jail initialization */
+
+static void pre_jail_init(char *unused_name, char **unused_argv)
+{
+    debug_peer_init();
+    qmqpd_clients = namadr_list_init(var_qmqpd_clients);
+}
+
+/* main - the main program */
+
+int     main(int argc, char **argv)
+{
+    static CONFIG_TIME_TABLE time_table[] = {
+       VAR_QMTPD_TMOUT, DEF_QMTPD_TMOUT, &var_qmqpd_timeout, 1, 0,
+       VAR_QMTPD_ERR_SLEEP, DEF_QMTPD_ERR_SLEEP, &var_qmqpd_err_sleep, 0, 0,
+       0,
+    };
+    static CONFIG_STR_TABLE str_table[] = {
+       VAR_ALWAYS_BCC, DEF_ALWAYS_BCC, &var_always_bcc, 0, 0,
+       VAR_FILTER_XPORT, DEF_FILTER_XPORT, &var_filter_xport, 0, 0,
+       VAR_QMQPD_CLIENTS, DEF_QMQPD_CLIENTS, &var_qmqpd_clients, 0, 0,
+       0,
+    };
+
+    /*
+     * Pass control to the single-threaded service skeleton.
+     */
+    single_server_main(argc, argv, qmqpd_service,
+                      MAIL_SERVER_TIME_TABLE, time_table,
+                      MAIL_SERVER_STR_TABLE, str_table,
+                      MAIL_SERVER_PRE_INIT, pre_jail_init,
+                      MAIL_SERVER_PRE_ACCEPT, pre_accept,
+                      0);
+}
diff --git a/postfix/src/qmqpd/qmqpd.h b/postfix/src/qmqpd/qmqpd.h
new file mode 100644 (file)
index 0000000..9bccc74
--- /dev/null
@@ -0,0 +1,78 @@
+/*++
+/* NAME
+/*     qmqpd 3h
+/* SUMMARY
+/*     Postfix QMQP server
+/* SYNOPSIS
+/*     include "qmqpd.h"
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * System library.
+  */
+#include <time.h>
+
+ /*
+  * Utility library.
+  */
+#include <vstream.h>
+#include <vstring.h>
+
+ /*
+  * Global library.
+  */
+#include <mail_stream.h>
+
+ /*
+  * Per-session state.
+  */
+typedef struct {
+    int     err;                       /* error flags */
+    VSTREAM *client;                   /* client connection */
+    VSTRING *message;                  /* message buffer */
+    VSTRING *buf;                      /* line buffer */
+    time_t  time;                      /* start of session */
+    char   *name;                      /* client name */
+    char   *addr;                      /* client IP address */
+    char   *namaddr;                   /* name[addr] */
+    char   *queue_id;                  /* queue file ID */
+    VSTREAM *cleanup;                  /* cleanup server */
+    MAIL_STREAM *dest;                 /* cleanup server */
+    int     rcpt_count;                        /* recipient count */
+    char   *reason;                    /* exception name */
+    char   *sender;                    /* sender address */
+    char   *recipient;                 /* recipient address */
+    char   *protocol;                  /* protocol name */
+    char   *where;                     /* protocol state */
+} QMQPD_STATE;
+
+ /*
+  * QMQP protocol status codes.
+  */
+#define QMQPD_STAT_OK          'K'
+#define QMQPD_STAT_RETRY       'Z'
+#define QMQPD_STAT_HARD                'D'
+
+ /*
+  * qmqpd_state.c
+  */
+QMQPD_STATE *qmqpd_state_alloc(VSTREAM *);
+void    qmqpd_state_free(QMQPD_STATE *);
+
+ /*
+  * qmqpd_peer.c
+  */
+void    qmqpd_peer_init(QMQPD_STATE *);
+void    qmqpd_peer_reset(QMQPD_STATE *);
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
diff --git a/postfix/src/qmqpd/qmqpd_peer.c b/postfix/src/qmqpd/qmqpd_peer.c
new file mode 100644 (file)
index 0000000..9f0e9f2
--- /dev/null
@@ -0,0 +1,183 @@
+/*++
+/* NAME
+/*     qmqpd_peer 3
+/* SUMMARY
+/*     look up peer name/address information
+/* SYNOPSIS
+/*     #include "qmqpd.h"
+/*
+/*     void    qmqpd_peer_init(state)
+/*     QMQPD_STATE *state;
+/*
+/*     void    qmqpd_peer_reset(state)
+/*     QMQPD_STATE *state;
+/* DESCRIPTION
+/*     The qmqpd_peer_init() routine attempts to produce a printable
+/*     version of the peer name and address of the specified socket.
+/*     Where information is unavailable, the name and/or address
+/*     are set to "unknown".
+/*
+/*     qmqpd_peer_init() updates the following fields:
+/* .IP name
+/*     The client hostname. An unknown name is represented by the
+/*     string "unknown".
+/* .IP addr
+/*     Printable representation of the client address.
+/* .IP namaddr
+/*     String of the form: "name[addr]".
+/* .PP
+/*     qmqpd_peer_reset() releases memory allocate by qmqpd_peer_init().
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>                     /* strerror() */
+#include <errno.h>
+#include <netdb.h>
+#include <string.h>
+
+ /*
+  * Older systems don't have h_errno. Even modern systems don't have
+  * hstrerror().
+  */
+#ifdef NO_HERRNO
+
+static int h_errno = TRY_AGAIN;
+
+#define  HSTRERROR(err) "Host not found"
+
+#else
+
+#define  HSTRERROR(err) (\
+       err == TRY_AGAIN ? "Host not found, try again" : \
+       err == HOST_NOT_FOUND ? "Host not found" : \
+       err == NO_DATA ? "Host name has no address" : \
+       err == NO_RECOVERY ? "Name server failure" : \
+       strerror(errno) \
+    )
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <valid_hostname.h>
+#include <stringops.h>
+
+/* Global library. */
+
+
+/* Application-specific. */
+
+#include "qmqpd.h"
+
+/* qmqpd_peer_init - initialize peer information */
+
+void    qmqpd_peer_init(QMQPD_STATE *state)
+{
+    struct sockaddr_in sin;
+    SOCKADDR_SIZE len = sizeof(sin);
+    struct hostent *hp;
+    int     i;
+
+    /*
+     * Look up the peer address information.
+     */
+    if (getpeername(vstream_fileno(state->client),
+                   (struct sockaddr *) & sin, &len) >= 0) {
+       errno = 0;
+    }
+
+    /*
+     * If peer went away, give up.
+     */
+    if (errno == ECONNRESET || errno == ECONNABORTED) {
+       state->name = mystrdup("unknown");
+       state->addr = mystrdup("unknown");
+    }
+
+    /*
+     * Look up and "verify" the client hostname.
+     */
+    else if (errno == 0 && sin.sin_family == AF_INET) {
+       state->addr = mystrdup(inet_ntoa(sin.sin_addr));
+       hp = gethostbyaddr((char *) &(sin.sin_addr),
+                          sizeof(sin.sin_addr), AF_INET);
+       if (hp == 0) {
+           state->name = mystrdup("unknown");
+       } else if (!valid_hostname(hp->h_name, DONT_GRIPE)) {
+           state->name = mystrdup("unknown");
+       } else {
+           state->name = mystrdup(hp->h_name); /* hp->name is clobbered!! */
+
+           /*
+            * Reject the hostname if it does not list the peer address.
+            */
+#define REJECT_PEER_NAME(state) { \
+       myfree(state->name); \
+       state->name = mystrdup("unknown"); \
+    }
+
+           hp = gethostbyname(state->name);    /* clobbers hp->name!! */
+           if (hp == 0) {
+               msg_warn("%s: hostname %s verification failed: %s",
+                        state->addr, state->name, HSTRERROR(h_errno));
+               REJECT_PEER_NAME(state);
+           } else if (hp->h_length != sizeof(sin.sin_addr)) {
+               msg_warn("%s: hostname %s verification failed: bad address size %d",
+                        state->addr, state->name, hp->h_length);
+               REJECT_PEER_NAME(state);
+           } else {
+               for (i = 0; /* void */ ; i++) {
+                   if (hp->h_addr_list[i] == 0) {
+                       msg_warn("%s: address not listed for hostname %s",
+                                state->addr, state->name);
+                       REJECT_PEER_NAME(state);
+                       break;
+                   }
+                   if (memcmp(hp->h_addr_list[i],
+                              (char *) &sin.sin_addr,
+                              sizeof(sin.sin_addr)) == 0)
+                       break;                  /* keep peer name */
+               }
+           }
+       }
+    }
+
+    /*
+     * If it's not Internet, assume the client is local, and avoid using the
+     * naming service because that can hang when the machine is disconnected.
+     */
+    else {
+       state->name = mystrdup("localhost");
+       state->addr = mystrdup("127.0.0.1");    /* XXX bogus. */
+    }
+
+    /*
+     * Do the name[addr] formatting for pretty reports.
+     */
+    state->namaddr =
+       concatenate(state->name, "[", state->addr, "]", (char *) 0);
+}
+
+/* qmqpd_peer_reset - destroy peer information */
+
+void    qmqpd_peer_reset(QMQPD_STATE *state)
+{
+    myfree(state->name);
+    myfree(state->addr);
+    myfree(state->namaddr);
+}
diff --git a/postfix/src/qmqpd/qmqpd_state.c b/postfix/src/qmqpd/qmqpd_state.c
new file mode 100644 (file)
index 0000000..f4eaf27
--- /dev/null
@@ -0,0 +1,96 @@
+/*++
+/* NAME
+/*     qmqpd_state 3
+/* SUMMARY
+/*     Postfix QMQP server
+/* SYNOPSIS
+/*     #include "qmqpd.h"
+/*
+/*     QMQPD_STATE *qmqpd_state_alloc(stream)
+/*     VSTREAM *stream;
+/*
+/*     void    qmqpd_state_free(state)
+/*     QMQPD_STATE *state;
+/* DESCRIPTION
+/*     qmqpd_state_alloc() creates and initializes session context.
+/*
+/*     qmqpd_state_free() destroys session context.
+/*
+/*     Arguments:
+/* .IP stream
+/*     Stream connected to peer. The stream is not copied.
+/* DIAGNOSTICS
+/*     All errors are fatal.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <time.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <vstream.h>
+#include <vstring.h>
+
+/* Global library. */
+
+#include <mail_stream.h>
+#include <cleanup_user.h>
+
+/* Application-specific. */
+
+#include <qmqpd.h>
+
+/* qmqpd_state_alloc - allocate and initialize session state */
+
+QMQPD_STATE *qmqpd_state_alloc(VSTREAM *stream)
+{
+    QMQPD_STATE *state;
+
+    state = (QMQPD_STATE *) mymalloc(sizeof(*state));
+    state->err = CLEANUP_STAT_OK;
+    state->client = stream;
+    state->message = vstring_alloc(1000);
+    state->buf = vstring_alloc(100);
+    state->time = time((time_t *) 0);
+    qmqpd_peer_init(state);
+    state->queue_id = 0;
+    state->cleanup = 0;
+    state->dest = 0;
+    state->rcpt_count = 0;
+    state->reason = 0;
+    state->sender = 0;
+    state->recipient = 0;
+    state->protocol = "QMQP";
+    state->where = "initializing client connection";
+    return (state);
+}
+
+/* qmqpd_state_free - destroy session state */
+
+void qmqpd_state_free(QMQPD_STATE *state)
+{
+    vstring_free(state->message);
+    vstring_free(state->buf);
+    qmqpd_peer_reset(state);
+    if (state->queue_id)
+       myfree(state->queue_id);
+    if (state->dest)
+       mail_stream_cleanup(state->dest);
+    if (state->sender)
+       myfree(state->sender);
+    if (state->recipient)
+       myfree(state->recipient);
+    myfree((char *) state);
+}
index 32c41a8d3a3ad0876d6890b46a25d50f7b898fc4..b884e7b82bfc21be2e8cb81d26013756e81c9400 100644 (file)
@@ -95,6 +95,9 @@
 /*     Always send EHLO at the start of a connection.
 /* .IP \fBsmtp_never_send_ehlo\fR
 /*     Never send EHLO at the start of a connection.
+/* .IP \fBsmtp_break_lines\fR
+/*     Break lines > \fB$line_length_limit\fR into multiple shorter lines.
+/*     Some SMTP servers misbehave on long lines.
 /* .IP \fBsmtp_skip_4xx_greeting\fR
 /*     Skip servers that greet us with a 4xx status code.
 /* .IP \fBsmtp_skip_5xx_greeting\fR
@@ -250,6 +253,7 @@ char   *var_smtp_sasl_passwd;
 bool    var_smtp_sasl_enable;
 char   *var_smtp_bind_addr;
 bool    var_smtp_rand_addr;
+bool    var_smtp_break_lines;
 
  /*
   * Global variables. smtp_errno is set by the address lookup routines and by
@@ -426,6 +430,7 @@ int     main(int argc, char **argv)
        VAR_SMTP_NEVER_EHLO, DEF_SMTP_NEVER_EHLO, &var_smtp_never_ehlo,
        VAR_SMTP_SASL_ENABLE, DEF_SMTP_SASL_ENABLE, &var_smtp_sasl_enable,
        VAR_SMTP_RAND_ADDR, DEF_SMTP_RAND_ADDR, &var_smtp_rand_addr,
+       VAR_SMTP_BREAK_LINES, DEF_SMTP_BREAK_LINES, &var_smtp_break_lines,
        0,
     };
 
index f04fcacbd48a288c2c65e3729219da5c0107b006..9be6cd8b9d2d43b3499bb3039e3bd1a99b8fdf5b 100644 (file)
 #include <ctype.h>
 #include <string.h>
 #include <unistd.h>
+#include <errno.h>
 
 #ifndef INADDR_NONE
 #define INADDR_NONE 0xffffffff
 #endif
 
+ /*
+  * Older systems don't have h_errno. Even modern systems don't have
+  * hstrerror().
+  */
+#ifdef NO_HERRNO
+
+static int h_errno = TRY_AGAIN;
+
+#define  HSTRERROR(err) "Host not found"
+
+#else
+
+#define  HSTRERROR(err) (\
+        err == TRY_AGAIN ? "Host not found, try again" : \
+        err == HOST_NOT_FOUND ? "Host not found" : \
+        err == NO_DATA ? "Host name has no address" : \
+        err == NO_RECOVERY ? "Name server failure" : \
+        strerror(errno) \
+    )
+#endif
+
 /* Utility library. */
 
 #include <msg.h>
@@ -158,8 +180,8 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref, VSTRI
     if (var_disable_dns) {
        memset((char *) &fixed, 0, sizeof(fixed));
        if ((hp = gethostbyname(host)) == 0) {
-           vstring_sprintf(why, "%s: host not found", host);
-           smtp_errno = SMTP_FAIL;
+           vstring_sprintf(why, "%s: %s", host, HSTRERROR(h_errno));
+           smtp_errno = (h_errno == TRY_AGAIN ? SMTP_RETRY : SMTP_FAIL);
        } else if (hp->h_addrtype != AF_INET) {
            vstring_sprintf(why, "%s: host not found", host);
            msg_warn("%s: unknown address family %d for %s",
index 6b9c58f6ecb08b1385733fbeb60603816c152347..8c947052017ac76692596c0dd8adc4fd8a22e70e 100644 (file)
@@ -394,7 +394,7 @@ SMTP_SESSION *smtp_connect(char *destination, VSTRING *why)
     char   *save;
     char   *dest;
     char   *cp;
-    int     found_myself;
+    int     found_myself = 0;
 
     /*
      * First try to deliver to the indicated destination, then try to deliver
index 2f6ffc44e4a37ff54bca7a7e5eed8069f035fdf1..b26095fd8b5eca247602b8dbf12921fb0bf53ce0 100644 (file)
@@ -505,7 +505,7 @@ int     smtp_xfer(SMTP_STATE *state)
                     */
                case SMTP_STATE_RCPT:
                    if (!mail_from_rejected) {
-#ifndef notRFC821_SYNTAX
+#ifdef notdef
                        if (resp->code == 552)
                            resp->code = 452;
 #endif
@@ -642,6 +642,8 @@ int     smtp_xfer(SMTP_STATE *state)
                if (prev_type != REC_TYPE_CONT)
                    if (vstring_str(state->scratch)[0] == '.')
                        smtp_fputc('.', session->stream);
+               if (var_smtp_break_lines)
+                   rec_type = REC_TYPE_NORM;
                if (rec_type == REC_TYPE_CONT)
                    smtp_fwrite(vstring_str(state->scratch),
                                VSTRING_LEN(state->scratch),
index 6336e2e5e486f9568bc78195f4f0e50809471262..0a4e28fe313e878081642574b3874f9b300b0cb0 100644 (file)
@@ -139,11 +139,11 @@ static int smtp_sasl_log(void *unused_context, int priority,
     switch (priority) {
        case SASL_LOG_ERR:
        case SASL_LOG_WARNING:
-       msg_warn("%s", message);
+       msg_warn("SASL authentication problem: %s", message);
        break;
     case SASL_LOG_INFO:
        if (msg_verbose)
-           msg_info("%s", message);
+           msg_info("SASL authentication info: %s", message);
        break;
     }
     return (SASL_OK);
index daf311184a7fe28e0b79978091b805ad9c2b3cc9..5353a8e6099ea8fdb818eb70686e93278f706c16 100644 (file)
@@ -339,6 +339,7 @@ bool    var_disable_vrfy_cmd;
 char   *var_canonical_maps;
 char   *var_rcpt_canon_maps;
 char   *var_virtual_maps;
+char   *var_virt_mailbox_maps;
 char   *var_relocated_maps;
 char   *var_alias_maps;
 char   *var_local_rcpt_maps;
@@ -1551,6 +1552,7 @@ int     main(int argc, char **argv)
        VAR_CANONICAL_MAPS, DEF_CANONICAL_MAPS, &var_canonical_maps, 0, 0,
        VAR_RCPT_CANON_MAPS, DEF_RCPT_CANON_MAPS, &var_rcpt_canon_maps, 0, 0,
        VAR_VIRTUAL_MAPS, DEF_VIRTUAL_MAPS, &var_virtual_maps, 0, 0,
+       VAR_VIRT_MAILBOX_MAPS, DEF_VIRT_MAILBOX_MAPS, &var_virt_mailbox_maps, 0, 0,
        VAR_RELOCATED_MAPS, DEF_RELOCATED_MAPS, &var_relocated_maps, 0, 0,
        VAR_ALIAS_MAPS, DEF_ALIAS_MAPS, &var_alias_maps, 0, 0,
        VAR_LOCAL_RCPT_MAPS, DEF_LOCAL_RCPT_MAPS, &var_local_rcpt_maps, 0, 0,
index cea04c04ad91ed7e1150ce888509ede17987c5a8..75e7694f41ebaea186aa6b039cf0be1662ec3146 100644 (file)
@@ -323,6 +323,7 @@ static MAPS *local_rcpt_maps;
 static MAPS *rcpt_canon_maps;
 static MAPS *canonical_maps;
 static MAPS *virtual_maps;
+static MAPS *virt_mailbox_maps;
 static MAPS *relocated_maps;
 
  /*
@@ -467,6 +468,8 @@ void    smtpd_check_init(void)
                                 DICT_FLAG_LOCK);
     virtual_maps = maps_create(VAR_VIRTUAL_MAPS, var_virtual_maps,
                               DICT_FLAG_LOCK);
+    virt_mailbox_maps = maps_create(VAR_VIRT_MAILBOX_MAPS, var_virt_mailbox_maps,
+                                   DICT_FLAG_LOCK);
     relocated_maps = maps_create(VAR_RELOCATED_MAPS, var_relocated_maps,
                                 DICT_FLAG_LOCK);
 
@@ -565,7 +568,7 @@ static int smtpd_check_reject(SMTPD_STATE *state, int error_class,
      * 
      * We could eliminate the code duplication and implement the soft_bounce
      * safety net only in the code below. But then the safety net would cover
-     * the UCE restrictions only. This would be at odds with the documentation
+     * the UCE restrictions only. This would be at odds with documentation
      * which says soft_bounce changes all 5xx replies into 4xx ones.
      */
     if (var_soft_bounce && STR(error_text)[0] == '5')
@@ -898,7 +901,9 @@ static int permit_auth_destination(SMTPD_STATE *state, char *recipient)
      */
     if (resolve_local(domain)
        || (*var_virtual_maps
-           && check_maps_find(state, recipient, virtual_maps, domain, 0)))
+           && check_maps_find(state, recipient, virtual_maps, domain, 0))
+       || (*var_virt_mailbox_maps
+       && check_maps_find(state, recipient, virt_mailbox_maps, domain, 0)))
        return (SMTPD_CHECK_OK);
 
     /*
@@ -1032,7 +1037,9 @@ static int permit_mx_backup(SMTPD_STATE *state, const char *recipient)
     domain += 1;
     if (resolve_local(domain)
        || (*var_virtual_maps
-           && check_maps_find(state, recipient, virtual_maps, domain, 0)))
+           && check_maps_find(state, recipient, virtual_maps, domain, 0))
+       || (*var_virt_mailbox_maps
+       && check_maps_find(state, recipient, virt_mailbox_maps, domain, 0)))
        return (SMTPD_CHECK_OK);
 
     if (msg_verbose)
@@ -1174,7 +1181,9 @@ static int reject_unknown_address(SMTPD_STATE *state, char *addr,
     domain += 1;
     if (resolve_local(domain)
        || (*var_virtual_maps
-           && check_maps_find(state, reply_name, virtual_maps, domain, 0)))
+           && check_maps_find(state, reply_name, virtual_maps, domain, 0))
+       || (*var_virt_mailbox_maps
+       && check_maps_find(state, reply_name, virt_mailbox_maps, domain, 0)))
        return (SMTPD_CHECK_DUNNO);
     if (domain[0] == '#')
        return (SMTPD_CHECK_DUNNO);
@@ -2066,6 +2075,23 @@ char   *smtpd_check_rcptmap(SMTPD_STATE *state, char *recipient)
        if (NOMATCH(rcpt_canon_maps, STR(reply.recipient))
            && NOMATCH(canonical_maps, STR(reply.recipient))
            && NOMATCH(relocated_maps, STR(reply.recipient))
+           && NOMATCH(virt_mailbox_maps, STR(reply.recipient))
+           && NOMATCH(virtual_maps, STR(reply.recipient))) {
+           (void) smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
+                                  "%d <%s>: User unknown", 550, recipient);
+           SMTPD_CHECK_RCPT_RETURN(STR(error_text));
+       }
+    }
+
+    /*
+     * Reject mail to unknown addresses in Postfix-style virtual domains.
+     */
+    if (*var_virt_mailbox_maps
+       && (check_maps_find(state, recipient, virt_mailbox_maps, domain, 0))) {
+       if (NOMATCH(rcpt_canon_maps, STR(reply.recipient))
+           && NOMATCH(canonical_maps, STR(reply.recipient))
+           && NOMATCH(relocated_maps, STR(reply.recipient))
+           && NOMATCH(virt_mailbox_maps, STR(reply.recipient))
            && NOMATCH(virtual_maps, STR(reply.recipient))) {
            (void) smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
                                   "%d <%s>: User unknown", 550, recipient);
@@ -2082,6 +2108,7 @@ char   *smtpd_check_rcptmap(SMTPD_STATE *state, char *recipient)
        if (NOMATCH(rcpt_canon_maps, STR(reply.recipient))
            && NOMATCH(canonical_maps, STR(reply.recipient))
            && NOMATCH(relocated_maps, STR(reply.recipient))
+           && NOMATCH(virt_mailbox_maps, STR(reply.recipient))
            && NOMATCH(virtual_maps, STR(reply.recipient))
            && NOMATCH(local_rcpt_maps, STR(reply.recipient))) {
            (void) smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
@@ -2175,6 +2202,7 @@ char   *var_alias_maps;
 char   *var_rcpt_canon_maps;
 char   *var_canonical_maps;
 char   *var_virtual_maps;
+char   *var_virt_mailbox_maps;
 char   *var_relocated_maps;
 char   *var_local_rcpt_maps;
 
@@ -2195,6 +2223,7 @@ static STRING_TABLE string_table[] = {
     VAR_RCPT_CANON_MAPS, DEF_RCPT_CANON_MAPS, &var_rcpt_canon_maps,
     VAR_CANONICAL_MAPS, DEF_CANONICAL_MAPS, &var_canonical_maps,
     VAR_VIRTUAL_MAPS, DEF_VIRTUAL_MAPS, &var_virtual_maps,
+    VAR_VIRT_MAILBOX_MAPS, DEF_VIRT_MAILBOX_MAPS, &var_virt_mailbox_maps,
     VAR_RELOCATED_MAPS, DEF_RELOCATED_MAPS, &var_relocated_maps,
     VAR_LOCAL_RCPT_MAPS, DEF_LOCAL_RCPT_MAPS, &var_local_rcpt_maps,
     0,
@@ -2503,6 +2532,13 @@ main(int argc, char **argv)
                resp = 0;
                break;
            }
+           if (strcasecmp(args->argv[0], VAR_VIRT_MAILBOX_MAPS) == 0) {
+               UPDATE_STRING(var_virt_mailbox_maps, args->argv[1]);
+               UPDATE_MAPS(virt_mailbox_maps, VAR_VIRT_MAILBOX_MAPS,
+                           var_virt_mailbox_maps, DICT_FLAG_LOCK);
+               resp = 0;
+               break;
+           }
            if (strcasecmp(args->argv[0], "local_recipient_maps") == 0) {
                UPDATE_STRING(var_local_rcpt_maps, args->argv[1]);
                UPDATE_MAPS(local_rcpt_maps, VAR_LOCAL_RCPT_MAPS,
index e6ef0ab3518d1b7cfcbf1b750db19141716b0e6c..81f88431a092c053bcb2f62f7170923b492438d3 100644 (file)
@@ -119,7 +119,6 @@ void    smtpd_peer_init(SMTPD_STATE *state)
      * If peer went away, give up.
      */
     if (errno == ECONNRESET || errno == ECONNABORTED) {
-       msg_info("errno %d %m", errno);
        state->name = mystrdup("unknown");
        state->addr = mystrdup("unknown");
        state->peer_code = 5;
index caa3ec04fb1305bed15715855a0f0dcfd50750ba..716b7873dd323b859d40841abd741bbbd5624b33 100644 (file)
@@ -118,11 +118,11 @@ static int smtpd_sasl_log(void *unused_context, int priority,
     switch (priority) {
        case SASL_LOG_ERR:
        case SASL_LOG_WARNING:
-       msg_warn("%s", message);
+       msg_warn("SASL authentication problem: %s", message);
        break;
     case SASL_LOG_INFO:
        if (msg_verbose)
-           msg_info("%s", message);
+           msg_info("SASL authentication info: %s", message);
        break;
     }
     return SASL_OK;
index 5cf7920e0e688b214e9ceb25b2ecacbc3abf934a..454a44c134319f415a418ce03fd4f56af7e3948c 100644 (file)
@@ -1,6 +1,6 @@
 SHELL  = /bin/sh
-SRCS   = smtp-source.c smtp-sink.c
-OBJS   = smtp-source.o smtp-sink.o
+SRCS   = smtp-source.c smtp-sink.c qmqp-source.c qmqp-sink.c
+OBJS   = smtp-source.o smtp-sink.o qmqp-source.o qmqp-sink.o
 HDRS   = 
 TESTSRC        = 
 WARN   = -W -Wformat -Wimplicit -Wmissing-prototypes \
@@ -10,7 +10,7 @@ DEFS  = -I. -I$(INC_DIR) -D$(SYSTYPE)
 CFLAGS = $(DEBUG) $(OPT) $(DEFS)
 TESTPROG= 
 INC_DIR        = ../../include
-PROG   = smtp-source smtp-sink
+PROG   = smtp-source smtp-sink qmqp-source qmqp-sink
 LIBS   = ../../lib/libglobal.a ../../lib/libutil.a
 
 .c.o:; $(CC) $(CFLAGS) -c $*.c
@@ -26,9 +26,15 @@ smtp-sink: smtp-sink.o $(LIBS)
 smtp-source: smtp-source.o $(LIBS)
        $(CC) $(CFLAGS) -o $@ smtp-source.o $(LIBS) $(SYSLIBS)
 
+qmqp-sink: qmqp-sink.o $(LIBS)
+       $(CC) $(CFLAGS) -o $@ qmqp-sink.o $(LIBS) $(SYSLIBS)
+
+qmqp-source: qmqp-source.o $(LIBS)
+       $(CC) $(CFLAGS) -o $@ qmqp-source.o $(LIBS) $(SYSLIBS)
+
 test:  $(TESTPROG)
 
-update: ../../bin/smtp-source ../../bin/smtp-sink
+update: ../../bin/smtp-source ../../bin/smtp-sink ../../bin/qmqp-source
 
 ../../bin/smtp-source: smtp-source
        cp $? $@
@@ -36,6 +42,12 @@ update: ../../bin/smtp-source ../../bin/smtp-sink
 ../../bin/smtp-sink: smtp-sink
        cp $? $@
 
+../../bin/qmqp-source: qmqp-source
+       cp $? $@
+
+../../bin/qmqp-sink: qmqp-sink
+       cp $? $@
+
 printfck: $(OBJS) $(PROG)
        rm -rf printfck
        mkdir printfck
@@ -61,6 +73,36 @@ depend: $(MAKES)
        @$(EXPORT) make -f Makefile.in Makefile 1>&2
 
 # do not edit below this line - it is generated by 'make depend'
+qmqp-sink.o: qmqp-sink.c
+qmqp-sink.o: ../../include/sys_defs.h
+qmqp-sink.o: ../../include/msg.h
+qmqp-sink.o: ../../include/vstring.h
+qmqp-sink.o: ../../include/vbuf.h
+qmqp-sink.o: ../../include/vstream.h
+qmqp-sink.o: ../../include/listen.h
+qmqp-sink.o: ../../include/iostuff.h
+qmqp-sink.o: ../../include/events.h
+qmqp-sink.o: ../../include/mymalloc.h
+qmqp-sink.o: ../../include/msg_vstream.h
+qmqp-sink.o: ../../include/netstring.h
+qmqp-sink.o: ../../include/qmqp_proto.h
+qmqp-source.o: qmqp-source.c
+qmqp-source.o: ../../include/sys_defs.h
+qmqp-source.o: ../../include/msg.h
+qmqp-source.o: ../../include/msg_vstream.h
+qmqp-source.o: ../../include/vstream.h
+qmqp-source.o: ../../include/vbuf.h
+qmqp-source.o: ../../include/vstring.h
+qmqp-source.o: ../../include/get_hostname.h
+qmqp-source.o: ../../include/split_at.h
+qmqp-source.o: ../../include/connect.h
+qmqp-source.o: ../../include/iostuff.h
+qmqp-source.o: ../../include/mymalloc.h
+qmqp-source.o: ../../include/events.h
+qmqp-source.o: ../../include/find_inet.h
+qmqp-source.o: ../../include/netstring.h
+qmqp-source.o: ../../include/mail_date.h
+qmqp-source.o: ../../include/qmqp_proto.h
 smtp-sink.o: smtp-sink.c
 smtp-sink.o: ../../include/sys_defs.h
 smtp-sink.o: ../../include/msg.h
diff --git a/postfix/src/smtpstone/qmqp-sink.c b/postfix/src/smtpstone/qmqp-sink.c
new file mode 100644 (file)
index 0000000..53b3958
--- /dev/null
@@ -0,0 +1,287 @@
+/*++
+/* NAME
+/*     qmqp-sink 8
+/* SUMMARY
+/*     multi-threaded QMQP test server
+/* SYNOPSIS
+/* .fi
+/*     \fBqmqp-sink\fR [\fB-cv\fR] [\fB-x \fItime\fR]
+/*     [\fBinet:\fR][\fIhost\fR]:\fIport\fR \fIbacklog\fR
+/*
+/*     \fBqmqp-sink\fR [\fB-cv\fR]
+/*     \fBunix:\fR\fIpathname\fR \fIbacklog\fR
+/* DESCRIPTION
+/*     \fIqmqp-sink\fR listens on the named host (or address) and port.
+/*     It receives messages from the network and throws them away.
+/*     The purpose is to measure QMQP client performance, not protocol
+/*     compliance.
+/*     Connections can be accepted on IPV4 endpoints or UNIX-domain sockets.
+/*     IPV4 is the default.
+/*     This program is the complement of the \fIqmqp-source\fR program.
+/* .IP \fB-c\fR
+/*     Display a running counter that is updated whenever a delivery
+/*     is completed.
+/* .IP \fB-v\fR
+/*     Increase verbosity. Specify \fB-v -v\fR to see some of the QMQP
+/*     conversation.
+/* .IP "\fB-x \fItime\fR
+/*     Terminate after \fItime\fR seconds. This is to facilitate memory
+/*     leak testing.
+/* SEE ALSO
+/*     qmqp-source, QMQP test message generator
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <listen.h>
+#include <events.h>
+#include <mymalloc.h>
+#include <iostuff.h>
+#include <msg_vstream.h>
+#include <netstring.h>
+
+/* Global library. */
+
+#include <qmqp_proto.h>
+
+/* Application-specific. */
+
+typedef struct {
+    VSTREAM *stream;                   /* client connection */
+    int     count;                     /* bytes to go */
+} SINK_STATE;
+
+static int var_tmout;
+static VSTRING *buffer;
+static void disconnect(SINK_STATE *);
+static int count;
+static int counter;
+
+/* send_reply - finish conversation */
+
+static void send_reply(SINK_STATE *state)
+{
+    vstring_sprintf(buffer, "%cOk", QMQP_STAT_OK);
+    NETSTRING_PUT_BUF(state->stream, buffer);
+    netstring_fflush(state->stream);
+    if (count) {
+       counter++;
+       vstream_printf("%d\r", counter);
+       vstream_fflush(VSTREAM_OUT);
+    }
+    disconnect(state);
+}
+
+/* read_data - read over-all netstring data */
+
+static void read_data(int unused_event, char *context)
+{
+    SINK_STATE *state = (SINK_STATE *) context;
+    int     fd = vstream_fileno(state->stream);
+    int     count;
+
+    /*
+     * Refill the VSTREAM buffer, if necessary.
+     */
+    if (VSTREAM_GETC(state->stream) == VSTREAM_EOF)
+       netstring_except(state->stream, vstream_ftimeout(state->stream) ?
+                        NETSTRING_ERR_TIME : NETSTRING_ERR_EOF);
+    state->count--;
+
+    /*
+     * Flush the VSTREAM buffer. As documented, vstream_fseek() discards
+     * unread input.
+     */
+    if ((count = vstream_peek(state->stream)) > 0) {
+       state->count -= count;
+       if (state->count <= 0) {
+           send_reply(state);
+           return;
+       }
+       vstream_fseek(state->stream, 0L, 0);
+    }
+
+    /*
+     * Do not block while waiting for the arrival of more data.
+     */
+    event_disable_readwrite(fd);
+    event_enable_read(fd, read_data, context);
+}
+
+/* read_length - read over-all netstring length */
+
+static void read_length(int event, char *context)
+{
+    SINK_STATE *state = (SINK_STATE *) context;
+
+    switch (vstream_setjmp(state->stream)) {
+
+    default:
+       msg_panic("unknown error reading input");
+
+    case NETSTRING_ERR_TIME:
+       msg_panic("attempt to read non-readable socket");
+       /* NOTREACHED */
+
+    case NETSTRING_ERR_EOF:
+       msg_warn("lost connection");
+       disconnect(state);
+       return;
+
+    case NETSTRING_ERR_FORMAT:
+       msg_warn("netstring format error");
+       disconnect(state);
+       return;
+
+    case NETSTRING_ERR_SIZE:
+       msg_warn("netstring size error");
+       disconnect(state);
+       return;
+
+       /*
+        * Include the netstring terminator in the read byte count. This
+        * violates abstractions.
+        */
+    case 0:
+       state->count = netstring_get_length(state->stream) + 1;
+       read_data(event, context);
+       return;
+    }
+}
+
+/* disconnect - handle disconnection events */
+
+static void disconnect(SINK_STATE *state)
+{
+    event_disable_readwrite(vstream_fileno(state->stream));
+    vstream_fclose(state->stream);
+    myfree((char *) state);
+}
+
+/* connect_event - handle connection events */
+
+static void connect_event(int unused_event, char *context)
+{
+    int     sock = CAST_CHAR_PTR_TO_INT(context);
+    struct sockaddr sa;
+    SOCKADDR_SIZE len = sizeof(sa);
+    SINK_STATE *state;
+    int     fd;
+
+    if ((fd = accept(sock, &sa, &len)) >= 0) {
+       if (msg_verbose)
+           msg_info("connect (%s)",
+#ifdef AF_LOCAL
+                    sa.sa_family == AF_LOCAL ? "AF_LOCAL" :
+#else
+                    sa.sa_family == AF_UNIX ? "AF_UNIX" :
+#endif
+                    sa.sa_family == AF_INET ? "AF_INET" :
+#ifdef AF_INET6
+                    sa.sa_family == AF_INET6 ? "AF_INET6" :
+#endif
+                    "unknown protocol family");
+       non_blocking(fd, NON_BLOCKING);
+       state = (SINK_STATE *) mymalloc(sizeof(*state));
+       state->stream = vstream_fdopen(fd, O_RDWR);
+       netstring_setup(state->stream, var_tmout);
+       event_enable_read(fd, read_length, (char *) state);
+    }
+}
+
+/* terminate - voluntary exit */
+
+static void terminate(int unused_event, char *unused_context)
+{
+    exit(0);
+}
+
+/* usage - explain */
+
+static void usage(char *myname)
+{
+    msg_fatal("usage: %s [-cv] [-x time] [host]:port backlog", myname);
+}
+
+int     main(int argc, char **argv)
+{
+    int     sock;
+    int     backlog;
+    int     ch;
+    int     ttl;
+
+    /*
+     * Initialize diagnostics.
+     */
+    msg_vstream_init(argv[0], VSTREAM_ERR);
+
+    /*
+     * Parse JCL.
+     */
+    while ((ch = GETOPT(argc, argv, "cvx:")) > 0) {
+       switch (ch) {
+       case 'c':
+           count++;
+           break;
+       case 'v':
+           msg_verbose++;
+           break;
+       case 'x':
+           if ((ttl = atoi(optarg)) <= 0)
+               usage(argv[0]);
+           event_request_timer(terminate, (char *) 0, ttl);
+           break;
+       default:
+           usage(argv[0]);
+       }
+    }
+    if (argc - optind != 2)
+       usage(argv[0]);
+    if ((backlog = atoi(argv[optind + 1])) <= 0)
+       usage(argv[0]);
+
+    /*
+     * Initialize.
+     */
+    buffer = vstring_alloc(1024);
+    if (strncmp(argv[optind], "unix:", 5) == 0) {
+       sock = unix_listen(argv[optind] + 5, backlog, BLOCKING);
+    } else {
+       if (strncmp(argv[optind], "inet:", 5) == 0)
+           argv[optind] += 5;
+       sock = inet_listen(argv[optind], backlog, BLOCKING);
+    }
+
+    /*
+     * Start the event handler.
+     */
+    event_enable_read(sock, connect_event, CAST_INT_TO_CHAR_PTR(sock));
+    for (;;)
+       event_loop(-1);
+}
diff --git a/postfix/src/smtpstone/qmqp-source.c b/postfix/src/smtpstone/qmqp-source.c
new file mode 100644 (file)
index 0000000..a06ab75
--- /dev/null
@@ -0,0 +1,592 @@
+/*++
+/* NAME
+/*     qmqp-source 8
+/* SUMMARY
+/*     multi-threaded QMQP test generator
+/* SYNOPSIS
+/* .fi
+/*     \fBqmqp-source\fR [\fIoptions\fR] [\fBinet:\fR]\fIhost\fR[:\fIport\fR]
+/*
+/*     \fBqmqp-source\fR [\fIoptions\fR] \fBunix:\fIpathname\fR
+/* DESCRIPTION
+/*     qmqp-source connects to the named host and TCP port (default 628)
+/*     and sends one or more messages to it, either sequentially
+/*     or in parallel. The program speaks the QMQP protocol.
+/*     Connections can be made to UNIX-domain and IPV4 servers.
+/*     IPV4 is the default.
+/*
+/*     Options:
+/* .IP -c
+/*     Display a running counter that is incremented each time
+/*     a delivery completes.
+/* .IP "-C count"
+/*     When a host sends RESET instead of SYN|ACK, try \fIcount\fR times
+/*     before giving up. The default count is 1. Specify a larger count in
+/*     order to work around a problem with TCP/IP stacks that send RESET
+/*     when the listen queue is full.
+/* .IP "-f from"
+/*     Use the specified sender address (default: <foo@myhostname>).
+/* .IP "-l length"
+/*     Send \fIlength\fR bytes as message payload. The length
+/*     includes the message headers.
+/* .IP "-m message_count"
+/*     Send the specified number of messages (default: 1).
+/* .IP "-r recipient_count"
+/*     Send the specified number of recipients per transaction (default: 1).
+/*     Recipient names are generated by prepending a number to the
+/*     recipient address.
+/* .IP "-s session_count"
+/*     Run the specified number of QMQP sessions in parallel (default: 1).
+/* .IP "-t to"
+/*     Use the specified recipient address (default: <foo@myhostname>).
+/* .IP "-R interval"
+/*     Wait for a random period of time 0 <= n <= interval between messages.
+/*     Suspending one thread does not affect other delivery threads.
+/* .IP "-w interval"
+/*     Wait a fixed time between messages.
+/*     Suspending one thread does not affect other delivery threads.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <msg_vstream.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <get_hostname.h>
+#include <split_at.h>
+#include <connect.h>
+#include <mymalloc.h>
+#include <events.h>
+#include <find_inet.h>
+#include <iostuff.h>
+#include <netstring.h>
+
+/* Global library. */
+
+#include <mail_date.h>
+#include <qmqp_proto.h>
+
+/* Application-specific. */
+
+ /*
+  * Per-session data structure with state.
+  * 
+  * This software can maintain multiple parallel connections to the same QMQP
+  * server. However, it makes no more than one connection request at a time
+  * to avoid overwhelming the server with SYN packets and having to back off.
+  * Back-off would screw up the benchmark. Pending connection requests are
+  * kept in a linear list.
+  */
+typedef struct SESSION {
+    int     xfer_count;                        /* # of xfers in session */
+    int     rcpt_done;                 /* # of recipients done */
+    int     rcpt_count;                        /* # of recipients to go */
+    VSTREAM *stream;                   /* open connection */
+    int     connect_count;             /* # of connect()s to retry */
+    struct SESSION *next;              /* connect() queue linkage */
+} SESSION;
+
+static SESSION *last_session;          /* connect() queue tail */
+
+static VSTRING *buffer;
+static int var_line_limit = 10240;
+static int var_timeout = 300;
+static const char *var_myhostname;
+static int session_count;
+static int message_count = 1;
+static struct sockaddr_in sin;
+
+#undef sun
+static struct sockaddr_un sun;
+static struct sockaddr *sa;
+static int sa_length;
+static int recipients = 1;
+static char *defaddr;
+static char *recipient;
+static char *sender;
+static int message_length = 1024;
+static int count = 0;
+static int counter = 0;
+static int connect_count = 1;
+static int random_delay = 0;
+static int fixed_delay = 0;
+static const char *mydate;
+static int mypid;
+
+static void enqueue_connect(SESSION *);
+static void start_connect(SESSION *);
+static void connect_done(int, char *);
+
+static void send_data(SESSION *);
+static void receive_reply(int, char *);
+
+static VSTRING *message_buffer;
+static VSTRING *sender_buffer;
+static VSTRING *recipient_buffer;
+
+/* Silly little macros. */
+
+#define STR(x) vstring_str(x)
+#define        LEN(x)  VSTRING_LEN(x)
+
+/* random_interval - generate a random value in 0 .. (small) interval */
+
+static int random_interval(int interval)
+{
+    return (rand() % (interval + 1));
+}
+
+/* socket_error - look up and reset the last socket error */
+
+static int socket_error(int sock)
+{
+    int     error;
+    SOCKOPT_SIZE error_len;
+
+    /*
+     * Some Solaris 2 versions have getsockopt() itself return the error,
+     * instead of returning it via the parameter list.
+     */
+    error = 0;
+    error_len = sizeof(error);
+    if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *) &error, &error_len) < 0)
+       return (-1);
+    if (error) {
+       errno = error;
+       return (-1);
+    }
+
+    /*
+     * No problems.
+     */
+    return (0);
+}
+
+/* exception_text - translate exceptions from the netstring module */
+
+static char *exception_text(int except)
+{
+    ;
+
+    switch (except) {
+    case NETSTRING_ERR_EOF:
+       return ("lost connection");
+    case NETSTRING_ERR_TIME:
+       return ("timeout");
+    case NETSTRING_ERR_FORMAT:
+       return ("netstring format error");
+    case NETSTRING_ERR_SIZE:
+       return ("netstring size exceeds limit");
+    default:
+       msg_panic("exception_text: unknown exception %d", except);
+    }
+    /* NOTREACHED */
+}
+
+/* startup - connect to server but do not wait */
+
+static void startup(SESSION *session)
+{
+    if (message_count-- <= 0) {
+       myfree((char *) session);
+       session_count--;
+       return;
+    }
+    enqueue_connect(session);
+}
+
+/* start_event - invoke startup from timer context */
+
+static void start_event(int unused_event, char *context)
+{
+    SESSION *session = (SESSION *) context;
+
+    startup(session);
+}
+
+/* start_another - start another session */
+
+static void start_another(SESSION *session)
+{
+    if (random_delay > 0) {
+       event_request_timer(start_event, (char *) session,
+                           random_interval(random_delay));
+    } else if (fixed_delay > 0) {
+       event_request_timer(start_event, (char *) session, fixed_delay);
+    } else {
+       startup(session);
+    }
+}
+
+/* enqueue_connect - queue a connection request */
+
+static void enqueue_connect(SESSION *session)
+{
+    session->next = 0;
+    if (last_session == 0) {
+       last_session = session;
+       start_connect(session);
+    } else {
+       last_session->next = session;
+       last_session = session;
+    }
+}
+
+/* dequeue_connect - connection request completed */
+
+static void dequeue_connect(SESSION *session)
+{
+    if (session == last_session) {
+       if (session->next != 0)
+           msg_panic("dequeue_connect: queue ends after last");
+       last_session = 0;
+    } else {
+       if (session->next == 0)
+           msg_panic("dequeue_connect: queue ends before last");
+       start_connect(session->next);
+    }
+}
+
+/* fail_connect - handle failed startup */
+
+static void fail_connect(SESSION *session)
+{
+    if (session->connect_count-- == 1)
+       msg_fatal("connect: %m");
+    msg_warn("connect: %m");
+    event_disable_readwrite(vstream_fileno(session->stream));
+    vstream_fclose(session->stream);
+    session->stream = 0;
+#ifdef MISSING_USLEEP
+    doze(10);
+#else
+    usleep(10);
+#endif
+    start_connect(session);
+}
+
+/* start_connect - start TCP handshake */
+
+static void start_connect(SESSION *session)
+{
+    int     fd;
+
+    /*
+     * Some systems don't set the socket error when connect() fails early
+     * (loopback) so we must deal with the error immediately, rather than
+     * retrieving it later with getsockopt(). We can't use MSG_PEEK to
+     * distinguish between server disconnect and connection refused.
+     */
+    if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0)
+       msg_fatal("socket: %m");
+    (void) non_blocking(fd, NON_BLOCKING);
+    session->stream = vstream_fdopen(fd, O_RDWR);
+    event_enable_write(fd, connect_done, (char *) session);
+    netstring_setup(session->stream, var_timeout);
+    if (connect(fd, sa, sa_length) < 0 && errno != EINPROGRESS)
+       fail_connect(session);
+}
+
+/* connect_done - send message sender info */
+
+static void connect_done(int unused_event, char *context)
+{
+    SESSION *session = (SESSION *) context;
+    int     fd = vstream_fileno(session->stream);
+
+    /*
+     * Try again after some delay when the connection failed, in case they
+     * run a Mickey Mouse protocol stack.
+     */
+    if (socket_error(fd) < 0) {
+       fail_connect(session);
+    } else {
+       dequeue_connect(session);
+       non_blocking(fd, BLOCKING);
+       event_disable_readwrite(fd);
+       send_data(session);
+    }
+}
+
+/* send_data - send message+sender+recipients */
+
+static void send_data(SESSION *session)
+{
+    int     fd = vstream_fileno(session->stream);
+    int     except;
+
+    /*
+     * Prepare for disaster.
+     */
+    if ((except = vstream_setjmp(session->stream)) != 0)
+       msg_fatal("%s while sending message", exception_text(except));
+
+    /*
+     * Send the message content, by wrapping three netstrings into an
+     * over-all netstring.
+     * 
+     * XXX This should be done more carefully to avoid blocking when sending
+     * large messages over slow networks.
+     */
+    netstring_put_multi(session->stream,
+                       STR(message_buffer), LEN(message_buffer),
+                       STR(sender_buffer), LEN(sender_buffer),
+                       STR(recipient_buffer), LEN(recipient_buffer),
+                       0);
+    netstring_fflush(session->stream);
+
+    /*
+     * Wake me up when the server replies or when something bad happens.
+     */
+    event_enable_read(fd, receive_reply, (char *) session);
+}
+
+/* receive_reply - read server reply */
+
+static void receive_reply(int unused_event, char *context)
+{
+    SESSION *session = (SESSION *) context;
+    int     except;
+
+    /*
+     * Prepare for disaster.
+     */
+    if ((except = vstream_setjmp(session->stream)) != 0)
+       msg_fatal("%s while receiving server reply", exception_text(except));
+
+    /*
+     * Receive and process the server reply.
+     */
+    netstring_get(session->stream, buffer, var_line_limit);
+    if (msg_verbose)
+       vstream_printf("<< %.*s\n", LEN(buffer), STR(buffer));
+    if (STR(buffer)[0] != QMQP_STAT_OK)
+       msg_fatal("%s error: %.*s",
+                 STR(buffer)[0] == QMQP_STAT_RETRY ? "recoverable" :
+                 STR(buffer)[0] == QMQP_STAT_HARD ? "unrecoverable" :
+                 "unknown", LEN(buffer) - 1, STR(buffer) + 1);
+
+    /*
+     * Update the optional running counter.
+     */
+    if (count) {
+       counter++;
+       vstream_printf("%d\r", counter);
+       vstream_fflush(VSTREAM_OUT);
+    }
+
+    /*
+     * Finish this session. QMQP sends only one message per session.
+     */
+    event_disable_readwrite(vstream_fileno(session->stream));
+    vstream_fclose(session->stream);
+    session->stream = 0;
+    start_another(session);
+}
+
+/* usage - explain */
+
+static void usage(char *myname)
+{
+    msg_fatal("usage: %s -s sess -l msglen -m msgs -c -C count -f from -t to -R delay -v -w delay host[:port]", myname);
+}
+
+/* main - parse JCL and start the machine */
+
+int     main(int argc, char **argv)
+{
+    SESSION *session;
+    char   *host;
+    char   *port;
+    char   *path;
+    int     path_len;
+    int     sessions = 1;
+    int     ch;
+    int     n;
+    int     i;
+
+    signal(SIGPIPE, SIG_IGN);
+    msg_vstream_init(argv[0], VSTREAM_ERR);
+
+    /*
+     * Parse JCL.
+     */
+    while ((ch = GETOPT(argc, argv, "cC:f:l:m:r:R:s:t:vw:")) > 0) {
+       switch (ch) {
+       case 'c':
+           count++;
+           break;
+       case 'C':
+           if ((connect_count = atoi(optarg)) <= 0)
+               usage(argv[0]);
+           break;
+       case 'f':
+           sender = optarg;
+           break;
+       case 'l':
+           if ((message_length = atoi(optarg)) <= 0)
+               usage(argv[0]);
+           break;
+       case 'm':
+           if ((message_count = atoi(optarg)) <= 0)
+               usage(argv[0]);
+           break;
+       case 'r':
+           if ((recipients = atoi(optarg)) <= 0)
+               usage(argv[0]);
+           break;
+       case 'R':
+           if (fixed_delay > 0 || (random_delay = atoi(optarg)) <= 0)
+               usage(argv[0]);
+           break;
+       case 's':
+           if ((sessions = atoi(optarg)) <= 0)
+               usage(argv[0]);
+           break;
+       case 't':
+           recipient = optarg;
+           break;
+       case 'v':
+           msg_verbose++;
+           break;
+       case 'w':
+           if (random_delay > 0 || (fixed_delay = atoi(optarg)) <= 0)
+               usage(argv[0]);
+           break;
+       default:
+           usage(argv[0]);
+       }
+    }
+    if (argc - optind != 1)
+       usage(argv[0]);
+
+    if (random_delay > 0)
+       srand(getpid());
+
+    /*
+     * Translate endpoint address to internal form.
+     */
+    if (strncmp(argv[optind], "unix:", 5) == 0) {
+       path = argv[optind] + 5;
+       path_len = strlen(path);
+       if (path_len >= (int) sizeof(sun.sun_path))
+           msg_fatal("unix-domain name too long: %s", path);
+       memset((char *) &sun, 0, sizeof(sun));
+       sun.sun_family = AF_UNIX;
+#ifdef HAS_SUN_LEN
+       sun.sun_len = path_len + 1;
+#endif
+       memcpy(sun.sun_path, path, path_len);
+       sa = (struct sockaddr *) & sun;
+       sa_length = sizeof(sun);
+    } else {
+       if (strncmp(argv[optind], "inet:", 5) == 0)
+           argv[optind] += 5;
+       if ((port = split_at(host = argv[optind], ':')) == 0 || *port == 0)
+           port = "628";
+       memset((char *) &sin, 0, sizeof(sin));
+       sin.sin_family = AF_INET;
+       sin.sin_addr.s_addr = find_inet_addr(host);
+       sin.sin_port = find_inet_port(port, "tcp");
+       sa = (struct sockaddr *) & sin;
+       sa_length = sizeof(sin);
+    }
+
+    /*
+     * Allocate space for temporary buffer.
+     */
+    buffer = vstring_alloc(100);
+
+    /*
+     * Make sure we have sender and recipient addresses.
+     */
+    var_myhostname = get_hostname();
+    if (sender == 0 || recipient == 0) {
+       vstring_sprintf(buffer, "foo@%s", var_myhostname);
+       defaddr = mystrdup(vstring_str(buffer));
+       if (sender == 0)
+           sender = defaddr;
+       if (recipient == 0)
+           recipient = defaddr;
+    }
+
+    /*
+     * Prepare some results that may be used multiple times: the message
+     * content netstring, the sender netstring, and the recipient netstrings.
+     */
+    mydate = mail_date(time((time_t *) 0));
+    mypid = getpid();
+
+    message_buffer = vstring_alloc(message_length + 200);
+    vstring_sprintf(buffer,
+                 "From: <%s>\nTo: <%s>\nDate: %s\nMessage-Id: <%d@%s>\n\n",
+                   sender, recipient, mydate, mypid, var_myhostname);
+    for (n = 1; LEN(buffer) < message_length; n++) {
+       for (i = 0; i < n && i < 79; i++)
+           VSTRING_ADDCH(buffer, 'X');
+       VSTRING_ADDCH(buffer, '\n');
+    }
+    netstring_memcpy(message_buffer, STR(buffer), message_length);
+
+    n = strlen(sender);
+    sender_buffer = vstring_alloc(n);
+    netstring_memcpy(sender_buffer, sender, n);
+
+    if (recipients == 1) {
+       n = strlen(recipient);
+       recipient_buffer = vstring_alloc(n);
+       netstring_memcpy(recipient_buffer, recipient, n);
+    } else {
+       recipient_buffer = vstring_alloc(100);
+       for (n = 0; n < recipients; n++) {
+           vstring_sprintf(buffer, "%d%s", n, recipient);
+           netstring_memcat(recipient_buffer, STR(buffer), LEN(buffer));
+       }
+    }
+
+    /*
+     * Start sessions.
+     */
+    while (sessions-- > 0) {
+       session = (SESSION *) mymalloc(sizeof(*session));
+       session->stream = 0;
+       session->xfer_count = 0;
+       session->connect_count = connect_count;
+       session->next = 0;
+       session_count++;
+       startup(session);
+    }
+    for (;;) {
+       event_loop(-1);
+       if (session_count <= 0 && message_count <= 0) {
+           if (count) {
+               VSTREAM_PUTC('\n', VSTREAM_OUT);
+               vstream_fflush(VSTREAM_OUT);
+           }
+           exit(0);
+       }
+    }
+}
index 50fe6ec9a792f20e0d62fdc1c8a5cedec845cce5..9e69fa122a7a06966023b2e3d10d722c7e24425a 100644 (file)
@@ -9,8 +9,8 @@
 /*
 /*     \fBsmtp-source\fR [\fIoptions\fR] \fBunix:\fIpathname\fR
 /* DESCRIPTION
-/*     smtp-source connects to the named host and port (default 25)
-/*     and sends one or more little messages to it, either sequentially
+/*     smtp-source connects to the named host and TCP port (default port 25)
+/*     and sends one or more messages to it, either sequentially
 /*     or in parallel. The program speaks either SMTP (default) or
 /*     LMTP. Connections can be made to UNIX-domain and IPV4 servers.
 /*     IPV4 is the default.
@@ -32,7 +32,8 @@
 /* .IP -o
 /*     Old mode: don't send HELO, and don't send message headers.
 /* .IP "-l length"
-/*     Send \fIlength\fR bytes as message payload.
+/*     Send \fIlength\fR bytes as message payload. The length does not
+/*     include message headers.
 /* .IP -L
 /*     Speak LMTP rather than SMTP.
 /* .IP "-m message_count"
@@ -40,7 +41,7 @@
 /* .IP "-r recipient_count"
 /*     Send the specified number of recipients per transaction (default: 1).
 /*     Recipient names are generated by prepending a number to the
-/*     recipient address. The default is one recipient per transaction.
+/*     recipient address.
 /* .IP "-s session_count"
 /*     Run the specified number of SMTP sessions in parallel (default: 1).
 /* .IP "-t to"
@@ -781,7 +782,7 @@ int     main(int argc, char **argv)
            message_data = mymalloc(message_length);
            memset(message_data, 'X', message_length);
            for (i = 80; i < message_length; i += 80) {
-               message_data[i - 80] = "0123456789"[(i/80) % 10];
+               message_data[i - 80] = "0123456789"[(i / 80) % 10];
                message_data[i - 2] = '\r';
                message_data[i - 1] = '\n';
            }
@@ -848,7 +849,7 @@ int     main(int argc, char **argv)
     } else {
        if (strncmp(argv[optind], "inet:", 5) == 0)
            argv[optind] += 5;
-       if ((port = split_at(host = argv[optind], ':')) == 0)
+       if ((port = split_at(host = argv[optind], ':')) == 0 || *port == 0)
            port = "smtp";
        memset((char *) &sin, 0, sizeof(sin));
        sin.sin_family = AF_INET;
index 244bae4ad910d2e761bd358a4500b827e9f78c7a..a2dd61bb24d35ebe70437a886a926fb78ebe758c 100644 (file)
@@ -23,7 +23,7 @@ SRCS  = argv.c argv_split.c attr.c basename.c binhash.c chroot_uid.c \
        clean_env.c watchdog.c spawn_command.c duplex_pipe.c sane_rename.c \
        sane_link.c unescape.c timed_read.c timed_write.c dict_tcp.c \
        hex_quote.c dict_alloc.c rand_sleep.c sane_time.c dict_debug.c \
-       sane_socketpair.c myrand.c
+       sane_socketpair.c myrand.c netstring.c
 OBJS   = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \
        close_on_exec.o concatenate.o dict.o dict_db.o dict_dbm.o \
        dict_env.o dict_ht.o dict_ldap.o dict_mysql.o dict_ni.o dict_nis.o \
@@ -48,7 +48,7 @@ OBJS  = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \
        clean_env.o watchdog.o spawn_command.o duplex_pipe.o sane_rename.o \
        sane_link.o unescape.o timed_read.o timed_write.o dict_tcp.o \
        hex_quote.o dict_alloc.o rand_sleep.o sane_time.o dict_debug.o \
-       sane_socketpair.o myrand.o
+       sane_socketpair.o myrand.o netstring.o
 HDRS   = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \
        dict_dbm.h dict_env.h dict_ht.h dict_ldap.h dict_mysql.h \
        dict_ni.h dict_nis.h dict_nisplus.h dir_forest.h events.h \
@@ -64,12 +64,12 @@ HDRS        = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \
        vbuf.h vbuf_print.h vstream.h vstring.h vstring_vstream.h \
        dict_unix.h dict_pcre.h dict_regexp.h mac_expand.h clean_env.h \
        watchdog.h spawn_command.h sane_fsops.h dict_tcp.h hex_quote.h \
-       sane_time.h sane_socketpair.h myrand.h
+       sane_time.h sane_socketpair.h myrand.h netstring.h
 TESTSRC        = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
        stream_test.c dup2_pass_on_exec.c
 WARN   = -W -Wformat -Wimplicit -Wmissing-prototypes \
        -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
-       -Wunused 
+       -Wunused
 DEFS   = -I. -D$(SYSTYPE)
 CFLAGS = $(DEBUG) $(OPT) $(DEFS)
 FILES  = Makefile $(SRCS) $(HDRS)
@@ -770,6 +770,13 @@ name_mask.o: stringops.h
 name_mask.o: vstring.h
 name_mask.o: vbuf.h
 name_mask.o: name_mask.h
+netstring.o: netstring.c
+netstring.o: sys_defs.h
+netstring.o: msg.h
+netstring.o: vstream.h
+netstring.o: vbuf.h
+netstring.o: vstring.h
+netstring.o: netstring.h
 non_blocking.o: non_blocking.c
 non_blocking.o: sys_defs.h
 non_blocking.o: msg.h
index 28fc70156390c76bab9b76068f25c863caf4a702..f778e928d082bb2876666a22fce22c5e5c5ee2be 100644 (file)
@@ -274,14 +274,14 @@ static MYSQL_RES *plmysql_query(PLMYSQL *PLDB,
        if (res == 0 && host->stat == STATACTIVE) {
            if (!(mysql_query(host->db, query))) {
                if ((res = mysql_store_result(host->db)) == 0) {
-                   msg_warn("%s", mysql_error(host->db));
+                   msg_warn("mysql query failed: %s", mysql_error(host->db));
                    plmysql_down_host(host);
                } else {
                    if (msg_verbose)
                        msg_info("dict_mysql: successful query from host %s", host->hostname);
                }
            } else {
-               msg_warn("%s", mysql_error(host->db));
+               msg_warn("mysql query failed: %s", mysql_error(host->db));
                plmysql_down_host(host);
            }
        }
@@ -323,7 +323,8 @@ static void plmysql_connect_single(HOST *host, char *dbname, char *username, cha
                     host->hostname);
        host->stat = STATACTIVE;
     } else {
-       msg_warn("%s", mysql_error(host->db));
+       msg_warn("connect to mysql server %s: %s",
+                host->hostname, mysql_error(host->db));
        plmysql_down_host(host);
     }
     if (hostname)
@@ -366,7 +367,7 @@ DICT   *dict_mysql_open(const char *name, int unused_open_flags, int dict_flags)
     if (dict_mysql->pldb == NULL)
        msg_fatal("couldn't intialize pldb!\n");
     dict_register(name, (DICT *) dict_mysql);
-    return (DICT_DEBUG(&dict_mysql->dict));
+    return (DICT_DEBUG (&dict_mysql->dict));
 }
 
 /* mysqlname_parse - parse mysql configuration file */
index e20531c060586fc19aef097749c4492fd00e4807..04c1fb49ab032875197009374ef974aa62387115 100644 (file)
@@ -173,10 +173,10 @@ char   *mystrdup(const char *str)
 char   *mystrndup(const char *str, int len)
 {
     char   *result;
-    int     slen;
+    char   *cp;
 
-    if ((slen = strlen(str)) < len)
-       len = slen;
+    if ((cp = memchr(str, 0, len)) != 0)
+       len = cp - str;
     result = memcpy(mymalloc(len + 1), str, len);
     result[len] = 0;
     return (result);
diff --git a/postfix/src/util/netstring.c b/postfix/src/util/netstring.c
new file mode 100644 (file)
index 0000000..63c6faf
--- /dev/null
@@ -0,0 +1,350 @@
+/*++
+/* NAME
+/*     netstring 3
+/* SUMMARY
+/*     netstring stream I/O support
+/* SYNOPSIS
+/*     #include <netstring.h>
+/*
+/*     void    netstring_setup(stream, timeout)
+/*     VSTREAM *stream;
+/*     int     timeout;
+/*
+/*     void    netstring_except(stream, exception)
+/*     VSTREAM *stream;
+/*     int     exception;
+/*
+/*     VSTRING *netstring_get(stream, buf, limit)
+/*     VSTREAM *stream;
+/*     VSTRING *buf;
+/*     int     limit;
+/*
+/*     void    netstring_put(stream, data, len)
+/*     VSTREAM *stream;
+/*     const char *data;
+/*     int     len;
+/*
+/*     void    netstring_put_multi(stream, data, len, data, len, ..., 0)
+/*     VSTREAM *stream;
+/*     const char *data;
+/*     int     len;
+/*
+/*     void    NETSTRING_PUT_BUF(stream, buf)
+/*     VSTREAM *stream;
+/*     VSTRING *buf;
+/*
+/*     void    netstring_fflush(stream)
+/*     VSTREAM *stream;
+/*
+/*     VSTRING *netstring_memcpy(buf, data, len)
+/*     VSTRING *buf;
+/*     const char *data;
+/*     int     len;
+/*
+/*     VSTRING *netstring_memcat(buf, data, len)
+/*     VSTRING *buf;
+/*     const char *src;
+/*     int len;
+/* AUXILIARY ROUTINES
+/*     int     netstring_get_length(stream)
+/*     VSTREAM *stream;
+/*
+/*     VSTRING *netstring_get_data(stream, buf, len)
+/*     VSTREAM *stream;
+/*     VSTRING *buf;
+/*     int     len;
+/*
+/*     void    netstring_get_terminator(stream)
+/*     VSTREAM *stream;
+/* DESCRIPTION
+/*     This module reads and writes netstrings with error detection:
+/*     timeouts, unexpected end-of-file, or format errors. Netstring
+/*     is a data format designed by Daniel Bernstein.
+/*
+/*     netstring_setup() arranges for a time limit on the netstring
+/*     read and write operations described below.
+/*     This routine alters the behavior of streams as follows:
+/* .IP \(bu
+/*     The read/write timeout is set to the specified value.
+/* .IP \(bu
+/*     The stream is configured to enable exception handling.
+/* .PP
+/*     netstring_except() raises the specified exception on the
+/*     named stream. See the DIAGNOSTICS section below.
+/*
+/*     netstring_get() reads a netstring from the specified stream
+/*     and extracts its content. The limit specifies a maximal size.
+/*     Specify zero to disable the size limit. The result is not null
+/*     terminated.  The result value is the buf argument.
+/*
+/*     netstring_put() encapsulates the specified string as a netstring
+/*     and sends the result to the specified stream.
+/*     The stream output buffer is not flushed.
+/*
+/*     netstring_put_multi() encapsulates the content of multiple strings
+/*     as one netstring and sends the result to the specified stream. The
+/*     argument list must be terminated with a null data pointer.
+/*     The stream output buffer is not flushed.
+/*
+/*     NETSTRING_PUT_BUF() is a macro that provides a VSTRING-based
+/*     wrapper for the netstring_put() routine.
+/*
+/*     netstring_fflush() flushes the output buffer of the specified
+/*     stream and handles any errors.
+/*
+/*     netstring_memcpy() encapsulates the specified data as a netstring
+/*     and copies the result over the specified buffer. The result
+/*     value is the buffer.
+/*
+/*     netstring_memcat() encapsulates the specified data as a netstring
+/*     and appends the result to the specified buffer. The result
+/*     value is the buffer.
+/*
+/*     The following routines provide low-level access to a netstring
+/*     stream.
+/*
+/*     netstring_get_length() reads a length field from the specified
+/*     stream, and absorbs the netstring length field terminator.
+/*
+/*     netstring_get_data() reads the specified number of bytes from the
+/*     specified stream into the specified buffer, and absorbs the
+/*     netstring terminator.  The result value is the buf argument.
+/*
+/*     netstring_get_terminator() reads the netstring terminator from
+/*     the specified stream.
+/* DIAGNOSTICS
+/* .fi
+/* .ad
+/*     In case of error, a vstream_longjmp() call is performed to the
+/*     context specified with vstream_setjmp().
+/*     Error codes passed along with vstream_longjmp() are:
+/* .IP NETSTRING_ERR_EOF
+/*     An I/O error happened, or the peer has disconnected unexpectedly.
+/* .IP NETSTRING_ERR_TIME
+/*     The time limit specified to netstring_setup() was exceeded.
+/* .IP NETSTRING_ERR_FORMAT
+/*     The input contains an unexpected character value.
+/* .IP NETSTRING_ERR_SIZE
+/*     The input is larger than acceptable.
+/* BUGS
+/*     The timeout deadline affects all I/O on the named stream, not
+/*     just the I/O done on behalf of this module.
+/*
+/*     The timeout deadline overwrites any previously set up state on
+/*     the named stream.
+/*
+/*     netstrings are not null terminated, which makes printing them
+/*     a bit awkward.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* SEE ALSO
+/*     http://cr.yp.to/proto/netstrings.txt, netstring definition
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <netstring.h>
+
+/* Application-specific. */
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* netstring_setup - initialize netstring stream */
+
+void    netstring_setup(VSTREAM *stream, int timeout)
+{
+    vstream_control(stream,
+                   VSTREAM_CTL_TIMEOUT, timeout,
+                   VSTREAM_CTL_EXCEPT,
+                   VSTREAM_CTL_END);
+}
+
+/* netstring_except - process netstring stream exception */
+
+void    netstring_except(VSTREAM *stream, int exception)
+{
+    vstream_longjmp(stream, exception);
+}
+
+/* netstring_get_length - read netstring length + terminator */
+
+int     netstring_get_length(VSTREAM *stream)
+{
+    char   *myname = "netstring_get_length";
+    int     len = 0;
+    int     ch;
+
+    for (;;) {
+       switch (ch = VSTREAM_GETC(stream)) {
+       case VSTREAM_EOF:
+           netstring_except(stream, vstream_ftimeout(stream) ?
+                            NETSTRING_ERR_TIME : NETSTRING_ERR_EOF);
+       case ':':
+           if (msg_verbose > 1)
+               msg_info("%s: read netstring length %d", myname, len);
+           return (len);
+       default:
+           if (!ISDIGIT(ch))
+               netstring_except(stream, NETSTRING_ERR_FORMAT);
+           len = len * 10 + ch - '0';
+           break;
+       }
+    }
+}
+
+/* netstring_get_data - read netstring payload + terminator */
+
+VSTRING *netstring_get_data(VSTREAM *stream, VSTRING *buf, int len)
+{
+    char   *myname = "netstring_get_data";
+
+    /*
+     * Allocate buffer space.
+     */
+    VSTRING_RESET(buf);
+    VSTRING_SPACE(buf, len);
+
+    /*
+     * Read the payload and absorb the terminator.
+     */
+    if (vstream_fread(stream, STR(buf), len) != len)
+       netstring_except(stream, vstream_ftimeout(stream) ?
+                        NETSTRING_ERR_TIME : NETSTRING_ERR_EOF);
+    if (msg_verbose > 1)
+       msg_info("%s: read netstring data %.*s",
+                myname, len < 30 ? len : 30, STR(buf));
+    netstring_get_terminator(stream);
+
+    /*
+     * Position the buffer.
+     */
+    VSTRING_AT_OFFSET(buf, len);
+    return (buf);
+}
+
+/* netstring_get_terminator - absorb netstring terminator */
+
+void    netstring_get_terminator(VSTREAM *stream)
+{
+    if (VSTREAM_GETC(stream) != ',')
+       netstring_except(stream, NETSTRING_ERR_FORMAT);
+}
+
+/* netstring_get - read string from netstring stream */
+
+VSTRING *netstring_get(VSTREAM *stream, VSTRING *buf, int limit)
+{
+    int     len;
+
+    len = netstring_get_length(stream);
+    if (limit && len > limit)
+       netstring_except(stream, NETSTRING_ERR_SIZE);
+    netstring_get_data(stream, buf, len);
+}
+
+/* netstring_put - send string as netstring */
+
+void    netstring_put(VSTREAM *stream, const char *data, int len)
+{
+    char   *myname = "netstring_put";
+
+    if (msg_verbose > 1)
+       msg_info("%s: write netstring len %d data %.*s",
+                myname, len, len < 30 ? len : 30, data);
+    vstream_fprintf(stream, "%d:", len);
+    vstream_fwrite(stream, data, len);
+    VSTREAM_PUTC(',', stream);
+}
+
+/* netstring_put_multi - send multiple strings as one netstring */
+
+void    netstring_put_multi(VSTREAM *stream,...)
+{
+    char   *myname = "netstring_put_multi";
+    int     total;
+    char   *data;
+    int     data_len;
+    va_list ap;
+
+    /*
+     * Figure out the total result size.
+     */
+    va_start(ap, stream);
+    for (total = 0; (data = va_arg(ap, char *)) != 0; total += data_len)
+       if ((data_len = va_arg(ap, int)) < 0)
+           msg_panic("netstring_put_multi: bad data length %d", data_len);
+    va_end(ap);
+
+    /*
+     * Debugging support.
+     */
+    if (msg_verbose > 1) {
+       va_start(ap, stream);
+       data = va_arg(ap, char *);
+       data_len = va_arg(ap, int);
+       msg_info("%s: write netstring len %d data %.*s",
+                myname, total, data_len < 30 ? data_len : 30, data);
+       va_end(ap);
+    }
+
+    /*
+     * Send the length, content and terminator.
+     */
+    vstream_fprintf(stream, "%d:", total);
+    va_start(ap, stream);
+    while ((data = va_arg(ap, char *)) != 0) {
+       data_len = va_arg(ap, int);
+       if (data_len > 0)
+           if (vstream_fwrite(stream, data, data_len) != data_len)
+               netstring_except(stream, vstream_ftimeout(stream) ?
+                                NETSTRING_ERR_TIME : NETSTRING_ERR_EOF);
+       va_end(ap);
+    }
+    vstream_fwrite(stream, ",", 1);
+}
+
+/* netstring_fflush - flush netstring stream */
+
+void    netstring_fflush(VSTREAM *stream)
+{
+    if (vstream_fflush(stream) == VSTREAM_EOF)
+       netstring_except(stream, vstream_ftimeout(stream) ?
+                        NETSTRING_ERR_TIME : NETSTRING_ERR_EOF);
+}
+
+/* netstring_memcpy - copy data as in-memory netstring */
+
+VSTRING *netstring_memcpy(VSTRING *buf, const char *src, int len)
+{
+    vstring_sprintf(buf, "%d:", len);
+    vstring_memcat(buf, src, len);
+    VSTRING_ADDCH(buf, ',');
+    return (buf);
+}
+
+/* netstring_memcat - append data as in-memory netstring */
+
+VSTRING *netstring_memcat(VSTRING *buf, const char *src, int len)
+{
+    vstring_sprintf_append(buf, "%d:", len);
+    vstring_memcat(buf, src, len);
+    VSTRING_ADDCH(buf, ',');
+    return (buf);
+}
diff --git a/postfix/src/util/netstring.h b/postfix/src/util/netstring.h
new file mode 100644 (file)
index 0000000..6b82d90
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef _NETSTRING_H_INCLUDED_
+#define _NETSTRING_H_INCLUDED_
+
+/*++
+/* NAME
+/*     netstring 3h
+/* SUMMARY
+/*     netstring stream I/O support
+/* SYNOPSIS
+/*     #include <netstring.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <vstring.h>
+#include <vstream.h>
+
+ /*
+  * External interface.
+  */
+#define NETSTRING_ERR_EOF      1       /* unexpected disconnect */
+#define NETSTRING_ERR_TIME     2       /* time out */
+#define NETSTRING_ERR_FORMAT   3       /* format error */
+#define NETSTRING_ERR_SIZE     4       /* netstring too large */
+
+extern void netstring_except(VSTREAM *, int);
+extern void netstring_setup(VSTREAM *, int);
+extern int netstring_get_length(VSTREAM *);
+extern VSTRING *netstring_get_data(VSTREAM *, VSTRING *, int);
+extern void netstring_get_terminator(VSTREAM *);
+extern VSTRING *netstring_get(VSTREAM *, VSTRING *, int);
+extern void netstring_put(VSTREAM *, const char *, int);
+extern void netstring_put_multi(VSTREAM *,...);
+extern void netstring_fflush(VSTREAM *);
+extern VSTRING *netstring_memcpy(VSTRING *, const char *, int);
+extern VSTRING *netstring_memcat(VSTRING *, const char *, int);
+
+#define NETSTRING_PUT_BUF(str, buf) \
+       netstring_put((str), vstring_str(buf), VSTRING_LEN(buf))
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
index e2ec94ddcf70437e4db62620349becc4f1a14ca0..c5bdbfdde1e54da65b6f48a58645d1bf39e72a1f 100644 (file)
 /*
 /*     VSTREAM_PUTCHAR(c) is an alias for VSTREAM_PUTC(c, VSTREAM_OUT).
 /*
-/*     vstream_unget() pushes back a character onto the specified stream
+/*     vstream_ungetc() pushes back a character onto the specified stream
 /*     and returns the character, or VSTREAM_EOF in case of problems.
 /*     It is an error to push back before reading (or immediately after
 /*     changing the stream offset via vstream_fseek()). Upon successful
-/*     return, vstream_unget() clears the end-of-file stream flag.
+/*     return, vstream_ungetc() clears the end-of-file stream flag.
 /*
 /*     vstream_fputs() appends the given null-terminated string to the
 /*     specified buffered stream. The result is 0 in case of success,
index 356b114b2b82f0cb590bf293d1b7f3b3720fd09f..400b5f0af1a0fc9f49c7f38a42fea9862c428c32 100644 (file)
 /*     const char *src;
 /*     int     len;
 /*
+/*     VSTRING *vstring_memcpy(vp, src, len)
+/*     VSTRING *vp;
+/*     const char *src;
+/*     int     len;
+/*
+/*     VSTRING *vstring_memcat(vp, src, len)
+/*     VSTRING *vp;
+/*     const char *src;
+/*     int     len;
+/*
 /*     VSTRING *vstring_sprintf(vp, format, ...)
 /*     VSTRING *vp;
 /*     const char *format;
 /*     vstring_strncat() copies at most \fIlen\fR characters. Otherwise it is
 /*     identical to vstring_strcat().
 /*
+/*     vstring_memcpy() copies \fIlen\fR bytes to a variable-length string.
+/*     \fIsrc\fP provides the data to be copied; \fIvp\fP is the
+/*     target and result value.  The result is not null-terminated.
+/*
+/*     vstring_memcat() appends \fIlen\fR bytes to a variable-length string.
+/*     \fIsrc\fP provides the data to be copied; \fIvp\fP is the
+/*     target and result value.  The result is not null-terminated.
+/*
 /*     vstring_sprintf() produces a formatted string according to its
 /*     \fIformat\fR argument. See vstring_vsprintf() for details.
 /*
@@ -392,6 +410,29 @@ VSTRING *vstring_strncat(VSTRING *vp, const char *src, int len)
     return (vp);
 }
 
+/* vstring_memcpy - copy buffer of limited length */
+
+VSTRING *vstring_memcpy(VSTRING *vp, const char *src, int len)
+{
+    VSTRING_RESET(vp);
+
+    VSTRING_SPACE(vp, len);
+    memcpy(vstring_str(vp), src, len);
+    VSTRING_AT_OFFSET(vp, len);
+    return (vp);
+}
+
+/* vstring_memcat - append buffer of limited length */
+
+VSTRING *vstring_memcat(VSTRING *vp, const char *src, int len)
+{
+    VSTRING_SPACE(vp, len);
+    memcpy(vstring_end(vp), src, len);
+    len += VSTRING_LEN(vp);
+    VSTRING_AT_OFFSET(vp, len);
+    return (vp);
+}
+
 /* vstring_export - VSTRING to bare string */
 
 char   *vstring_export(VSTRING *vp)
index 6ae8947e78414d20683fa74e643942aa4dcefdf0..b1b81bb8acd8eccc740e551c70ee56e12748d9a9 100644 (file)
@@ -38,6 +38,8 @@ extern VSTRING *vstring_strcpy(VSTRING *, const char *);
 extern VSTRING *vstring_strncpy(VSTRING *, const char *, int);
 extern VSTRING *vstring_strcat(VSTRING *, const char *);
 extern VSTRING *vstring_strncat(VSTRING *, const char *, int);
+extern VSTRING *vstring_memcpy(VSTRING *, const char *, int);
+extern VSTRING *vstring_memcat(VSTRING *, const char *, int);
 extern VSTRING *PRINTFLIKE(2, 3) vstring_sprintf(VSTRING *, const char *,...);
 extern VSTRING *PRINTFLIKE(2, 3) vstring_sprintf_append(VSTRING *, const char *,...);
 extern char *vstring_export(VSTRING *);