2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-30 05:38:06 +00:00

snapshot-20010707

This commit is contained in:
Wietse Venema 2001-07-07 00:00:00 -05:00 committed by Viktor Dukhovni
parent 14829b5994
commit 7e8044da83
59 changed files with 3162 additions and 68 deletions

3
postfix/.indent.pro vendored
View File

@ -24,10 +24,10 @@
-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

View 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.

View 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

39
postfix/README_QMQP Normal file
View File

@ -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 (!).

View 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
===========================================

View 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

View 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
@ -10,13 +10,13 @@
}
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

2
postfix/conf/postfix-script-sgid Executable file → Normal file
View File

@ -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

View File

@ -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

View 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).

View 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 >$@

View 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>

View 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.

View 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

122
postfix/html/qmqpd.8.html Normal file
View File

@ -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>

View 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.

View 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 $? >$@

View 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

View 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.

120
postfix/man/man8/qmqpd.8 Normal file
View File

@ -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

View 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

View File

@ -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>/

View 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 \

View 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

View 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

View 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

View File

@ -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
/*--*/

View 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

View 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

View 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);

View 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.

1
postfix/src/qmqpd/.indent.pro vendored Symbolic link
View File

@ -0,0 +1 @@
../../.indent.pro

View File

@ -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

View File

@ -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

645
postfix/src/qmqpd/qmqpd.c Normal file
View File

@ -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);
}

78
postfix/src/qmqpd/qmqpd.h Normal file
View File

@ -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
/*--*/

View File

@ -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);
}

View File

@ -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);
}

View 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,
};

View File

@ -79,11 +79,33 @@
#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",

View 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

View 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),

View 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);

View 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,

View 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,

View 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;

View 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;

View 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

View File

@ -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);
}

View File

@ -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);
}
}
}

View 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;

View 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

View 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 */

View 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);

View File

@ -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);
}

View File

@ -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

View File

@ -175,11 +175,11 @@
/*
/* 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,

View File

@ -67,6 +67,16 @@
/* 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;
@ -174,6 +184,14 @@
/* 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)

View 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 *);