2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-29 05:07:58 +00:00

postfix-2.0.13-20030702

This commit is contained in:
Wietse Venema 2003-07-02 00:00:00 -05:00 committed by Viktor Dukhovni
parent b1771bb6f5
commit 5884ef624c
101 changed files with 3223 additions and 829 deletions

2
postfix/.indent.pro vendored
View File

@ -26,6 +26,8 @@
-TDELIVER_ATTR -TDELIVER_ATTR
-TDELIVER_REQUEST -TDELIVER_REQUEST
-TDICT -TDICT
-TDICT_CIDR
-TDICT_CIDR_ENTRY
-TDICT_DB -TDICT_DB
-TDICT_DBM -TDICT_DBM
-TDICT_DEBUG -TDICT_DEBUG

View File

@ -8189,8 +8189,8 @@ Apologies for any names omitted.
of mail probes, so it will no longer block for in_flow_delay of mail probes, so it will no longer block for in_flow_delay
seconds when mail arrives faster than it is delivered. seconds when mail arrives faster than it is delivered.
Still need to make mail_stream_finish() asynchronous in Still need to make mail_stream_finish() asynchronous in
order to avoid blocking for trigger_timeout seconds when order to avoid blocking for trigger_timeout seconds when the
the queue manager is overwhelmed. Files: global/post_mail.c, queue manager is overwhelmed. Files: global/post_mail.c,
verify/verify.c. verify/verify.c.
Bugfix: removed extraneous sleep() after the last attempt Bugfix: removed extraneous sleep() after the last attempt
@ -8201,6 +8201,42 @@ Apologies for any names omitted.
Bugfix: the stricter postdrop input filter broke "sendmail Bugfix: the stricter postdrop input filter broke "sendmail
-bs". Found by Lutz Jaenicke. File: smtpd/smtpd.c. -bs". Found by Lutz Jaenicke. File: smtpd/smtpd.c.
20030614
Portability: Dropped support for client side LDAP caching.
As of release 2.1.13 OpenLDAP no longer supports client
side caching, it has been deprecated for some time, and
never worked well. Implemented by Victor Duchovni, Morgan
Stanley, and further enhanced by Lamont Jones, HP. Files:
src/util/dict_ldap.c, conf/sample-ldap.cf,
README_FILES/LDAP_README.
Safety: Given suitable invalid database contents, LDAP
lookups can produce too many results, enter an infinite
loop in the expansion of "special result attributes" (LDAP
DNs and LDAP URLs) or just consume excessive server resources
returning large result sets. Three new (per LDAP map)
configuration parameters enable one to set limits on
recursive nesting, result expansion and the server response
"entry" count. Implemented by Victor Duchovni, Morgan
Stanley, further enanced by Lamont Jones, HP. Files:
src/util/dict_ldap.c, conf/sample-ldap.cf,
README_FILES/LDAP_README.
20030616
Feature: in mail delivery status reports, report the sender
address as X-Postfix-Sender. Matthias Andree. File:
bounce/bounce_notify_util.c.
Cleanup: in mail delivery status reports, transform the
original recipient into xtext format as required by RFC
1891. Files: bounce/bounce_notify_util.c, util/xtext.[hc].
Cleanup: more accurate "postfix check" warning for files
that miss one or more of the required mode 02111 execute
permission bits. Matthias Andree. File: conf/postfix-script.
20030618 20030618
After "postfix reload", the master daemon now warns when After "postfix reload", the master daemon now warns when
@ -8208,6 +8244,20 @@ Apologies for any names omitted.
of passing incorrect information to the smtp server. File: of passing incorrect information to the smtp server. File:
master/master_ent.c. master/master_ent.c.
20030619
Feature: the Postfix SMTP server can send all mail into a
proxy server, for example a real-time SPAM filter. This
proxy is supposed to send the mail into another Postfix
SMTP server process for normal delivery. Files: smtpd/smtpd.c
smtpd/smtpd_proxy.[hc].
20030620
Bugfix: a cut-and-paste error caused the proxy server's
354 status code to be reported when a proxy connection
broke during the DATA phase. File: smtpd.c.
20030620 20030620
Bugfix: after the last change to postdrop, postcat no longer Bugfix: after the last change to postdrop, postcat no longer
@ -8217,6 +8267,74 @@ Apologies for any names omitted.
sendmail, "-t" broke multi-line recipient headers. Victor sendmail, "-t" broke multi-line recipient headers. Victor
Duchovni, Morgan Stanley. File: sendmail/sendmail.c. Duchovni, Morgan Stanley. File: sendmail/sendmail.c.
20030621
Workaround: the safe_open(O_CREAT) race condition exploit
avoiding code tries a little harder when it encounters a
race condition. File: util/safe_open.c.
20030623
Non-prod operator precedence bug with detecting end of
DATA. Matthias Andree. File: smtpd/smtpd.c.
20030624
Bugfix: reject_unverified_address() set the defer_if_reject
flag when the verify service was unavailable (which never
happens). Victor Duchovni, Morgan Stanley. File:
smtpd/smtpd_check.c.
New parameters address_verify_poll_{count,delay} that
control how often to poll the address verification service
for the completion of an address verification request.
Specify address_verify_poll_count=1 to implement a crude
form of greylisting, that is, always defer the first delivery
attempt for an unknown address. File: smtpd/smtpd_check.c.
Bugfix: after the last change to postdrop, postcat no longer
recognized non-maildrop queue files as valid. File:
postcat/postcat.c.
20030629
Cleanup: replaced references to "simulated virtual domains"
by "virtual alias domains". Victor Duchovni, Morgan Stanley.
20030630
Feature: smtp_quote_rfc821_envelope=(yes|no) to control
RFC 821 style quoting of MAIL FROM and RCPT TO addresses.
Files: global/mail_params.h, smtp/smtp.c, smtp/smtp_proto.c.
20030701
Bugfix: multi-recipient probes triggered a bug in the SMTP
client. File: smtp/smtp_proto.c.
Feature: enable_original_recipient (default: yes) to control
whether Postfix keeps track of original recipient address
information. Victor Duchovni, Morgan Stanley. Files:
cleanup/cleanup.c, cleanup/cleanup_init.c,
cleanup/cleanup_out_recipient.c, global/log_adhoc.c,
global/mail_copy.c, *qmgr/qmgr_message.c.
Feature: !/pattern/ support for PCRE lookup tables. Victor
Duchovni, Morgan Stanley. Files: util/dict_pcre.c.
Cleanup: allow whitespace after patterns in repexp and pcre
tables. Victor Duchovni, Morgan Stanley. Files:
util/dict_pcre.c, util/dict_regexp.c.
20030702
Feature: CIDR lookup table support, very remotely based on
code by Jozsef Kadlecsik. Files: proto/cidr_table,
util/dict_cidr.[hc].
Feature: TCP lookup table support, finally finished. Files:
proto/tcp_table, proto/dict_tcp.[hc].
Open problems: Open problems:
Low: smtp-source may block when sending large test messages. Low: smtp-source may block when sending large test messages.

View File

@ -154,21 +154,41 @@ parameter below, "server_host", would be defined in main.cf as
the Postfix user. Example: the Postfix user. Example:
ldapsource_bind_pw = postfixpw ldapsource_bind_pw = postfixpw
cache (no) cache (IGNORED with a warning)
Whether to use a client-side cache for the LDAP connection. See cache_expiry (IGNORED with a warning)
ldap_enable_cache(3). It's off by default. cache_size (IGNORED with a warning)
The above parameters are NO LONGER SUPPORTED by Postfix.
Cache support has been dropped from OpenLDAP as of release 2.1.13.
cache_expiry (30 seconds) recursion_limit (1000)
If the client-side cache is enabled, cached results will expire A limit on the nesting depth of DN and URL special result
after this many seconds. attribute evaluation. The limit must be a non-zero positive
number.
cache_size (32768 bytes) expansion_limit (0)
If the client-side cache is enabled, this is its size in bytes. A limit on the total number of result elements returned (as a
comma separated list) by a lookup against the map. A setting of
zero disables the limit. Lookups fail with a temporary error
if the limit is exceeded. Setting the limit to 1 ensures that
lookups do not return multiple values.
size_limit ($expansion_limit)
A limit on the number of LDAP entries returned by any single LDAP
query performed as part of the lookup. A setting of 0 disables
the limit. Expansion of DN and URL references involves nested
LDAP queries, each of which is separately subjected to this
limit.
Note: even a single LDAP entry can generate multiple lookup
results, via multiple result attributes and/or multi-valued
result attributes. This limit caps the per query resource
utilization on the LDAP server, not the final multiplicity of the
lookup result. It is analogous to the "-z" option of "ldapsearch".
dereference (0) dereference (0)
When to dereference LDAP aliases. (Note that this has nothing When to dereference LDAP aliases. (Note that this has nothing
do with Postfix aliases.) The permitted values are those do with Postfix aliases.) The permitted values are those legal
legal for the OpenLDAP/UM LDAP implementations: for the OpenLDAP/UM LDAP implementations:
0 never 0 never
1 when searching 1 when searching

View File

@ -0,0 +1,81 @@
Purpose of the SMTPD pass-through proxy feature
===============================================
The Postfix SMTP server can be configured to forward all mail to
a proxy server, for example, a real-time SPAM filter. The proxy is
supposed to send the mail into another Postfix SMTP server process
for normal delivery.
The proxy server receives only the commands that the Postfix SMTP
server has approved. The proxy server should accept the same MAIL
FROM and RCPT TO command syntax as Postfix, but does not need to
support ESMTP command pipelining.
This feature is meant to be used as follows:
Internet -> smtpd -> proxy -> smtpd -> cleanup -> queue
Postfix Postfix Postfix Postfix
Limitations
===========
When used with a real-time SPAM filter, this approach allows Postfix
to reject mail before the SMTP mail transfer completes, so that
Postfix does not have to send rejected mail back to the sender.
Mail that is not accepted remains the responsibility of the client.
In all other respects this content filtering approach is inferior
to the existing content filter (see FILTER_README) which processes
mail AFTER it is queued, because that gives you full control over
how many filtering processes can be run in parallel.
The problem with real-time content filtering is that the remote
SMTP client expects an SMTP reply within a deadline. As the system
load increases, fewer and fewer CPU cycles remain available to
answer within the deadline, and eventually you either have to stop
accepting mail or you have to accept unfiltered mail.
A possible workaround is to have the proxy take special action when
the deadline is reached: add a distinctive message header that
triggers a Postfix header_checks FILTER action, or send the mail
into Postfix via an alternative Postfix SMTP server that always
turns on content filtering.
Configuration parameters
========================
Parameters that control proxying:
smtpd_proxy_filter (syntax: host:port)
The host and TCP port of the SMTP proxy server. When no host
or host: is specified, localhost is assumed.
smtpd_proxy_timeout (default: 100s)
Timeout for connecting to the SMTP proxy server and for sending
and receiving data. All proxy errors are logged to the maillog
file, but the client sees "451 Error: queue file write error".
smtpd_proxy_ehlo (default: $myhostname)
The hostname to use when sending an EHLO command to the SMTP
proxy server.
Testing the SMTP pass-through proxy feature
===========================================
The following example sets up a null proxy, that is, the Postfix
SMTP server gives the mail directly to another Postfix SMTP server
process.
/etc/postfix/master.cf
smtp inet n - n - - smtpd
-o smtpd_proxy_filter=26
26 inet n - n - - smtpd
The result is as follows:
Internet -> smtpd on port 25 -> smtpd on port 26 -> cleanup -> queue
This configuration is sufficient for stress testing.

View File

@ -8,7 +8,7 @@ Purpose of this software
You can use the virtual delivery agent for mailbox delivery of some You can use the virtual delivery agent for mailbox delivery of some
or all domains that are handled by a machine. or all domains that are handled by a machine.
This mechanism is different from simulated virtual domains. Those This mechanism is different from virtual alias domains. Those
are implemented by translating every recipient address into a are implemented by translating every recipient address into a
different address. For that, see the virtual(5) manual page. different address. For that, see the virtual(5) manual page.

View File

@ -22,6 +22,51 @@ snapshot release). Patches change the patchlevel and the release
date. Snapshots change only the release date, unless they include date. Snapshots change only the release date, unless they include
the same bugfixes as a patch release. the same bugfixes as a patch release.
Incompatible changes with Postfix snapshot 2.0.13-20030702
==========================================================
Support for client side LDAP caching is gone. OpenLDAP 2.1.13 and
later no longer support it, and the feature never worked well.
Postfix now ignores cache controlling parameters in an LDAP
configuration file and logs a warning. Credits to Victor Duchovni
and Lamont Jones.
Major changes with Postfix snapshot 2.0.13-20030702
===================================================
The Postfix SMTP server can be configured to send all mail into a
proxy server, for example a real-time SPAM filter. This proxy is
expected to send the mail into another Postfix SMTP server process
for normal delivery. See the SMTPD_PROXY_README file for details.
Improved LDAP client robustness. Given suitable invalid database
contents, LDAP lookups can produce too many results, enter an
infinite loop in the expansion of "special result attributes" (LDAP
DNs and LDAP URLs) or can simply consume excessive server resources.
Credits to Victor Duchovni and Lamont Jones.
New CIDR-based lookup table, remotely based on code by Jozsef
Kadlecsik. For details and examples, see "man cidr_table".
The TCP-based client-server table lookup protocol is finished.
For details and examples, see "man tcp_table". This will allow you
to implement your own greylisting.
Support for !/pattern/ (negative matches) in PCRE lookup tables by
Victor Duchovni. See "man pcre_table" for more.
New enable_original_recipient parameter (default: yes) to control
whether Postfix keeps track of original recipient address information.
If this is turned off Postfix produces no X-Original-To: headers
and ignores the original recipient when eliminating duplicates
after virtual alias expansion. Code by Victor Duchovni.
Finer control over how long the SMTP server waits for address
verification probes to complete. address_verify_poll_{count,delay}
control how often to query the verify server and how long to wait
between queries. Specify address_verify_poll_count=1 to implement
a crude form of greylisting.
Major changes with Postfix snapshot 2.0.11-20030611 Major changes with Postfix snapshot 2.0.11-20030611
=================================================== ===================================================

View File

@ -108,6 +108,9 @@
# A network address is a sequence of one or more # A network address is a sequence of one or more
# octets separated by ".". # octets separated by ".".
# #
# NOTE: use the cidr lookup table type if you want to
# specify arbitrary network blocks.
#
# ACTIONS # ACTIONS
# [45]NN text # [45]NN text
# Reject the address etc. that matches the pattern, # Reject the address etc. that matches the pattern,
@ -207,8 +210,9 @@
# The table format does not understand quoting conventions. # The table format does not understand quoting conventions.
# #
# SEE ALSO # SEE ALSO
# postmap(1) create mapping table # postmap(1) create lookup table
# smtpd(8) smtp server # smtpd(8) smtp server
# cidr_table(5) format of CIDR tables
# pcre_table(5) format of PCRE tables # pcre_table(5) format of PCRE tables
# regexp_table(5) format of POSIX regular expression tables # regexp_table(5) format of POSIX regular expression tables
# #

77
postfix/conf/cidr_table Normal file
View File

@ -0,0 +1,77 @@
# CIDR_TABLE(5) CIDR_TABLE(5)
#
# NAME
# cidr_table - format of Postfix CIDR tables
#
# SYNOPSIS
# postmap -q "string" cidr:/etc/postfix/filename
#
# postmap -q - cidr:/etc/postfix/filename <inputfile
#
# DESCRIPTION
# The Postfix mail system uses optional access control
# tables. These tables are usually in dbm or db format.
# Alternatively, access control tables can be specified in
# CIDR form.
#
# To find out what types of lookup tables your Postfix sys-
# tem supports use the postconf -m command.
#
# To test lookup tables, use the postmap command as
# described in the SYNOPSIS above.
#
# TABLE FORMAT
# The general form of a Postfix CIDR table is:
#
# network_address/network_mask result
# When a search string matches the specified network
# block, use the corresponding result value.
#
# network_address result
# When a search string matches the specified network
# address, use the corresponding result value.
#
# blank lines and comments
# Empty lines and whitespace-only lines are ignored,
# as are lines whose first non-whitespace character
# is a `#'.
#
# multi-line text
# A logical line starts with non-whitespace text. A
# line that starts with whitespace continues a logi-
# cal line.
#
# Patterns are applied in the order as specified in the
# table, until a pattern is found that matches the search
# string.
#
# EXAMPLE SMTPD ACCESS MAP
# /etc/postfix/main.cf:
# smtpd_client_restrictions = ... cidr:/etc/postfix/client_cidr ...
#
# /etc/postfix/client_cidr:
# # Rule order matters. Put more specific whitelist entries
# # before more general blacklist entries.
# 192.168.1.1 OK
# 192.168.0.0/16 REJECT
#
# SEE ALSO
# regexp_table(5) format of regular expression tables
# pcre_table(5) format of PCRE tables
# tcp_table(5) TCP client/server table lookup protocol
#
# AUTHOR(S)
# The CIDR table lookup code was originally written by:
# Jozsef Kadlecsik
# kadlec@blackhole.kfki.hu
# KFKI Research Institute for Particle and Nuclear Physics
# POB. 49
# 1525 Budapest, Hungary
#
# Adopted and adapted by:
# Wietse Venema
# IBM T.J. Watson Research
# P.O. Box 704
# Yorktown Heights, NY 10598, USA
#
# CIDR_TABLE(5)

View File

@ -4,8 +4,6 @@
# pcre_table - format of Postfix PCRE tables # pcre_table - format of Postfix PCRE tables
# #
# SYNOPSIS # SYNOPSIS
# pcre:/etc/postfix/filename
#
# postmap -q "string" pcre:/etc/postfix/filename # postmap -q "string" pcre:/etc/postfix/filename
# #
# postmap -q - pcre:/etc/postfix/filename <inputfile # postmap -q - pcre:/etc/postfix/filename <inputfile
@ -25,8 +23,10 @@
# The general form of a PCRE table is: # The general form of a PCRE table is:
# #
# /pattern/flags result # /pattern/flags result
# When pattern matches a search string, use the cor- #
# responding result value. # !/pattern/flags result
# When pattern matches (does not match) a search
# string, use the corresponding result value.
# #
# blank lines and comments # blank lines and comments
# Empty lines and whitespace-only lines are ignored, # Empty lines and whitespace-only lines are ignored,
@ -40,9 +40,12 @@
# #
# if /pattern/flags # if /pattern/flags
# #
# if !/pattern/flags
#
# endif Examine the lines between if..endif only if pattern # endif Examine the lines between if..endif only if pattern
# matches. The if..endif can nest. Do not prepend # matches (does not match). The if..endif can nest.
# whitespace to patterns inside if..endif. # Do not prepend whitespace to patterns inside
# if..endif.
# #
# Each pattern is a perl-like regular expression. The # Each pattern is a perl-like regular expression. The
# expression delimiter can be any character, except whites- # expression delimiter can be any character, except whites-
@ -131,7 +134,10 @@
# into the result string is possible using the conventional # into the result string is possible using the conventional
# perl syntax ($1, $2, etc.). The macros in the result # perl syntax ($1, $2, etc.). The macros in the result
# string may need to be written as ${n} or $(n) if they # string may need to be written as ${n} or $(n) if they
# aren't followed by whitespace. # aren't followed by whitespace. Since negated patterns
# (those preceded by !) return a result when the expression
# does not match, substitutions are not available for
# negated patterns.
# #
# EXAMPLE SMTPD ACCESS MAP # EXAMPLE SMTPD ACCESS MAP
# # Protect your outgoing majordomo exploders # # Protect your outgoing majordomo exploders
@ -160,6 +166,8 @@
# #
# SEE ALSO # SEE ALSO
# regexp_table(5) format of POSIX regular expression tables # regexp_table(5) format of POSIX regular expression tables
# cidr_table(5) format of CIDR tables
# tcp_table(5) TCP client/server table lookup protocol
# #
# AUTHOR(S) # AUTHOR(S)
# The PCRE table lookup code was originally written by: # The PCRE table lookup code was originally written by:

View File

@ -92,6 +92,7 @@ $config_directory/LICENSE:f:root:-:644
$config_directory/access:f:root:-:644:p $config_directory/access:f:root:-:644:p
$config_directory/aliases:f:root:-:644:p $config_directory/aliases:f:root:-:644:p
$config_directory/canonical:f:root:-:644:p $config_directory/canonical:f:root:-:644:p
$config_directory/cidr_table:f:root:-:644:p
$config_directory/main.cf:f:root:-:644:p $config_directory/main.cf:f:root:-:644:p
$config_directory/main.cf.default:f:root:-:644 $config_directory/main.cf.default:f:root:-:644
$config_directory/makedefs.out:f:root:-:644 $config_directory/makedefs.out:f:root:-:644
@ -100,6 +101,7 @@ $config_directory/pcre_table:f:root:-:644:p
$config_directory/postfix-files:f:root:-:644 $config_directory/postfix-files:f:root:-:644
$config_directory/regexp_table:f:root:-:644:p $config_directory/regexp_table:f:root:-:644:p
$config_directory/relocated:f:root:-:644:p $config_directory/relocated:f:root:-:644:p
$config_directory/tcp_table:f:root:-:644:p
$config_directory/transport:f:root:-:644:p $config_directory/transport:f:root:-:644:p
$config_directory/virtual:f:root:-:644:p $config_directory/virtual:f:root:-:644:p
$config_directory/postfix-script:f:root:-:755 $config_directory/postfix-script:f:root:-:755
@ -121,9 +123,11 @@ $manpage_directory/man1/sendmail.1:f:root:-:644
$manpage_directory/man5/access.5:f:root:-:644 $manpage_directory/man5/access.5:f:root:-:644
$manpage_directory/man5/aliases.5:f:root:-:644 $manpage_directory/man5/aliases.5:f:root:-:644
$manpage_directory/man5/canonical.5:f:root:-:644 $manpage_directory/man5/canonical.5:f:root:-:644
$manpage_directory/man5/cidr_table.5:f:root:-:644
$manpage_directory/man5/pcre_table.5:f:root:-:644 $manpage_directory/man5/pcre_table.5:f:root:-:644
$manpage_directory/man5/regexp_table.5:f:root:-:644 $manpage_directory/man5/regexp_table.5:f:root:-:644
$manpage_directory/man5/relocated.5:f:root:-:644 $manpage_directory/man5/relocated.5:f:root:-:644
$manpage_directory/man5/tcp_table.5:f:root:-:644
$manpage_directory/man5/transport.5:f:root:-:644 $manpage_directory/man5/transport.5:f:root:-:644
$manpage_directory/man5/virtual.5:f:root:-:644 $manpage_directory/man5/virtual.5:f:root:-:644
$manpage_directory/man8/bounce.8:f:root:-:644 $manpage_directory/man8/bounce.8:f:root:-:644

View File

@ -181,7 +181,7 @@ check)
find $command_directory/postqueue $command_directory/postdrop \ find $command_directory/postqueue $command_directory/postdrop \
-prune ! -perm -02111 \ -prune ! -perm -02111 \
-exec $WARN not set-gid: {} \; -exec $WARN not set-gid or not owner+group+world executable: {} \;
for name in `ls -d $queue_directory/* | \ for name in `ls -d $queue_directory/* | \
egrep '/(bin|etc|lib|usr)$'` ; \ egrep '/(bin|etc|lib|usr)$'` ; \

View File

@ -4,8 +4,6 @@
# regexp_table - format of Postfix regular expression tables # regexp_table - format of Postfix regular expression tables
# #
# SYNOPSIS # SYNOPSIS
# regexp:/etc/postfix/filename
#
# postmap -q "string" regexp:/etc/postfix/filename # postmap -q "string" regexp:/etc/postfix/filename
# #
# postmap -q - regexp:/etc/postfix/filename <inputfile # postmap -q - regexp:/etc/postfix/filename <inputfile
@ -77,7 +75,10 @@
# Substitution of substrings from the matched expression # Substitution of substrings from the matched expression
# into the result string is possible using $1, $2, etc.. The # into the result string is possible using $1, $2, etc.. The
# macros in the result string may need to be written as ${n} # macros in the result string may need to be written as ${n}
# or $(n) if they aren't followed by whitespace. # or $(n) if they aren't followed by whitespace. Since
# negated patterns (those preceded by !) return a result
# when the expression does not match, substitutions are not
# available for negated patterns.
# #
# EXAMPLE SMTPD ACCESS MAP # EXAMPLE SMTPD ACCESS MAP
# # Disallow sender-specified routing. This is a must if you relay mail # # Disallow sender-specified routing. This is a must if you relay mail
@ -106,6 +107,8 @@
# #
# SEE ALSO # SEE ALSO
# pcre_table(5) format of PCRE tables # pcre_table(5) format of PCRE tables
# cidr_table(5) format of CIDR tables
# tcp_table(5) TCP client/server table lookup protocol
# #
# AUTHOR(S) # AUTHOR(S)
# The regexp table lookup code was originally written by: # The regexp table lookup code was originally written by:

View File

@ -68,19 +68,40 @@
# #
#ldap_bind_pw = #ldap_bind_pw =
# The ldap_cache parameter specifies whether or not to turn on client-side #ldap_cache (IGNORED with a warning)
# caching. #ldap_cache_expiry (IGNORED with a warning)
#ldap_cache_size (IGNORED with a warning)
# #
#ldap_cache = no # The above parameters are NO LONGER SUPPORTED by Postfix.
# Cache support has been dropped from OpenLDAP as of release 2.1.13.
# The ldap_cache_expiry parameter specifies how many seconds to cache results # The ldap_recursion_limit parameter specifies a limit on the nesting
# for (if ldap_cache=yes) # depth of DN and URL special result attribute evaluation. The limit
# must be a non-zero positive number. The default value is 1000.
# #
#ldap_cache_expiry = 30 #ldap_recursion_limit = 1000
# The ldap_cache_size parameter specifies the cache size, in bytes. # The ldap_expansion_limit parameter specifies a limit on the total
# number of result elements returned (as a comma separated list) by a lookup
# against the map. A setting of 0 disables the limit. Lookups fail with a
# temporary error if the limit is exceeded. Setting the limit to 1 ensures
# that lookups do not return multiple values. The default value is 0.
# #
#ldap_cache_size = 32768 #ldap_expansion_limit = 0
# The ldap_size_limit parameter specifies a limit on the number of LDAP
# entries returned by any single LDAP query performed as part of the
# lookup. A setting of 0 disables the limit. Expansion of DN and URL
# references involves nested LDAP queries, each of which is separately
# subjected to this limit. The default value is $ldap_expansion_limit.
#
# Note: even a single LDAP entry can generate multiple lookup results, via
# multiple result attributes and/or multi-valued result attributes.
# This limit caps the per query resource utilization on the LDAP server,
# not the final multiplicity of the lookup result. It is analogous to the
# "-z" option of "ldapsearch".
#
#ldap_size_limit = $ldap_expansion_limit
# The ldap_deference parameter specifies how to handle LDAP aliases. See the # The ldap_deference parameter specifies how to handle LDAP aliases. See the
# ldap_open(3) man page. # ldap_open(3) man page.

View File

@ -71,6 +71,18 @@ default_transport = smtp
# #
double_bounce_sender = double-bounce double_bounce_sender = double-bounce
# The enable_original_recipient parameter enables support for the
# X-Original-To message header, which is needed for multi-recipient
# mailboxes. When this parameter is set to yes, the cleanup daemon
# performs duplicate elimination on distinct pairs of (original
# recipient, rewritten recipient), and generates non-empty original
# recipient queue file records. When this parameter is set to no,
# the cleanup daemon performs duplicate elimination on the rewritten
# recipient address only, and generates empty original recipient
# queue file records. The default value is "yes".
#
enable_original_recipient = yes
# The export_environment parameter specifies the names of environment # The export_environment parameter specifies the names of environment
# parameters that Postfix will export to non-Postfix processes. # parameters that Postfix will export to non-Postfix processes.
# #

View File

@ -215,6 +215,41 @@ smtpd_soft_error_limit = 10
# #
smtpd_hard_error_limit = 20 smtpd_hard_error_limit = 20
#
# PASS-THROUGH PROXY OPERATION
#
# The smtpd_proxy_filter parameter specifies the host:port of a proxy
# filter, for example a real-time SPAM filter. The proxy receives
# all mail from the Postfix SMTP server, and is supposed to give the
# result to another Postfix SMTP server process.
#
# WARNING: the proxy filter must reply within a fixed deadline or
# else the remote SMTP client times out and mail duplication happens.
# This becomes a problem as mail load increases so that fewer and
# fewer CPU cycles remain available to mead the fixed deadline.
#
# Specify host:port. When no host or host: are specified, the local
# machine is assumed.
#
smtpd_proxy_filter =
# The smtpd_proxy_timeout parameter specifies a deadline for
# connecting to a proxy filter and for sending or receiving information.
# When a connection fails the client gets a generic error message
# while more detailed information is logged to the maillog file.
#
# Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).
# The default time unit is s (seconds).
#
smtpd_proxy_timeout = 100s
# The smtpd_proxy_ehlo parameter specifies how the Postfix SMTP
# server announces itself to the proxy filter. By default, the
# Postfix hostname is used.
#
smtpd_proxy_ehlo = $myhostname
# #
# UCE RESTRICTIONS # UCE RESTRICTIONS
# #

View File

@ -8,22 +8,6 @@
# ADDRESS VERIFICATION (see also: verify(8) and SENDER_VERIFICATION_README) # ADDRESS VERIFICATION (see also: verify(8) and SENDER_VERIFICATION_README)
# #
# The address_verify_map configuration parameter specifies an optional
# table for persistent recipient status storage. The file is opened
# before the process enters a chroot jail and before it drops root
# privileges.
#
# By default, the information is kept in volatile memory, and is lost
# after postfix reload or postfix stop.
#
# Specify a pathname in a file system that will not fill up. If the
# database becomes corrupted, the world comes to an end. To recover
# you have to delete the file and do "postfix reload".
#
#address_verify_map = hash:/etc/postfix/verify
#address_verify_map = btree:/etc/postfix/verify
address_verify_map =
# The address_verify_sender configuration parameter specifies the # The address_verify_sender configuration parameter specifies the
# sender address that Postfix will use in address verification probe # sender address that Postfix will use in address verification probe
# messages. # messages.
@ -38,6 +22,40 @@ address_verify_map =
#address_verify_sender = postmaster@my.domain #address_verify_sender = postmaster@my.domain
address_verify_sender = postmaster address_verify_sender = postmaster
# The address_verify_poll_count parameter specifies how many times
# to query the address verification service for completion of an
# address verification request. Specify 0 to implement a simple form
# of greylisting, that is, always defer the first delivery request
# from an unknown sender address.
#
#address_verify_poll_count = 0
address_verify_poll_count = 3
# The address_verify_poll_delay parameter specifies how long to wait
# after querying the address verification service for completion of
# an address verification request.
#
address_verify_poll_delay = 3
#
# CACHE CONTROL
#
# The address_verify_map configuration parameter specifies an optional
# table for persistent address status storage. The file is opened
# before the process enters a chroot jail and before it drops root
# privileges.
#
# By default, the information is kept in volatile memory, and is lost
# after postfix reload or postfix stop.
#
# Specify a pathname in a file system that will not fill up. If the
# database becomes corrupted, the world comes to an end. To recover
# you have to delete the file and do "postfix reload".
#
#address_verify_map = hash:/etc/postfix/verify
#address_verify_map = btree:/etc/postfix/verify
address_verify_map =
# The address_verify_positive_expire_time configuration parameter # The address_verify_positive_expire_time configuration parameter
# specifies the amount of time after which a known to be good address # specifies the amount of time after which a known to be good address
# expires. # expires.

85
postfix/conf/tcp_table Normal file
View File

@ -0,0 +1,85 @@
# TCP_TABLE(5) TCP_TABLE(5)
#
# NAME
# tcp_table - Postfix client/server table lookup protocol
#
# SYNOPSIS
# postmap -q "string" tcp:host:port
#
# postmap -q - regexp:host:port <inputfile
#
# DESCRIPTION
# The Postfix mail system uses optional tables for address
# rewriting or mail routing. These tables are usually in dbm
# or db format. Alternatively, lookup tables can be speci-
# fied as a TCP client/server pair.
#
# To find out what types of lookup tables your Postfix sys-
# tem supports use the postconf -m command.
#
# To test lookup tables, use the postmap command as
# described in the SYNOPSIS above.
#
# PROTOCOL DESCRIPTION
# The TCP map class implements a very simple protocol: the
# client sends a request, and the server sends one reply.
# Requests and replies are sent as one line of ASCII text,
# terminated by the ASCII newline character. Request and
# reply parameters (see below) are separated by whitespace.
#
# ENCODING
# In request and reply parameters, the character % and any
# non-printing and whitespace characters must be replaced by
# %XX, XX being the corresponding ASCII hexadecimal charac-
# ter value. The hexadecimal codes can be specified in any
# case (upper, lower, mixed).
#
# REQUEST FORMAT
# Requests are strings that serve as lookup key in the simu-
# lated table.
#
# get SPACE key NEWLINE
# Look up data under the specified key.
#
# put SPACE key SPACE value NEWLINE
# This request is currently not implemented.
#
# REPLY FORMAT
# Replies must be no longer than 4096 characters including
# the newline terminator, and must have the following form:
#
# 500 SPACE optional-text NEWLINE
# In case of a lookup request, the requested data
# does not exist. In case of an update request, the
# request was rejected.
#
# 400 SPACE optional-text NEWLINE
# This indicates an error condition. The text gives
# the nature of the problem. The client should retry
# the request later.
#
# 200 SPACE text NEWLINE
# The request was successful. In the case of a lookup
# request, the text contains an encoded version of
# the requested data. Otherwise the text is
# optional.
#
# SEE ALSO
# regexp_table(5) format of regular expression tables
# pcre_table(5) format of PCRE tables
# cidr_table(5) format of CIDR tables
#
# BUGS
# Only the lookup method is currently implemented.
#
# LICENSE
# 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
#
# TCP_TABLE(5)

View File

@ -16,6 +16,25 @@
# relay hosts. The mapping is used by the trivial-rewrite(8) # relay hosts. The mapping is used by the trivial-rewrite(8)
# daemon. # daemon.
# #
# This mapping overrides the default routing that is built
# into Postfix:
#
# mydestination
# A list of domains that is by default delivered via
# $local_transport.
#
# virtual_mailbox_domains
# A list of domains that is by default delivered via
# $virtual_transport.
#
# relay_domains
# A list of domains that is by default delivered via
# $relay_transport.
#
# any other destination
# Mail for any other destination is by default deliv-
# ered via $default_transport.
#
# Normally, the transport table is specified as a text file # Normally, the transport table is specified as a text file
# that serves as input to the postmap(1) command. The # that serves as input to the postmap(1) command. The
# result, an indexed file in dbm or db format, is used for # result, an indexed file in dbm or db format, is used for

View File

@ -109,6 +109,9 @@ ACCESS(5) ACCESS(5)
A network address is a sequence of one or more A network address is a sequence of one or more
octets separated by ".". octets separated by ".".
NOTE: use the <b>cidr</b> lookup table type if you want to
specify arbitrary network blocks.
<b>ACTIONS</b> <b>ACTIONS</b>
[<b>45</b>]<i>NN</i> <i>text</i> [<b>45</b>]<i>NN</i> <i>text</i>
Reject the address etc. that matches the pattern, Reject the address etc. that matches the pattern,
@ -208,8 +211,9 @@ ACCESS(5) ACCESS(5)
The table format does not understand quoting conventions. The table format does not understand quoting conventions.
<b>SEE</b> <b>ALSO</b> <b>SEE</b> <b>ALSO</b>
<a href="postmap.1.html">postmap(1)</a> create mapping table <a href="postmap.1.html">postmap(1)</a> create lookup table
<a href="smtpd.8.html">smtpd(8)</a> smtp server <a href="smtpd.8.html">smtpd(8)</a> smtp server
cidr_table(5) format of CIDR tables
<a href="pcre_table.5.html">pcre_table(5)</a> format of PCRE tables <a href="pcre_table.5.html">pcre_table(5)</a> format of PCRE tables
<a href="regexp_table.5.html">regexp_table(5)</a> format of POSIX regular expression tables <a href="regexp_table.5.html">regexp_table(5)</a> format of POSIX regular expression tables

View File

@ -171,6 +171,14 @@ CLEANUP(8) CLEANUP(8)
Address mapping lookup table for sender and recipi- Address mapping lookup table for sender and recipi-
ent addresses in envelopes and headers. ent addresses in envelopes and headers.
<b>enable</b><i>_</i><b>original</b><i>_</i><b>recipient</b>
Enable support for the X-Original-To message
header, which is needed for multi-recipient mail-
boxes. When this is enabled, Postfix performs
duplicate elimination on (original recipient,
rewritten recipient) pairs, instead of looking at
the rewritten recipient only.
<b>recipient</b><i>_</i><b>canonical</b><i>_</i><b>maps</b> <b>recipient</b><i>_</i><b>canonical</b><i>_</i><b>maps</b>
Address mapping lookup table for envelope and Address mapping lookup table for envelope and
header recipient addresses. header recipient addresses.

View File

@ -2618,8 +2618,9 @@ the <b>virtual_mailbox_maps</b> parameter.
<p> <p>
If you want to deliver the domain as a Postfix simulated <a If you want to deliver the domain as a <a href="virtual.8.html">
href="virtual.8.html">virtual</a>(5) domain, then you should list virtual</a>(5) alias domain, where each address is aliased to
a real local or remote address, then you should list
the virtual domain name in the tables specified with the the virtual domain name in the tables specified with the
<b>virtual_alias_domains</b> parameter instead. <b>virtual_alias_domains</b> parameter instead.
@ -2638,7 +2639,7 @@ Solutions:
<ul> <ul>
<li>Specify a simulated virtual domain as per the <li>Specify a virtual alias domain as per the
<a href="virtual.5.html">virtual(5)</a> manual page. <a href="virtual.5.html">virtual(5)</a> manual page.
<p> <p>
@ -2715,6 +2716,15 @@ that the mail was sent to.
Answer: Postfix logs the original recipient address in the Answer: Postfix logs the original recipient address in the
<b>X-Original-To:</b> message header. <b>X-Original-To:</b> message header.
<p>
This requires that the <b>enable_original_recipient</b> parameter
is not changed from its default value of <b>yes</b>. With
<b>enable_original_recipient</b> set to <b>no</b>, messages to
multiple recipients in the domain will only be delivered to
the first recipient, and the <b>X-Original-To:<b> header will
not be added to the message.
<hr> <hr>
<a name="masquerade"><h3>Address masquerading with exceptions</h3></a> <a name="masquerade"><h3>Address masquerading with exceptions</h3></a>

View File

@ -5,8 +5,6 @@ PCRE_TABLE(5) PCRE_TABLE(5)
pcre_table - format of Postfix PCRE tables pcre_table - format of Postfix PCRE tables
<b>SYNOPSIS</b> <b>SYNOPSIS</b>
<b>pcre:/etc/postfix/</b><i>filename</i>
<b>postmap</b> <b>-q</b> <b>"</b><i>string</i><b>"</b> <b>pcre:/etc/postfix/</b><i>filename</i> <b>postmap</b> <b>-q</b> <b>"</b><i>string</i><b>"</b> <b>pcre:/etc/postfix/</b><i>filename</i>
<b>postmap</b> <b>-q</b> <b>-</b> <b>pcre:/etc/postfix/</b><i>filename</i> &lt;<i>inputfile</i> <b>postmap</b> <b>-q</b> <b>-</b> <b>pcre:/etc/postfix/</b><i>filename</i> &lt;<i>inputfile</i>
@ -26,8 +24,10 @@ PCRE_TABLE(5) PCRE_TABLE(5)
The general form of a PCRE table is: The general form of a PCRE table is:
<b>/</b><i>pattern</i><b>/</b><i>flags</i> <i>result</i> <b>/</b><i>pattern</i><b>/</b><i>flags</i> <i>result</i>
When <i>pattern</i> matches a search string, use the cor-
responding <i>result</i> value. <b>!/</b><i>pattern</i><b>/</b><i>flags</i> <i>result</i>
When <i>pattern</i> matches (does not match) a search
string, use the corresponding <i>result</i> value.
blank lines and comments blank lines and comments
Empty lines and whitespace-only lines are ignored, Empty lines and whitespace-only lines are ignored,
@ -41,9 +41,12 @@ PCRE_TABLE(5) PCRE_TABLE(5)
<b>if</b> <b>/</b><i>pattern</i><b>/</b><i>flags</i> <b>if</b> <b>/</b><i>pattern</i><b>/</b><i>flags</i>
<b>if</b> <b>!/</b><i>pattern</i><b>/</b><i>flags</i>
<b>endif</b> Examine the lines between <b>if</b>..<b>endif</b> only if <i>pattern</i> <b>endif</b> Examine the lines between <b>if</b>..<b>endif</b> only if <i>pattern</i>
matches. The <b>if</b>..<b>endif</b> can nest. Do not prepend matches (does not match). The <b>if</b>..<b>endif</b> can nest.
whitespace to patterns inside <b>if</b>..<b>endif</b>. Do not prepend whitespace to patterns inside
<b>if</b>..<b>endif</b>.
Each pattern is a perl-like regular expression. The Each pattern is a perl-like regular expression. The
expression delimiter can be any character, except whites- expression delimiter can be any character, except whites-
@ -132,7 +135,10 @@ PCRE_TABLE(5) PCRE_TABLE(5)
into the result string is possible using the conventional into the result string is possible using the conventional
perl syntax ($1, $2, etc.). The macros in the result perl syntax ($1, $2, etc.). The macros in the result
string may need to be written as ${n} or $(n) if they string may need to be written as ${n} or $(n) if they
aren't followed by whitespace. aren't followed by whitespace. Since negated patterns
(those preceded by <b>!</b>) return a result when the expression
does not match, substitutions are not available for
negated patterns.
<b>EXAMPLE</b> <b>SMTPD</b> <b>ACCESS</b> <b>MAP</b> <b>EXAMPLE</b> <b>SMTPD</b> <b>ACCESS</b> <b>MAP</b>
# Protect your outgoing majordomo exploders # Protect your outgoing majordomo exploders
@ -161,6 +167,8 @@ PCRE_TABLE(5) PCRE_TABLE(5)
<b>SEE</b> <b>ALSO</b> <b>SEE</b> <b>ALSO</b>
<a href="regexp_table.5.html">regexp_table(5)</a> format of POSIX regular expression tables <a href="regexp_table.5.html">regexp_table(5)</a> format of POSIX regular expression tables
cidr_table(5) format of CIDR tables
tcp_table(5) TCP client/server table lookup protocol
<b>AUTHOR(S)</b> <b>AUTHOR(S)</b>
The PCRE table lookup code was originally written by: The PCRE table lookup code was originally written by:

View File

@ -5,8 +5,6 @@ REGEXP_TABLE(5) REGEXP_TABLE(5)
regexp_table - format of Postfix regular expression tables regexp_table - format of Postfix regular expression tables
<b>SYNOPSIS</b> <b>SYNOPSIS</b>
<b>regexp:/etc/postfix/</b><i>filename</i>
<b>postmap</b> <b>-q</b> <b>"</b><i>string</i><b>"</b> <b>regexp:/etc/postfix/</b><i>filename</i> <b>postmap</b> <b>-q</b> <b>"</b><i>string</i><b>"</b> <b>regexp:/etc/postfix/</b><i>filename</i>
<b>postmap</b> <b>-q</b> <b>-</b> <b>regexp:/etc/postfix/</b><i>filename</i> &lt;<i>inputfile</i> <b>postmap</b> <b>-q</b> <b>-</b> <b>regexp:/etc/postfix/</b><i>filename</i> &lt;<i>inputfile</i>
@ -78,7 +76,10 @@ REGEXP_TABLE(5) REGEXP_TABLE(5)
Substitution of substrings from the matched expression Substitution of substrings from the matched expression
into the result string is possible using $1, $2, etc.. The into the result string is possible using $1, $2, etc.. The
macros in the result string may need to be written as ${n} macros in the result string may need to be written as ${n}
or $(n) if they aren't followed by whitespace. or $(n) if they aren't followed by whitespace. Since
negated patterns (those preceded by <b>!</b>) return a result
when the expression does not match, substitutions are not
available for negated patterns.
<b>EXAMPLE</b> <b>SMTPD</b> <b>ACCESS</b> <b>MAP</b> <b>EXAMPLE</b> <b>SMTPD</b> <b>ACCESS</b> <b>MAP</b>
# Disallow sender-specified routing. This is a must if you relay mail # Disallow sender-specified routing. This is a must if you relay mail
@ -107,6 +108,8 @@ REGEXP_TABLE(5) REGEXP_TABLE(5)
<b>SEE</b> <b>ALSO</b> <b>SEE</b> <b>ALSO</b>
<a href="pcre_table.5.html">pcre_table(5)</a> format of PCRE tables <a href="pcre_table.5.html">pcre_table(5)</a> format of PCRE tables
cidr_table(5) format of CIDR tables
tcp_table(5) TCP client/server table lookup protocol
<b>AUTHOR(S)</b> <b>AUTHOR(S)</b>
The regexp table lookup code was originally written by: The regexp table lookup code was originally written by:

View File

@ -318,8 +318,8 @@ href="cleanup.8.html">cleanup</a> daemon uses the <a
href="virtual.5.html">virtual alias</a> table to redirect mail for all href="virtual.5.html">virtual alias</a> table to redirect mail for all
recipients, local or remote. The mapping affects only envelope recipients, local or remote. The mapping affects only envelope
recipients; it has no effect on message headers or envelope senders. recipients; it has no effect on message headers or envelope senders.
Virtual alias lookups are useful to redirect mail for simulated Virtual alias lookups are useful to redirect mail for virtual
virtual domains to real user mailboxes, and to redirect mail for alias domains to real user mailboxes, and to redirect mail for
domains that no longer exist. Virtual alias lookups can also be domains that no longer exist. Virtual alias lookups can also be
used to transform <i> Firstname.Lastname </i> back into UNIX login used to transform <i> Firstname.Lastname </i> back into UNIX login
names, although it seems that local <a href="#aliases">aliases</a> names, although it seems that local <a href="#aliases">aliases</a>

View File

@ -25,11 +25,6 @@ SMTP(8) SMTP(8)
preference, and connects to each listed address until it preference, and connects to each listed address until it
finds a server that responds. finds a server that responds.
When the domain or host is specified as a comma/whitespace
separated list, the SMTP client repeats the above process
for all destinations until it finds a server that
responds.
Once the SMTP client has received the server greeting ban- Once the SMTP client has received the server greeting ban-
ner, no error will cause it to proceed to the next address ner, no error will cause it to proceed to the next address
on the mail exchanger list. Instead, the message is either on the mail exchanger list. Instead, the message is either
@ -148,6 +143,10 @@ SMTP(8) SMTP(8)
<b>smtp</b><i>_</i><b>helo</b><i>_</i><b>name</b> <b>smtp</b><i>_</i><b>helo</b><i>_</i><b>name</b>
The hostname to be used in HELO and EHLO commands. The hostname to be used in HELO and EHLO commands.
<b>smtp</b><i>_</i><b>quote</b><i>_</i><b>rfc821</b><i>_</i><b>envelope</b>
Whether or not to quote MAIL FROM and RCPT TO
addresses as per the rules laid out in <a href="http://www.faqs.org/rfcs/rfc821.html">RFC 821</a>.
<b>smtp</b><i>_</i><b>skip</b><i>_</i><b>4xx</b><i>_</i><b>greeting</b> <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. Skip servers that greet us with a 4xx status code.

View File

@ -114,6 +114,25 @@ SMTPD(8) SMTPD(8)
<b>reject</b><i>_</i><b>sender</b><i>_</i><b>login</b><i>_</i><b>mismatch</b> sender anti-spoofing <b>reject</b><i>_</i><b>sender</b><i>_</i><b>login</b><i>_</i><b>mismatch</b> sender anti-spoofing
restriction. restriction.
<b>Pass-through</b> <b>proxy</b>
Optionally, the Postfix SMTP server can be configured to
forward all mail to a proxy server, for example a real-
time content filter. This proxy server should support the
same MAIL FROM and RCPT TO command syntax as Postfix, but
does not need to support ESMTP command pipelining.
<b>smtpd</b><i>_</i><b>proxy</b><i>_</i><b>filter</b>
The <i>host:port</i> of the SMTP proxy server. The <i>host</i> or
<i>host:</i> portion is optional.
<b>smtpd</b><i>_</i><b>proxy</b><i>_</i><b>timeout</b>
Timeout for connecting to, sending to and receiving
from the SMTP proxy server.
<b>smtpd</b><i>_</i><b>proxy</b><i>_</i><b>ehlo</b>
The hostname to use when sending an EHLO command to
the SMTP proxy server.
<b>Miscellaneous</b> <b>Miscellaneous</b>
<b>authorized</b><i>_</i><b>verp</b><i>_</i><b>clients</b> <b>authorized</b><i>_</i><b>verp</b><i>_</i><b>clients</b>
Hostnames, domain names and/or addresses of clients Hostnames, domain names and/or addresses of clients
@ -318,6 +337,24 @@ SMTPD(8) SMTPD(8)
mail to. The domains are routed to the delivery mail to. The domains are routed to the delivery
agent specified with the <b>relay</b><i>_</i><b>transport</b> setting. agent specified with the <b>relay</b><i>_</i><b>transport</b> setting.
<b>Sender/recipient</b> <b>address</b> <b>verification</b>
Address verification is implemented by sending probe email
messages that are not actually delivered, and is enabled
via the reject_unverified_{sender,recipient} access
restriction. The status of verification probes is main-
tained by the address verification service.
<b>address</b><i>_</i><b>verify</b><i>_</i><b>poll</b><i>_</i><b>count</b>
How many times to query the address verification
service for completion of an address verification
request. Specify 0 to implement a simple form of
greylisting.
<b>address</b><i>_</i><b>verify</b><i>_</i><b>poll</b><i>_</i><b>delay</b>
Time to wait after querying the address verifica-
tion service for completion of an address verifica-
tion request.
<b>UCE</b> <b>control</b> <b>responses</b> <b>UCE</b> <b>control</b> <b>responses</b>
<b>access</b><i>_</i><b>map</b><i>_</i><b>reject</b><i>_</i><b>code</b> <b>access</b><i>_</i><b>map</b><i>_</i><b>reject</b><i>_</i><b>code</b>
Response code when a client violates an access Response code when a client violates an access
@ -381,10 +418,11 @@ SMTPD(8) SMTPD(8)
be undeliverable. be undeliverable.
<b>SEE</b> <b>ALSO</b> <b>SEE</b> <b>ALSO</b>
<a href="trivial-rewrite.8.html">trivial-rewrite(8)</a> address resolver
<a href="cleanup.8.html">cleanup(8)</a> message canonicalization <a href="cleanup.8.html">cleanup(8)</a> message canonicalization
<a href="master.8.html">master(8)</a> process manager <a href="master.8.html">master(8)</a> process manager
syslogd(8) system logging syslogd(8) system logging
<a href="trivial-rewrite.8.html">trivial-rewrite(8)</a> address resolver
<a href="verify.8.html">verify(8)</a> address verification service
<b>LICENSE</b> <b>LICENSE</b>
The Secure Mailer license must be distributed with this The Secure Mailer license must be distributed with this

View File

@ -17,6 +17,25 @@ TRANSPORT(5) TRANSPORT(5)
relay hosts. The mapping is used by the <a href="trivial-rewrite.8.html"><b>trivial-rewrite</b>(8)</a> relay hosts. The mapping is used by the <a href="trivial-rewrite.8.html"><b>trivial-rewrite</b>(8)</a>
daemon. daemon.
This mapping overrides the default routing that is built
into Postfix:
<b>mydestination</b>
A list of domains that is by default delivered via
<b>$local</b><i>_</i><b>transport</b>.
<b>virtual</b><i>_</i><b>mailbox</b><i>_</i><b>domains</b>
A list of domains that is by default delivered via
<b>$virtual</b><i>_</i><b>transport</b>.
<b>relay</b><i>_</i><b>domains</b>
A list of domains that is by default delivered via
<b>$relay</b><i>_</i><b>transport</b>.
any other destination
Mail for any other destination is by default deliv-
ered via <b>$default</b><i>_</i><b>transport</b>.
Normally, the <b>transport</b> table is specified as a text file Normally, the <b>transport</b> table is specified as a text file
that serves as input to the <a href="postmap.1.html"><b>postmap</b>(1)</a> command. The that serves as input to the <a href="postmap.1.html"><b>postmap</b>(1)</a> command. The
result, an indexed file in <b>dbm</b> or <b>db</b> format, is used for result, an indexed file in <b>dbm</b> or <b>db</b> format, is used for

View File

@ -33,9 +33,9 @@ VERIFY(8) VERIFY(8)
Update the status of the specified address. Update the status of the specified address.
<b>VRFY</b><i>_</i><b>ADDR</b><i>_</i><b>QUERY</b> <i>address</i> <b>VRFY</b><i>_</i><b>ADDR</b><i>_</i><b>QUERY</b> <i>address</i>
Look up the <i>status</i> and <i>text</i> of the specified Look up the <i>status</i>, <i>last</i> <i>update</i> <i>time</i> and <i>text</i> of
address. If the status is unknown, a probe is sent the specified address. If the status is unknown, a
and a default status is returned. probe is sent and a default status is returned.
The server reply status is one of: The server reply status is one of:

View File

@ -12,7 +12,8 @@ COMMANDS= man1/postalias.1 man1/postcat.1 man1/postconf.1 man1/postfix.1 \
man1/postmap.1 man1/sendmail.1 man1/mailq.1 man1/newaliases.1 \ man1/postmap.1 man1/sendmail.1 man1/mailq.1 man1/newaliases.1 \
man1/postqueue.1 man1/postsuper.1 man1/postqueue.1 man1/postsuper.1
CONFIG = man5/access.5 man5/aliases.5 man5/canonical.5 man5/relocated.5 \ CONFIG = man5/access.5 man5/aliases.5 man5/canonical.5 man5/relocated.5 \
man5/transport.5 man5/virtual.5 man5/pcre_table.5 man5/regexp_table.5 man5/transport.5 man5/virtual.5 man5/pcre_table.5 man5/regexp_table.5 \
man5/cidr_table.5 man5/tcp_table.5
TOOLS = man1/smtp-sink.1 man1/smtp-source.1 man1/qmqp-sink.1 \ TOOLS = man1/smtp-sink.1 man1/smtp-source.1 man1/qmqp-sink.1 \
man1/qmqp-source.1 man1/qmqp-source.1
@ -149,6 +150,9 @@ man5/aliases.5: ../proto/aliases
man5/canonical.5: ../proto/canonical man5/canonical.5: ../proto/canonical
../mantools/srctoman - $? >$@ ../mantools/srctoman - $? >$@
man5/cidr_table.5: ../proto/cidr_table
../mantools/srctoman - $? >$@
man5/pcre_table.5: ../proto/pcre_table man5/pcre_table.5: ../proto/pcre_table
../mantools/srctoman - $? >$@ ../mantools/srctoman - $? >$@
@ -170,6 +174,9 @@ man1/smtp-sink.1: ../src/smtpstone/smtp-sink.c
man1/smtp-source.1: ../src/smtpstone/smtp-source.c man1/smtp-source.1: ../src/smtpstone/smtp-source.c
../mantools/srctoman $? >$@ ../mantools/srctoman $? >$@
man5/tcp_table.5: ../proto/tcp_table
../mantools/srctoman - $? >$@
man1/qmqp-sink.1: ../src/smtpstone/qmqp-sink.c man1/qmqp-sink.1: ../src/smtpstone/qmqp-sink.c
../mantools/srctoman $? >$@ ../mantools/srctoman $? >$@

View File

@ -106,6 +106,9 @@ order to match subdomains.
.IP \fInet\fR .IP \fInet\fR
Matches any host address in the specified network. A network Matches any host address in the specified network. A network
address is a sequence of one or more octets separated by ".". address is a sequence of one or more octets separated by ".".
NOTE: use the \fBcidr\fR lookup table type if you want to
specify arbitrary network blocks.
.SH ACTIONS .SH ACTIONS
.na .na
.nf .nf
@ -194,8 +197,9 @@ The table format does not understand quoting conventions.
.SH SEE ALSO .SH SEE ALSO
.na .na
.nf .nf
postmap(1) create mapping table postmap(1) create lookup table
smtpd(8) smtp server smtpd(8) smtp server
cidr_table(5) format of CIDR tables
pcre_table(5) format of PCRE tables pcre_table(5) format of PCRE tables
regexp_table(5) format of POSIX regular expression tables regexp_table(5) format of POSIX regular expression tables
.SH LICENSE .SH LICENSE

View File

@ -0,0 +1,81 @@
.TH CIDR_TABLE 5
.ad
.fi
.SH NAME
cidr_table
\-
format of Postfix CIDR tables
.SH SYNOPSIS
.na
.nf
\fBpostmap -q "\fIstring\fB" cidr:/etc/postfix/\fIfilename\fR
\fBpostmap -q - cidr:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
.SH DESCRIPTION
.ad
.fi
The Postfix mail system uses optional access control tables.
These tables are usually in \fBdbm\fR or \fBdb\fR format.
Alternatively, access control tables can be specified in CIDR form.
To find out what types of lookup tables your Postfix system
supports use the \fBpostconf -m\fR command.
To test lookup tables, use the \fBpostmap\fR command as
described in the SYNOPSIS above.
.SH TABLE FORMAT
.na
.nf
.ad
.fi
The general form of a Postfix CIDR table is:
.IP "\fInetwork_address\fB/\fInetwork_mask result\fR"
When a search string matches the specified network block,
use the corresponding \fIresult\fR value.
.IP "\fInetwork_address result\fR"
When a search string matches the specified network address,
use the corresponding \fIresult\fR value.
.IP "blank lines and comments"
Empty lines and whitespace-only lines are ignored, as
are lines whose first non-whitespace character is a `#'.
.IP "multi-line text"
A logical line starts with non-whitespace text. A line that
starts with whitespace continues a logical line.
.PP
Patterns are applied in the order as specified in the table, until a
pattern is found that matches the search string.
.SH EXAMPLE SMTPD ACCESS MAP
.na
.nf
/etc/postfix/main.cf:
.ti +4
smtpd_client_restrictions = ... cidr:/etc/postfix/client_cidr ...
/etc/postfix/client_cidr:
.in +4
# Rule order matters. Put more specific whitelist entries
# before more general blacklist entries.
192.168.1.1 OK
192.168.0.0/16 REJECT
.in -4
.SH SEE ALSO
.na
.nf
regexp_table(5) format of regular expression tables
pcre_table(5) format of PCRE tables
tcp_table(5) TCP client/server table lookup protocol
.SH AUTHOR(S)
.na
.nf
The CIDR table lookup code was originally written by:
Jozsef Kadlecsik
kadlec@blackhole.kfki.hu
KFKI Research Institute for Particle and Nuclear Physics
POB. 49
1525 Budapest, Hungary
Adopted and adapted by:
Wietse Venema
IBM T.J. Watson Research
P.O. Box 704
Yorktown Heights, NY 10598, USA

View File

@ -8,8 +8,6 @@ format of Postfix PCRE tables
.SH SYNOPSIS .SH SYNOPSIS
.na .na
.nf .nf
\fBpcre:/etc/postfix/\fIfilename\fR
\fBpostmap -q "\fIstring\fB" pcre:/etc/postfix/\fIfilename\fR \fBpostmap -q "\fIstring\fB" pcre:/etc/postfix/\fIfilename\fR
\fBpostmap -q - pcre:/etc/postfix/\fIfilename\fR <\fIinputfile\fR \fBpostmap -q - pcre:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
@ -29,8 +27,9 @@ described in the SYNOPSIS above.
The general form of a PCRE table is: The general form of a PCRE table is:
.IP "\fB/\fIpattern\fB/\fIflags result\fR" .IP "\fB/\fIpattern\fB/\fIflags result\fR"
When \fIpattern\fR matches a search string, use the corresponding .IP "\fB!/\fIpattern\fB/\fIflags result\fR"
\fIresult\fR value. When \fIpattern\fR matches (does not match) a search string, use
the corresponding \fIresult\fR value.
.IP "blank lines and comments" .IP "blank lines and comments"
Empty lines and whitespace-only lines are ignored, as Empty lines and whitespace-only lines are ignored, as
are lines whose first non-whitespace character is a `#'. are lines whose first non-whitespace character is a `#'.
@ -38,10 +37,12 @@ are lines whose first non-whitespace character is a `#'.
A logical line starts with non-whitespace text. A line that A logical line starts with non-whitespace text. A line that
starts with whitespace continues a logical line. starts with whitespace continues a logical line.
.IP "\fBif /\fIpattern\fB/\fIflags\fR" .IP "\fBif /\fIpattern\fB/\fIflags\fR"
.IP "\fBif !/\fIpattern\fB/\fIflags\fR"
.IP "\fBendif\fR" .IP "\fBendif\fR"
Examine the lines between \fBif\fR..\fBendif\fR only if Examine the lines between \fBif\fR..\fBendif\fR only if
\fIpattern\fR matches. The \fBif\fR..\fBendif\fR can nest. \fIpattern\fR matches (does not match). The \fBif\fR..\fBendif\fR
Do not prepend whitespace to patterns inside \fBif\fR..\fBendif\fR. can nest. Do not prepend whitespace to patterns inside
\fBif\fR..\fBendif\fR.
.PP .PP
Each pattern is a perl-like regular expression. The expression Each pattern is a perl-like regular expression. The expression
delimiter can be any character, except whitespace or characters delimiter can be any character, except whitespace or characters
@ -114,7 +115,9 @@ pattern is found that matches the search string.
Substitution of substrings from the matched expression into the result Substitution of substrings from the matched expression into the result
string is possible using the conventional perl syntax ($1, $2, etc.). string is possible using the conventional perl syntax ($1, $2, etc.).
The macros in the result string may need to be written as ${n} The macros in the result string may need to be written as ${n}
or $(n) if they aren't followed by whitespace. or $(n) if they aren't followed by whitespace. Since negated patterns
(those preceded by \fB!\fR) return a result when the expression does
not match, substitutions are not available for negated patterns.
.SH EXAMPLE SMTPD ACCESS MAP .SH EXAMPLE SMTPD ACCESS MAP
.na .na
.nf .nf
@ -147,6 +150,8 @@ or $(n) if they aren't followed by whitespace.
.na .na
.nf .nf
regexp_table(5) format of POSIX regular expression tables regexp_table(5) format of POSIX regular expression tables
cidr_table(5) format of CIDR tables
tcp_table(5) TCP client/server table lookup protocol
.SH AUTHOR(S) .SH AUTHOR(S)
.na .na
.nf .nf

View File

@ -8,8 +8,6 @@ format of Postfix regular expression tables
.SH SYNOPSIS .SH SYNOPSIS
.na .na
.nf .nf
\fBregexp:/etc/postfix/\fIfilename\fR
\fBpostmap -q "\fIstring\fB" regexp:/etc/postfix/\fIfilename\fR \fBpostmap -q "\fIstring\fB" regexp:/etc/postfix/\fIfilename\fR
\fBpostmap -q - regexp:/etc/postfix/\fIfilename\fR <\fIinputfile\fR \fBpostmap -q - regexp:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
@ -71,7 +69,9 @@ pattern is found that matches the search string.
Substitution of substrings from the matched expression into the result Substitution of substrings from the matched expression into the result
string is possible using $1, $2, etc.. The macros in the result string string is possible using $1, $2, etc.. The macros in the result string
may need to be written as ${n} or $(n) if they aren't followed may need to be written as ${n} or $(n) if they aren't followed
by whitespace. by whitespace. Since negated patterns (those preceded by \fB!\fR)
return a result when the expression does not match, substitutions are
not available for negated patterns.
.SH EXAMPLE SMTPD ACCESS MAP .SH EXAMPLE SMTPD ACCESS MAP
.na .na
.nf .nf
@ -104,6 +104,8 @@ endif
.na .na
.nf .nf
pcre_table(5) format of PCRE tables pcre_table(5) format of PCRE tables
cidr_table(5) format of CIDR tables
tcp_table(5) TCP client/server table lookup protocol
.SH AUTHOR(S) .SH AUTHOR(S)
.na .na
.nf .nf

View File

@ -0,0 +1,96 @@
.TH TCP_TABLE 5
.ad
.fi
.SH NAME
tcp_table
\-
Postfix client/server table lookup protocol
.SH SYNOPSIS
.na
.nf
\fBpostmap -q "\fIstring\fB" tcp:\fIhost:port\fR
\fBpostmap -q - regexp:\fIhost:port\fR <\fIinputfile\fR
.SH DESCRIPTION
.ad
.fi
The Postfix mail system uses optional tables for address
rewriting or mail routing. These tables are usually in
\fBdbm\fR or \fBdb\fR format. Alternatively, lookup tables
can be specified as a TCP client/server pair.
To find out what types of lookup tables your Postfix system
supports use the \fBpostconf -m\fR command.
To test lookup tables, use the \fBpostmap\fR command as
described in the SYNOPSIS above.
.SH PROTOCOL DESCRIPTION
.na
.nf
.ad
.fi
The TCP map class implements a very simple protocol: the client
sends a request, and the server sends one reply. Requests and
replies are sent as one line of ASCII text, terminated by the
ASCII newline character. Request and reply parameters (see below)
are separated by whitespace.
.SH ENCODING
.na
.nf
.ad
.fi
In request and reply parameters, the character % and any non-printing
and whitespace characters must be replaced by %XX, XX being the
corresponding ASCII hexadecimal character value. The hexadecimal codes
can be specified in any case (upper, lower, mixed).
.SH REQUEST FORMAT
.na
.nf
.ad
.fi
Requests are strings that serve as lookup key in the simulated
table.
.IP "\fBget\fR SPACE \fIkey\fR NEWLINE"
Look up data under the specified key.
.IP "\fBput\fR SPACE \fIkey\fR SPACE \fIvalue\fR NEWLINE"
This request is currently not implemented.
.SH REPLY FORMAT
.na
.nf
.ad
.fi
Replies must be no longer than 4096 characters including the
newline terminator, and must have the following form:
.IP "\fB500\fR SPACE \fIoptional-text\fR NEWLINE"
In case of a lookup request, the requested data does not exist.
In case of an update request, the request was rejected.
.IP "\fB400\fR SPACE \fIoptional-text\fR NEWLINE"
This indicates an error condition. The text gives the nature of
the problem. The client should retry the request later.
.IP "\fB200\fR SPACE \fItext\fR NEWLINE"
The request was successful. In the case of a lookup request,
the text contains an encoded version of the requested data.
Otherwise the text is optional.
.SH SEE ALSO
.na
.nf
regexp_table(5) format of regular expression tables
pcre_table(5) format of PCRE tables
cidr_table(5) format of CIDR tables
.SH BUGS
.ad
.fi
Only the lookup method is currently implemented.
.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

@ -20,6 +20,21 @@ The optional \fBtransport\fR table specifies a mapping from email
addresses to message delivery transports and/or relay hosts. The addresses to message delivery transports and/or relay hosts. The
mapping is used by the \fBtrivial-rewrite\fR(8) daemon. mapping is used by the \fBtrivial-rewrite\fR(8) daemon.
This mapping overrides the default routing that is built into
Postfix:
.IP \fBmydestination\fR
A list of domains that is by default delivered via
\fB$local_transport\fR.
.IP \fBvirtual_mailbox_domains\fR
A list of domains that is by default delivered via
\fB$virtual_transport\fR.
.IP \fBrelay_domains\fR
A list of domains that is by default delivered via
\fB$relay_transport\fR.
.IP "any other destination"
Mail for any other destination is by default delivered via
\fB$default_transport\fR.
.PP
Normally, the \fBtransport\fR table is specified as a text file Normally, the \fBtransport\fR table is specified as a text file
that serves as input to the \fBpostmap\fR(1) command. that serves as input to the \fBpostmap\fR(1) command.
The result, an indexed file in \fBdbm\fR or \fBdb\fR format, is used The result, an indexed file in \fBdbm\fR or \fBdb\fR format, is used

View File

@ -150,6 +150,11 @@ substitution is done before all other address rewriting.
.IP \fBcanonical_maps\fR .IP \fBcanonical_maps\fR
Address mapping lookup table for sender and recipient addresses Address mapping lookup table for sender and recipient addresses
in envelopes and headers. in envelopes and headers.
.IP \fBenable_original_recipient\fR
Enable support for the X-Original-To message header, which is
needed for multi-recipient mailboxes. When this is enabled, Postfix
performs duplicate elimination on (original recipient, rewritten
recipient) pairs, instead of looking at the rewritten recipient only.
.IP \fBrecipient_canonical_maps\fR .IP \fBrecipient_canonical_maps\fR
Address mapping lookup table for envelope and header recipient Address mapping lookup table for envelope and header recipient
addresses. addresses.

View File

@ -27,10 +27,6 @@ The SMTP client looks up a list of mail exchanger addresses for
the destination host, sorts the list by preference, and connects the destination host, sorts the list by preference, and connects
to each listed address until it finds a server that responds. to each listed address until it finds a server that responds.
When the domain or host is specified as a comma/whitespace
separated list, the SMTP client repeats the above process
for all destinations until it finds a server that responds.
Once the SMTP client has received the server greeting banner, no Once the SMTP client has received the server greeting banner, no
error will cause it to proceed to the next address on the mail error will cause it to proceed to the next address on the mail
exchanger list. Instead, the message is either bounced, or its exchanger list. Instead, the message is either bounced, or its
@ -134,6 +130,9 @@ Length limit for SMTP message content lines. Zero means no limit.
Some SMTP servers misbehave on long lines. Some SMTP servers misbehave on long lines.
.IP \fBsmtp_helo_name\fR .IP \fBsmtp_helo_name\fR
The hostname to be used in HELO and EHLO commands. The hostname to be used in HELO and EHLO commands.
.IP \fBsmtp_quote_rfc821_envelope\fR
Whether or not to quote MAIL FROM and RCPT TO addresses as
per the rules laid out in RFC 821.
.IP \fBsmtp_skip_4xx_greeting\fR .IP \fBsmtp_skip_4xx_greeting\fR
Skip servers that greet us with a 4xx status code. Skip servers that greet us with a 4xx status code.
.IP \fBsmtp_skip_5xx_greeting\fR .IP \fBsmtp_skip_5xx_greeting\fR

View File

@ -111,6 +111,24 @@ Disallow anonymous logins.
Maps that specify the SASL login name that owns a MAIL FROM sender Maps that specify the SASL login name that owns a MAIL FROM sender
address. Used by the \fBreject_sender_login_mismatch\fR sender address. Used by the \fBreject_sender_login_mismatch\fR sender
anti-spoofing restriction. anti-spoofing restriction.
.SH "Pass-through proxy"
.ad
.fi
.ad
Optionally, the Postfix SMTP server can be configured to
forward all mail to a proxy server, for example a real-time
content filter. This proxy server should support the same
MAIL FROM and RCPT TO command syntax as Postfix, but does not
need to support ESMTP command pipelining.
.IP \fBsmtpd_proxy_filter\fR
The \fIhost:port\fR of the SMTP proxy server. The \fIhost\fR
or \fIhost:\fR portion is optional.
.IP \fBsmtpd_proxy_timeout\fR
Timeout for connecting to, sending to and receiving from
the SMTP proxy server.
.IP \fBsmtpd_proxy_ehlo\fR
The hostname to use when sending an EHLO command to the
SMTP proxy server.
.SH Miscellaneous .SH Miscellaneous
.ad .ad
.fi .fi
@ -259,6 +277,21 @@ are eligible for the \fBpermit_mx_backup\fR feature.
Restrict what domains this mail system will relay Restrict what domains this mail system will relay
mail to. The domains are routed to the delivery agent mail to. The domains are routed to the delivery agent
specified with the \fBrelay_transport\fR setting. specified with the \fBrelay_transport\fR setting.
.SH "Sender/recipient address verification"
.ad
.fi
Address verification is implemented by sending probe email
messages that are not actually delivered, and is enabled
via the reject_unverified_{sender,recipient} access restriction.
The status of verification probes is maintained by the address
verification service.
.IP \fBaddress_verify_poll_count\fR
How many times to query the address verification service
for completion of an address verification request.
Specify 0 to implement a simple form of greylisting.
.IP \fBaddress_verify_poll_delay\fR
Time to wait after querying the address verification service
for completion of an address verification request.
.SH "UCE control responses" .SH "UCE control responses"
.ad .ad
.fi .fi
@ -305,10 +338,11 @@ Response code when a recipient address is known to be undeliverable.
.SH SEE ALSO .SH SEE ALSO
.na .na
.nf .nf
trivial-rewrite(8) address resolver
cleanup(8) message canonicalization cleanup(8) message canonicalization
master(8) process manager master(8) process manager
syslogd(8) system logging syslogd(8) system logging
trivial-rewrite(8) address resolver
verify(8) address verification service
.SH LICENSE .SH LICENSE
.na .na
.nf .nf

View File

@ -35,7 +35,8 @@ This server implements the following requests:
.IP "\fBVRFY_ADDR_UPDATE\fI address status text\fR" .IP "\fBVRFY_ADDR_UPDATE\fI address status text\fR"
Update the status of the specified address. Update the status of the specified address.
.IP "\fBVRFY_ADDR_QUERY\fI address\fR" .IP "\fBVRFY_ADDR_QUERY\fI address\fR"
Look up the \fIstatus\fR and \fItext\fR of the specified address. Look up the \fIstatus\fR, \fIlast update time\fR and \fItext\fR
of the specified address.
If the status is unknown, a probe is sent and a default status is If the status is unknown, a probe is sent and a default status is
returned. returned.
.PP .PP

View File

@ -4,7 +4,7 @@ SHELL = /bin/sh
CONFIG = ../conf/access ../conf/aliases ../conf/canonical ../conf/relocated \ CONFIG = ../conf/access ../conf/aliases ../conf/canonical ../conf/relocated \
../conf/transport ../conf/virtual ../conf/pcre_table \ ../conf/transport ../conf/virtual ../conf/pcre_table \
../conf/regexp_table ../conf/regexp_table ../conf/cidr_table ../conf/tcp_table
AWK = awk '{ print; if (NR == 1) print ".pl 9999" }' AWK = awk '{ print; if (NR == 1) print ".pl 9999" }'
@ -30,6 +30,9 @@ clobber:
../conf/canonical: canonical ../conf/canonical: canonical
srctoman - $? | $(AWK) | nroff -man | col -bx | uniq | sed 's/^/# /' >$@ srctoman - $? | $(AWK) | nroff -man | col -bx | uniq | sed 's/^/# /' >$@
../conf/cidr_table: cidr_table
srctoman - $? | $(AWK) | nroff -man | col -bx | uniq | sed 's/^/# /' >$@
../conf/pcre_table: pcre_table ../conf/pcre_table: pcre_table
srctoman - $? | $(AWK) | nroff -man | col -bx | uniq | sed 's/^/# /' >$@ srctoman - $? | $(AWK) | nroff -man | col -bx | uniq | sed 's/^/# /' >$@
@ -39,6 +42,9 @@ clobber:
../conf/relocated: relocated ../conf/relocated: relocated
srctoman - $? | $(AWK) | nroff -man | col -bx | uniq | sed 's/^/# /' >$@ srctoman - $? | $(AWK) | nroff -man | col -bx | uniq | sed 's/^/# /' >$@
../conf/tcp_table: tcp_table
srctoman - $? | $(AWK) | nroff -man | col -bx | uniq | sed 's/^/# /' >$@
../conf/transport: transport ../conf/transport: transport
srctoman - $? | $(AWK) | nroff -man | col -bx | uniq | sed 's/^/# /' >$@ srctoman - $? | $(AWK) | nroff -man | col -bx | uniq | sed 's/^/# /' >$@

View File

@ -92,6 +92,9 @@
# .IP \fInet\fR # .IP \fInet\fR
# Matches any host address in the specified network. A network # Matches any host address in the specified network. A network
# address is a sequence of one or more octets separated by ".". # address is a sequence of one or more octets separated by ".".
#
# NOTE: use the \fBcidr\fR lookup table type if you want to
# specify arbitrary network blocks.
# ACTIONS # ACTIONS
# .ad # .ad
# .fi # .fi
@ -172,8 +175,9 @@
# BUGS # BUGS
# The table format does not understand quoting conventions. # The table format does not understand quoting conventions.
# SEE ALSO # SEE ALSO
# postmap(1) create mapping table # postmap(1) create lookup table
# smtpd(8) smtp server # smtpd(8) smtp server
# cidr_table(5) format of CIDR tables
# pcre_table(5) format of PCRE tables # pcre_table(5) format of PCRE tables
# regexp_table(5) format of POSIX regular expression tables # regexp_table(5) format of POSIX regular expression tables
# LICENSE # LICENSE

68
postfix/proto/cidr_table Normal file
View File

@ -0,0 +1,68 @@
#++
# NAME
# cidr_table 5
# SUMMARY
# format of Postfix CIDR tables
# SYNOPSIS
# \fBpostmap -q "\fIstring\fB" cidr:/etc/postfix/\fIfilename\fR
#
# \fBpostmap -q - cidr:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
# DESCRIPTION
# The Postfix mail system uses optional access control tables.
# These tables are usually in \fBdbm\fR or \fBdb\fR format.
# Alternatively, access control tables can be specified in CIDR form.
#
# To find out what types of lookup tables your Postfix system
# supports use the \fBpostconf -m\fR command.
#
# To test lookup tables, use the \fBpostmap\fR command as
# described in the SYNOPSIS above.
# TABLE FORMAT
# .ad
# .fi
# The general form of a Postfix CIDR table is:
# .IP "\fInetwork_address\fB/\fInetwork_mask result\fR"
# When a search string matches the specified network block,
# use the corresponding \fIresult\fR value.
# .IP "\fInetwork_address result\fR"
# When a search string matches the specified network address,
# use the corresponding \fIresult\fR value.
# .IP "blank lines and comments"
# Empty lines and whitespace-only lines are ignored, as
# are lines whose first non-whitespace character is a `#'.
# .IP "multi-line text"
# A logical line starts with non-whitespace text. A line that
# starts with whitespace continues a logical line.
# .PP
# Patterns are applied in the order as specified in the table, until a
# pattern is found that matches the search string.
# EXAMPLE SMTPD ACCESS MAP
# /etc/postfix/main.cf:
# .ti +4
# smtpd_client_restrictions = ... cidr:/etc/postfix/client_cidr ...
#
# /etc/postfix/client_cidr:
# .in +4
# # Rule order matters. Put more specific whitelist entries
# # before more general blacklist entries.
# 192.168.1.1 OK
# 192.168.0.0/16 REJECT
# .in -4
# SEE ALSO
# regexp_table(5) format of regular expression tables
# pcre_table(5) format of PCRE tables
# tcp_table(5) TCP client/server table lookup protocol
# AUTHOR(S)
# The CIDR table lookup code was originally written by:
# Jozsef Kadlecsik
# kadlec@blackhole.kfki.hu
# KFKI Research Institute for Particle and Nuclear Physics
# POB. 49
# 1525 Budapest, Hungary
#
# Adopted and adapted by:
# Wietse Venema
# IBM T.J. Watson Research
# P.O. Box 704
# Yorktown Heights, NY 10598, USA
#--

View File

@ -4,8 +4,6 @@
# SUMMARY # SUMMARY
# format of Postfix PCRE tables # format of Postfix PCRE tables
# SYNOPSIS # SYNOPSIS
# \fBpcre:/etc/postfix/\fIfilename\fR
#
# \fBpostmap -q "\fIstring\fB" pcre:/etc/postfix/\fIfilename\fR # \fBpostmap -q "\fIstring\fB" pcre:/etc/postfix/\fIfilename\fR
# #
# \fBpostmap -q - pcre:/etc/postfix/\fIfilename\fR <\fIinputfile\fR # \fBpostmap -q - pcre:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
@ -23,8 +21,9 @@
# #
# The general form of a PCRE table is: # The general form of a PCRE table is:
# .IP "\fB/\fIpattern\fB/\fIflags result\fR" # .IP "\fB/\fIpattern\fB/\fIflags result\fR"
# When \fIpattern\fR matches a search string, use the corresponding # .IP "\fB!/\fIpattern\fB/\fIflags result\fR"
# \fIresult\fR value. # When \fIpattern\fR matches (does not match) a search string, use
# the corresponding \fIresult\fR value.
# .IP "blank lines and comments" # .IP "blank lines and comments"
# Empty lines and whitespace-only lines are ignored, as # Empty lines and whitespace-only lines are ignored, as
# are lines whose first non-whitespace character is a `#'. # are lines whose first non-whitespace character is a `#'.
@ -32,10 +31,12 @@
# A logical line starts with non-whitespace text. A line that # A logical line starts with non-whitespace text. A line that
# starts with whitespace continues a logical line. # starts with whitespace continues a logical line.
# .IP "\fBif /\fIpattern\fB/\fIflags\fR" # .IP "\fBif /\fIpattern\fB/\fIflags\fR"
# .IP "\fBif !/\fIpattern\fB/\fIflags\fR"
# .IP "\fBendif\fR" # .IP "\fBendif\fR"
# Examine the lines between \fBif\fR..\fBendif\fR only if # Examine the lines between \fBif\fR..\fBendif\fR only if
# \fIpattern\fR matches. The \fBif\fR..\fBendif\fR can nest. # \fIpattern\fR matches (does not match). The \fBif\fR..\fBendif\fR
# Do not prepend whitespace to patterns inside \fBif\fR..\fBendif\fR. # can nest. Do not prepend whitespace to patterns inside
# \fBif\fR..\fBendif\fR.
# .PP # .PP
# Each pattern is a perl-like regular expression. The expression # Each pattern is a perl-like regular expression. The expression
# delimiter can be any character, except whitespace or characters # delimiter can be any character, except whitespace or characters
@ -108,7 +109,9 @@
# Substitution of substrings from the matched expression into the result # Substitution of substrings from the matched expression into the result
# string is possible using the conventional perl syntax ($1, $2, etc.). # string is possible using the conventional perl syntax ($1, $2, etc.).
# The macros in the result string may need to be written as ${n} # The macros in the result string may need to be written as ${n}
# or $(n) if they aren't followed by whitespace. # or $(n) if they aren't followed by whitespace. Since negated patterns
# (those preceded by \fB!\fR) return a result when the expression does
# not match, substitutions are not available for negated patterns.
# EXAMPLE SMTPD ACCESS MAP # EXAMPLE SMTPD ACCESS MAP
# # Protect your outgoing majordomo exploders # # Protect your outgoing majordomo exploders
# /^(?!owner-)(.*)-outgoing@(.*)/ 550 Use ${1}@${2} instead # /^(?!owner-)(.*)-outgoing@(.*)/ 550 Use ${1}@${2} instead
@ -133,6 +136,8 @@
# # Put your own body patterns here. # # Put your own body patterns here.
# SEE ALSO # SEE ALSO
# regexp_table(5) format of POSIX regular expression tables # regexp_table(5) format of POSIX regular expression tables
# cidr_table(5) format of CIDR tables
# tcp_table(5) TCP client/server table lookup protocol
# AUTHOR(S) # AUTHOR(S)
# The PCRE table lookup code was originally written by: # The PCRE table lookup code was originally written by:
# Andrew McNamara # Andrew McNamara

View File

@ -4,8 +4,6 @@
# SUMMARY # SUMMARY
# format of Postfix regular expression tables # format of Postfix regular expression tables
# SYNOPSIS # SYNOPSIS
# \fBregexp:/etc/postfix/\fIfilename\fR
#
# \fBpostmap -q "\fIstring\fB" regexp:/etc/postfix/\fIfilename\fR # \fBpostmap -q "\fIstring\fB" regexp:/etc/postfix/\fIfilename\fR
# #
# \fBpostmap -q - regexp:/etc/postfix/\fIfilename\fR <\fIinputfile\fR # \fBpostmap -q - regexp:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
@ -65,7 +63,9 @@
# Substitution of substrings from the matched expression into the result # Substitution of substrings from the matched expression into the result
# string is possible using $1, $2, etc.. The macros in the result string # string is possible using $1, $2, etc.. The macros in the result string
# may need to be written as ${n} or $(n) if they aren't followed # may need to be written as ${n} or $(n) if they aren't followed
# by whitespace. # by whitespace. Since negated patterns (those preceded by \fB!\fR)
# return a result when the expression does not match, substitutions are
# not available for negated patterns.
# EXAMPLE SMTPD ACCESS MAP # EXAMPLE SMTPD ACCESS MAP
# # Disallow sender-specified routing. This is a must if you relay mail # # Disallow sender-specified routing. This is a must if you relay mail
# # for other domains. # # for other domains.
@ -90,6 +90,8 @@
# # Put your own body patterns here. # # Put your own body patterns here.
# SEE ALSO # SEE ALSO
# pcre_table(5) format of PCRE tables # pcre_table(5) format of PCRE tables
# cidr_table(5) format of CIDR tables
# tcp_table(5) TCP client/server table lookup protocol
# AUTHOR(S) # AUTHOR(S)
# The regexp table lookup code was originally written by: # The regexp table lookup code was originally written by:
# LaMont Jones # LaMont Jones

75
postfix/proto/tcp_table Normal file
View File

@ -0,0 +1,75 @@
#++
# NAME
# tcp_table 5
# SUMMARY
# Postfix client/server table lookup protocol
# SYNOPSIS
# \fBpostmap -q "\fIstring\fB" tcp:\fIhost:port\fR
#
# \fBpostmap -q - regexp:\fIhost:port\fR <\fIinputfile\fR
# DESCRIPTION
# The Postfix mail system uses optional tables for address
# rewriting or mail routing. These tables are usually in
# \fBdbm\fR or \fBdb\fR format. Alternatively, lookup tables
# can be specified as a TCP client/server pair.
#
# To find out what types of lookup tables your Postfix system
# supports use the \fBpostconf -m\fR command.
#
# To test lookup tables, use the \fBpostmap\fR command as
# described in the SYNOPSIS above.
# PROTOCOL DESCRIPTION
# .ad
# .fi
# The TCP map class implements a very simple protocol: the client
# sends a request, and the server sends one reply. Requests and
# replies are sent as one line of ASCII text, terminated by the
# ASCII newline character. Request and reply parameters (see below)
# are separated by whitespace.
# ENCODING
# .ad
# .fi
# In request and reply parameters, the character % and any non-printing
# and whitespace characters must be replaced by %XX, XX being the
# corresponding ASCII hexadecimal character value. The hexadecimal codes
# can be specified in any case (upper, lower, mixed).
# REQUEST FORMAT
# .ad
# .fi
# Requests are strings that serve as lookup key in the simulated
# table.
# .IP "\fBget\fR SPACE \fIkey\fR NEWLINE"
# Look up data under the specified key.
# .IP "\fBput\fR SPACE \fIkey\fR SPACE \fIvalue\fR NEWLINE"
# This request is currently not implemented.
# REPLY FORMAT
# .ad
# .fi
# Replies must be no longer than 4096 characters including the
# newline terminator, and must have the following form:
# .IP "\fB500\fR SPACE \fIoptional-text\fR NEWLINE"
# In case of a lookup request, the requested data does not exist.
# In case of an update request, the request was rejected.
# .IP "\fB400\fR SPACE \fIoptional-text\fR NEWLINE"
# This indicates an error condition. The text gives the nature of
# the problem. The client should retry the request later.
# .IP "\fB200\fR SPACE \fItext\fR NEWLINE"
# The request was successful. In the case of a lookup request,
# the text contains an encoded version of the requested data.
# Otherwise the text is optional.
# SEE ALSO
# regexp_table(5) format of regular expression tables
# pcre_table(5) format of PCRE tables
# cidr_table(5) format of CIDR tables
# BUGS
# Only the lookup method is currently implemented.
# 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

@ -14,6 +14,21 @@
# addresses to message delivery transports and/or relay hosts. The # addresses to message delivery transports and/or relay hosts. The
# mapping is used by the \fBtrivial-rewrite\fR(8) daemon. # mapping is used by the \fBtrivial-rewrite\fR(8) daemon.
# #
# This mapping overrides the default routing that is built into
# Postfix:
# .IP \fBmydestination\fR
# A list of domains that is by default delivered via
# \fB$local_transport\fR.
# .IP \fBvirtual_mailbox_domains\fR
# A list of domains that is by default delivered via
# \fB$virtual_transport\fR.
# .IP \fBrelay_domains\fR
# A list of domains that is by default delivered via
# \fB$relay_transport\fR.
# .IP "any other destination"
# Mail for any other destination is by default delivered via
# \fB$default_transport\fR.
# .PP
# Normally, the \fBtransport\fR table is specified as a text file # Normally, the \fBtransport\fR table is specified as a text file
# that serves as input to the \fBpostmap\fR(1) command. # that serves as input to the \fBpostmap\fR(1) command.
# The result, an indexed file in \fBdbm\fR or \fBdb\fR format, is used # The result, an indexed file in \fBdbm\fR or \fBdb\fR format, is used

View File

@ -132,6 +132,8 @@ bounce_notify_util.o: ../../include/vstring.h
bounce_notify_util.o: ../../include/vbuf.h bounce_notify_util.o: ../../include/vbuf.h
bounce_notify_util.o: ../../include/vstream.h bounce_notify_util.o: ../../include/vstream.h
bounce_notify_util.o: ../../include/line_wrap.h bounce_notify_util.o: ../../include/line_wrap.h
bounce_notify_util.o: ../../include/stringops.h
bounce_notify_util.o: ../../include/xtext.h
bounce_notify_util.o: ../../include/mail_queue.h bounce_notify_util.o: ../../include/mail_queue.h
bounce_notify_util.o: ../../include/quote_822_local.h bounce_notify_util.o: ../../include/quote_822_local.h
bounce_notify_util.o: ../../include/quote_flags.h bounce_notify_util.o: ../../include/quote_flags.h

View File

@ -155,6 +155,8 @@
#include <vstring.h> #include <vstring.h>
#include <vstream.h> #include <vstream.h>
#include <line_wrap.h> #include <line_wrap.h>
#include <stringops.h>
#include <xtext.h>
/* Global library. */ /* Global library. */
@ -210,10 +212,19 @@ static BOUNCE_INFO *bounce_mail_alloc(const char *service,
} }
bounce_info->flush = flush; bounce_info->flush = flush;
bounce_info->buf = vstring_alloc(100); bounce_info->buf = vstring_alloc(100);
bounce_info->sender = vstring_alloc(100);
bounce_info->arrival_time = 0; bounce_info->arrival_time = 0;
bounce_info->orig_offs = 0; bounce_info->orig_offs = 0;
bounce_info->log_handle = log_handle; bounce_info->log_handle = log_handle;
/*
* RFC 1894: diagnostic-type is an RFC 822 atom. We use X-$mail_name and
* must ensure it is valid.
*/
bounce_info->mail_name = mystrdup(var_mail_name);
translit(bounce_info->mail_name, " \t\r\n()<>@,;:\\\".[]",
"-----------------");
/* /*
* Compute a supposedly unique boundary string. This assumes that a queue * Compute a supposedly unique boundary string. This assumes that a queue
* ID and a hostname contain acceptable characters for a boundary string, * ID and a hostname contain acceptable characters for a boundary string,
@ -247,7 +258,16 @@ static BOUNCE_INFO *bounce_mail_alloc(const char *service,
if (rec_type == REC_TYPE_TIME && bounce_info->arrival_time == 0) { if (rec_type == REC_TYPE_TIME && bounce_info->arrival_time == 0) {
if ((bounce_info->arrival_time = atol(STR(bounce_info->buf))) < 0) if ((bounce_info->arrival_time = atol(STR(bounce_info->buf))) < 0)
bounce_info->arrival_time = 0; bounce_info->arrival_time = 0;
} else if (rec_type == REC_TYPE_FROM) {
quote_822_local_flags(bounce_info->sender,
VSTRING_LEN(bounce_info->buf) ?
STR(bounce_info->buf) :
mail_addr_mail_daemon(), 0);
} else if (rec_type == REC_TYPE_MESG) { } else if (rec_type == REC_TYPE_MESG) {
/* XXX Future: sender+recipient after message content. */
if (VSTRING_LEN(bounce_info->sender) == 0)
msg_warn("%s: no sender before message content record",
bounce_info->queue_id);
bounce_info->orig_offs = vstream_ftell(bounce_info->orig_fp); bounce_info->orig_offs = vstream_ftell(bounce_info->orig_fp);
break; break;
} }
@ -322,6 +342,8 @@ void bounce_mail_free(BOUNCE_INFO *bounce_info)
bounce_info->queue_id, bounce_info->queue_name, bounce_info->queue_id, bounce_info->queue_name,
bounce_info->queue_id); bounce_info->queue_id);
vstring_free(bounce_info->buf); vstring_free(bounce_info->buf);
vstring_free(bounce_info->sender);
myfree(bounce_info->mail_name);
myfree((char *) bounce_info->mime_boundary); myfree((char *) bounce_info->mime_boundary);
myfree((char *) bounce_info); myfree((char *) bounce_info);
} }
@ -542,6 +564,11 @@ int bounce_header_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
#if 0 #if 0
post_mail_fprintf(bounce, "Received-From-MTA: dns; %s", "whatever"); post_mail_fprintf(bounce, "Received-From-MTA: dns; %s", "whatever");
#endif #endif
post_mail_fprintf(bounce, "X-%s-Queue-ID: %s",
bounce_info->mail_name, bounce_info->queue_id);
if (VSTRING_LEN(bounce_info->sender) > 0)
post_mail_fprintf(bounce, "X-%s-Sender: rfc822; %s",
bounce_info->mail_name, STR(bounce_info->sender));
if (bounce_info->arrival_time > 0) if (bounce_info->arrival_time > 0)
post_mail_fprintf(bounce, "Arrival-Date: %s", post_mail_fprintf(bounce, "Arrival-Date: %s",
mail_date(bounce_info->arrival_time)); mail_date(bounce_info->arrival_time));
@ -552,25 +579,21 @@ int bounce_header_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
int bounce_recipient_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info) int bounce_recipient_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
{ {
char *fixed_mail_name;
post_mail_fputs(bounce, ""); post_mail_fputs(bounce, "");
post_mail_fprintf(bounce, "Final-Recipient: rfc822; %s", post_mail_fprintf(bounce, "Final-Recipient: rfc822; %s",
bounce_info->log_handle->recipient); bounce_info->log_handle->recipient);
if (bounce_info->log_handle->orig_rcpt) if (bounce_info->log_handle->orig_rcpt) {
xtext_quote(bounce_info->buf, bounce_info->log_handle->orig_rcpt, "+=");
post_mail_fprintf(bounce, "Original-Recipient: rfc822; %s", post_mail_fprintf(bounce, "Original-Recipient: rfc822; %s",
bounce_info->log_handle->orig_rcpt); STR(bounce_info->buf));
}
post_mail_fprintf(bounce, "Action: %s", post_mail_fprintf(bounce, "Action: %s",
bounce_info->flush == BOUNCE_MSG_FAIL ? bounce_info->flush == BOUNCE_MSG_FAIL ?
"failed" : bounce_info->log_handle->dsn_action); "failed" : bounce_info->log_handle->dsn_action);
post_mail_fprintf(bounce, "Status: %s", post_mail_fprintf(bounce, "Status: %s",
bounce_info->log_handle->dsn_status); bounce_info->log_handle->dsn_status);
/* RFC 1894: diagnostic-type is an RFC 822 atom. */
fixed_mail_name = mystrdup(var_mail_name);
translit(fixed_mail_name, " \t\r\n()<>@,;:\\\".[]", "-----------------");
bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: X-%s; %s", bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: X-%s; %s",
fixed_mail_name, bounce_info->log_handle->text); bounce_info->mail_name, bounce_info->log_handle->text);
myfree(fixed_mail_name);
#if 0 #if 0
post_mail_fprintf(bounce, "Last-Attempt-Date: %s", post_mail_fprintf(bounce, "Last-Attempt-Date: %s",
bounce_info->log_handle->log_time); bounce_info->log_handle->log_time);

View File

@ -69,10 +69,12 @@ typedef struct {
const char *mime_boundary; /* for MIME */ const char *mime_boundary; /* for MIME */
int flush; /* 0=defer, other=bounce */ int flush; /* 0=defer, other=bounce */
VSTRING *buf; /* scratch pad */ VSTRING *buf; /* scratch pad */
VSTRING *sender; /* envelope sender */
VSTREAM *orig_fp; /* open queue file */ VSTREAM *orig_fp; /* open queue file */
long orig_offs; /* start of content */ long orig_offs; /* start of content */
time_t arrival_time; /* time of arrival */ time_t arrival_time; /* time of arrival */
BOUNCE_LOG *log_handle; /* open logfile */ BOUNCE_LOG *log_handle; /* open logfile */
char *mail_name; /* $mail_name, cooked */
} BOUNCE_INFO; } BOUNCE_INFO;
extern BOUNCE_INFO *bounce_mail_init(const char *, const char *, const char *, const char *, int); extern BOUNCE_INFO *bounce_mail_init(const char *, const char *, const char *, const char *, int);

View File

@ -136,6 +136,11 @@
/* .IP \fBcanonical_maps\fR /* .IP \fBcanonical_maps\fR
/* Address mapping lookup table for sender and recipient addresses /* Address mapping lookup table for sender and recipient addresses
/* in envelopes and headers. /* in envelopes and headers.
/* .IP \fBenable_original_recipient\fR
/* Enable support for the X-Original-To message header, which is
/* needed for multi-recipient mailboxes. When this is enabled, Postfix
/* performs duplicate elimination on (original recipient, rewritten
/* recipient) pairs, instead of looking at the rewritten recipient only.
/* .IP \fBrecipient_canonical_maps\fR /* .IP \fBrecipient_canonical_maps\fR
/* Address mapping lookup table for envelope and header recipient /* Address mapping lookup table for envelope and header recipient
/* addresses. /* addresses.
@ -342,6 +347,7 @@ int main(int argc, char **argv)
*/ */
single_server_main(argc, argv, cleanup_service, single_server_main(argc, argv, cleanup_service,
MAIL_SERVER_INT_TABLE, cleanup_int_table, MAIL_SERVER_INT_TABLE, cleanup_int_table,
MAIL_SERVER_BOOL_TABLE, cleanup_bool_table,
MAIL_SERVER_STR_TABLE, cleanup_str_table, MAIL_SERVER_STR_TABLE, cleanup_str_table,
MAIL_SERVER_TIME_TABLE, cleanup_time_table, MAIL_SERVER_TIME_TABLE, cleanup_time_table,
MAIL_SERVER_PRE_INIT, cleanup_pre_jail, MAIL_SERVER_PRE_INIT, cleanup_pre_jail,

View File

@ -127,6 +127,7 @@ extern void cleanup_all(void);
extern void cleanup_pre_jail(char *, char **); extern void cleanup_pre_jail(char *, char **);
extern void cleanup_post_jail(char *, char **); extern void cleanup_post_jail(char *, char **);
extern CONFIG_INT_TABLE cleanup_int_table[]; extern CONFIG_INT_TABLE cleanup_int_table[];
extern CONFIG_BOOL_TABLE cleanup_bool_table[];
extern CONFIG_STR_TABLE cleanup_str_table[]; extern CONFIG_STR_TABLE cleanup_str_table[];
extern CONFIG_TIME_TABLE cleanup_time_table[]; extern CONFIG_TIME_TABLE cleanup_time_table[];

View File

@ -8,6 +8,8 @@
/* /*
/* CONFIG_INT_TABLE cleanup_int_table[]; /* CONFIG_INT_TABLE cleanup_int_table[];
/* /*
/* CONFIG_BOOL_TABLE cleanup_bool_table[];
/*
/* CONFIG_STR_TABLE cleanup_str_table[]; /* CONFIG_STR_TABLE cleanup_str_table[];
/* /*
/* CONFIG_TIME_TABLE cleanup_time_table[]; /* CONFIG_TIME_TABLE cleanup_time_table[];
@ -102,6 +104,7 @@ char *var_mimehdr_checks; /* mime header checks */
char *var_nesthdr_checks; /* nested header checks */ char *var_nesthdr_checks; /* nested header checks */
char *var_body_checks; /* any body checks */ char *var_body_checks; /* any body checks */
int var_dup_filter_limit; /* recipient dup filter */ int var_dup_filter_limit; /* recipient dup filter */
bool var_enable_orcpt; /* Include orcpt in dup filter? */
char *var_empty_addr; /* destination of bounced bounces */ char *var_empty_addr; /* destination of bounced bounces */
int var_delay_warn_time; /* delay that triggers warning */ int var_delay_warn_time; /* delay that triggers warning */
char *var_prop_extension; /* propagate unmatched extension */ char *var_prop_extension; /* propagate unmatched extension */
@ -125,6 +128,11 @@ CONFIG_INT_TABLE cleanup_int_table[] = {
0, 0,
}; };
CONFIG_BOOL_TABLE cleanup_bool_table[] = {
VAR_ENABLE_ORCPT, DEF_ENABLE_ORCPT, &var_enable_orcpt,
0,
};
CONFIG_TIME_TABLE cleanup_time_table[] = { CONFIG_TIME_TABLE cleanup_time_table[] = {
VAR_DELAY_WARN_TIME, DEF_DELAY_WARN_TIME, &var_delay_warn_time, 0, 0, VAR_DELAY_WARN_TIME, DEF_DELAY_WARN_TIME, &var_delay_warn_time, 0, 0,
0, 0,

View File

@ -68,6 +68,12 @@ void cleanup_out_recipient(CLEANUP_STATE *state, const char *orcpt,
ARGV *argv; ARGV *argv;
char **cpp; char **cpp;
/*
* XXX Not elegant, but eliminates complexity in the record reading loop.
*/
if (!var_enable_orcpt)
orcpt = "";
/* /*
* Distinguish between different original recipient addresses that map * Distinguish between different original recipient addresses that map
* onto the same mailbox. The recipient will use our original recipient * onto the same mailbox. The recipient will use our original recipient

View File

@ -77,7 +77,7 @@ TESTPROG= domain_list dot_lockfile mail_addr_crunch mail_addr_find \
off_cvt quote_822_local rec2stream recdump resolve_clnt \ off_cvt quote_822_local rec2stream recdump resolve_clnt \
resolve_local rewrite_clnt stream2rec string_list tok822_parse \ resolve_local rewrite_clnt stream2rec string_list tok822_parse \
quote_821_local mail_conf_time mime_state strip_addr \ quote_821_local mail_conf_time mime_state strip_addr \
virtual8_maps verify_clnt virtual8_maps verify_clnt xtext
LIBS = ../../lib/libutil.a LIBS = ../../lib/libutil.a
LIB_DIR = ../../lib LIB_DIR = ../../lib
@ -240,9 +240,14 @@ verify_clnt: $(LIB) $(LIBS)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
mv junk $@.o mv junk $@.o
xtext: $(LIB)
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
mv junk $@.o
tests: tok822_test mime_test mime_nest mime_8bit mime_dom mime_trunc \ tests: tok822_test mime_test mime_nest mime_8bit mime_dom mime_trunc \
mime_cvt mime_cvt2 mime_cvt3 strip_addr_test tok822_limit_test \ mime_cvt mime_cvt2 mime_cvt3 strip_addr_test tok822_limit_test \
virtual8_test virtual8_test xtext_test
tok822_test: tok822_parse tok822_parse.in tok822_parse.ref tok822_test: tok822_parse tok822_parse.in tok822_parse.ref
./tok822_parse <tok822_parse.in >tok822_parse.tmp 2>&1 ./tok822_parse <tok822_parse.in >tok822_parse.tmp 2>&1
@ -306,6 +311,12 @@ virtual8_test: virtual8_maps virtual8_map virtual8.in virtual8.ref \
diff virtual8.ref virtual8.tmp diff virtual8.ref virtual8.tmp
rm -f virtual8.tmp virtual8_map.db rm -f virtual8.tmp virtual8_map.db
xtext_test: xtext
./xtext <xtext.c | od -cb >xtext.tmp
od -cb <xtext.c >xtext.ref
cmp xtext.ref xtext.tmp
rm -f xtext.ref xtext.tmp
# Requires: Postfix running, root privileges # Requires: Postfix running, root privileges
rewrite_clnt_test: rewrite_clnt rewrite_clnt.in rewrite_clnt.ref rewrite_clnt_test: rewrite_clnt rewrite_clnt.in rewrite_clnt.ref
@ -1344,7 +1355,7 @@ virtual8_maps.o: strip_addr.h
virtual8_maps.o: virtual8_maps.h virtual8_maps.o: virtual8_maps.h
xtext.o: xtext.c xtext.o: xtext.c
xtext.o: ../../include/sys_defs.h xtext.o: ../../include/sys_defs.h
xtext.o: ../../include/vstream.h xtext.o: ../../include/msg.h
xtext.o: ../../include/vbuf.h
xtext.o: ../../include/vstring.h xtext.o: ../../include/vstring.h
xtext.o: ../../include/vbuf.h
xtext.o: xtext.h xtext.o: xtext.h

View File

@ -38,6 +38,7 @@
#define CLEANUP_STAT_CONT (1<<3) /* Message content rejected */ #define CLEANUP_STAT_CONT (1<<3) /* Message content rejected */
#define CLEANUP_STAT_HOPS (1<<4) /* Too many hops */ #define CLEANUP_STAT_HOPS (1<<4) /* Too many hops */
#define CLEANUP_STAT_RCPT (1<<6) /* No recipients found */ #define CLEANUP_STAT_RCPT (1<<6) /* No recipients found */
#define CLEANUP_STAT_PROXY (1<<7) /* Proxy reject */
/* /*
* These are set when we can't bounce even if we were asked to. * These are set when we can't bounce even if we were asked to.

View File

@ -176,11 +176,14 @@ int header_token(HEADER_TOKEN *token, int token_len,
if (ch == '"') if (ch == '"')
break; break;
if (ch == '\n') { /* unfold */ if (ch == '\n') { /* unfold */
if (tok_count < token_len) {
len = LEN(token_buffer); len = LEN(token_buffer);
while (len > 0 && IS_SPACE_TAB_CR_LF(STR(token_buffer)[len - 1])) while (len > 0
&& IS_SPACE_TAB_CR_LF(STR(token_buffer)[len - 1]))
len--; len--;
if (len < LEN(token_buffer)) if (len < LEN(token_buffer))
vstring_truncate(token_buffer, len); vstring_truncate(token_buffer, len);
}
continue; continue;
} }
if (ch == '\\') { if (ch == '\\') {

View File

@ -115,9 +115,7 @@ void vlog_adhoc(const char *id, const char *orig_rcpt,
int delay = time((time_t *) 0) - entry; int delay = time((time_t *) 0) - entry;
vstring_vsprintf(why, fmt, ap); vstring_vsprintf(why, fmt, ap);
if (orig_rcpt == 0) if (orig_rcpt && *orig_rcpt && strcasecmp(recipient, orig_rcpt) != 0)
orig_rcpt = "";
if (strcasecmp(recipient, orig_rcpt) != 0)
msg_info("%s: to=<%s>, orig_to=<%s>, relay=%s, delay=%d, status=%s (%s)", msg_info("%s: to=<%s>, orig_to=<%s>, relay=%s, delay=%d, status=%s (%s)",
id, recipient, orig_rcpt, relay, delay, status, vstring_str(why)); id, recipient, orig_rcpt, relay, delay, status, vstring_str(why));
else else

View File

@ -112,9 +112,9 @@ static int convert_mail_conf_int(const char *name, int *intval)
static void check_mail_conf_int(const char *name, int intval, int min, int max) static void check_mail_conf_int(const char *name, int intval, int min, int max)
{ {
if (min && intval < min) if (min && intval < min)
msg_fatal("invalid %s: %d (min %d)", name, intval, min); msg_fatal("invalid %s parameter value %d < %d", name, intval, min);
if (max && intval > max) if (max && intval > max)
msg_fatal("invalid %s: %d (max %d)", name, intval, max); msg_fatal("invalid %s parameter value %d > %d", name, intval, max);
} }
/* get_mail_conf_int - evaluate integer-valued configuration variable */ /* get_mail_conf_int - evaluate integer-valued configuration variable */

View File

@ -87,10 +87,10 @@ static void check_mail_conf_str(const char *name, const char *strval,
int len = strlen(strval); int len = strlen(strval);
if (min && len < min) if (min && len < min)
msg_fatal("bad string length (%d < %d): %s = %s", msg_fatal("bad string length %d < %d: %s = %s",
len, min, name, strval); len, min, name, strval);
if (max && len > max) if (max && len > max)
msg_fatal("bad string length (%d > %d): %s = %s", msg_fatal("bad string length %d > %d: %s = %s",
len, max, name, strval); len, max, name, strval);
} }

View File

@ -165,9 +165,16 @@ int mail_copy(const char *sender,
if (flags & MAIL_COPY_ORIG_RCPT) { if (flags & MAIL_COPY_ORIG_RCPT) {
if (orig_rcpt == 0) if (orig_rcpt == 0)
msg_panic("%s: null orig_rcpt", myname); msg_panic("%s: null orig_rcpt", myname);
/*
* An empty original recipient record almost certainly means that
* original recipient processing was disabled.
*/
if (*orig_rcpt) {
quote_822_local(buf, orig_rcpt); quote_822_local(buf, orig_rcpt);
vstream_fprintf(dst, "X-Original-To: %s%s", vstring_str(buf), eol); vstream_fprintf(dst, "X-Original-To: %s%s", vstring_str(buf), eol);
} }
}
if (flags & MAIL_COPY_DELIVERED) { if (flags & MAIL_COPY_DELIVERED) {
if (delivered == 0) if (delivered == 0)
msg_panic("%s: null delivered", myname); msg_panic("%s: null delivered", myname);

View File

@ -486,6 +486,16 @@ extern char *var_fwd_exp_filter;
#define DEF_DELIVER_HDR "command, file, forward" #define DEF_DELIVER_HDR "command, file, forward"
extern char *var_deliver_hdr; extern char *var_deliver_hdr;
/*
* Cleanup: enable support for X-Original-To message headers, which are
* needed for multi-recipient mailboxes. When this is turned on, perform
* duplicate elimination on (original rcpt, rewritten rcpt) pairs, and
* generating non-empty original recipient records in the queue file.
*/
#define VAR_ENABLE_ORCPT "enable_original_recipient"
#define DEF_ENABLE_ORCPT 1
extern bool var_enable_orcpt;
#define VAR_EXP_OWN_ALIAS "expand_owner_alias" #define VAR_EXP_OWN_ALIAS "expand_owner_alias"
#define DEF_EXP_OWN_ALIAS 0 #define DEF_EXP_OWN_ALIAS 0
extern bool var_exp_own_alias; extern bool var_exp_own_alias;
@ -763,6 +773,10 @@ extern int var_smtp_rset_tmout;
#define DEF_SMTP_QUIT_TMOUT "300s" #define DEF_SMTP_QUIT_TMOUT "300s"
extern int var_smtp_quit_tmout; extern int var_smtp_quit_tmout;
#define VAR_SMTP_QUOTE_821_ENV "smtp_quote_rfc821_envelope"
#define DEF_SMTP_QUOTE_821_ENV 1
extern int var_smtp_quote_821_env;
#define VAR_SMTP_SKIP_4XX "smtp_skip_4xx_greeting" #define VAR_SMTP_SKIP_4XX "smtp_skip_4xx_greeting"
#define DEF_SMTP_SKIP_4XX 1 #define DEF_SMTP_SKIP_4XX 1
extern bool var_smtp_skip_4xx_greeting; extern bool var_smtp_skip_4xx_greeting;
@ -1691,6 +1705,14 @@ extern bool var_verify_neg_cache;
#define DEF_VERIFY_SENDER "postmaster" #define DEF_VERIFY_SENDER "postmaster"
extern char *var_verify_sender; extern char *var_verify_sender;
#define VAR_VERIFY_POLL_COUNT "address_verify_poll_count"
#define DEF_VERIFY_POLL_COUNT 3
extern int var_verify_poll_count;
#define VAR_VERIFY_POLL_DELAY "address_verify_poll_delay"
#define DEF_VERIFY_POLL_DELAY "3s"
extern int var_verify_poll_delay;
#define VAR_VRFY_LOCAL_XPORT "address_verify_local_transport" #define VAR_VRFY_LOCAL_XPORT "address_verify_local_transport"
#define DEF_VRFY_LOCAL_XPORT "$" VAR_LOCAL_TRANSPORT #define DEF_VRFY_LOCAL_XPORT "$" VAR_LOCAL_TRANSPORT
extern char *var_vrfy_local_xport; extern char *var_vrfy_local_xport;
@ -1804,6 +1826,21 @@ extern char *var_xport_null_key;
#define DEF_OLDLOG_COMPAT 1 #define DEF_OLDLOG_COMPAT 1
extern bool var_oldlog_compat; extern bool var_oldlog_compat;
/*
* SMTPD content proxy.
*/
#define VAR_SMTPD_PROXY_FILT "smtpd_proxy_filter"
#define DEF_SMTPD_PROXY_FILT ""
extern char *var_smtpd_proxy_filt;
#define VAR_SMTPD_PROXY_EHLO "smtpd_proxy_ehlo"
#define DEF_SMTPD_PROXY_EHLO "$" VAR_MYHOSTNAME
extern char *var_smtpd_proxy_ehlo;
#define VAR_SMTPD_PROXY_TMOUT "smtpd_proxy_timeout"
#define DEF_SMTPD_PROXY_TMOUT "100s"
extern int var_smtpd_proxy_tmout;
/* LICENSE /* LICENSE
/* .ad /* .ad
/* .fi /* .fi

View File

@ -20,10 +20,10 @@
* Patches change the patchlevel and the release date. Snapshots change the * Patches change the patchlevel and the release date. Snapshots change the
* release date only, unless they include the same bugfix as a patch release. * release date only, unless they include the same bugfix as a patch release.
*/ */
#define MAIL_RELEASE_DATE "20030621" #define MAIL_RELEASE_DATE "20030702"
#define VAR_MAIL_VERSION "mail_version" #define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "2.0.12-" MAIL_RELEASE_DATE #define DEF_MAIL_VERSION "2.0.13-" MAIL_RELEASE_DATE
extern char *var_mail_version; extern char *var_mail_version;
/* /*

View File

@ -66,8 +66,8 @@
/* .IP RESOLVE_CLASS_LOCAL /* .IP RESOLVE_CLASS_LOCAL
/* The address domain matches $mydestination or $inet_interfaces. /* The address domain matches $mydestination or $inet_interfaces.
/* .IP RESOLVE_CLASS_ALIAS /* .IP RESOLVE_CLASS_ALIAS
/* The address domain matches $virtual_alias_domains (simulated /* The address domain matches $virtual_alias_domains (virtual
/* virtual domains, where each address is redirected to a real /* alias domains, where each address is redirected to a real
/* local or remote address). /* local or remote address).
/* .IP RESOLVE_CLASS_VIRTUAL /* .IP RESOLVE_CLASS_VIRTUAL
/* The address domain matches $virtual_mailbox_domains (true /* The address domain matches $virtual_mailbox_domains (true

View File

@ -1,13 +1,13 @@
#ifndef _VRFY_STAT_H_INCLUDED_ #ifndef _VRFY_CLNT_H_INCLUDED_
#define _VRFY_STAT_H_INCLUDED_ #define _VRFY_CLNT_H_INCLUDED_
/*++ /*++
/* NAME /* NAME
/* mail_proto 3h /* verify_clnt 3h
/* SUMMARY /* SUMMARY
/* mail internal IPC support /* address verification client interface
/* SYNOPSIS /* SYNOPSIS
/* #include <mail_proto.h> /* #include <verify_clnt.h>
/* DESCRIPTION /* DESCRIPTION
/* .nf /* .nf

View File

@ -2,22 +2,29 @@
/* NAME /* NAME
/* xtext 3 /* xtext 3
/* SUMMARY /* SUMMARY
/* translate characters according to RFC 1894 /* quote/unquote text, HTTP style.
/* SYNOPSIS /* SYNOPSIS
/* #include <xtext.h> /* #include <xtext.h>
/* /*
/* VSTRING *xtext(result, original) /* VSTRING *xtext_quote(quoted, unquoted, special)
/* VSTRING *result; /* VSTRING *quoted;
/* const char *original; /* const char *unquoted;
/* DESCRIPTION /* const char *special;
/* xtext() takes a null-terminated string, and produces a translation
/* according to RFC 1894 (DSN).
/* BUGS
/* Cannot replace null characters.
/* /*
/* Does not insert CR LF SPACE to limit output line length. /* VSTRING *xtext_unquote(unquoted, quoted)
/* SEE ALSO /* VSTRING *unquoted;
/* RFC 1894, Delivery Status Notifications /* const char *quoted;
/* DESCRIPTION
/* xtext_quote() takes a null-terminated string and replaces characters
/* <33(10) and >126(10), as well as characters specified with "special"
/* by +XX, XX being the two-digit uppercase hexadecimal equivalent.
/*
/* xtext_unquote() performs the opposite transformation. This function
/* understands lowercase, uppercase, and mixed case %XX sequences. The
/* result value is the unquoted argument in case of success, a null pointer
/* otherwise.
/* BUGS
/* This module cannot process null characters in data.
/* LICENSE /* LICENSE
/* .ad /* .ad
/* .fi /* .fi
@ -31,56 +38,112 @@
/* System library. */ /* System library. */
#include "sys_defs.h" #include <sys_defs.h>
#include <vstream.h> #include <string.h>
#include <ctype.h>
/* Utility library. */ /* Utility library. */
#include <vstring.h> #include "msg.h"
#include "vstring.h"
#include "xtext.h"
/* Global library. */ /* Application-specific. */
#include <xtext.h> #define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
/* xtext - translate text according to RFC 1894 */ /* xtext_quote - unquoted data to quoted */
VSTRING *xtext(VSTRING *result, const char *original) VSTRING *xtext_quote(VSTRING *quoted, const char *unquoted, const char *special)
{ {
const char *cp; const char *cp;
int ch; int ch;
/* VSTRING_RESET(quoted);
* Preliminary implementation. ASCII specific!! for (cp = unquoted; (ch = *(unsigned const char *) cp) != 0; cp++) {
*/ if (ch != '+' && ch > 32 && ch < 127 && strchr(special, ch) == 0) {
VSTRING_RESET(result); VSTRING_ADDCH(quoted, ch);
for (cp = original; (ch = *(unsigned char *) cp) != 0; cp++) { } else {
if (ch == '+' || ch == '\\' || ch == '(' || ch < 33 || ch > 126) vstring_sprintf_append(quoted, "+%02X", ch);
vstring_sprintf_append(result, "+%02X", ch);
else
VSTRING_ADDCH(result, ch);
} }
VSTRING_TERMINATE(result); }
VSTRING_TERMINATE(quoted);
return (quoted);
}
return (result); /* xtext_unquote - quoted data to unquoted */
VSTRING *xtext_unquote(VSTRING *unquoted, const char *quoted)
{
const char *cp;
int ch;
VSTRING_RESET(unquoted);
for (cp = quoted; (ch = *cp) != 0; cp++) {
if (ch == '+') {
if (ISDIGIT(cp[1]))
ch = (cp[1] - '0') << 4;
else if (cp[1] >= 'a' && cp[1] <= 'f')
ch = (cp[1] - 'a' + 10) << 4;
else if (cp[1] >= 'A' && cp[1] <= 'F')
ch = (cp[1] - 'A' + 10) << 4;
else
return (0);
if (ISDIGIT(cp[2]))
ch |= (cp[2] - '0');
else if (cp[2] >= 'a' && cp[2] <= 'f')
ch |= (cp[2] - 'a' + 10);
else if (cp[2] >= 'A' && cp[2] <= 'F')
ch |= (cp[2] - 'A' + 10);
else
return (0);
cp += 2;
}
VSTRING_ADDCH(unquoted, ch);
}
VSTRING_TERMINATE(unquoted);
return (unquoted);
} }
#ifdef TEST #ifdef TEST
#define STR(x) vstring_str(x) /*
* Proof-of-concept test program: convert to quoted and back.
*/
#include <vstream.h> #include <vstream.h>
int main(int unused_argc, char **unused_argv) #define BUFLEN 1024
{
VSTRING *ibuf = vstring_alloc(100);
VSTRING *obuf = vstring_alloc(100);
while (vstring_fgets(ibuf, VSTREAM_IN)) { static int read_buf(VSTREAM *fp, VSTRING *buf)
vstream_fputs(STR(xtext(obuf, STR(ibuf)))); {
vstream_fflush(VSTREAM_OUT); int len;
VSTRING_RESET(buf);
len = vstream_fread(fp, STR(buf), vstring_avail(buf));
VSTRING_AT_OFFSET(buf, len); /* XXX */
VSTRING_TERMINATE(buf);
return (len);
}
main(int unused_argc, char **unused_argv)
{
VSTRING *unquoted = vstring_alloc(BUFLEN);
VSTRING *quoted = vstring_alloc(100);
int len;
while ((len = read_buf(VSTREAM_IN, unquoted)) > 0) {
xtext_quote(quoted, STR(unquoted), "+=");
if (xtext_unquote(unquoted, STR(quoted)) == 0)
msg_fatal("bad input: %.100s", STR(quoted));
if (LEN(unquoted) != len)
msg_fatal("len %d != unquoted len %d", len, LEN(unquoted));
if (vstream_fwrite(VSTREAM_OUT, STR(unquoted), LEN(unquoted)) != LEN(unquoted))
msg_fatal("write error: %m");
} }
vstring_free(ibuf); vstream_fflush(VSTREAM_OUT);
vstring_free(obuf); vstring_free(unquoted);
vstring_free(quoted);
return (0); return (0);
} }

View File

@ -1,8 +1,11 @@
#ifndef _XTEXT_H_INCLUDED_
#define _XTEXT_H_INCLUDED_
/*++ /*++
/* NAME /* NAME
/* xtext 3h /* xtext 3h
/* SUMMARY /* SUMMARY
/* translate characters according to RFC 1894 /* quote/unquote text, xtext style.
/* SYNOPSIS /* SYNOPSIS
/* #include <xtext.h> /* #include <xtext.h>
/* DESCRIPTION /* DESCRIPTION
@ -16,7 +19,8 @@
/* /*
* External interface. * External interface.
*/ */
extern VSTRING *xtext(VSTRING *, const char *); extern VSTRING *xtext_quote(VSTRING *, const char *, const char *);
extern VSTRING *xtext_unquote(VSTRING *, const char *);
/* LICENSE /* LICENSE
/* .ad /* .ad
@ -28,3 +32,5 @@ extern VSTRING *xtext(VSTRING *, const char *);
/* P.O. Box 704 /* P.O. Box 704
/* Yorktown Heights, NY 10598, USA /* Yorktown Heights, NY 10598, USA
/*--*/ /*--*/
#endif

View File

@ -389,7 +389,7 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
if (message->rcpt_offset == 0) { if (message->rcpt_offset == 0) {
message->rcpt_unread--; message->rcpt_unread--;
qmgr_rcpt_list_add(&message->rcpt_list, curr_offset, qmgr_rcpt_list_add(&message->rcpt_list, curr_offset,
orig_rcpt ? orig_rcpt : "unknown", start); orig_rcpt ? orig_rcpt : "", start);
if (orig_rcpt) { if (orig_rcpt) {
myfree(orig_rcpt); myfree(orig_rcpt);
orig_rcpt = 0; orig_rcpt = 0;

View File

@ -98,7 +98,7 @@ static void postcat(VSTREAM *fp, VSTRING *buffer)
* See if this is a plausible file. * See if this is a plausible file.
*/ */
if ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF) { if ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF) {
if (!strchr(REC_TYPE_POST_ENVELOPE, ch)) { if (!strchr(REC_TYPE_ENVELOPE, ch)) {
msg_warn("%s: input is not a valid queue file", VSTREAM_PATH(fp)); msg_warn("%s: input is not a valid queue file", VSTREAM_PATH(fp));
return; return;
} }

View File

@ -357,7 +357,7 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
#define FUDGE(x) ((x) * (var_qmgr_fudge / 100.0)) #define FUDGE(x) ((x) * (var_qmgr_fudge / 100.0))
if (message->rcpt_offset == 0) { if (message->rcpt_offset == 0) {
qmgr_rcpt_list_add(&message->rcpt_list, curr_offset, qmgr_rcpt_list_add(&message->rcpt_list, curr_offset,
orig_rcpt ? orig_rcpt : "unknown", start); orig_rcpt ? orig_rcpt : "", start);
if (orig_rcpt) { if (orig_rcpt) {
myfree(orig_rcpt); myfree(orig_rcpt);
orig_rcpt = 0; orig_rcpt = 0;

View File

@ -104,7 +104,7 @@ static void showq_reasons(VSTREAM *, BOUNCE_LOG *, HTABLE *);
/* showq_report - report status of sender and recipients */ /* showq_report - report status of sender and recipients */
static void showq_report(VSTREAM *client, char *queue, char *id, static void showq_report(VSTREAM *client, char *queue, char *id,
VSTREAM *qfile, long size) VSTREAM *qfile, long size, time_t mtime)
{ {
VSTRING *buf = vstring_alloc(100); VSTRING *buf = vstring_alloc(100);
VSTRING *printable_quoted_addr = vstring_alloc(100); VSTRING *printable_quoted_addr = vstring_alloc(100);
@ -152,7 +152,8 @@ static void showq_report(VSTREAM *client, char *queue, char *id,
printable(STR(printable_quoted_addr), '?'); printable(STR(printable_quoted_addr), '?');
vstream_fprintf(client, DATA_FORMAT, id, status, vstream_fprintf(client, DATA_FORMAT, id, status,
msg_size > 0 ? msg_size : size, arrival_time > 0 ? msg_size > 0 ? msg_size : size, arrival_time > 0 ?
asctime(localtime(&arrival_time)) : "??", asctime(localtime(&arrival_time)) :
asctime(localtime(&mtime)),
STR(printable_quoted_addr)); STR(printable_quoted_addr));
break; break;
case REC_TYPE_RCPT: case REC_TYPE_RCPT:
@ -302,7 +303,8 @@ static void showq_service(VSTREAM *client, char *unused_service, char **argv)
vstream_fprintf(client, "\n"); vstream_fprintf(client, "\n");
if ((qfile = mail_queue_open(qp->name, id, O_RDONLY, 0)) != 0) { if ((qfile = mail_queue_open(qp->name, id, O_RDONLY, 0)) != 0) {
queue_size += st.st_size; queue_size += st.st_size;
showq_report(client, qp->name, id, qfile, (long) st.st_size); showq_report(client, qp->name, id, qfile, (long) st.st_size,
st.st_mtime);
if (vstream_fclose(qfile)) if (vstream_fclose(qfile))
msg_warn("close file %s %s: %m", qp->name, id); msg_warn("close file %s %s: %m", qp->name, id);
} else if (strcmp(qp->name, MAIL_QUEUE_MAILDROP) == 0) { } else if (strcmp(qp->name, MAIL_QUEUE_MAILDROP) == 0) {

View File

@ -21,10 +21,6 @@
/* the destination host, sorts the list by preference, and connects /* the destination host, sorts the list by preference, and connects
/* to each listed address until it finds a server that responds. /* to each listed address until it finds a server that responds.
/* /*
/* When the domain or host is specified as a comma/whitespace
/* separated list, the SMTP client repeats the above process
/* for all destinations until it finds a server that responds.
/*
/* Once the SMTP client has received the server greeting banner, no /* Once the SMTP client has received the server greeting banner, no
/* error will cause it to proceed to the next address on the mail /* error will cause it to proceed to the next address on the mail
/* exchanger list. Instead, the message is either bounced, or its /* exchanger list. Instead, the message is either bounced, or its
@ -118,6 +114,9 @@
/* Some SMTP servers misbehave on long lines. /* Some SMTP servers misbehave on long lines.
/* .IP \fBsmtp_helo_name\fR /* .IP \fBsmtp_helo_name\fR
/* The hostname to be used in HELO and EHLO commands. /* The hostname to be used in HELO and EHLO commands.
/* .IP \fBsmtp_quote_rfc821_envelope\fR
/* Whether or not to quote MAIL FROM and RCPT TO addresses as
/* per the rules laid out in RFC 821.
/* .IP \fBsmtp_skip_4xx_greeting\fR /* .IP \fBsmtp_skip_4xx_greeting\fR
/* Skip servers that greet us with a 4xx status code. /* Skip servers that greet us with a 4xx status code.
/* .IP \fBsmtp_skip_5xx_greeting\fR /* .IP \fBsmtp_skip_5xx_greeting\fR
@ -298,6 +297,7 @@ int var_smtp_pix_delay;
int var_smtp_line_limit; int var_smtp_line_limit;
char *var_smtp_helo_name; char *var_smtp_helo_name;
char *var_smtp_host_lookup; char *var_smtp_host_lookup;
int var_smtp_quote_821_env;
/* /*
* Global variables. smtp_errno is set by the address lookup routines and by * Global variables. smtp_errno is set by the address lookup routines and by
@ -509,6 +509,7 @@ int main(int argc, char **argv)
VAR_SMTP_NEVER_EHLO, DEF_SMTP_NEVER_EHLO, &var_smtp_never_ehlo, 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_SASL_ENABLE, DEF_SMTP_SASL_ENABLE, &var_smtp_sasl_enable,
VAR_SMTP_RAND_ADDR, DEF_SMTP_RAND_ADDR, &var_smtp_rand_addr, VAR_SMTP_RAND_ADDR, DEF_SMTP_RAND_ADDR, &var_smtp_rand_addr,
VAR_SMTP_QUOTE_821_ENV, DEF_SMTP_QUOTE_821_ENV, &var_smtp_quote_821_env,
0, 0,
}; };

View File

@ -379,7 +379,7 @@ int smtp_xfer(SMTP_STATE *state)
* Macros for readability. * Macros for readability.
*/ */
#define REWRITE_ADDRESS(dst, mid, src) do { \ #define REWRITE_ADDRESS(dst, mid, src) do { \
if (*(src)) { \ if (*(src) && var_smtp_quote_821_env) { \
quote_821_local(mid, src); \ quote_821_local(mid, src); \
smtp_unalias_addr(dst, vstring_str(mid)); \ smtp_unalias_addr(dst, vstring_str(mid)); \
} else { \ } else { \
@ -388,7 +388,7 @@ int smtp_xfer(SMTP_STATE *state)
} while (0) } while (0)
#define QUOTE_ADDRESS(dst, src) do { \ #define QUOTE_ADDRESS(dst, src) do { \
if (*(src)) { \ if (*(src) && var_smtp_quote_821_env) { \
quote_821_local(dst, src); \ quote_821_local(dst, src); \
} else { \ } else { \
vstring_strcpy(dst, src); \ vstring_strcpy(dst, src); \
@ -640,6 +640,7 @@ int smtp_xfer(SMTP_STATE *state)
if (resp->code == 552) if (resp->code == 552)
resp->code = 452; resp->code = 452;
#endif #endif
rcpt = request->rcpt_list.info + recv_rcpt;
if (resp->code / 100 == 2) { if (resp->code / 100 == 2) {
++nrcpt; ++nrcpt;
/* If trace-only, mark the recipient done. */ /* If trace-only, mark the recipient done. */
@ -654,7 +655,6 @@ int smtp_xfer(SMTP_STATE *state)
rcpt->offset = 0; /* in case deferred */ rcpt->offset = 0; /* in case deferred */
} }
} else { } else {
rcpt = request->rcpt_list.info + recv_rcpt;
smtp_rcpt_fail(state, resp->code, rcpt, smtp_rcpt_fail(state, resp->code, rcpt,
"host %s said: %s (in reply to %s)", "host %s said: %s (in reply to %s)",
session->namaddr, session->namaddr,

View File

@ -1,10 +1,10 @@
SHELL = /bin/sh SHELL = /bin/sh
SRCS = smtpd.c smtpd_token.c smtpd_check.c smtpd_chat.c smtpd_state.c \ SRCS = smtpd.c smtpd_token.c smtpd_check.c smtpd_chat.c smtpd_state.c \
smtpd_peer.c smtpd_sasl_proto.c smtpd_sasl_glue.c smtpd_peer.c smtpd_sasl_proto.c smtpd_sasl_glue.c smtpd_proxy.c
OBJS = smtpd.o smtpd_token.o smtpd_check.o smtpd_chat.o smtpd_state.o \ OBJS = smtpd.o smtpd_token.o smtpd_check.o smtpd_chat.o smtpd_state.o \
smtpd_peer.o smtpd_sasl_proto.o smtpd_sasl_glue.o smtpd_peer.o smtpd_sasl_proto.o smtpd_sasl_glue.o smtpd_proxy.o
HDRS = smtpd_token.h smtpd_check.h smtpd_chat.h smtpd_sasl_proto.h \ HDRS = smtpd_token.h smtpd_check.h smtpd_chat.h smtpd_sasl_proto.h \
smtpd_sasl_glue.h smtpd_sasl_glue.h smtpd_proxy.h
TESTSRC = smtpd_token_test.c TESTSRC = smtpd_token_test.c
WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
-Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \ -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
@ -154,6 +154,7 @@ smtpd.o: smtpd_check.h
smtpd.o: smtpd_chat.h smtpd.o: smtpd_chat.h
smtpd.o: smtpd_sasl_proto.h smtpd.o: smtpd_sasl_proto.h
smtpd.o: smtpd_sasl_glue.h smtpd.o: smtpd_sasl_glue.h
smtpd.o: smtpd_proxy.h
smtpd_chat.o: smtpd_chat.c smtpd_chat.o: smtpd_chat.c
smtpd_chat.o: ../../include/sys_defs.h smtpd_chat.o: ../../include/sys_defs.h
smtpd_chat.o: ../../include/msg.h smtpd_chat.o: ../../include/msg.h
@ -241,6 +242,25 @@ smtpd_peer.o: smtpd.h
smtpd_peer.o: ../../include/vstream.h smtpd_peer.o: ../../include/vstream.h
smtpd_peer.o: ../../include/argv.h smtpd_peer.o: ../../include/argv.h
smtpd_peer.o: ../../include/mail_stream.h smtpd_peer.o: ../../include/mail_stream.h
smtpd_proxy.o: smtpd_proxy.c
smtpd_proxy.o: ../../include/sys_defs.h
smtpd_proxy.o: ../../include/msg.h
smtpd_proxy.o: ../../include/vstream.h
smtpd_proxy.o: ../../include/vbuf.h
smtpd_proxy.o: ../../include/vstring.h
smtpd_proxy.o: ../../include/stringops.h
smtpd_proxy.o: ../../include/connect.h
smtpd_proxy.o: ../../include/iostuff.h
smtpd_proxy.o: ../../include/mail_error.h
smtpd_proxy.o: ../../include/name_mask.h
smtpd_proxy.o: ../../include/smtp_stream.h
smtpd_proxy.o: ../../include/cleanup_user.h
smtpd_proxy.o: ../../include/mail_params.h
smtpd_proxy.o: ../../include/rec_type.h
smtpd_proxy.o: smtpd.h
smtpd_proxy.o: ../../include/argv.h
smtpd_proxy.o: ../../include/mail_stream.h
smtpd_proxy.o: smtpd_proxy.h
smtpd_sasl_glue.o: smtpd_sasl_glue.c smtpd_sasl_glue.o: smtpd_sasl_glue.c
smtpd_sasl_glue.o: ../../include/sys_defs.h smtpd_sasl_glue.o: ../../include/sys_defs.h
smtpd_sasl_glue.o: ../../include/msg.h smtpd_sasl_glue.o: ../../include/msg.h

View File

@ -97,6 +97,24 @@
/* Maps that specify the SASL login name that owns a MAIL FROM sender /* Maps that specify the SASL login name that owns a MAIL FROM sender
/* address. Used by the \fBreject_sender_login_mismatch\fR sender /* address. Used by the \fBreject_sender_login_mismatch\fR sender
/* anti-spoofing restriction. /* anti-spoofing restriction.
/* .SH "Pass-through proxy"
/* .ad
/* .fi
/* .ad
/* Optionally, the Postfix SMTP server can be configured to
/* forward all mail to a proxy server, for example a real-time
/* content filter. This proxy server should support the same
/* MAIL FROM and RCPT TO command syntax as Postfix, but does not
/* need to support ESMTP command pipelining.
/* .IP \fBsmtpd_proxy_filter\fR
/* The \fIhost:port\fR of the SMTP proxy server. The \fIhost\fR
/* or \fIhost:\fR portion is optional.
/* .IP \fBsmtpd_proxy_timeout\fR
/* Timeout for connecting to, sending to and receiving from
/* the SMTP proxy server.
/* .IP \fBsmtpd_proxy_ehlo\fR
/* The hostname to use when sending an EHLO command to the
/* SMTP proxy server.
/* .SH Miscellaneous /* .SH Miscellaneous
/* .ad /* .ad
/* .fi /* .fi
@ -245,6 +263,21 @@
/* Restrict what domains this mail system will relay /* Restrict what domains this mail system will relay
/* mail to. The domains are routed to the delivery agent /* mail to. The domains are routed to the delivery agent
/* specified with the \fBrelay_transport\fR setting. /* specified with the \fBrelay_transport\fR setting.
/* .SH "Sender/recipient address verification"
/* .ad
/* .fi
/* Address verification is implemented by sending probe email
/* messages that are not actually delivered, and is enabled
/* via the reject_unverified_{sender,recipient} access restriction.
/* The status of verification probes is maintained by the address
/* verification service.
/* .IP \fBaddress_verify_poll_count\fR
/* How many times to query the address verification service
/* for completion of an address verification request.
/* Specify 0 to implement a simple form of greylisting.
/* .IP \fBaddress_verify_poll_delay\fR
/* Time to wait after querying the address verification service
/* for completion of an address verification request.
/* .SH "UCE control responses" /* .SH "UCE control responses"
/* .ad /* .ad
/* .fi /* .fi
@ -289,10 +322,11 @@
/* .IP \fBunverified_recipient_reject_code\fR /* .IP \fBunverified_recipient_reject_code\fR
/* Response code when a recipient address is known to be undeliverable. /* Response code when a recipient address is known to be undeliverable.
/* SEE ALSO /* SEE ALSO
/* trivial-rewrite(8) address resolver
/* cleanup(8) message canonicalization /* cleanup(8) message canonicalization
/* master(8) process manager /* master(8) process manager
/* syslogd(8) system logging /* syslogd(8) system logging
/* trivial-rewrite(8) address resolver
/* verify(8) address verification service
/* LICENSE /* LICENSE
/* .ad /* .ad
/* .fi /* .fi
@ -368,12 +402,13 @@
/* Application-specific */ /* Application-specific */
#include "smtpd_token.h" #include <smtpd_token.h>
#include "smtpd.h" #include <smtpd.h>
#include "smtpd_check.h" #include <smtpd_check.h>
#include "smtpd_chat.h" #include <smtpd_chat.h>
#include "smtpd_sasl_proto.h" #include <smtpd_sasl_proto.h>
#include "smtpd_sasl_glue.h" #include <smtpd_sasl_glue.h>
#include <smtpd_proxy.h>
/* /*
* Tunable parameters. Make sure that there is some bound on the length of * Tunable parameters. Make sure that there is some bound on the length of
@ -446,6 +481,12 @@ int var_virt_mailbox_code;
int var_relay_rcpt_code; int var_relay_rcpt_code;
char *var_verp_clients; char *var_verp_clients;
int var_show_unk_rcpt_table; int var_show_unk_rcpt_table;
int var_verify_poll_count;
int var_verify_poll_delay;
char *var_smtpd_proxy_filt;
int var_smtpd_proxy_tmout;
char *var_smtpd_proxy_ehlo;
/* /*
* Silly little macros. * Silly little macros.
@ -762,7 +803,9 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
smtpd_chat_reply(state, "503 Error: send HELO/EHLO first"); smtpd_chat_reply(state, "503 Error: send HELO/EHLO first");
return (-1); return (-1);
} }
if (state->cleanup != 0) { #define IN_MAIL_TRANSACTION(state) ((state)->cleanup || (state)->proxy)
if (IN_MAIL_TRANSACTION(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL; state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 Error: nested MAIL command"); smtpd_chat_reply(state, "503 Error: nested MAIL command");
return (-1); return (-1);
@ -840,6 +883,13 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
smtpd_chat_reply(state, "%s", err); smtpd_chat_reply(state, "%s", err);
return (-1); return (-1);
} }
if (SMTPD_STAND_ALONE(state) == 0 && *var_smtpd_proxy_filt) {
if (smtpd_proxy_open(state, var_smtpd_proxy_filt, var_smtpd_proxy_tmout,
var_smtpd_proxy_ehlo, STR(state->buffer)) != 0) {
smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
return (-1);
}
} else {
if ((err = smtpd_check_size(state, state->msg_size)) != 0) { if ((err = smtpd_check_size(state, state->msg_size)) != 0) {
smtpd_chat_reply(state, "%s", err); smtpd_chat_reply(state, "%s", err);
return (-1); return (-1);
@ -884,6 +934,7 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
} }
if (verp_delims) if (verp_delims)
rec_fputs(state->cleanup, REC_TYPE_VERP, verp_delims); rec_fputs(state->cleanup, REC_TYPE_VERP, verp_delims);
}
state->sender = mystrdup(argv[2].strval); state->sender = mystrdup(argv[2].strval);
smtpd_chat_reply(state, "250 Ok"); smtpd_chat_reply(state, "250 Ok");
return (0); return (0);
@ -918,6 +969,8 @@ static void mail_reset(SMTPD_STATE *state)
smtpd_sasl_mail_reset(state); smtpd_sasl_mail_reset(state);
#endif #endif
state->discard = 0; state->discard = 0;
if (state->proxy)
smtpd_proxy_close(state);
} }
/* rcpt_cmd - process RCPT TO command */ /* rcpt_cmd - process RCPT TO command */
@ -937,7 +990,7 @@ static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
* command with a 501 response. So much for the principle of "be liberal * command with a 501 response. So much for the principle of "be liberal
* in what you accept, be strict in what you send". * in what you accept, be strict in what you send".
*/ */
if (state->cleanup == 0) { if (!IN_MAIL_TRANSACTION(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL; state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 Error: need MAIL command"); smtpd_chat_reply(state, "503 Error: need MAIL command");
return (-1); return (-1);
@ -977,6 +1030,11 @@ static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
return (-1); return (-1);
} }
} }
if (state->proxy && smtpd_proxy_cmd(state, SMTPD_PROX_STAT_OK,
"%s", STR(state->buffer)) != 0) {
smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
return (-1);
}
/* /*
* Store the recipient. Remember the first one. * Store the recipient. Remember the first one.
@ -984,6 +1042,7 @@ static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
state->rcpt_count++; state->rcpt_count++;
if (state->recipient == 0) if (state->recipient == 0)
state->recipient = mystrdup(argv[2].strval); state->recipient = mystrdup(argv[2].strval);
if (state->cleanup)
rec_fputs(state->cleanup, REC_TYPE_RCPT, argv[2].strval); rec_fputs(state->cleanup, REC_TYPE_RCPT, argv[2].strval);
smtpd_chat_reply(state, "250 Ok"); smtpd_chat_reply(state, "250 Ok");
return (0); return (0);
@ -1012,6 +1071,10 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
int first = 1; int first = 1;
VSTRING *why = 0; VSTRING *why = 0;
int saved_err; int saved_err;
int (*out_record) (VSTREAM *, int, const char *, int);
int (*out_fprintf) (VSTREAM *, int, const char *,...);
VSTREAM *out_stream;
int out_error;
/* /*
* Sanity checks. With ESMTP command pipelining the client can send DATA * Sanity checks. With ESMTP command pipelining the client can send DATA
@ -1019,7 +1082,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
* error. * error.
*/ */
if (state->rcpt_count == 0) { if (state->rcpt_count == 0) {
if (state->cleanup == 0) { if (!IN_MAIL_TRANSACTION(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL; state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 Error: need RCPT command"); smtpd_chat_reply(state, "503 Error: need RCPT command");
} else { } else {
@ -1036,35 +1099,61 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
smtpd_chat_reply(state, "%s", err); smtpd_chat_reply(state, "%s", err);
return (-1); return (-1);
} }
if (state->proxy && smtpd_proxy_cmd(state, SMTPD_PROX_STAT_MORE,
"%s", STR(state->buffer)) != 0) {
smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
return (-1);
}
/*
* One level of indirection to choose between normal or proxied
* operation. We want to avoid massive code duplication within tons of
* if-else clauses.
*/
if (state->proxy) {
out_stream = state->proxy;
out_record = smtpd_proxy_rec_put;
out_fprintf = smtpd_proxy_rec_fprintf;
out_error = CLEANUP_STAT_PROXY;
} else {
out_stream = state->cleanup;
out_record = rec_put;
out_fprintf = rec_fprintf;
out_error = CLEANUP_STAT_WRITE;
}
/* /*
* Terminate the message envelope segment. Start the message content * Terminate the message envelope segment. Start the message content
* segment, and prepend our own Received: header. If there is only one * segment, and prepend our own Received: header. If there is only one
* recipient, list the recipient address. * recipient, list the recipient address.
*/ */
if (state->cleanup)
rec_fputs(state->cleanup, REC_TYPE_MESG, ""); rec_fputs(state->cleanup, REC_TYPE_MESG, "");
rec_fprintf(state->cleanup, REC_TYPE_NORM, out_fprintf(out_stream, REC_TYPE_NORM,
"Received: from %s (%s [%s])", "Received: from %s (%s [%s])",
state->helo_name ? state->helo_name : state->name, state->helo_name ? state->helo_name : state->name,
state->name, state->addr); state->name, state->addr);
if (state->rcpt_count == 1 && state->recipient) { if (state->rcpt_count == 1 && state->recipient) {
rec_fprintf(state->cleanup, REC_TYPE_NORM, out_fprintf(out_stream, REC_TYPE_NORM,
"\tby %s (%s) with %s id %s", state->cleanup ? "\tby %s (%s) with %s id %s" :
"\tby %s (%s) with %s",
var_myhostname, var_mail_name, var_myhostname, var_mail_name,
state->protocol, state->queue_id); state->protocol, state->queue_id);
quote_822_local(state->buffer, state->recipient); quote_822_local(state->buffer, state->recipient);
rec_fprintf(state->cleanup, REC_TYPE_NORM, out_fprintf(out_stream, REC_TYPE_NORM,
"\tfor <%s>; %s", STR(state->buffer), mail_date(state->time)); "\tfor <%s>; %s", STR(state->buffer), mail_date(state->time));
} else { } else {
rec_fprintf(state->cleanup, REC_TYPE_NORM, out_fprintf(out_stream, REC_TYPE_NORM,
"\tby %s (%s) with %s", state->cleanup ? "\tby %s (%s) with %s id %s;" :
var_myhostname, var_mail_name, state->protocol); "\tby %s (%s) with %s;",
rec_fprintf(state->cleanup, REC_TYPE_NORM, var_myhostname, var_mail_name,
"\tid %s; %s", state->queue_id, mail_date(state->time)); state->protocol, state->queue_id);
out_fprintf(out_stream, REC_TYPE_NORM,
"\t%s", mail_date(state->time));
} }
#ifdef RECEIVED_ENVELOPE_FROM #ifdef RECEIVED_ENVELOPE_FROM
quote_822_local(state->buffer, state->sender); quote_822_local(state->buffer, state->sender);
rec_fprintf(state->cleanup, REC_TYPE_NORM, out_fprintf(out_stream, REC_TYPE_NORM,
"\t(envelope-from %s)", STR(state->buffer)); "\t(envelope-from %s)", STR(state->buffer));
#endif #endif
smtpd_chat_reply(state, "354 End data with <CR><LF>.<CR><LF>"); smtpd_chat_reply(state, "354 End data with <CR><LF>.<CR><LF>");
@ -1081,9 +1170,6 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
* XXX Deal with UNIX-style From_ lines at the start of message content * XXX Deal with UNIX-style From_ lines at the start of message content
* because sendmail permits it. * because sendmail permits it.
*/ */
if (vstream_fflush(state->cleanup))
state->err = CLEANUP_STAT_WRITE;
for (prev_rec_type = 0; /* void */ ; prev_rec_type = curr_rec_type) { for (prev_rec_type = 0; /* void */ ; prev_rec_type = curr_rec_type) {
if (smtp_get(state->buffer, state->client, var_line_limit) == '\n') if (smtp_get(state->buffer, state->client, var_line_limit) == '\n')
curr_rec_type = REC_TYPE_NORM; curr_rec_type = REC_TYPE_NORM;
@ -1093,25 +1179,30 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
len = VSTRING_LEN(state->buffer); len = VSTRING_LEN(state->buffer);
if (first) { if (first) {
if (strncmp(start + strspn(start, ">"), "From ", 5) == 0) { if (strncmp(start + strspn(start, ">"), "From ", 5) == 0) {
rec_fprintf(state->cleanup, curr_rec_type, out_fprintf(out_stream, curr_rec_type,
"X-Mailbox-Line: %s", start); "X-Mailbox-Line: %s", start);
continue; continue;
} }
first = 0; first = 0;
if (len > 0 && IS_SPACE_TAB(start[0])) if (len > 0 && IS_SPACE_TAB(start[0]))
rec_put(state->cleanup, REC_TYPE_NORM, "", 0); out_record(out_stream, REC_TYPE_NORM, "", 0);
} }
if (prev_rec_type != REC_TYPE_CONT if (prev_rec_type != REC_TYPE_CONT && *start == '.'
&& *start == '.' && (++start, --len) == 0) && (state->proxy == 0 ? (++start, --len) == 0 : len == 1))
break; break;
if (state->err == CLEANUP_STAT_OK if (state->err == CLEANUP_STAT_OK
&& rec_put(state->cleanup, curr_rec_type, start, len) < 0) && out_record(out_stream, curr_rec_type, start, len) < 0)
state->err = CLEANUP_STAT_WRITE; state->err = out_error;
} }
/* /*
* Send the end-of-segment markers. * Send the end-of-segment markers.
*/ */
if (state->proxy) {
if (state->err == CLEANUP_STAT_OK)
(void) smtpd_proxy_cmd(state, SMTPD_PROX_STAT_ANY, ".");
smtpd_proxy_close(state);
} else {
if (state->err == CLEANUP_STAT_OK) if (state->err == CLEANUP_STAT_OK)
if (rec_fputs(state->cleanup, REC_TYPE_XTRA, "") < 0 if (rec_fputs(state->cleanup, REC_TYPE_XTRA, "") < 0
|| rec_fputs(state->cleanup, REC_TYPE_END, "") < 0 || rec_fputs(state->cleanup, REC_TYPE_END, "") < 0
@ -1127,6 +1218,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
mail_stream_cleanup(state->dest); mail_stream_cleanup(state->dest);
state->dest = 0; state->dest = 0;
state->cleanup = 0; state->cleanup = 0;
}
/* /*
* Handle any errors. One message may suffer from multiple errors, so * Handle any errors. One message may suffer from multiple errors, so
@ -1139,7 +1231,10 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
state->error_count = 0; state->error_count = 0;
state->error_mask = 0; state->error_mask = 0;
state->junk_cmds = 0; state->junk_cmds = 0;
if (state->queue_id)
smtpd_chat_reply(state, "250 Ok: queued as %s", state->queue_id); smtpd_chat_reply(state, "250 Ok: queued as %s", state->queue_id);
else
smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
} else if ((state->err & CLEANUP_STAT_BAD) != 0) { } else if ((state->err & CLEANUP_STAT_BAD) != 0) {
state->error_mask |= MAIL_ERROR_SOFTWARE; state->error_mask |= MAIL_ERROR_SOFTWARE;
smtpd_chat_reply(state, "451 Error: internal error %d", state->err); smtpd_chat_reply(state, "451 Error: internal error %d", state->err);
@ -1156,6 +1251,9 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
} else if ((state->err & CLEANUP_STAT_WRITE) != 0) { } else if ((state->err & CLEANUP_STAT_WRITE) != 0) {
state->error_mask |= MAIL_ERROR_RESOURCE; state->error_mask |= MAIL_ERROR_RESOURCE;
smtpd_chat_reply(state, "451 Error: queue file write error"); smtpd_chat_reply(state, "451 Error: queue file write error");
} else if ((state->err & CLEANUP_STAT_PROXY) != 0) {
state->error_mask |= MAIL_ERROR_SOFTWARE;
smtpd_chat_reply(state, "451 Error: queue file write error");
} else { } else {
state->error_mask |= MAIL_ERROR_SOFTWARE; state->error_mask |= MAIL_ERROR_SOFTWARE;
smtpd_chat_reply(state, "451 Error: internal error %d", state->err); smtpd_chat_reply(state, "451 Error: internal error %d", state->err);
@ -1310,7 +1408,7 @@ static int etrn_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
smtpd_chat_reply(state, "503 Error: send HELO/EHLO first"); smtpd_chat_reply(state, "503 Error: send HELO/EHLO first");
return (-1); return (-1);
} }
if (state->cleanup != 0) { if (IN_MAIL_TRANSACTION(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL; state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 Error: MAIL transaction in progress"); smtpd_chat_reply(state, "503 Error: MAIL transaction in progress");
return (-1); return (-1);
@ -1409,7 +1507,7 @@ typedef struct SMTPD_CMD {
} SMTPD_CMD; } SMTPD_CMD;
#define SMTPD_CMD_FLAG_LIMIT (1<<0) /* limit usage */ #define SMTPD_CMD_FLAG_LIMIT (1<<0) /* limit usage */
#define SMTPD_CMD_FLAG_HEADER (1<<1) /* RFC 2822 mail header */ #define SMTPD_CMD_FLAG_FORBIDDEN (1<<1) /* RFC 2822 mail header */
static SMTPD_CMD smtpd_cmd_table[] = { static SMTPD_CMD smtpd_cmd_table[] = {
"HELO", helo_cmd, SMTPD_CMD_FLAG_LIMIT, "HELO", helo_cmd, SMTPD_CMD_FLAG_LIMIT,
@ -1427,11 +1525,13 @@ static SMTPD_CMD smtpd_cmd_table[] = {
"VRFY", vrfy_cmd, SMTPD_CMD_FLAG_LIMIT, "VRFY", vrfy_cmd, SMTPD_CMD_FLAG_LIMIT,
"ETRN", etrn_cmd, SMTPD_CMD_FLAG_LIMIT, "ETRN", etrn_cmd, SMTPD_CMD_FLAG_LIMIT,
"QUIT", quit_cmd, 0, "QUIT", quit_cmd, 0,
"Received:", 0, SMTPD_CMD_FLAG_HEADER, "Received:", 0, SMTPD_CMD_FLAG_FORBIDDEN,
"Reply-To:", 0, SMTPD_CMD_FLAG_HEADER, "Reply-To:", 0, SMTPD_CMD_FLAG_FORBIDDEN,
"Message-ID:", 0, SMTPD_CMD_FLAG_HEADER, "Message-ID:", 0, SMTPD_CMD_FLAG_FORBIDDEN,
"Subject:", 0, SMTPD_CMD_FLAG_HEADER, "Subject:", 0, SMTPD_CMD_FLAG_FORBIDDEN,
"From:", 0, SMTPD_CMD_FLAG_HEADER, "From:", 0, SMTPD_CMD_FLAG_FORBIDDEN,
"CONNECT", 0, SMTPD_CMD_FLAG_FORBIDDEN,
"User-Agent:", 0, SMTPD_CMD_FLAG_FORBIDDEN,
0, 0,
}; };
@ -1521,8 +1621,8 @@ static void smtpd_proto(SMTPD_STATE *state)
state->error_count++; state->error_count++;
continue; continue;
} }
if (cmdp->flags & SMTPD_CMD_FLAG_HEADER) { if (cmdp->flags & SMTPD_CMD_FLAG_FORBIDDEN) {
msg_warn("%s sent %s header instead of SMTP command: %.100s", msg_warn("%s sent %s instead of SMTP command: %.100s",
state->namaddr, cmdp->name, vstring_str(state->buffer)); state->namaddr, cmdp->name, vstring_str(state->buffer));
smtpd_chat_reply(state, "221 Error: I can break rules, too. Goodbye."); smtpd_chat_reply(state, "221 Error: I can break rules, too. Goodbye.");
break; break;
@ -1685,11 +1785,14 @@ int main(int argc, char **argv)
VAR_VIRT_ALIAS_CODE, DEF_VIRT_ALIAS_CODE, &var_virt_alias_code, 0, 0, VAR_VIRT_ALIAS_CODE, DEF_VIRT_ALIAS_CODE, &var_virt_alias_code, 0, 0,
VAR_VIRT_MAILBOX_CODE, DEF_VIRT_MAILBOX_CODE, &var_virt_mailbox_code, 0, 0, VAR_VIRT_MAILBOX_CODE, DEF_VIRT_MAILBOX_CODE, &var_virt_mailbox_code, 0, 0,
VAR_RELAY_RCPT_CODE, DEF_RELAY_RCPT_CODE, &var_relay_rcpt_code, 0, 0, VAR_RELAY_RCPT_CODE, DEF_RELAY_RCPT_CODE, &var_relay_rcpt_code, 0, 0,
VAR_VERIFY_POLL_COUNT, DEF_VERIFY_POLL_COUNT, &var_verify_poll_count, 1, 0,
0, 0,
}; };
static CONFIG_TIME_TABLE time_table[] = { static CONFIG_TIME_TABLE time_table[] = {
VAR_SMTPD_TMOUT, DEF_SMTPD_TMOUT, &var_smtpd_tmout, 1, 0, VAR_SMTPD_TMOUT, DEF_SMTPD_TMOUT, &var_smtpd_tmout, 1, 0,
VAR_SMTPD_ERR_SLEEP, DEF_SMTPD_ERR_SLEEP, &var_smtpd_err_sleep, 0, 0, VAR_SMTPD_ERR_SLEEP, DEF_SMTPD_ERR_SLEEP, &var_smtpd_err_sleep, 0, 0,
VAR_SMTPD_PROXY_TMOUT, DEF_SMTPD_PROXY_TMOUT, &var_smtpd_proxy_tmout, 1, 0,
VAR_VERIFY_POLL_DELAY, DEF_VERIFY_POLL_DELAY, &var_verify_poll_delay, 1, 0,
0, 0,
}; };
static CONFIG_BOOL_TABLE bool_table[] = { static CONFIG_BOOL_TABLE bool_table[] = {
@ -1732,6 +1835,8 @@ int main(int argc, char **argv)
VAR_RELAY_RCPT_MAPS, DEF_RELAY_RCPT_MAPS, &var_relay_rcpt_maps, 0, 0, VAR_RELAY_RCPT_MAPS, DEF_RELAY_RCPT_MAPS, &var_relay_rcpt_maps, 0, 0,
VAR_VERIFY_SENDER, DEF_VERIFY_SENDER, &var_verify_sender, 0, 0, VAR_VERIFY_SENDER, DEF_VERIFY_SENDER, &var_verify_sender, 0, 0,
VAR_VERP_CLIENTS, DEF_VERP_CLIENTS, &var_verp_clients, 0, 0, VAR_VERP_CLIENTS, DEF_VERP_CLIENTS, &var_verp_clients, 0, 0,
VAR_SMTPD_PROXY_FILT, DEF_SMTPD_PROXY_FILT, &var_smtpd_proxy_filt, 0, 0,
VAR_SMTPD_PROXY_EHLO, DEF_SMTPD_PROXY_EHLO, &var_smtpd_proxy_ehlo, 0, 0,
0, 0,
}; };
static CONFIG_RAW_TABLE raw_table[] = { static CONFIG_RAW_TABLE raw_table[] = {

View File

@ -95,6 +95,8 @@ typedef struct SMTPD_STATE {
int defer_if_permit_sender; /* force permit into warning */ int defer_if_permit_sender; /* force permit into warning */
int discard; /* discard message */ int discard; /* discard message */
VSTRING *expand_buf; /* scratch space for $name expansion */ VSTRING *expand_buf; /* scratch space for $name expansion */
VSTREAM *proxy; /* proxy handle */
VSTRING *proxy_buffer; /* proxy query/reply buffer */
} SMTPD_STATE; } SMTPD_STATE;
extern void smtpd_state_init(SMTPD_STATE *, VSTREAM *); extern void smtpd_state_init(SMTPD_STATE *, VSTREAM *);

View File

@ -1153,8 +1153,9 @@ static int check_relay_domains(SMTPD_STATE *state, char *recipient,
if (once == 0) { if (once == 0) {
once = 1; once = 1;
msg_warn("the \"%s\" restriction is going away; use \"%s\" instead", msg_warn("support for restriction \"%s\" will be removed from %s; "
CHECK_RELAY_DOMAINS, REJECT_UNAUTH_DEST); "use \"%s\" instead",
CHECK_RELAY_DOMAINS, var_mail_name, REJECT_UNAUTH_DEST);
} }
#endif #endif
@ -1650,17 +1651,17 @@ static int reject_unverified_address(SMTPD_STATE *state, const char *addr,
/* /*
* Verify the address. Don't waste too much of their or our time. * Verify the address. Don't waste too much of their or our time.
*/ */
for (count = 0; /* see below */ ; count++) { for (count = 0; /* see below */ ; /* see below */ ) {
verify_status = verify_clnt_query(addr, &rcpt_status, why); verify_status = verify_clnt_query(addr, &rcpt_status, why);
if (verify_status != VRFY_STAT_OK || rcpt_status != DEL_RCPT_STAT_TODO) if (verify_status != VRFY_STAT_OK || rcpt_status != DEL_RCPT_STAT_TODO)
break; break;
if (count >= 2) if (++count >= var_verify_poll_count)
break; break;
sleep(3); sleep(var_verify_poll_delay);
} }
if (verify_status != VRFY_STAT_OK) { if (verify_status != VRFY_STAT_OK) {
msg_warn("%s service failure", var_verify_service); msg_warn("%s service failure", var_verify_service);
DEFER_IF_REJECT2(state, MAIL_ERROR_POLICY, DEFER_IF_PERMIT2(state, MAIL_ERROR_POLICY,
"450 <%s>: %s rejected: address verification problem", "450 <%s>: %s rejected: address verification problem",
reply_name, reply_class); reply_name, reply_class);
rqst_status = SMTPD_CHECK_DUNNO; rqst_status = SMTPD_CHECK_DUNNO;
@ -2589,8 +2590,9 @@ static int reject_maps_rbl(SMTPD_STATE *state)
if (warned == 0) { if (warned == 0) {
warned++; warned++;
msg_warn("restriction %s is going away. Please use %s <domain> instead", msg_warn("support for restriction \"%s\" will be removed from %s; "
REJECT_MAPS_RBL, REJECT_RBL_CLIENT); "use \"%s <domain-name>\" instead",
REJECT_MAPS_RBL, var_mail_name, REJECT_RBL_CLIENT);
} }
while ((rbl_domain = mystrtok(&bp, " \t\r\n,")) != 0) { while ((rbl_domain = mystrtok(&bp, " \t\r\n,")) != 0) {
result = reject_rbl_addr(state, rbl_domain, state->addr, result = reject_rbl_addr(state, rbl_domain, state->addr,
@ -2683,7 +2685,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
int saved_recursion = state->recursion++; int saved_recursion = state->recursion++;
if (msg_verbose) if (msg_verbose)
msg_info("%s: START", myname); msg_info(">>> START %s RESTRICTIONS <<<", reply_class);
for (cpp = restrictions->argv; (name = *cpp) != 0; cpp++) { for (cpp = restrictions->argv; (name = *cpp) != 0; cpp++) {
@ -2980,7 +2982,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
break; break;
} }
if (msg_verbose && name == 0) if (msg_verbose && name == 0)
msg_info("%s: END", myname); msg_info(">>> END %s RESTRICTIONS <<<", reply_class);
state->recursion = saved_recursion; state->recursion = saved_recursion;
@ -3296,6 +3298,9 @@ static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient)
return (0); return (0);
state->rcptmap_checked = 1; state->rcptmap_checked = 1;
if (msg_verbose)
msg_info(">>> CHECKING RECIPIENT MAPS <<<");
/* /*
* Resolve the address. * Resolve the address.
*/ */
@ -3653,6 +3658,8 @@ int var_relay_rcpt_code;
int var_virt_mailbox_code; int var_virt_mailbox_code;
int var_virt_alias_code; int var_virt_alias_code;
int var_show_unk_rcpt_table; int var_show_unk_rcpt_table;
int var_verify_poll_count;
int var_verify_poll_delay;
static INT_TABLE int_table[] = { static INT_TABLE int_table[] = {
"msg_verbose", 0, &msg_verbose, "msg_verbose", 0, &msg_verbose,
@ -3676,6 +3683,8 @@ static INT_TABLE int_table[] = {
VAR_VIRT_ALIAS_CODE, DEF_VIRT_ALIAS_CODE, &var_virt_alias_code, VAR_VIRT_ALIAS_CODE, DEF_VIRT_ALIAS_CODE, &var_virt_alias_code,
VAR_VIRT_MAILBOX_CODE, DEF_VIRT_MAILBOX_CODE, &var_virt_mailbox_code, VAR_VIRT_MAILBOX_CODE, DEF_VIRT_MAILBOX_CODE, &var_virt_mailbox_code,
VAR_SHOW_UNK_RCPT_TABLE, DEF_SHOW_UNK_RCPT_TABLE, &var_show_unk_rcpt_table, VAR_SHOW_UNK_RCPT_TABLE, DEF_SHOW_UNK_RCPT_TABLE, &var_show_unk_rcpt_table,
VAR_VERIFY_POLL_COUNT, DEF_VERIFY_POLL_COUNT, &var_verify_poll_count,
VAR_VERIFY_POLL_DELAY, DEF_VERIFY_POLL_DELAY, &var_verify_poll_delay,
0, 0,
}; };
@ -3803,6 +3812,14 @@ int permit_sasl_auth(SMTPD_STATE *state, int ifyes, int ifnot)
#endif #endif
/* verify_clnt_query - stub */
int verify_clnt_query(const char *addr, int *addr_status, VSTRING *why)
{
*addr_status = DEL_RCPT_STAT_OK;
return (VRFY_STAT_OK);
}
/* canon_addr_internal - stub */ /* canon_addr_internal - stub */
VSTRING *canon_addr_internal(VSTRING *result, const char *addr) VSTRING *canon_addr_internal(VSTRING *result, const char *addr)
@ -3816,7 +3833,7 @@ VSTRING *canon_addr_internal(VSTRING *result, const char *addr)
/* resolve_clnt_query - stub */ /* resolve_clnt_query - stub */
void resolve_clnt_query(const char *addr, RESOLVE_REPLY *reply) void resolve_clnt(const char *class, const char *addr, RESOLVE_REPLY *reply)
{ {
const char *domain; const char *domain;

View File

@ -0,0 +1,437 @@
/*++
/* NAME
/* smtpd_proto 3
/* SUMMARY
/* SMTP server pass-through proxy client
/* SYNOPSIS
/* #include <smtpd.h>
/* #include <smtpd_proxy.h>
/*
/* typedef struct {
/* .in +4
/* /* other fields... */
/* VSTREAM *proxy; /* connection to SMTP proxy */
/* VSTRING *proxy_reply; /* last SMTP proxy response */
/* /* other fields... */
/* .in -4
/* } SMTPD_STATE;
/*
/* int smtpd_proxy_open(state, service, timeout, ehlo_name, mail_from)
/* SMTPD_STATE *state;
/* const char *service;
/* int timeout;
/* const char *ehlo_name;
/* const char *mail_from;
/*
/* int smtpd_proxy_cmd(state, expect, format, ...)
/* SMTPD_STATE *state;
/* int expect;
/* cont char *format;
/*
/* void smtpd_proxy_open(state)
/* SMTPD_STATE *state;
/* RECORD-LEVEL ROUTINES
/* int smtpd_proxy_rec_put(stream, rec_type, data, len)
/* VSTREAM *stream;
/* int rec_type;
/* const char *data;
/* int len;
/*
/* int smtpd_proxy_rec_fprintf(stream, rec_type, format, ...)
/* VSTREAM *stream;
/* int rec_type;
/* cont char *format;
/* DESCRIPTION
/* The functions in this module implement a pass-through proxy
/* client.
/*
/* In order to minimize the intrusiveness of pass-through proxying, 1) the
/* proxy server must support the same MAIL FROM/RCPT syntax that Postfix
/* supports, 2) the record-level routines for message content proxying
/* have the same interface as the routines that are used for non-proxied
/* mail.
/*
/* smtpd_proxy_open() should be called after receiving the MAIL FROM
/* command. It connects to the proxy service, sends EHLO, sends the
/* MAIL FROM command, and receives the reply. A non-zero result means
/* trouble: either the proxy is unavailable, or it did not send the
/* expected reply.
/* All results are reported via the state->proxy_reply field in a form
/* that can be sent to the SMTP client. In case of error, the
/* state->error_mask and state->err fields are updated.
/* A state->proxy_reply field is created automatically; this field
/* persists beyond the end of a proxy session.
/*
/* smtpd_proxy_cmd() formats and sends the specified command to the
/* proxy server, and receives the proxy server reply. A non-zero result
/* means trouble: either the proxy is unavailable, or it did not send the
/* expected reply.
/* All results are reported via the state->proxy_reply field in a form
/* that can be sent to the SMTP client. In case of error, the
/* state->error_mask and state->err fields are updated.
/*
/* smtpd_proxy_close() disconnects from a proxy server and resets
/* the state->proxy field. The last proxy server reply or error
/* description remains available via state->proxy-reply.
/*
/* smtpd_proxy_rec_put() is a rec_put() clone that passes arbitrary
/* message content records to the proxy server. The data is expected
/* to be in SMTP dot-escaped form. All errors are reported as a
/* REC_TYPE_ERROR result value.
/*
/* smtpd_proxy_rec_fprintf() is a rec_fprintf() clone that formats
/* message content and sends it to the proxy server. Leading dots are
/* not escaped. All errors are reported as a REC_TYPE_ERROR result
/* value.
/*
/* Arguments:
/* .IP server
/* The SMTP proxy server host:port. The host or host: part is optional.
/* .IP timeout
/* Time limit for connecting to the proxy server and for
/* sending and receiving proxy server commands and replies.
/* .IP ehlo_name
/* The EHLO Hostname that will be sent to the proxy server.
/* .IP mail_from
/* The MAIL FROM command.
/* .IP state
/* SMTP server state.
/* .IP expect
/* Expected proxy server reply status code range. A warning is logged
/* when an unexpected reply is received. Specify one of the following:
/* .RS
/* .IP SMTPD_PROX_STAT_ANY
/* The caller has no expectation. Do not warn for unexpected replies.
/* .IP SMTPD_PROX_STAT_OK
/* The caller expects a reply in the 200 range.
/* .IP SMTPD_PROX_STAT_MORE
/* The caller expects a reply in the 300 range.
/* .IP SMTPD_PROX_STAT_DEFER
/* .IP SMTPD_PROX_STAT_FAIL
/* The caller perversely expects a reply in the 400 and 500 range,
/* respectively.
/* .RE
/* .IP format
/* A format string.
/* .IP stream
/* Connection to proxy server.
/* .IP data
/* Pointer to the content of one message content record.
/* .IP len
/* The length of a message content record.
/* SEE ALSO
/* smtpd(8) Postfix smtp server
/* DIAGNOSTICS
/* Fatal errors: memory allocation problem.
/*
/* Warnings: unexpected response from proxy server, unable
/* to connect to proxy server, proxy server read/write 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
/*--*/
/* System library. */
#include <sys_defs.h>
#include <ctype.h>
/* Utility library. */
#include <msg.h>
#include <vstream.h>
#include <vstring.h>
#include <stringops.h>
#include <connect.h>
/* Global library. */
#include <mail_error.h>
#include <smtp_stream.h>
#include <cleanup_user.h>
#include <mail_params.h>
#include <rec_type.h>
/* Application-specific. */
#include <smtpd.h>
#include <smtpd_proxy.h>
/*
* SLMs.
*/
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
/* smtpd_proxy_open - open proxy connection after MAIL FROM */
int smtpd_proxy_open(SMTPD_STATE *state, const char *service,
int timeout, const char *ehlo_name,
const char *mail_from)
{
int fd;
/*
* This buffer persists beyond the end of a proxy session so we can
* inspect the last command's reply.
*/
if (state->proxy_buffer == 0)
state->proxy_buffer = vstring_alloc(10);
/*
* Connect to proxy.
*/
if ((fd = inet_connect(service, BLOCKING, timeout)) < 0) {
state->error_mask |= MAIL_ERROR_SOFTWARE;
state->err |= CLEANUP_STAT_PROXY;
msg_warn("connect to proxy service %s: %m", service);
vstring_sprintf(state->proxy_buffer,
"451 Error: queue file write error");
return (-1);
}
state->proxy = vstream_fdopen(fd, O_RDWR);
vstream_control(state->proxy, VSTREAM_CTL_PATH, service, VSTREAM_CTL_END);
smtp_timeout_setup(state->proxy, timeout);
/*
* Get server greeting banner.
*
* XXX If this fails then we should not send the initial reply when the
* client expects the MAIL FROM reply.
*/
if (smtpd_proxy_cmd(state, SMTPD_PROX_STAT_OK, (char *) 0) != 0) {
vstring_sprintf(state->proxy_buffer,
"451 Error: queue file write error");
smtpd_proxy_close(state);
return (-1);
}
/*
* Send our own EHLO command.
*
* XXX If this fails then we should not send the EHLO reply when the client
* expects the MAIL FROM reply.
*/
if (smtpd_proxy_cmd(state, SMTPD_PROX_STAT_OK, "EHLO %s", ehlo_name) != 0) {
vstring_sprintf(state->proxy_buffer,
"451 Error: queue file write error");
smtpd_proxy_close(state);
return (-1);
}
/*
* Pass-through the client's MAIL FROM command.
*/
if (smtpd_proxy_cmd(state, SMTPD_PROX_STAT_OK, "%s", mail_from) != 0) {
smtpd_proxy_close(state);
return (-1);
}
return (0);
}
/* smtpd_proxy_comms_error - report proxy communication error */
static int smtpd_proxy_comms_error(VSTREAM *stream, int err)
{
switch (err) {
case SMTP_ERR_EOF:
msg_warn("lost connection with proxy %s", VSTREAM_PATH(stream));
return (err);
case SMTP_ERR_TIME:
msg_warn("timeout talking to proxy %s", VSTREAM_PATH(stream));
return (err);
default:
msg_panic("smtpd_proxy_comms_error: unknown proxy %s stream error %d",
VSTREAM_PATH(stream), err);
}
}
/* smtpd_proxy_cmd_error - report unexpected proxy reply */
static void smtpd_proxy_cmd_error(SMTPD_STATE *state, const char *fmt,
va_list ap)
{
VSTRING *buf;
/*
* The command can be omitted at the start of an SMTP session. A null
* format string is not documented as part of the official interface
* because it is used only internally to this module.
*/
buf = vstring_alloc(100);
vstring_vsprintf(buf, fmt && *fmt ? fmt : "connection request", ap);
msg_warn("proxy %s rejected \"%s\": \"%s\"", VSTREAM_PATH(state->proxy),
STR(buf), STR(state->proxy_buffer));
vstring_free(buf);
}
/* smtpd_proxy_cmd - send command to proxy, receive reply */
int smtpd_proxy_cmd(SMTPD_STATE *state, int expect, const char *fmt,...)
{
va_list ap;
char *cp;
int last_char;
int err = 0;
/*
* Errors first. Be prepared for delayed errors from the DATA phase.
*/
if (vstream_ftimeout(state->proxy)
|| vstream_ferror(state->proxy)
|| vstream_feof(state->proxy)
|| ((err = vstream_setjmp(state->proxy) != 0)
&& smtpd_proxy_comms_error(state->proxy, err))) {
state->error_mask |= MAIL_ERROR_SOFTWARE;
state->err |= CLEANUP_STAT_PROXY;
vstring_sprintf(state->proxy_buffer,
"451 Error: queue file write error");
return (-1);
}
/*
* The command can be omitted at the start of an SMTP session. A null
* format string is not documented as part of the official interface
* because it is used only internally to this module.
*/
if (fmt && *fmt) {
/*
* Format the command.
*/
va_start(ap, fmt);
vstring_vsprintf(state->proxy_buffer, fmt, ap);
va_end(ap);
/*
* Optionally log the command first, so that we can see in the log
* what the program is trying to do.
*/
if (msg_verbose)
msg_info("> %s: %s", VSTREAM_PATH(state->proxy),
STR(state->proxy_buffer));
/*
* Send the command to the proxy server. Since we're going to read a
* reply immediately, there is no need to flush buffers.
*/
smtp_fputs(STR(state->proxy_buffer), LEN(state->proxy_buffer),
state->proxy);
}
/*
* Censor out non-printable characters in server responses and keep the
* last line of multi-line responses.
*/
for (;;) {
last_char = smtp_get(state->proxy_buffer, state->proxy, var_line_limit);
printable(STR(state->proxy_buffer), '?');
if (last_char != '\n')
msg_warn("%s: response longer than %d: %.30s...",
VSTREAM_PATH(state->proxy), var_line_limit,
STR(state->proxy_buffer));
if (msg_verbose)
msg_info("< %s: %s", VSTREAM_PATH(state->proxy),
STR(state->proxy_buffer));
/*
* Parse the response into code and text. Ignore unrecognized
* garbage. This means that any character except space (or end of
* line) will have the same effect as the '-' line continuation
* character.
*/
for (cp = STR(state->proxy_buffer); *cp && ISDIGIT(*cp); cp++)
/* void */ ;
if (cp - STR(state->proxy_buffer) == 3) {
if (*cp == '-')
continue;
if (*cp == ' ' || *cp == 0)
break;
}
msg_warn("received garbage from proxy %s: %.100s",
VSTREAM_PATH(state->proxy), STR(state->proxy_buffer));
}
/*
* Log a warning in case the proxy does not send the expected response.
* Silently accept any response when the client expressed no expectation.
*/
if (expect != SMTPD_PROX_STAT_ANY
&& expect != (STR(state->proxy_buffer)[0] - '0')) {
va_start(ap, fmt);
smtpd_proxy_cmd_error(state, fmt, ap);
va_end(ap);
return (-1);
} else {
return (0);
}
}
/* smtpd_proxy_rec_put - send message content, rec_put() clone */
int smtpd_proxy_rec_put(VSTREAM *stream, int rec_type,
const char *data, int len)
{
int err;
/*
* Errors first.
*/
if (vstream_ftimeout(stream) || vstream_ferror(stream)
|| vstream_feof(stream))
return (REC_TYPE_ERROR);
if ((err = vstream_setjmp(stream)) != 0)
return (smtpd_proxy_comms_error(stream, err), REC_TYPE_ERROR);
/*
* Send one content record. Errors and results must be as with rec_put().
*/
if (rec_type == REC_TYPE_NORM)
smtp_fputs(data, len, stream);
else
smtp_fwrite(data, len, stream);
return (rec_type);
}
/* smtpd_proxy_rec_fprintf - send message content, rec_fprintf() clone */
int smtpd_proxy_rec_fprintf(VSTREAM *stream, int rec_type,
const char *fmt,...)
{
va_list ap;
int err;
/*
* Errors first.
*/
if (vstream_ftimeout(stream) || vstream_ferror(stream)
|| vstream_feof(stream))
return (REC_TYPE_ERROR);
if ((err = vstream_setjmp(stream)) != 0)
return (smtpd_proxy_comms_error(stream, err), REC_TYPE_ERROR);
/*
* Send one content record. Errors and results must be as with
* rec_fprintf().
*/
va_start(ap, fmt);
if (rec_type != REC_TYPE_NORM)
msg_panic("smtpd_proxy_rec_fprintf: need REC_TYPE_NORM");
smtp_vprintf(stream, fmt, ap);
va_end(ap);
return (rec_type);
}
/* smtpd_proxy_close - close proxy connection */
void smtpd_proxy_close(SMTPD_STATE *state)
{
(void) vstream_fclose(state->proxy);
state->proxy = 0;
}

View File

@ -0,0 +1,42 @@
/*++
/* NAME
/* smtpd_proxy 3h
/* SUMMARY
/* SMTP server pass-through proxy client
/* SYNOPSIS
/* #include <smtpd.h>
/* #include <smtpd_proxy.h>
/* DESCRIPTION
/* .nf
/*
* Utility library.
*/
#include <vstream.h>
#include <vstring.h>
/*
* Application-specific.
*/
#define SMTPD_PROX_STAT_ANY 0
#define SMTPD_PROX_STAT_OK 2
#define SMTPD_PROX_STAT_MORE 3
#define SMTPD_PROX_STAT_DEFER 4
#define SMTPD_PROX_STAT_FAIL 5
extern int smtpd_proxy_open(SMTPD_STATE *, const char *, int, const char *, const char *);
extern int smtpd_proxy_cmd(SMTPD_STATE *, int, const char *,...);
extern int smtpd_proxy_rec_put(VSTREAM *, int, const char *, int);
extern int smtpd_proxy_rec_fprintf(VSTREAM *, int, const char *,...);
extern void smtpd_proxy_close(SMTPD_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

@ -99,6 +99,8 @@ void smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream)
state->defer_if_permit.reason = 0; state->defer_if_permit.reason = 0;
state->discard = 0; state->discard = 0;
state->expand_buf = 0; state->expand_buf = 0;
state->proxy = 0;
state->proxy_buffer = 0;
#ifdef USE_SASL_AUTH #ifdef USE_SASL_AUTH
if (SMTPD_STAND_ALONE(state)) if (SMTPD_STAND_ALONE(state))
@ -137,6 +139,8 @@ void smtpd_state_reset(SMTPD_STATE *state)
vstring_free(state->defer_if_reject.reason); vstring_free(state->defer_if_reject.reason);
if (state->expand_buf) if (state->expand_buf)
vstring_free(state->expand_buf); vstring_free(state->expand_buf);
if (state->proxy_buffer)
vstring_free(state->proxy_buffer);
#ifdef USE_SASL_AUTH #ifdef USE_SASL_AUTH
if (var_smtpd_sasl_enable) if (var_smtpd_sasl_enable)

View File

@ -3,7 +3,7 @@ SRCS = alldig.c argv.c argv_split.c attr_print0.c attr_print64.c \
attr_scan0.c attr_scan64.c base64_code.c basename.c binhash.c \ attr_scan0.c attr_scan64.c base64_code.c basename.c binhash.c \
chroot_uid.c clean_env.c close_on_exec.c concatenate.c ctable.c \ chroot_uid.c clean_env.c close_on_exec.c concatenate.c ctable.c \
dict.c dict_alloc.c dict_db.c dict_dbm.c dict_debug.c dict_env.c \ dict.c dict_alloc.c dict_db.c dict_dbm.c dict_debug.c dict_env.c \
dict_ht.c dict_ldap.c dict_mysql.c dict_ni.c dict_nis.c \ dict_cidr.c dict_ht.c dict_ldap.c dict_mysql.c dict_ni.c dict_nis.c \
dict_nisplus.c dict_open.c dict_pcre.c dict_pgsql.c dict_regexp.c \ dict_nisplus.c dict_open.c dict_pcre.c dict_pgsql.c dict_regexp.c \
dict_static.c dict_tcp.c dict_unix.c dir_forest.c doze.c \ dict_static.c dict_tcp.c dict_unix.c dir_forest.c doze.c \
duplex_pipe.c environ.c events.c exec_command.c fifo_listen.c \ duplex_pipe.c environ.c events.c exec_command.c fifo_listen.c \
@ -31,7 +31,7 @@ OBJS = alldig.o argv.o argv_split.o attr_print0.o attr_print64.o \
attr_scan0.o attr_scan64.o base64_code.o basename.o binhash.o \ attr_scan0.o attr_scan64.o base64_code.o basename.o binhash.o \
chroot_uid.o clean_env.o close_on_exec.o concatenate.o ctable.o \ chroot_uid.o clean_env.o close_on_exec.o concatenate.o ctable.o \
dict.o dict_alloc.o dict_db.o dict_dbm.o dict_debug.o dict_env.o \ dict.o dict_alloc.o dict_db.o dict_dbm.o dict_debug.o dict_env.o \
dict_ht.o dict_ldap.o dict_mysql.o dict_ni.o dict_nis.o \ dict_cidr.o dict_ht.o dict_ldap.o dict_mysql.o dict_ni.o dict_nis.o \
dict_nisplus.o dict_open.o dict_pcre.o dict_pgsql.o dict_regexp.o \ dict_nisplus.o dict_open.o dict_pcre.o dict_pgsql.o dict_regexp.o \
dict_static.o dict_tcp.o dict_unix.o dir_forest.o doze.o \ dict_static.o dict_tcp.o dict_unix.o dir_forest.o doze.o \
duplex_pipe.o environ.o events.o exec_command.o fifo_listen.o \ duplex_pipe.o environ.o events.o exec_command.o fifo_listen.o \
@ -57,7 +57,7 @@ OBJS = alldig.o argv.o argv_split.o attr_print0.o attr_print64.o \
write_buf.o write_wait.o $(STRCASE) write_buf.o write_wait.o $(STRCASE)
HDRS = argv.h attr.h base64_code.h binhash.h chroot_uid.h clean_env.h \ HDRS = argv.h attr.h base64_code.h binhash.h chroot_uid.h clean_env.h \
connect.h ctable.h dict.h dict_db.h dict_dbm.h dict_env.h \ connect.h ctable.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_cidr.h dict_ht.h dict_ldap.h dict_mysql.h dict_ni.h dict_nis.h \
dict_nisplus.h dict_pcre.h dict_pgsql.h dict_regexp.h \ dict_nisplus.h dict_pcre.h dict_pgsql.h dict_regexp.h \
dict_static.h dict_tcp.h dict_unix.h dir_forest.h events.h \ dict_static.h dict_tcp.h dict_unix.h dir_forest.h events.h \
exec_command.h find_inet.h fsspace.h fullname.h get_domainname.h \ exec_command.h find_inet.h fsspace.h fullname.h get_domainname.h \
@ -337,7 +337,8 @@ stream_test: stream_test.c $(LIB)
tests: valid_hostname_test mac_expand_test dict_test unescape_test \ tests: valid_hostname_test mac_expand_test dict_test unescape_test \
hex_quote_test ctable_test inet_addr_list_test base64_code_test \ hex_quote_test ctable_test inet_addr_list_test base64_code_test \
attr_scan64_test attr_scan0_test dict_pcre_test host_port_test attr_scan64_test attr_scan0_test dict_pcre_test host_port_test \
dict_cidr_test
valid_hostname_test: valid_hostname valid_hostname.in valid_hostname.ref valid_hostname_test: valid_hostname valid_hostname.in valid_hostname.ref
./valid_hostname <valid_hostname.in 2>valid_hostname.tmp ./valid_hostname <valid_hostname.in 2>valid_hostname.tmp
@ -403,6 +404,11 @@ dict_regexp_test: dict_open dict_regexp.in dict_regexp.map dict_regexp.ref
diff dict_regexp.ref dict_regexp.tmp diff dict_regexp.ref dict_regexp.tmp
rm -f dict_regexp.tmp rm -f dict_regexp.tmp
dict_cidr_test: dict_open dict_cidr.in dict_cidr.map dict_cidr.ref
./dict_open cidr:dict_cidr.map read <dict_cidr.in >dict_cidr.tmp 2>&1
diff dict_cidr.ref dict_cidr.tmp
rm -f dict_cidr.tmp
host_port_test: host_port host_port.in host_port.ref host_port_test: host_port host_port.in host_port.ref
./host_port <host_port.in >host_port.tmp 2>&1 ./host_port <host_port.in >host_port.tmp 2>&1
diff host_port.ref host_port.tmp diff host_port.ref host_port.tmp
@ -529,6 +535,19 @@ dict_alloc.o: dict.h
dict_alloc.o: vstream.h dict_alloc.o: vstream.h
dict_alloc.o: vbuf.h dict_alloc.o: vbuf.h
dict_alloc.o: argv.h dict_alloc.o: argv.h
dict_cidr.o: dict_cidr.c
dict_cidr.o: sys_defs.h
dict_cidr.o: mymalloc.h
dict_cidr.o: msg.h
dict_cidr.o: vstream.h
dict_cidr.o: vbuf.h
dict_cidr.o: vstring.h
dict_cidr.o: stringops.h
dict_cidr.o: readlline.h
dict_cidr.o: dict.h
dict_cidr.o: argv.h
dict_cidr.o: dict_cidr.h
dict_cidr.o: split_at.h
dict_db.o: dict_db.c dict_db.o: dict_db.c
dict_db.o: sys_defs.h dict_db.o: sys_defs.h
dict_db.o: msg.h dict_db.o: msg.h
@ -619,6 +638,7 @@ dict_open.o: dict_pgsql.h
dict_open.o: dict_pcre.h dict_open.o: dict_pcre.h
dict_open.o: dict_regexp.h dict_open.o: dict_regexp.h
dict_open.o: dict_static.h dict_open.o: dict_static.h
dict_open.o: dict_cidr.h
dict_open.o: stringops.h dict_open.o: stringops.h
dict_open.o: vstring.h dict_open.o: vstring.h
dict_open.o: split_at.h dict_open.o: split_at.h

View File

@ -0,0 +1,242 @@
/*++
/* NAME
/* dict_cidr 3
/* SUMMARY
/* Dictionary interface for CIDR data
/* SYNOPSIS
/* #include <dict_cidr.h>
/*
/* DICT *dict_cidr_open(name, dummy, dict_flags)
/* const char *name;
/* int dummy;
/* int dict_flags;
/* DESCRIPTION
/* dict_cidr_open() opens the named file and stores
/* the key/value pairs where the key must be either a
/* "naked" IP address or a netblock in CIDR notation.
/* SEE ALSO
/* dict(3) generic dictionary manager
/* AUTHOR(S)
/* Jozsef Kadlecsik
/* kadlec@blackhole.kfki.hu
/* KFKI Research Institute for Particle and Nuclear Physics
/* POB. 49
/* 1525 Budapest, Hungary
/*
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
/* System library. */
#include <sys_defs.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif
/* Utility library. */
#include <mymalloc.h>
#include <msg.h>
#include <vstream.h>
#include <vstring.h>
#include <stringops.h>
#include <readlline.h>
#include <dict.h>
#include <dict_cidr.h>
#include <split_at.h>
/* Application-specific. */
/*
* Each rule in a CIDR table is parsed and stored in a linked list.
* Obviously all this is IPV4 specific and needs to be redone for IPV6.
*/
typedef struct DICT_CIDR_ENTRY {
unsigned long net_bits; /* network portion of address */
unsigned long mask_bits; /* network mask */
char *value; /* lookup result */
struct DICT_CIDR_ENTRY *next; /* next entry */
} DICT_CIDR_ENTRY;
typedef struct {
DICT dict; /* generic members */
DICT_CIDR_ENTRY *head; /* first entry */
} DICT_CIDR;
#define BITS_PER_ADDR 32
/* dict_cidr_lookup - CIDR table lookup */
static const char *dict_cidr_lookup(DICT *dict, const char *key)
{
DICT_CIDR *dict_cidr = (DICT_CIDR *) dict;
DICT_CIDR_ENTRY *entry;
unsigned long addr;
if (msg_verbose)
msg_info("dict_cidr_lookup: %s: %s", dict_cidr->dict.name, key);
if ((addr = inet_addr(key)) == INADDR_NONE)
return (0);
for (entry = dict_cidr->head; entry; entry = entry->next)
if ((addr & entry->mask_bits) == entry->net_bits)
return (entry->value);
return (0);
}
/* dict_cidr_close - close the CIDR table */
static void dict_cidr_close(DICT *dict)
{
DICT_CIDR *dict_cidr = (DICT_CIDR *) dict;
DICT_CIDR_ENTRY *entry;
DICT_CIDR_ENTRY *next;
for (entry = dict_cidr->head; entry; entry = next) {
next = entry->next;
myfree(entry->value);
myfree((char *) entry);
}
dict_free(dict);
}
/* dict_cidr_parse_rule - parse CIDR table rule into network, mask and value */
static DICT_CIDR_ENTRY *dict_cidr_parse_rule(const char *mapname, int lineno,
char *p)
{
DICT_CIDR_ENTRY *rule;
char *key;
char *value;
char *mask;
int mask_shift;
unsigned long net_bits;
unsigned long mask_bits;
struct in_addr net_addr;
/*
* Split into key and value. We already eliminated leading whitespace,
* comments, empty lines or lines with whitespace only. This means a null
* key can't happen but we will handle this anyway.
*/
key = p;
while (*p && !ISSPACE(*p)) /* Skip over key */
p++;
if (*p) /* Terminate key */
*p++ = 0;
while (*p && ISSPACE(*p)) /* Skip whitespace */
p++;
value = p;
trimblanks(value, 0)[0] = 0; /* Trim trailing blanks */
if (*key == 0) {
msg_warn("cidr map %s, line %d: no address pattern: skipping this rule",
mapname, lineno);
return (0);
}
if (*value == 0) {
msg_warn("cidr map %s, line %d: no lookup result: skipping this rule",
mapname, lineno);
return (0);
}
/*
* Parse the key into network and mask, and destroy the key. Treat a bare
* network address as /32.
*/
if ((mask = split_at(key, '/')) != 0) {
if ((mask_shift = atoi(mask)) <= 0 || mask_shift > BITS_PER_ADDR
|| (net_bits = inet_addr(key)) == INADDR_NONE) {
msg_warn("cidr map %s, line %d: bad net/mask pattern: \"%s/%s\": "
"skipping this rule", mapname, lineno, key, mask);
return (0);
}
mask_bits = htonl((0xffffffff) << (BITS_PER_ADDR - mask_shift));
if (net_bits & ~mask_bits) {
net_addr.s_addr = (net_bits & mask_bits);
msg_warn("cidr map %s, line %d: net/mask pattern \"%s/%s\" with "
"non-null host portion: skipping this rule",
mapname, lineno, key, mask);
msg_warn("specify \"%s/%d\" if this is really what you want",
inet_ntoa(net_addr), mask_shift);
return (0);
}
} else {
if ((net_bits = inet_addr(key)) == INADDR_NONE) {
msg_warn("cidr map %s, line %d: bad address pattern: \"%s\": "
"skipping this rule", mapname, lineno, key);
return (0);
}
mask_shift = 32;
mask_bits = htonl(0xffffffff);
}
rule = (DICT_CIDR_ENTRY *) mymalloc(sizeof(DICT_CIDR_ENTRY));
rule->net_bits = net_bits;
rule->mask_bits = mask_bits;
rule->value = mystrdup(value);
rule->next = 0;
if (msg_verbose)
msg_info("dict_cidr_open: %s: %lu/%d %s",
mapname, rule->net_bits, mask_shift, rule->value);
return (rule);
}
/* dict_cidr_open - parse CIDR table */
DICT *dict_cidr_open(const char *mapname, int unused_flags, int dict_flags)
{
DICT_CIDR *dict_cidr;
VSTREAM *map_fp;
VSTRING *line_buffer = vstring_alloc(100);
DICT_CIDR_ENTRY *rule;
DICT_CIDR_ENTRY *last_rule = 0;
int lineno = 0;
/*
* XXX Eliminate unnecessary queries by setting a flag that says "this
* map matches network addresses only".
*/
dict_cidr = (DICT_CIDR *) dict_alloc(DICT_TYPE_CIDR, mapname,
sizeof(*dict_cidr));
dict_cidr->dict.lookup = dict_cidr_lookup;
dict_cidr->dict.close = dict_cidr_close;
dict_cidr->dict.flags = dict_flags | DICT_FLAG_PATTERN;
dict_cidr->head = 0;
if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0)
msg_fatal("open %s: %m", mapname);
while (readlline(line_buffer, map_fp, &lineno)) {
rule = dict_cidr_parse_rule(mapname, lineno, vstring_str(line_buffer));
if (rule == 0)
continue;
if (last_rule == 0)
dict_cidr->head = rule;
else
last_rule->next = rule;
last_rule = rule;
}
/*
* Clean up.
*/
if (vstream_fclose(map_fp))
msg_fatal("cidr map %s: read error: %m", mapname);
vstring_free(line_buffer);
return (DICT_DEBUG (&dict_cidr->dict));
}

View File

@ -0,0 +1,43 @@
#ifndef _DICT_CIDR_H_INCLUDED_
#define _DICT_CIDR_H_INCLUDED_
/*++
/* NAME
/* dict_cidr 3h
/* SUMMARY
/* Dictionary manager interface to handle cidr data.
/* SYNOPSIS
/* #include <dict_cidr.h>
/* DESCRIPTION
/* .nf
/*
* Utility library.
*/
#include <dict.h>
/*
* External interface.
*/
extern DICT *dict_cidr_open(const char *, int, int);
#define DICT_TYPE_CIDR "cidr"
/* 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
/*
/* Jozsef Kadlecsik
/* kadlec@blackhole.kfki.hu
/* KFKI Research Institute for Particle and Nuclear Physics
/* POB. 49
/* 1525 Budapest 114, Hungary
/*--*/
#endif

View File

@ -0,0 +1,7 @@
get 172.16.0.0
get 172.16.0.1
get 172.16.7.255
get 172.16.8.1
get 172.16.17.1
get 172.17.1.1
get 172.17.1.2

View File

@ -0,0 +1,9 @@
172.16.0.0/21 554 match bad netblock 172.16.0.0/21
172.16.8.0/21 554 match bad netblock 172.16.8.0/21
172.16.0.0/16 554 match bad netblock 172.16.0.0/16
172.17.1.1 554 match bad naked address
172.16.1.3/21 whatever
172.16.1.3/33 whatever
172.999.0.0/21 whatever
172.16.1.999 whatever
172.16.1.4

View File

@ -0,0 +1,13 @@
./dict_open: warning: cidr map dict_cidr.map, line 5: net/mask pattern "172.16.1.3/21" with non-null host portion: skipping this rule
./dict_open: warning: specify "172.16.0.0/21" if this is really what you want
./dict_open: warning: cidr map dict_cidr.map, line 6: bad net/mask pattern: "172.16.1.3/33": skipping this rule
./dict_open: warning: cidr map dict_cidr.map, line 7: bad net/mask pattern: "172.999.0.0/21": skipping this rule
./dict_open: warning: cidr map dict_cidr.map, line 8: bad address pattern: "172.16.1.999": skipping this rule
./dict_open: warning: cidr map dict_cidr.map, line 9: no lookup result: skipping this rule
172.16.0.0=554 match bad netblock 172.16.0.0/21
172.16.0.1=554 match bad netblock 172.16.0.0/21
172.16.7.255=554 match bad netblock 172.16.0.0/21
172.16.8.1=554 match bad netblock 172.16.8.0/21
172.16.17.1=554 match bad netblock 172.16.0.0/16
172.17.1.1=554 match bad naked address
172.17.1.2: not found

View File

@ -57,12 +57,27 @@
/* If you must bind to the server, do it with this distinguished name ... /* If you must bind to the server, do it with this distinguished name ...
/* .IP \fIldapsource_\fRbind_pw /* .IP \fIldapsource_\fRbind_pw
/* \&... and this password. /* \&... and this password.
/* .IP \fIldapsource_\fRcache /* .IP \fIldapsource_\fRcache (no longer supported)
/* Whether or not to turn on client-side caching. /* Whether or not to turn on client-side caching.
/* .IP \fIldapsource_\fRcache_expiry /* .IP \fIldapsource_\fRcache_expiry (no longer supported)
/* If you do cache results, expire them after this many seconds. /* If you do cache results, expire them after this many seconds.
/* .IP \fIldapsource_\fRcache_size /* .IP \fIldapsource_\fRcache_size (no longer supported)
/* The cache size in bytes. Does nothing if the cache is off, of course. /* The cache size in bytes. Does nothing if the cache is off, of course.
/* .IP \fIldapsource_\fRrecursion_limit
/* Maximum recursion depth when expanding DN or URL references.
/* Queries which exceed the recursion limit fail with
/* dict_errno = DICT_ERR_RETRY.
/* .IP \fIldapsource_\fRexpansion_limit
/* Limit (if any) on the total number of lookup result values. Lookups which
/* exceed the limit fail with dict_errno=DICT_ERR_RETRY. Note that
/* each value of a multivalued result attribute counts as one result.
/* .IP \fIldapsource_\fRsize_limit
/* Limit on the number of entries returned by individual LDAP queries.
/* Queries which exceed the limit fail with dict_errno=DICT_ERR_RETRY.
/* This is an *entry* count, for any single query performed during the
/* possibly recursive lookup.
/* .IP \fIldapsource_\fRchase_referrals
/* Controls whether LDAP referrals are obeyed.
/* .IP \fIldapsource_\fRdereference /* .IP \fIldapsource_\fRdereference
/* How to handle LDAP aliases. See ldap.h or ldap_open(3) man page. /* How to handle LDAP aliases. See ldap.h or ldap_open(3) man page.
/* .IP \fIldapsource_\fRdebuglevel /* .IP \fIldapsource_\fRdebuglevel
@ -152,10 +167,10 @@ typedef struct {
char *bind_dn; char *bind_dn;
char *bind_pw; char *bind_pw;
int timeout; int timeout;
int cache;
long cache_expiry;
long cache_size;
int dereference; int dereference;
long recursion_limit;
long expansion_limit;
long size_limit;
int chase_referrals; int chase_referrals;
int debuglevel; int debuglevel;
int version; int version;
@ -231,11 +246,6 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
char *myname = "dict_ldap_connect"; char *myname = "dict_ldap_connect";
int rc = 0; int rc = 0;
#ifdef LDAP_API_FEATURE_X_MEMCACHE
LDAPMemCache *dircache;
#endif
#ifdef LDAP_OPT_NETWORK_TIMEOUT #ifdef LDAP_OPT_NETWORK_TIMEOUT
struct timeval mytimeval; struct timeval mytimeval;
@ -313,6 +323,16 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
} }
#endif #endif
/*
* Limit the number of entries returned by each query.
*/
if (dict_ldap->size_limit) {
if (ldap_set_option(dict_ldap->ld, LDAP_OPT_SIZELIMIT,
&dict_ldap->size_limit) != LDAP_OPT_SUCCESS)
msg_warn("%s: %s: Unable to set query result size limit to %ld.",
myname, dict_ldap->ldapsource, dict_ldap->size_limit);
}
/* /*
* Configure alias dereferencing for this connection. Thanks to Mike * Configure alias dereferencing for this connection. Thanks to Mike
* Mattice for this, and to Hery Rakotoarisoa for the v3 update. * Mattice for this, and to Hery Rakotoarisoa for the v3 update.
@ -370,52 +390,6 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
msg_info("%s: Successful bind to server %s as %s ", msg_info("%s: Successful bind to server %s as %s ",
myname, dict_ldap->server_host, dict_ldap->bind_dn); myname, dict_ldap->server_host, dict_ldap->bind_dn);
} }
/*
* Set up client-side caching if it's configured.
*/
if (dict_ldap->cache) {
if (msg_verbose)
msg_info
("%s: Enabling %ld-byte cache for %s with %ld-second expiry",
myname, dict_ldap->cache_size, dict_ldap->ldapsource,
dict_ldap->cache_expiry);
#ifdef LDAP_API_FEATURE_X_MEMCACHE
rc = ldap_memcache_init(dict_ldap->cache_expiry, dict_ldap->cache_size,
NULL, NULL, &dircache);
if (rc != LDAP_SUCCESS) {
msg_warn
("%s: Unable to configure cache for %s: %d (%s) -- continuing",
myname, dict_ldap->ldapsource, rc, ldap_err2string(rc));
} else {
rc = ldap_memcache_set(dict_ldap->ld, dircache);
if (rc != LDAP_SUCCESS) {
msg_warn
("%s: Unable to configure cache for %s: %d (%s) -- continuing",
myname, dict_ldap->ldapsource, rc, ldap_err2string(rc));
} else {
if (msg_verbose)
msg_info("%s: Caching enabled for %s",
myname, dict_ldap->ldapsource);
}
}
#else
rc = ldap_enable_cache(dict_ldap->ld, dict_ldap->cache_expiry,
dict_ldap->cache_size);
if (rc != LDAP_SUCCESS) {
msg_warn
("%s: Unable to configure cache for %s: %d (%s) -- continuing",
myname, dict_ldap->ldapsource, rc, ldap_err2string(rc));
} else {
if (msg_verbose)
msg_info("%s: Caching enabled for %s",
myname, dict_ldap->ldapsource);
}
#endif
}
if (msg_verbose) if (msg_verbose)
msg_info("%s: Cached connection handle for LDAP source %s", msg_info("%s: Cached connection handle for LDAP source %s",
myname, dict_ldap->ldapsource); myname, dict_ldap->ldapsource);
@ -426,7 +400,8 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
/* /*
* expand a filter (lookup or result) * expand a filter (lookup or result)
*/ */
static void dict_ldap_expand_filter(char *filter, char *value, VSTRING *out) static void dict_ldap_expand_filter(char *ldapsource, char *filter,
char *value, VSTRING *out)
{ {
char *myname = "dict_ldap_expand_filter"; char *myname = "dict_ldap_expand_filter";
char *sub, char *sub,
@ -461,9 +436,8 @@ static void dict_ldap_expand_filter(char *filter, char *value, VSTRING *out)
vstring_strcat(out, u); vstring_strcat(out, u);
break; break;
default: default:
msg_warn msg_warn("%s: %s: Invalid filter substitution format '%%%c'!",
("%s: Invalid filter substitution format '%%%c'!", myname, ldapsource, *(sub + 1));
myname, *(sub + 1));
/* fall through */ /* fall through */
case 's': case 's':
vstring_strcat(out, u); vstring_strcat(out, u);
@ -486,6 +460,9 @@ static void dict_ldap_expand_filter(char *filter, char *value, VSTRING *out)
static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res, static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
VSTRING *result) VSTRING *result)
{ {
static int recursion = 0;
static int expansion;
long entries = 0;
long i = 0; long i = 0;
int rc = 0; int rc = 0;
LDAPMessage *resloop = 0; LDAPMessage *resloop = 0;
@ -500,13 +477,27 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
tv.tv_sec = dict_ldap->timeout; tv.tv_sec = dict_ldap->timeout;
tv.tv_usec = 0; tv.tv_usec = 0;
if (++recursion == 1)
expansion = 0;
if (msg_verbose) if (msg_verbose)
msg_info("%s: Search found %d match(es)", myname, msg_info("%s[%d]: Search found %d match(es)", myname, recursion,
ldap_count_entries(dict_ldap->ld, res)); ldap_count_entries(dict_ldap->ld, res));
for (entry = ldap_first_entry(dict_ldap->ld, res); entry != NULL; for (entry = ldap_first_entry(dict_ldap->ld, res); entry != NULL;
entry = ldap_next_entry(dict_ldap->ld, entry)) { entry = ldap_next_entry(dict_ldap->ld, entry)) {
ber = NULL; ber = NULL;
/*
* LDAP should not, but may produce more than the requested maximum
* number of entries.
*/
if (dict_errno == 0 && ++entries > dict_ldap->size_limit
&& dict_ldap->size_limit) {
msg_warn("%s[%d]: %s: Query size limit (%ld) exceeded", myname,
recursion, dict_ldap->ldapsource, dict_ldap->size_limit);
dict_errno = DICT_ERR_RETRY;
}
for (attr = ldap_first_attribute(dict_ldap->ld, entry, &ber); for (attr = ldap_first_attribute(dict_ldap->ld, entry, &ber);
attr != NULL; attr != NULL;
ldap_memfree(attr), attr = ldap_next_attribute(dict_ldap->ld, ldap_memfree(attr), attr = ldap_next_attribute(dict_ldap->ld,
@ -514,17 +505,38 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
vals = ldap_get_values(dict_ldap->ld, entry, attr); vals = ldap_get_values(dict_ldap->ld, entry, attr);
if (vals == NULL) { if (vals == NULL) {
if (msg_verbose) if (msg_verbose)
msg_info("%s: Entry doesn't have any values for %s", msg_info("%s[%d]: Entry doesn't have any values for %s",
myname, attr); myname, recursion, attr);
continue; continue;
} }
for (i = 0; dict_ldap->result_attributes->argv[i]; i++) {
if (strcasecmp(dict_ldap->result_attributes->argv[i], /*
attr) == 0) { * If we previously encountered an error, we still continue
if (msg_verbose) * through the loop, to avoid memory leaks, but we don't waste
msg_info("%s: search returned %ld value(s) for requested result attribute %s", myname, i, attr); * time accumulating any further results.
break; *
* XXX: There may be a more efficient way to exit the loop with no
* leaks, but it will likely be more fragile and not worth the
* extra code.
*/
if (dict_errno != 0 || vals[0] == 0) {
ldap_value_free(vals);
continue;
} }
/*
* The "result_attributes" list enumerates all the requested
* attributes, first the ordinary result attribtutes and then the
* special result attributes that hold DN or LDAP URL values.
*
* The number of ordinary attributes is "num_attributes".
*
* We compute the attribute type (ordinary or special) from its
* index on the "result_attributes" list.
*/
for (i = 0; dict_ldap->result_attributes->argv[i]; i++) {
if (strcasecmp(dict_ldap->result_attributes->argv[i], attr) == 0)
break;
} }
/* /*
@ -532,21 +544,39 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
* recursing (for dn or url attributes). * recursing (for dn or url attributes).
*/ */
if (i < dict_ldap->num_attributes) { if (i < dict_ldap->num_attributes) {
/* Ordinary result attribute */
for (i = 0; vals[i] != NULL; i++) { for (i = 0; vals[i] != NULL; i++) {
if (++expansion > dict_ldap->expansion_limit &&
dict_ldap->expansion_limit) {
msg_warn("%s[%d]: %s: Expansion limit exceeded at"
" result attribute %s=%s", myname, recursion,
dict_ldap->ldapsource, attr, vals[i]);
dict_errno = DICT_ERR_RETRY;
break;
}
if (VSTRING_LEN(result) > 0) if (VSTRING_LEN(result) > 0)
vstring_strcat(result, ","); vstring_strcat(result, ",");
if (dict_ldap->result_filter == NULL) if (dict_ldap->result_filter == NULL)
vstring_strcat(result, vals[i]); vstring_strcat(result, vals[i]);
else else
dict_ldap_expand_filter(dict_ldap->result_filter, dict_ldap_expand_filter(dict_ldap->ldapsource,
dict_ldap->result_filter,
vals[i], result); vals[i], result);
} }
} else if (dict_ldap->result_attributes->argv[i]) { if (dict_errno != 0)
continue;
if (msg_verbose)
msg_info("%s[%d]: search returned %ld value(s) for"
" requested result attribute %s",
myname, recursion, i, attr);
} else if (recursion < dict_ldap->recursion_limit
&& dict_ldap->result_attributes->argv[i]) {
/* Special result attribute */
for (i = 0; vals[i] != NULL; i++) { for (i = 0; vals[i] != NULL; i++) {
if (ldap_is_ldap_url(vals[i])) { if (ldap_is_ldap_url(vals[i])) {
if (msg_verbose) if (msg_verbose)
msg_info("%s: looking up URL %s", myname, msg_info("%s[%d]: looking up URL %s", myname,
vals[i]); recursion, vals[i]);
rc = ldap_url_parse(vals[i], &url); rc = ldap_url_parse(vals[i], &url);
if (rc == 0) { if (rc == 0) {
rc = ldap_search_st(dict_ldap->ld, url->lud_dn, rc = ldap_search_st(dict_ldap->ld, url->lud_dn,
@ -557,7 +587,8 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
} }
} else { } else {
if (msg_verbose) if (msg_verbose)
msg_info("%s: looking up DN %s", myname, vals[i]); msg_info("%s[%d]: looking up DN %s",
myname, recursion, vals[i]);
rc = ldap_search_st(dict_ldap->ld, vals[i], rc = ldap_search_st(dict_ldap->ld, vals[i],
LDAP_SCOPE_BASE, "objectclass=*", LDAP_SCOPE_BASE, "objectclass=*",
dict_ldap->result_attributes->argv, dict_ldap->result_attributes->argv,
@ -573,27 +604,44 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
* Go ahead and treat this as though the DN existed * Go ahead and treat this as though the DN existed
* and just didn't have any result attributes. * and just didn't have any result attributes.
*/ */
msg_warn("%s: DN %s not found, skipping ", myname, msg_warn("%s[%d]: DN %s not found, skipping ", myname,
vals[i]); recursion, vals[i]);
break; break;
default: default:
msg_warn("%s: search error %d: %s ", myname, rc, msg_warn("%s[%d]: search error %d: %s ", myname,
ldap_err2string(rc)); recursion, rc, ldap_err2string(rc));
dict_errno = DICT_ERR_RETRY; dict_errno = DICT_ERR_RETRY;
break; break;
} }
if (resloop != 0) if (resloop != 0)
ldap_msgfree(resloop); ldap_msgfree(resloop);
if (dict_errno != 0)
break;
} }
if (dict_errno != 0)
continue;
if (msg_verbose)
msg_info("%s[%d]: search returned %ld value(s) for"
" special result attribute %s",
myname, recursion, i, attr);
} else if (recursion >= dict_ldap->recursion_limit
&& dict_ldap->result_attributes->argv[i]) {
msg_warn("%s[%d]: %s: Recursion limit exceeded"
" for special attribute %s=%s",
myname, recursion, dict_ldap->ldapsource, attr, vals[0]);
dict_errno = DICT_ERR_RETRY;
} }
ldap_value_free(vals); ldap_value_free(vals);
} }
if (ber) if (ber)
ber_free(ber, 0); ber_free(ber, 0);
} }
if (msg_verbose) if (msg_verbose)
msg_info("%s: Leaving %s", myname, myname); msg_info("%s[%d]: Leaving %s", myname, recursion, myname);
--recursion;
} }
/* dict_ldap_lookup - find database entry */ /* dict_ldap_lookup - find database entry */
@ -720,11 +768,11 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
/* /*
* No, log the fact and continue. * No, log the fact and continue.
*/ */
msg_warn("%s: Fixed query_filter %s is probably useless", myname, msg_warn("%s: %s: Fixed query_filter %s is probably useless",
dict_ldap->query_filter); myname, dict_ldap->ldapsource, dict_ldap->query_filter);
vstring_strcpy(filter_buf, dict_ldap->query_filter); vstring_strcpy(filter_buf, dict_ldap->query_filter);
} else { } else {
dict_ldap_expand_filter(dict_ldap->query_filter, dict_ldap_expand_filter(dict_ldap->ldapsource, dict_ldap->query_filter,
vstring_str(escaped_name), filter_buf); vstring_str(escaped_name), filter_buf);
} }
@ -858,6 +906,7 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
char *domainlist; char *domainlist;
char *scope; char *scope;
char *attr; char *attr;
int tmp;
if (msg_verbose) if (msg_verbose)
msg_info("%s: Using LDAP source %s", myname, ldapsource); msg_info("%s: Using LDAP source %s", myname, ldapsource);
@ -1040,31 +1089,58 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
* get configured value of "ldapsource_cache"; default to false * get configured value of "ldapsource_cache"; default to false
*/ */
vstring_sprintf(config_param, "%s_cache", ldapsource); vstring_sprintf(config_param, "%s_cache", ldapsource);
dict_ldap->cache = get_mail_conf_bool(vstring_str(config_param), 0); tmp = get_mail_conf_bool(vstring_str(config_param), 0);
if (msg_verbose) if (tmp)
msg_info("%s: %s is %d", myname, vstring_str(config_param), msg_warn("%s: ignoring %s", myname, vstring_str(config_param));
dict_ldap->cache);
/* /*
* get configured value of "ldapsource_cache_expiry"; default to 30 * get configured value of "ldapsource_cache_expiry"; default to 30
* seconds * seconds
*/ */
vstring_sprintf(config_param, "%s_cache_expiry", ldapsource); vstring_sprintf(config_param, "%s_cache_expiry", ldapsource);
dict_ldap->cache_expiry = get_mail_conf_int(vstring_str(config_param), tmp = get_mail_conf_int(vstring_str(config_param), -1, 0, 0);
30, 0, 0); if (tmp >= 0)
if (msg_verbose) msg_warn("%s: ignoring %s", myname, vstring_str(config_param));
msg_info("%s: %s is %ld", myname, vstring_str(config_param),
dict_ldap->cache_expiry);
/* /*
* get configured value of "ldapsource_cache_size"; default to 32k * get configured value of "ldapsource_cache_size"; default to 32k
*/ */
vstring_sprintf(config_param, "%s_cache_size", ldapsource); vstring_sprintf(config_param, "%s_cache_size", ldapsource);
dict_ldap->cache_size = get_mail_conf_int(vstring_str(config_param), tmp = get_mail_conf_int(vstring_str(config_param), -1, 0, 0);
32768, 0, 0); if (tmp >= 0)
msg_warn("%s: ignoring %s", myname, vstring_str(config_param));
/*
* get configured value of "ldapsource_recursion_limit"; default to 1000
*/
vstring_sprintf(config_param, "%s_recursion_limit", ldapsource);
dict_ldap->recursion_limit = get_mail_conf_int(vstring_str(config_param),
1000, 1, 0);
if (msg_verbose) if (msg_verbose)
msg_info("%s: %s is %ld", myname, vstring_str(config_param), msg_info("%s: %s is %ld", myname, vstring_str(config_param),
dict_ldap->cache_size); dict_ldap->recursion_limit);
/*
* get configured value of "ldapsource_expansion_limit"; default to 1000
*/
vstring_sprintf(config_param, "%s_expansion_limit", ldapsource);
dict_ldap->expansion_limit = get_mail_conf_int(vstring_str(config_param),
0, 0, 0);
if (msg_verbose)
msg_info("%s: %s is %ld", myname, vstring_str(config_param),
dict_ldap->expansion_limit);
/*
* get configured value of "ldapsource_size_limit"; default to
* expansion_limit
*/
vstring_sprintf(config_param, "%s_size_limit", ldapsource);
dict_ldap->size_limit = get_mail_conf_int(vstring_str(config_param),
dict_ldap->expansion_limit,
0, 0);
if (msg_verbose)
msg_info("%s: %s is %ld", myname, vstring_str(config_param),
dict_ldap->size_limit);
/* /*
* Alias dereferencing suggested by Mike Mattice. * Alias dereferencing suggested by Mike Mattice.

View File

@ -178,6 +178,7 @@
#include <dict_pcre.h> #include <dict_pcre.h>
#include <dict_regexp.h> #include <dict_regexp.h>
#include <dict_static.h> #include <dict_static.h>
#include <dict_cidr.h>
#include <stringops.h> #include <stringops.h>
#include <split_at.h> #include <split_at.h>
#include <htable.h> #include <htable.h>
@ -193,9 +194,7 @@ typedef struct {
static DICT_OPEN_INFO dict_open_info[] = { static DICT_OPEN_INFO dict_open_info[] = {
DICT_TYPE_ENVIRON, dict_env_open, DICT_TYPE_ENVIRON, dict_env_open,
DICT_TYPE_UNIX, dict_unix_open, DICT_TYPE_UNIX, dict_unix_open,
#if 0
DICT_TYPE_TCP, dict_tcp_open, DICT_TYPE_TCP, dict_tcp_open,
#endif
#ifdef HAS_DBM #ifdef HAS_DBM
DICT_TYPE_DBM, dict_dbm_open, DICT_TYPE_DBM, dict_dbm_open,
#endif #endif
@ -228,6 +227,7 @@ static DICT_OPEN_INFO dict_open_info[] = {
DICT_TYPE_REGEXP, dict_regexp_open, DICT_TYPE_REGEXP, dict_regexp_open,
#endif #endif
DICT_TYPE_STATIC, dict_static_open, DICT_TYPE_STATIC, dict_static_open,
DICT_TYPE_CIDR, dict_cidr_open,
0, 0,
}; };

View File

@ -73,6 +73,7 @@
typedef struct { typedef struct {
char *regexp; /* regular expression */ char *regexp; /* regular expression */
int options; /* options */ int options; /* options */
int match; /* positive or negative match */
} DICT_PCRE_REGEXP; } DICT_PCRE_REGEXP;
typedef struct { typedef struct {
@ -95,12 +96,14 @@ typedef struct {
pcre *pattern; /* compiled pattern */ pcre *pattern; /* compiled pattern */
pcre_extra *hints; /* hints to speed pattern execution */ pcre_extra *hints; /* hints to speed pattern execution */
char *replacement; /* replacement string */ char *replacement; /* replacement string */
int match; /* positive or negative match */
} DICT_PCRE_MATCH_RULE; } DICT_PCRE_MATCH_RULE;
typedef struct { typedef struct {
DICT_PCRE_RULE rule; /* generic members */ DICT_PCRE_RULE rule; /* generic members */
pcre *pattern; /* compiled pattern */ pcre *pattern; /* compiled pattern */
pcre_extra *hints; /* hints to speed pattern execution */ pcre_extra *hints; /* hints to speed pattern execution */
int match; /* positive or negative match */
} DICT_PCRE_IF_RULE; } DICT_PCRE_IF_RULE;
/* /*
@ -132,6 +135,7 @@ typedef struct {
const char *mapname; /* name of regexp map */ const char *mapname; /* name of regexp map */
int lineno; /* where in file */ int lineno; /* where in file */
int flags; /* dict_flags */ int flags; /* dict_flags */
size_t max_sub; /* Largest $n seen */
} DICT_PCRE_PRESCAN_CONTEXT; } DICT_PCRE_PRESCAN_CONTEXT;
/* /*
@ -251,13 +255,23 @@ static const char *dict_pcre_lookup(DICT *dict, const char *lookup_string)
lookup_string, lookup_len, lookup_string, lookup_len,
NULL_STARTOFFSET, NULL_EXEC_OPTIONS, NULL_STARTOFFSET, NULL_EXEC_OPTIONS,
ctxt.offsets, PCRE_MAX_CAPTURE * 3); ctxt.offsets, PCRE_MAX_CAPTURE * 3);
if (ctxt.matches == PCRE_ERROR_NOMATCH)
continue; if (ctxt.matches > 0) {
if (ctxt.matches <= 0) { if (!match_rule->match)
continue; /* Negative rule matched */
} else if (ctxt.matches == PCRE_ERROR_NOMATCH) {
if (match_rule->match)
continue; /* Positive rule did not
* match */
} else {
dict_pcre_exec_error(dict->name, rule->lineno, ctxt.matches); dict_pcre_exec_error(dict->name, rule->lineno, ctxt.matches);
continue; continue; /* pcre_exec failed */
} }
/* Negative rules can't have any substitutions */
if (!match_rule->match)
return match_rule->replacement;
/* /*
* We've got a match. Perform substitution on replacement string. * We've got a match. Perform substitution on replacement string.
*/ */
@ -289,11 +303,17 @@ static const char *dict_pcre_lookup(DICT *dict, const char *lookup_string)
lookup_string, lookup_len, lookup_string, lookup_len,
NULL_STARTOFFSET, NULL_EXEC_OPTIONS, NULL_STARTOFFSET, NULL_EXEC_OPTIONS,
ctxt.offsets, PCRE_MAX_CAPTURE * 3); ctxt.offsets, PCRE_MAX_CAPTURE * 3);
if (ctxt.matches == PCRE_ERROR_NOMATCH)
continue; if (ctxt.matches > 0) {
if (ctxt.matches <= 0) { if (!if_rule->match)
continue; /* Negative rule matched */
} else if (ctxt.matches == PCRE_ERROR_NOMATCH) {
if (if_rule->match)
continue; /* Positive rule did not
* match */
} else {
dict_pcre_exec_error(dict->name, rule->lineno, ctxt.matches); dict_pcre_exec_error(dict->name, rule->lineno, ctxt.matches);
continue; continue; /* pcre_exec failed */
} }
nesting++; nesting++;
continue; continue;
@ -359,6 +379,25 @@ static int dict_pcre_get_pattern(const char *mapname, int lineno, char **bufp,
char *p = *bufp; char *p = *bufp;
char re_delimiter; char re_delimiter;
/*
* Process negation operators.
*/
pattern->match = 1;
while (*p == '!') {
pattern->match = !pattern->match;
p++;
}
/*
* Grr...aceful handling of whitespace after '!'.
*/
while (*p && ISSPACE(*p))
p++;
if (*p == 0) {
msg_warn("pcre map %s, line %d: no regexp: skipping this rule",
mapname, lineno);
return (0);
}
re_delimiter = *p++; re_delimiter = *p++;
pattern->regexp = p; pattern->regexp = p;
@ -437,7 +476,6 @@ static int dict_pcre_prescan(int type, VSTRING *buf, char *context)
ctxt->mapname, ctxt->lineno); ctxt->mapname, ctxt->lineno);
return (MAC_PARSE_ERROR); return (MAC_PARSE_ERROR);
} }
if (!alldig(vstring_str(buf))) { if (!alldig(vstring_str(buf))) {
msg_warn("pcre map %s, line %d: non-numeric replacement index \"%s\"", msg_warn("pcre map %s, line %d: non-numeric replacement index \"%s\"",
ctxt->mapname, ctxt->lineno, vstring_str(buf)); ctxt->mapname, ctxt->lineno, vstring_str(buf));
@ -449,6 +487,8 @@ static int dict_pcre_prescan(int type, VSTRING *buf, char *context)
ctxt->mapname, ctxt->lineno, vstring_str(buf)); ctxt->mapname, ctxt->lineno, vstring_str(buf));
return (MAC_PARSE_ERROR); return (MAC_PARSE_ERROR);
} }
if (n > ctxt->max_sub)
ctxt->max_sub = n;
} }
return (MAC_PARSE_OK); return (MAC_PARSE_OK);
} }
@ -536,6 +576,7 @@ static DICT_PCRE_RULE *dict_pcre_parse_rule(const char *mapname, int lineno,
prescan_context.mapname = mapname; prescan_context.mapname = mapname;
prescan_context.lineno = lineno; prescan_context.lineno = lineno;
prescan_context.flags = dict_flags; prescan_context.flags = dict_flags;
prescan_context.max_sub = 0;
if (mac_parse(p, dict_pcre_prescan, (char *) &prescan_context) if (mac_parse(p, dict_pcre_prescan, (char *) &prescan_context)
& MAC_PARSE_ERROR) { & MAC_PARSE_ERROR) {
@ -544,6 +585,15 @@ static DICT_PCRE_RULE *dict_pcre_parse_rule(const char *mapname, int lineno,
return (0); return (0);
} }
/*
* Substring replacement not possible with negative regexps.
*/
if (prescan_context.max_sub > 0 && regexp.match == 0) {
msg_warn("pcre map %s, line %d: $number found in negative match "
"replacement text: skipping this rule", mapname, lineno);
return (0);
}
/* /*
* Compile the pattern. * Compile the pattern.
*/ */
@ -556,6 +606,7 @@ static DICT_PCRE_RULE *dict_pcre_parse_rule(const char *mapname, int lineno,
match_rule = (DICT_PCRE_MATCH_RULE *) match_rule = (DICT_PCRE_MATCH_RULE *)
dict_pcre_rule_alloc(DICT_PCRE_OP_MATCH, nesting, lineno, dict_pcre_rule_alloc(DICT_PCRE_OP_MATCH, nesting, lineno,
sizeof(DICT_PCRE_MATCH_RULE)); sizeof(DICT_PCRE_MATCH_RULE));
match_rule->match = regexp.match;
match_rule->replacement = mystrdup(p); match_rule->replacement = mystrdup(p);
match_rule->pattern = engine.pattern; match_rule->pattern = engine.pattern;
match_rule->hints = engine.hints; match_rule->hints = engine.hints;
@ -583,6 +634,8 @@ static DICT_PCRE_RULE *dict_pcre_parse_rule(const char *mapname, int lineno,
/* /*
* Warn about out-of-place text. * Warn about out-of-place text.
*/ */
while (*p && ISSPACE(*p))
++p;
if (*p) if (*p)
msg_warn("pcre map %s, line %d: ignoring extra text after IF", msg_warn("pcre map %s, line %d: ignoring extra text after IF",
mapname, lineno); mapname, lineno);
@ -599,6 +652,7 @@ static DICT_PCRE_RULE *dict_pcre_parse_rule(const char *mapname, int lineno,
if_rule = (DICT_PCRE_IF_RULE *) if_rule = (DICT_PCRE_IF_RULE *)
dict_pcre_rule_alloc(DICT_PCRE_OP_IF, nesting, lineno, dict_pcre_rule_alloc(DICT_PCRE_OP_IF, nesting, lineno,
sizeof(DICT_PCRE_IF_RULE)); sizeof(DICT_PCRE_IF_RULE));
if_rule->match = regexp.match;
if_rule->pattern = engine.pattern; if_rule->pattern = engine.pattern;
if_rule->hints = engine.hints; if_rule->hints = engine.hints;
return ((DICT_PCRE_RULE *) if_rule); return ((DICT_PCRE_RULE *) if_rule);
@ -624,6 +678,8 @@ static DICT_PCRE_RULE *dict_pcre_parse_rule(const char *mapname, int lineno,
/* /*
* Warn about out-of-place text. * Warn about out-of-place text.
*/ */
while (*p && ISSPACE(*p))
++p;
if (*p) if (*p)
msg_warn("pcre map %s, line %d: ignoring extra text after ENDIF", msg_warn("pcre map %s, line %d: ignoring extra text after ENDIF",
mapname, lineno); mapname, lineno);

View File

@ -8,3 +8,7 @@ get c
get d get d
get 1234 get 1234
get 123 get 123
get bar/find
get bar/whynot
get bar/elbereth
get say/elbereth

View File

@ -10,3 +10,12 @@ endif
/(1)(2)(3)(5)/ ($1)($2)($3)($4)($5) /(1)(2)(3)(5)/ ($1)($2)($3)($4)($5)
/(1)(2)(3)(4)/ ($1)($2)($3)($4) /(1)(2)(3)(4)/ ($1)($2)($3)($4)
/(1)(2)(3)/ ($1)($2)($3) /(1)(2)(3)/ ($1)($2)($3)
# trailing whitespace below
if /bar/
if !/xyzzy/
/(elbereth)/ ($1)
!/(bogus)/ ($1)
!/find/ Don't have a liquor license
endif
endif
# trailing whitespace above

View File

@ -2,6 +2,7 @@
./dict_open: warning: pcre map dict_pcre.map, line 5: ignoring extra text after ENDIF ./dict_open: warning: pcre map dict_pcre.map, line 5: ignoring extra text after ENDIF
./dict_open: warning: pcre map dict_pcre.map, line 8: unknown regexp option "!": skipping this rule ./dict_open: warning: pcre map dict_pcre.map, line 8: unknown regexp option "!": skipping this rule
./dict_open: warning: dict_pcre.map, line 9: no replacement text: using empty string ./dict_open: warning: dict_pcre.map, line 9: no replacement text: using empty string
./dict_open: warning: pcre map dict_pcre.map, line 17: $number found in negative match replacement text: skipping this rule
true: not found true: not found
true1=1 true1=1
true2: not found true2: not found
@ -12,3 +13,7 @@ c=
d: not found d: not found
1234=(1)(2)(3)(4) 1234=(1)(2)(3)(4)
123=(1)(2)(3) 123=(1)(2)(3)
bar/find: not found
bar/whynot=Don't have a liquor license
bar/elbereth=(elbereth)
say/elbereth: not found

View File

@ -586,7 +586,7 @@ static DICT_REGEXP_RULE *dict_regexp_parseline(const char *mapname, int lineno,
msg_warn("regexp map %s, line %d: " msg_warn("regexp map %s, line %d: "
"regular expression substitution is not allowed: " "regular expression substitution is not allowed: "
"skipping this rule", mapname, lineno); "skipping this rule", mapname, lineno);
return(0); return (0);
} }
if ((first_exp = dict_regexp_compile_pat(mapname, lineno, if ((first_exp = dict_regexp_compile_pat(mapname, lineno,
&first_pat)) == 0) &first_pat)) == 0)
@ -636,6 +636,8 @@ static DICT_REGEXP_RULE *dict_regexp_parseline(const char *mapname, int lineno,
p++; p++;
if (!dict_regexp_get_pat(mapname, lineno, &p, &pattern)) if (!dict_regexp_get_pat(mapname, lineno, &p, &pattern))
return (0); return (0);
while (*p && ISSPACE(*p))
++p;
if (*p) if (*p)
msg_warn("regexp map %s, line %d: ignoring extra text after IF", msg_warn("regexp map %s, line %d: ignoring extra text after IF",
mapname, lineno); mapname, lineno);
@ -661,6 +663,8 @@ static DICT_REGEXP_RULE *dict_regexp_parseline(const char *mapname, int lineno,
mapname, lineno); mapname, lineno);
return (0); return (0);
} }
while (*p && ISSPACE(*p))
++p;
if (*p) if (*p)
msg_warn("regexp map %s, line %d: ignoring extra text after ENDIF", msg_warn("regexp map %s, line %d: ignoring extra text after ENDIF",
mapname, lineno); mapname, lineno);

View File

@ -11,3 +11,7 @@ get aa
get 1235 get 1235
get 1234 get 1234
get 123 get 123
get bar/find
get bar/whynot
get bar/elbereth
get say/elbereth

View File

@ -10,3 +10,12 @@ endif
/(1)(2)(3)(5)/ ($1)($2)($3)($4)($5) /(1)(2)(3)(5)/ ($1)($2)($3)($4)($5)
/(1)(2)(3)(4)/ ($1)($2)($3)($4) /(1)(2)(3)(4)/ ($1)($2)($3)($4)
/(1)(2)(3)/ ($1)($2)($3) /(1)(2)(3)/ ($1)($2)($3)
# trailing whitespace below
if /bar/
if !/xyzzy/
/(elbereth)/ ($1)
!/(bogus)/ ($1)
!/find/ Don't have a liquor license
endif
endif
# trailing whitespace above

View File

@ -2,6 +2,7 @@
./dict_open: warning: regexp map dict_regexp.map, line 5: ignoring extra text after ENDIF ./dict_open: warning: regexp map dict_regexp.map, line 5: ignoring extra text after ENDIF
./dict_open: warning: regexp map dict_regexp.map, line 9: using empty replacement string ./dict_open: warning: regexp map dict_regexp.map, line 9: using empty replacement string
./dict_open: warning: regexp map dict_regexp.map, line 10: out of range replacement index "5": skipping this rule ./dict_open: warning: regexp map dict_regexp.map, line 10: out of range replacement index "5": skipping this rule
./dict_open: warning: regexp map dict_regexp.map, line 17: $number found in negative match replacement text: skipping this rule
true: not found true: not found
true1=1 true1=1
true2: not found true2: not found
@ -15,3 +16,7 @@ aa=a!b
1235=(1)(2)(3) 1235=(1)(2)(3)
1234=(1)(2)(3)(4) 1234=(1)(2)(3)(4)
123=(1)(2)(3) 123=(1)(2)(3)
bar/find: not found
bar/whynot=Don't have a liquor license
bar/elbereth=(elbereth)
say/elbereth: not found

View File

@ -27,8 +27,8 @@
/* ENCODING /* ENCODING
/* .ad /* .ad
/* .fi /* .fi
/* In request and reply parameters, the character % and any non-printable /* In request and reply parameters, the character % and any non-printing
/* characters (including whitespace) are replaced by %XX, XX being the /* and whitespace characters must be replaced by %XX, XX being the
/* corresponding ASCII hexadecimal character value. The hexadecimal codes /* corresponding ASCII hexadecimal character value. The hexadecimal codes
/* can be specified in any case (upper, lower, mixed). /* can be specified in any case (upper, lower, mixed).
/* REQUEST FORMAT /* REQUEST FORMAT
@ -43,7 +43,8 @@
/* REPLY FORMAT /* REPLY FORMAT
/* .ad /* .ad
/* .fi /* .fi
/* Replies can have the following form: /* Replies must be no longer than 4096 characters including the
/* newline terminator, and must have the following form:
/* .IP "500 SPACE optional-text NEWLINE" /* .IP "500 SPACE optional-text NEWLINE"
/* In case of a lookup request, the requested data does not exist. /* In case of a lookup request, the requested data does not exist.
/* In case of an update request, the request was rejected. /* In case of an update request, the request was rejected.
@ -83,16 +84,16 @@
/* Utility library. */ /* Utility library. */
#include "msg.h" #include <msg.h>
#include "mymalloc.h" #include <mymalloc.h>
#include "vstring.h" #include <vstring.h>
#include "vstream.h" #include <vstream.h>
#include "vstring_vstream.h" #include <vstring_vstream.h>
#include "connect.h" #include <connect.h>
#include "hex_quote.h" #include <hex_quote.h>
#include "dict.h" #include <dict.h>
#include "stringops.h" #include <stringops.h>
#include "dict_tcp.h" #include <dict_tcp.h>
/* Application-specific. */ /* Application-specific. */
@ -103,8 +104,9 @@ typedef struct {
VSTREAM *fp; /* I/O stream */ VSTREAM *fp; /* I/O stream */
} DICT_TCP; } DICT_TCP;
#define DICT_TCP_MAXTRY 10 #define DICT_TCP_MAXTRY 10 /* attempts before giving up */
#define DICT_TCP_TMOUT 100 #define DICT_TCP_TMOUT 100 /* connect/read/write timeout */
#define DICT_TCP_MAXLEN 4096 /* server reply size limit */
#define STR(x) vstring_str(x) #define STR(x) vstring_str(x)
@ -115,10 +117,10 @@ static int dict_tcp_connect(DICT_TCP *dict_tcp)
int fd; int fd;
/* /*
* Connect to the server. Enforce a time limit on read/write operations * Connect to the server. Enforce a time limit on all operations so that
* so that we do not get stuck. * we do not get stuck.
*/ */
if ((fd = inet_connect(dict_tcp->dict.name, BLOCKING, 0)) < 0) { if ((fd = inet_connect(dict_tcp->dict.name, NON_BLOCKING, DICT_TCP_TMOUT)) < 0) {
msg_warn("connect to TCP map %s: %m", dict_tcp->dict.name); msg_warn("connect to TCP map %s: %m", dict_tcp->dict.name);
return (-1); return (-1);
} }
@ -153,6 +155,7 @@ static const char *dict_tcp_lookup(DICT *dict, const char *key)
char *myname = "dict_tcp_lookup"; char *myname = "dict_tcp_lookup";
int tries; int tries;
char *start; char *start;
int last_ch;
#define RETURN(errval, result) { dict_errno = errval; return (result); } #define RETURN(errval, result) { dict_errno = errval; return (result); }
@ -173,14 +176,22 @@ static const char *dict_tcp_lookup(DICT *dict, const char *key)
*/ */
hex_quote(dict_tcp->hex_buf, key); hex_quote(dict_tcp->hex_buf, key);
vstream_fprintf(dict_tcp->fp, "get %s\n", STR(dict_tcp->hex_buf)); vstream_fprintf(dict_tcp->fp, "get %s\n", STR(dict_tcp->hex_buf));
if (vstring_get_nonl(dict_tcp->hex_buf, dict_tcp->fp) > 0) if (msg_verbose)
msg_info("%s: send \"get %s\"", myname, STR(dict_tcp->hex_buf));
last_ch = vstring_get_nonl_bound(dict_tcp->hex_buf, dict_tcp->fp,
DICT_TCP_MAXLEN);
if (last_ch == '\n')
break; break;
/* /*
* Disconnect from the server if it can't talk to us. * Disconnect from the server if it can't talk to us.
*/ */
if (last_ch < 0)
msg_warn("read TCP map reply from %s: unexpected EOF (%m)", msg_warn("read TCP map reply from %s: unexpected EOF (%m)",
dict_tcp->dict.name); dict_tcp->dict.name);
else
msg_warn("read TCP map reply from %s: text longer than %d",
dict_tcp->dict.name, DICT_TCP_MAXLEN);
dict_tcp_disconnect(dict_tcp); dict_tcp_disconnect(dict_tcp);
} }
@ -195,6 +206,8 @@ static const char *dict_tcp_lookup(DICT *dict, const char *key)
*/ */
sleep(1); sleep(1);
} }
if (msg_verbose)
msg_info("%s: recv: \"%s\"", myname, STR(dict_tcp->hex_buf));
/* /*
* Check the general reply syntax. If the reply is malformed, disconnect * Check the general reply syntax. If the reply is malformed, disconnect
@ -268,5 +281,5 @@ DICT *dict_tcp_open(const char *map, int unused_flags, int dict_flags)
dict_tcp->dict.lookup = dict_tcp_lookup; dict_tcp->dict.lookup = dict_tcp_lookup;
dict_tcp->dict.close = dict_tcp_close; dict_tcp->dict.close = dict_tcp_close;
dict_tcp->dict.flags = dict_flags | DICT_FLAG_FIXED; dict_tcp->dict.flags = dict_flags | DICT_FLAG_FIXED;
return (DICT_DEBUG(&dict_tcp->dict)); return (DICT_DEBUG (&dict_tcp->dict));
} }

View File

@ -15,7 +15,8 @@
/* const char *hex; /* const char *hex;
/* DESCRIPTION /* DESCRIPTION
/* hex_quote() takes a null-terminated string and replaces non-printable /* hex_quote() takes a null-terminated string and replaces non-printable
/* characters and % by %XX, XX being the two-digit hexadecimal equivalent. /* and whitespace characters and the % by %XX, XX being the two-digit
/* hexadecimal equivalent.
/* The hexadecimal codes are produced as upper-case characters. The result /* The hexadecimal codes are produced as upper-case characters. The result
/* value is the hex argument. /* value is the hex argument.
/* /*
@ -61,7 +62,7 @@ VSTRING *hex_quote(VSTRING *hex, const char *raw)
VSTRING_RESET(hex); VSTRING_RESET(hex);
for (cp = raw; (ch = *(unsigned const char *) cp) != 0; cp++) { for (cp = raw; (ch = *(unsigned const char *) cp) != 0; cp++) {
if (ch != '%' && ISPRINT(ch)) { if (ch != '%' && !ISSPACE(ch) && ISPRINT(ch)) {
VSTRING_ADDCH(hex, ch); VSTRING_ADDCH(hex, ch);
} else { } else {
vstring_sprintf_append(hex, "%%%02X", ch); vstring_sprintf_append(hex, "%%%02X", ch);

View File

@ -69,7 +69,7 @@
#endif #endif
#ifndef INADDR_NONE #ifndef INADDR_NONE
#define INADDR_NONE 0xffffff #define INADDR_NONE 0xffffffff
#endif #endif
/* Utility library. */ /* Utility library. */

View File

@ -244,12 +244,16 @@ VSTREAM *safe_open(const char *path, int flags, int mode,
/* /*
* Open an existing file or create a new one, carefully. When opening * Open an existing file or create a new one, carefully. When opening
* an existing file, we are prepared to deal with "no file" errors * an existing file, we are prepared to deal with "no file" errors
* only. Any other error means we better give up trying. * only. When creating a file, we are prepared for "file exists"
* errors only. Any other error means we better give up trying.
*/ */
case O_CREAT: case O_CREAT:
if ((fp = safe_open_exist(path, flags, st, why)) == 0) fp = safe_open_exist(path, flags, st, why);
if (errno == ENOENT) if (fp == 0 && errno == ENOENT) {
fp = safe_open_create(path, flags, mode, st, user, group, why); fp = safe_open_create(path, flags, mode, st, user, group, why);
if (fp == 0 && errno == EEXIST)
fp = safe_open_exist(path, flags, st, why);
}
return (fp); return (fp);
/* /*

View File

@ -234,7 +234,7 @@ int valid_hostliteral(const char *addr, int gripe)
msg_warn("%s: unexpected text after ']': %.100s", myname, addr); msg_warn("%s: unexpected text after ']': %.100s", myname, addr);
return (0); return (0);
} }
if (last - addr >= sizeof(buf)) { if (last >= addr + sizeof(buf)) {
if (gripe) if (gripe)
msg_warn("%s: too much text: %.100s", myname, addr); msg_warn("%s: too much text: %.100s", myname, addr);
return (0); return (0);

Some files were not shown because too many files have changed in this diff Show More