-TDELIVER_ATTR
-TDELIVER_REQUEST
-TDICT
+-TDICT_CIDR
+-TDICT_CIDR_ENTRY
-TDICT_DB
-TDICT_DBM
-TDICT_DEBUG
of mail probes, so it will no longer block for in_flow_delay
seconds when mail arrives faster than it is delivered.
Still need to make mail_stream_finish() asynchronous in
- order to avoid blocking for trigger_timeout seconds when
- the queue manager is overwhelmed. Files: global/post_mail.c,
+ order to avoid blocking for trigger_timeout seconds when the
+ queue manager is overwhelmed. Files: global/post_mail.c,
verify/verify.c.
Bugfix: removed extraneous sleep() after the last attempt
Bugfix: the stricter postdrop input filter broke "sendmail
-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
After "postfix reload", the master daemon now warns when
of passing incorrect information to the smtp server. File:
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
Bugfix: after the last change to postdrop, postcat no longer
sendmail, "-t" broke multi-line recipient headers. Victor
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:
Low: smtp-source may block when sending large test messages.
the Postfix user. Example:
ldapsource_bind_pw = postfixpw
- cache (no)
- Whether to use a client-side cache for the LDAP connection. See
- ldap_enable_cache(3). It's off by default.
-
- cache_expiry (30 seconds)
- If the client-side cache is enabled, cached results will expire
- after this many seconds.
-
- cache_size (32768 bytes)
- If the client-side cache is enabled, this is its size in bytes.
+ cache (IGNORED with a warning)
+ cache_expiry (IGNORED with a warning)
+ 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.
+
+ recursion_limit (1000)
+ A limit on the nesting depth of DN and URL special result
+ attribute evaluation. The limit must be a non-zero positive
+ number.
+
+ expansion_limit (0)
+ 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)
- When to dereference LDAP aliases. (Note that this has nothing
- do with Postfix aliases.) The permitted values are those
- legal for the OpenLDAP/UM LDAP implementations:
+ When to dereference LDAP aliases. (Note that this has nothing
+ do with Postfix aliases.) The permitted values are those legal
+ for the OpenLDAP/UM LDAP implementations:
0 never
1 when searching
--- /dev/null
+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.
You can use the virtual delivery agent for mailbox delivery of some
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
different address. For that, see the virtual(5) manual page.
date. Snapshots change only the release date, unless they include
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
===================================================
# A network address is a sequence of one or more
# octets separated by ".".
#
+# NOTE: use the cidr lookup table type if you want to
+# specify arbitrary network blocks.
+#
# ACTIONS
# [45]NN text
-# Reject the address etc. that matches the pattern,
+# Reject the address etc. that matches the pattern,
# and respond with the numerical code and text.
#
# REJECT
#
# REJECT optional text...
-# Reject the address etc. that matches the pattern.
-# Reply with $reject_code optional text... when the
-# optional text is specified, otherwise reply with a
+# Reject the address etc. that matches the pattern.
+# Reply with $reject_code optional text... when the
+# optional text is specified, otherwise reply with a
# generic error response message.
#
# OK Accept the address etc. that matches the pattern.
#
# all-numerical
# An all-numerical result is treated as OK. This for-
-# mat is generated by address-based relay authoriza-
+# mat is generated by address-based relay authoriza-
# tion schemes.
#
-# DUNNO Pretend that the lookup key was not found in this
+# DUNNO Pretend that the lookup key was not found in this
# table. This prevents Postfix from trying substrings
-# of the lookup key (such as a subdomain name, or a
+# of the lookup key (such as a subdomain name, or a
# network address subnetwork).
#
# HOLD
#
# HOLD optional text...
-# Place the message on the hold queue, where it will
-# sit until someone either deletes it or releases it
-# for delivery. Log the optional text if specified,
+# Place the message on the hold queue, where it will
+# sit until someone either deletes it or releases it
+# for delivery. Log the optional text if specified,
# otherwise log a generic message.
#
-# Mail that is placed on hold can be examined with
-# the postcat(1) command, and can be destroyed or
+# Mail that is placed on hold can be examined with
+# the postcat(1) command, and can be destroyed or
# released with the postsuper(1) command.
#
-# Note: this action currently affects all recipients
+# Note: this action currently affects all recipients
# of the message.
#
# DISCARD
#
# DISCARD optional text...
-# Claim successful delivery and silently discard the
-# message. Log the optional text if specified, oth-
+# Claim successful delivery and silently discard the
+# message. Log the optional text if specified, oth-
# erwise log a generic message.
#
-# Note: this action currently affects all recipients
+# Note: this action currently affects all recipients
# of the message.
#
# FILTER transport:destination
-# After the message is queued, send the entire mes-
-# sage through a content filter. More information
+# After the message is queued, send the entire mes-
+# sage through a content filter. More information
# about content filters is in the Postfix FIL-
# TER_README file.
#
-# Note: this action overrides the main.cf con-
+# Note: this action overrides the main.cf con-
# tent_filter setting, and currently affects all
# recipients of the message.
#
# REDIRECT user@domain
-# After the message is queued, send the message to
+# After the message is queued, send the message to
# the specified address instead of the intended
# recipient(s).
#
-# Note: this action overrides the FILTER action, and
+# Note: this action overrides the FILTER action, and
# currently affects all recipients of the message.
#
# restriction...
# reject_unauth_destination, and so on).
#
# REGULAR EXPRESSION TABLES
-# This section describes how the table lookups change when
+# This section describes how the table lookups change when
# the table is given in the form of regular expressions. For
-# a description of regular expression lookup table syntax,
+# a description of regular expression lookup table syntax,
# see regexp_table(5) or pcre_table(5).
#
-# Each pattern is a regular expression that is applied to
+# Each pattern is a regular expression that is applied to
# the entire string being looked up. Depending on the appli-
-# cation, that string is an entire client hostname, an
+# cation, that string is an entire client hostname, an
# entire client IP address, or an entire mail address. Thus,
# no parent domain or parent network search is done,
-# user@domain mail addresses are not broken up into their
+# user@domain mail addresses are not broken up into their
# user@ and domain constituent parts, nor is user+foo broken
# up into user and foo.
#
-# Patterns are applied in the order as specified in the
-# table, until a pattern is found that matches the search
+# Patterns are applied in the order as specified in the
+# table, until a pattern is found that matches the search
# string.
#
-# Actions are the same as with indexed file lookups, with
-# the additional feature that parenthesized substrings from
+# Actions are the same as with indexed file lookups, with
+# the additional feature that parenthesized substrings from
# the pattern can be interpolated as $1, $2 and so on.
#
# BUGS
-# The table format does not understand quoting conventions.
+# The table format does not understand quoting conventions.
#
# SEE ALSO
-# postmap(1) create mapping table
+# postmap(1) create lookup table
# smtpd(8) smtp server
+# cidr_table(5) format of CIDR tables
# pcre_table(5) format of PCRE tables
# regexp_table(5) format of POSIX regular expression tables
#
# LICENSE
-# The Secure Mailer license must be distributed with this
+# The Secure Mailer license must be distributed with this
# software.
#
# AUTHOR(S)
--- /dev/null
+# 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)
# pcre_table - format of Postfix PCRE tables
#
# SYNOPSIS
-# pcre:/etc/postfix/filename
-#
# postmap -q "string" pcre:/etc/postfix/filename
#
# postmap -q - pcre:/etc/postfix/filename <inputfile
# The general form of a PCRE table is:
#
# /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
# Empty lines and whitespace-only lines are ignored,
#
# if /pattern/flags
#
+# if !/pattern/flags
+#
# endif Examine the lines between if..endif only if pattern
-# matches. The if..endif can nest. Do not prepend
-# whitespace to patterns inside if..endif.
+# matches (does not match). The if..endif can nest.
+# Do not prepend whitespace to patterns inside
+# if..endif.
#
# Each pattern is a perl-like regular expression. The
-# expression delimiter can be any character, except whites-
-# pace or characters that have special meaning (tradition-
-# ally the forward slash is used). The regular expression
+# expression delimiter can be any character, except whites-
+# pace or characters that have special meaning (tradition-
+# ally the forward slash is used). The regular expression
# can contain whitespace.
#
# By default, matching is case-insensitive, and newlines are
-# not treated as special characters. The behavior is con-
-# trolled by flags, which are toggled by appending one or
+# not treated as special characters. The behavior is con-
+# trolled by flags, which are toggled by appending one or
# more of the following characters after the pattern:
#
# i (default: on)
-# Toggles the case sensitivity flag. By default,
+# Toggles the case sensitivity flag. By default,
# matching is case insensitive.
#
# m (default: off)
-# Toggles the PCRE_MULTILINE flag. When this flag is
-# on, the ^ and $ metacharacters match immediately
-# after and immediately before a newline character,
-# respectively, in addition to matching at the start
+# Toggles the PCRE_MULTILINE flag. When this flag is
+# on, the ^ and $ metacharacters match immediately
+# after and immediately before a newline character,
+# respectively, in addition to matching at the start
# and end of the subject string.
#
# s (default: on)
# Toggles the PCRE_DOTALL flag. When this flag is on,
# the . metacharacter matches the newline character.
-# With Postfix versions prior to 20020528, The flag
+# With Postfix versions prior to 20020528, The flag
# is off by default, which is inconvenient for multi-
# line message header matching.
#
# x (default: off)
-# Toggles the pcre extended flag. When this flag is
-# on, whitespace in the pattern (other than in a
+# Toggles the pcre extended flag. When this flag is
+# on, whitespace in the pattern (other than in a
# character class) and characters between a # outside
-# a character class and the next newline character
-# are ignored. An escaping backslash can be used to
-# include a whitespace or # character as part of the
+# a character class and the next newline character
+# are ignored. An escaping backslash can be used to
+# include a whitespace or # character as part of the
# pattern.
#
# A (default: off)
-# Toggles the PCRE_ANCHORED flag. When this flag is
-# on, the pattern is forced to be "anchored", that
+# Toggles the PCRE_ANCHORED flag. When this flag is
+# on, the pattern is forced to be "anchored", that
# is, it is constrained to match only at the start of
-# the string which is being searched (the "subject
-# string"). This effect can also be achieved by
+# the string which is being searched (the "subject
+# string"). This effect can also be achieved by
# appropriate constructs in the pattern itself.
#
# E (default: off)
-# Toggles the PCRE_DOLLAR_ENDONLY flag. When this
-# flag is on, a $ metacharacter in the pattern
-# matches only at the end of the subject string.
-# Without this flag, a dollar also matches immedi-
+# Toggles the PCRE_DOLLAR_ENDONLY flag. When this
+# flag is on, a $ metacharacter in the pattern
+# matches only at the end of the subject string.
+# Without this flag, a dollar also matches immedi-
# ately before the final character if it is a newline
# character (but not before any other newline charac-
-# ters). This flag is ignored if PCRE_MULTILINE flag
+# ters). This flag is ignored if PCRE_MULTILINE flag
# is set.
#
# U (default: off)
# Toggles the ungreedy matching flag. When this flag
-# is on, the pattern matching engine inverts the
-# "greediness" of the quantifiers so that they are
-# not greedy by default, but become greedy if fol-
-# lowed by "?". This flag can also set by a (?U)
+# is on, the pattern matching engine inverts the
+# "greediness" of the quantifiers so that they are
+# not greedy by default, but become greedy if fol-
+# lowed by "?". This flag can also set by a (?U)
# modifier within the pattern.
#
# X (default: off)
# Toggles the PCRE_EXTRA flag. When this flag is on,
-# any backslash in a pattern that is followed by a
+# any backslash in a pattern that is followed by a
# letter that has no special meaning causes an error,
# thus reserving these combinations for future expan-
# sion.
#
-# Each pattern is applied to the entire lookup key string.
-# Depending on the application, that string is an entire
+# Each pattern is applied to the entire lookup key string.
+# Depending on the application, that string is an entire
# client hostname, an entire client IP address, or an entire
-# mail address. Thus, no parent domain or parent network
-# search is done, and user@domain mail addresses are not
-# broken up into their user and domain constituent parts,
+# mail address. Thus, no parent domain or parent network
+# search is done, and user@domain mail addresses are not
+# broken up into their user and domain constituent parts,
# nor is user+foo broken up into user and foo.
#
-# Patterns are applied in the order as specified in the
-# table, until a pattern is found that matches the search
+# Patterns are applied in the order as specified in the
+# table, until a pattern is found that matches the search
# string.
#
-# Substitution of substrings from the matched expression
-# into the result string is possible using the conventional
-# perl syntax ($1, $2, etc.). The macros in the result
-# string may need to be written as ${n} or $(n) if they
-# aren't followed by whitespace.
+# Substitution of substrings from the matched expression
+# into the result string is possible using the conventional
+# perl syntax ($1, $2, etc.). The macros in the result
+# string may need to be written as ${n} 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
# # Protect your outgoing majordomo exploders
#
# SEE ALSO
# 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)
# The PCRE table lookup code was originally written by:
$config_directory/access:f:root:-:644:p
$config_directory/aliases: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.default:f:root:-:644
$config_directory/makedefs.out:f:root:-:644
$config_directory/postfix-files:f:root:-:644
$config_directory/regexp_table: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/virtual:f:root:-:644:p
$config_directory/postfix-script:f:root:-:755
$manpage_directory/man5/access.5:f:root:-:644
$manpage_directory/man5/aliases.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/regexp_table.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/virtual.5:f:root:-:644
$manpage_directory/man8/bounce.8:f:root:-:644
find $command_directory/postqueue $command_directory/postdrop \
-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/* | \
egrep '/(bin|etc|lib|usr)$'` ; \
# regexp_table - format of Postfix regular expression tables
#
# SYNOPSIS
-# regexp:/etc/postfix/filename
-#
# postmap -q "string" regexp:/etc/postfix/filename
#
# postmap -q - regexp:/etc/postfix/filename <inputfile
# Substitution of substrings from the matched expression
# into the result 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 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
# # Disallow sender-specified routing. This is a must if you relay mail
#
# SEE ALSO
# 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)
# The regexp table lookup code was originally written by:
#
#ldap_bind_pw =
-# The ldap_cache parameter specifies whether or not to turn on client-side
-# caching.
-#
-#ldap_cache = no
-
-# The ldap_cache_expiry parameter specifies how many seconds to cache results
-# for (if ldap_cache=yes)
-#
-#ldap_cache_expiry = 30
-
-# The ldap_cache_size parameter specifies the cache size, in bytes.
-#
-#ldap_cache_size = 32768
+#ldap_cache (IGNORED with a warning)
+#ldap_cache_expiry (IGNORED with a warning)
+#ldap_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.
+
+# The ldap_recursion_limit parameter specifies a limit on the nesting
+# depth of DN and URL special result attribute evaluation. The limit
+# must be a non-zero positive number. The default value is 1000.
+#
+#ldap_recursion_limit = 1000
+
+# 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_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
# ldap_open(3) man page.
#
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
# parameters that Postfix will export to non-Postfix processes.
#
#
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
#
# ADDRESS VERIFICATION (see also: verify(8) and SENDER_VERIFICATION_README)
#
+# The address_verify_sender configuration parameter specifies the
+# sender address that Postfix will use in address verification probe
+# messages.
+#
+# By default, the probe sender address is postmaster@$myorigin.
+#
+# Specify an empty value (address_verify_sender =) or <> if you want
+# to use the null sender address. Beware, some sites reject mail from
+# <>, even though RFCs require that such addresses to be accepted.
+#
+#address_verify_sender = <>
+#address_verify_sender = postmaster@my.domain
+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 recipient status storage. The file is opened
+# table for persistent address status storage. The file is opened
# before the process enters a chroot jail and before it drops root
# privileges.
#
#address_verify_map = btree:/etc/postfix/verify
address_verify_map =
-# The address_verify_sender configuration parameter specifies the
-# sender address that Postfix will use in address verification probe
-# messages.
-#
-# By default, the probe sender address is postmaster@$myorigin.
-#
-# Specify an empty value (address_verify_sender =) or <> if you want
-# to use the null sender address. Beware, some sites reject mail from
-# <>, even though RFCs require that such addresses to be accepted.
-#
-#address_verify_sender = <>
-#address_verify_sender = postmaster@my.domain
-address_verify_sender = postmaster
-
# The address_verify_positive_expire_time configuration parameter
# specifies the amount of time after which a known to be good address
# expires.
--- /dev/null
+# 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)
# relay hosts. The mapping is used by the trivial-rewrite(8)
# daemon.
#
-# Normally, the transport table is specified as a text file
-# that serves as input to the postmap(1) command. The
-# result, an indexed file in dbm or db format, is used for
-# fast searching by the mail system. Execute the command
-# postmap /etc/postfix/transport in order to rebuild the
+# 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
+# that serves as input to the postmap(1) command. The
+# result, an indexed file in dbm or db format, is used for
+# fast searching by the mail system. Execute the command
+# postmap /etc/postfix/transport in order to rebuild the
# indexed file after changing the transport table.
#
-# When the table is provided via other means such as NIS,
-# LDAP or SQL, the same lookups are done as for ordinary
+# When the table is provided via other means such as NIS,
+# LDAP or SQL, the same lookups are done as for ordinary
# indexed files.
#
-# Alternatively, the table can be provided as a regular-
+# Alternatively, the table can be provided as a regular-
# expression map where patterns are given as regular expres-
-# sions. In that case, the lookups are done in a slightly
-# different way as described in section "REGULAR EXPRESSION
+# sions. In that case, the lookups are done in a slightly
+# different way as described in section "REGULAR EXPRESSION
# TABLES".
#
# TABLE FORMAT
# domain, use the corresponding result.
#
# blank lines and comments
-# Empty lines and whitespace-only lines are ignored,
-# as are lines whose first non-whitespace character
+# 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-
+# A logical line starts with non-whitespace text. A
+# line that starts with whitespace continues a logi-
# cal line.
#
-# The pattern specifies an email address, a domain name, or
-# a domain name hierarchy, as described in section "TABLE
+# The pattern specifies an email address, a domain name, or
+# a domain name hierarchy, as described in section "TABLE
# LOOKUP".
#
-# The result is of the form transport:nexthop. The trans-
-# port field specifies a mail delivery transport such as
-# smtp or local. The nexthop field specifies where and how
+# The result is of the form transport:nexthop. The trans-
+# port field specifies a mail delivery transport such as
+# smtp or local. The nexthop field specifies where and how
# to deliver mail. More details are given in section "RESULT
# FORMAT".
#
# TABLE LOOKUP
# With lookups from indexed files such as DB or DBM, or from
-# networked tables such as NIS, LDAP or SQL, patterns are
+# networked tables such as NIS, LDAP or SQL, patterns are
# tried in the order as listed below:
#
# user+extension@domain transport:nexthop
# to nexthop.
#
# domain transport:nexthop
-# Mail for domain is delivered through transport to
+# Mail for domain is delivered through transport to
# nexthop.
#
# .domain transport:nexthop
-# Mail for any subdomain of domain is delivered
-# through transport to nexthop. This applies only
+# Mail for any subdomain of domain is delivered
+# through transport to nexthop. This applies only
# when the string transport_maps is not listed in the
# parent_domain_matches_subdomains configuration set-
-# ting. Otherwise, a domain name matches itself and
+# ting. Otherwise, a domain name matches itself and
# its subdomains.
#
# Note 1: the special pattern * represents any address (i.e.
# it functions as the wild-card pattern).
#
-# Note 2: the null recipient address is looked up as
+# Note 2: the null recipient address is looked up as
# $empty_address_recipient@$myhostname (default: mailer-dae-
# mon@hostname).
#
# RESULT FORMAT
-# The transport field specifies the name of a mail delivery
+# The transport field specifies the name of a mail delivery
# transport (the first name of a mail delivery service entry
# in the Postfix master.cf file).
#
-# The interpretation of the nexthop field is transport
+# The interpretation of the nexthop field is transport
# dependent. In the case of SMTP, specify host:service for a
-# non-default server port, and use [host] or [host]:port in
-# order to disable MX (mail exchanger) DNS lookups. The []
+# non-default server port, and use [host] or [host]:port in
+# order to disable MX (mail exchanger) DNS lookups. The []
# form is required when you specify an IP address instead of
# a hostname.
#
-# A null transport and null nexthop result means "do not
-# change": use the delivery transport and nexthop informa-
-# tion that would be used when the entire transport table
+# A null transport and null nexthop result means "do not
+# change": use the delivery transport and nexthop informa-
+# tion that would be used when the entire transport table
# did not exist.
#
-# A non-null transport field with a null nexthop field
+# A non-null transport field with a null nexthop field
# resets the nexthop information to the recipient domain.
#
-# A null transport field with non-null nexthop field does
+# A null transport field with non-null nexthop field does
# not modify the transport information.
#
# EXAMPLES
-# In order to deliver internal mail directly, while using a
-# mail relay for all other mail, specify a null entry for
-# internal destinations (do not change the delivery trans-
-# port or the nexthop information) and specify a wildcard
+# In order to deliver internal mail directly, while using a
+# mail relay for all other mail, specify a null entry for
+# internal destinations (do not change the delivery trans-
+# port or the nexthop information) and specify a wildcard
# for all other destinations.
#
# my.domain :
# .my.domain :
# * smtp:outbound-relay.my.domain
#
-# In order to send mail for foo.org and its subdomains via
+# In order to send mail for foo.org and its subdomains via
# the uucp transport to the UUCP host named foo:
#
# foo.org uucp:foo
# .foo.org uucp:foo
#
-# When no nexthop host name is specified, the destination
-# domain name is used instead. For example, the following
-# directs mail for user@foo.org via the slow transport to a
-# mail exchanger for foo.org. The slow transport could be
-# something that runs at most one delivery process at a
+# When no nexthop host name is specified, the destination
+# domain name is used instead. For example, the following
+# directs mail for user@foo.org via the slow transport to a
+# mail exchanger for foo.org. The slow transport could be
+# something that runs at most one delivery process at a
# time:
#
# foo.org slow:
#
# When no transport is specified, Postfix uses the transport
# that matches the address domain class (see TRANSPORT FIELD
-# discussion above). The following sends all mail for
+# discussion above). The following sends all mail for
# foo.org and its subdomains to host gateway.foo.org:
#
# foo.org :[gateway.foo.org]
# .foo.org :[gateway.foo.org]
#
-# In the above example, the [] are used to suppress MX
-# lookups. The result would likely point to your local
+# In the above example, the [] are used to suppress MX
+# lookups. The result would likely point to your local
# machine.
#
-# In the case of delivery via SMTP, one may specify host-
+# In the case of delivery via SMTP, one may specify host-
# name:service instead of just a host:
#
# foo.org smtp:bar.org:2025
#
-# This directs mail for user@foo.org to host bar.org port
-# 2025. Instead of a numerical port a symbolic name may be
-# used. Specify [] around the hostname in order to disable
+# This directs mail for user@foo.org to host bar.org port
+# 2025. Instead of a numerical port a symbolic name may be
+# used. Specify [] around the hostname in order to disable
# MX lookups.
#
# The error mailer can be used to bounce mail:
#
-# .foo.org error:mail for *.foo.org is not deliv-
+# .foo.org error:mail for *.foo.org is not deliv-
# erable
#
-# This causes all mail for user@anything.foo.org to be
+# This causes all mail for user@anything.foo.org to be
# bounced.
#
# REGULAR EXPRESSION TABLES
-# This section describes how the table lookups change when
+# This section describes how the table lookups change when
# the table is given in the form of regular expressions. For
-# a description of regular expression lookup table syntax,
+# a description of regular expression lookup table syntax,
# see regexp_table(5) or pcre_table(5).
#
-# Each pattern is a regular expression that is applied to
+# Each pattern is a regular expression that is applied to
# the entire domain being looked up. Thus, some.domain.hier-
# archy is not broken up into parent domains.
#
-# Patterns are applied in the order as specified in the
-# table, until a pattern is found that matches the search
+# Patterns are applied in the order as specified in the
+# table, until a pattern is found that matches the search
# string.
#
-# Results are the same as with indexed file lookups, with
-# the additional feature that parenthesized substrings from
+# Results are the same as with indexed file lookups, with
+# the additional feature that parenthesized substrings from
# the pattern can be interpolated as $1, $2 and so on.
#
# CONFIGURATION PARAMETERS
-# The following main.cf parameters are especially relevant
-# to this topic. See the Postfix main.cf file for syntax
-# details and for default values. Use the postfix reload
+# The following main.cf parameters are especially relevant
+# to this topic. See the Postfix main.cf file for syntax
+# details and for default values. Use the postfix reload
# command after a configuration change.
#
# empty_address_recipient
-# The address that is looked up instead of the null
+# The address that is looked up instead of the null
# sender address.
#
# parent_domain_matches_subdomains
-# List of Postfix features that use domain.tld pat-
-# terns to match sub.domain.tld (as opposed to
+# List of Postfix features that use domain.tld pat-
+# terns to match sub.domain.tld (as opposed to
# requiring .domain.tld patterns).
#
# transport_maps
# regexp_table(5) format of POSIX regular expression tables
#
# LICENSE
-# The Secure Mailer license must be distributed with this
+# The Secure Mailer license must be distributed with this
# software.
#
# AUTHOR(S)
A network address is a sequence of one or more
octets separated by ".".
+ NOTE: use the <b>cidr</b> lookup table type if you want to
+ specify arbitrary network blocks.
+
<b>ACTIONS</b>
[<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,
and respond with the numerical code and text.
<b>REJECT</b>
<b>REJECT</b> <i>optional</i> <i>text...</i>
- Reject the address etc. that matches the pattern.
- Reply with <i>$reject_code</i> <i>optional</i> <i>text...</i> when the
- optional text is specified, otherwise reply with a
+ Reject the address etc. that matches the pattern.
+ Reply with <i>$reject_code</i> <i>optional</i> <i>text...</i> when the
+ optional text is specified, otherwise reply with a
generic error response message.
<b>OK</b> Accept the address etc. that matches the pattern.
<i>all-numerical</i>
An all-numerical result is treated as OK. This for-
- mat is generated by address-based relay authoriza-
+ mat is generated by address-based relay authoriza-
tion schemes.
- <b>DUNNO</b> Pretend that the lookup key was not found in this
+ <b>DUNNO</b> Pretend that the lookup key was not found in this
table. This prevents Postfix from trying substrings
- of the lookup key (such as a subdomain name, or a
+ of the lookup key (such as a subdomain name, or a
network address subnetwork).
<b>HOLD</b>
<b>HOLD</b> <i>optional</i> <i>text...</i>
- Place the message on the <b>hold</b> queue, where it will
- sit until someone either deletes it or releases it
- for delivery. Log the optional text if specified,
+ Place the message on the <b>hold</b> queue, where it will
+ sit until someone either deletes it or releases it
+ for delivery. Log the optional text if specified,
otherwise log a generic message.
- Mail that is placed on hold can be examined with
- the <a href="postcat.1.html"><b>postcat</b>(1)</a> command, and can be destroyed or
+ Mail that is placed on hold can be examined with
+ the <a href="postcat.1.html"><b>postcat</b>(1)</a> command, and can be destroyed or
released with the <a href="postsuper.1.html"><b>postsuper</b>(1)</a> command.
- Note: this action currently affects all recipients
+ Note: this action currently affects all recipients
of the message.
<b>DISCARD</b>
<b>DISCARD</b> <i>optional</i> <i>text...</i>
- Claim successful delivery and silently discard the
- message. Log the optional text if specified, oth-
+ Claim successful delivery and silently discard the
+ message. Log the optional text if specified, oth-
erwise log a generic message.
- Note: this action currently affects all recipients
+ Note: this action currently affects all recipients
of the message.
<b>FILTER</b> <i>transport:destination</i>
- After the message is queued, send the entire mes-
- sage through a content filter. More information
+ After the message is queued, send the entire mes-
+ sage through a content filter. More information
about content filters is in the Postfix FIL-
TER_README file.
- Note: this action overrides the <b>main.cf</b> <b>con-</b>
+ Note: this action overrides the <b>main.cf</b> <b>con-</b>
<b>tent</b><i>_</i><b>filter</b> setting, and currently affects all
recipients of the message.
<b>REDIRECT</b> <i>user@domain</i>
- After the message is queued, send the message to
+ After the message is queued, send the message to
the specified address instead of the intended
recipient(s).
- Note: this action overrides the FILTER action, and
+ Note: this action overrides the FILTER action, and
currently affects all recipients of the message.
<i>restriction...</i>
<b>reject</b><i>_</i><b>unauth</b><i>_</i><b>destination</b>, and so on).
<b>REGULAR</b> <b>EXPRESSION</b> <b>TABLES</b>
- This section describes how the table lookups change when
+ This section describes how the table lookups change when
the table is given in the form of regular expressions. For
- a description of regular expression lookup table syntax,
+ a description of regular expression lookup table syntax,
see <a href="regexp_table.5.html"><b>regexp</b><i>_</i><b>table</b>(5)</a> or <a href="pcre_table.5.html"><b>pcre</b><i>_</i><b>table</b>(5)</a>.
- Each pattern is a regular expression that is applied to
+ Each pattern is a regular expression that is applied to
the entire string being looked up. Depending on the appli-
- cation, that string is an entire client hostname, an
+ cation, that string is an entire client hostname, an
entire client IP address, or an entire mail address. Thus,
no parent domain or parent network search is done,
- <i>user@domain</i> mail addresses are not broken up into their
+ <i>user@domain</i> mail addresses are not broken up into their
<i>user@</i> and <i>domain</i> constituent parts, nor is <i>user+foo</i> broken
up into <i>user</i> and <i>foo</i>.
- Patterns are applied in the order as specified in the
- table, until a pattern is found that matches the search
+ Patterns are applied in the order as specified in the
+ table, until a pattern is found that matches the search
string.
- Actions are the same as with indexed file lookups, with
- the additional feature that parenthesized substrings from
+ Actions are the same as with indexed file lookups, with
+ the additional feature that parenthesized substrings from
the pattern can be interpolated as <b>$1</b>, <b>$2</b> and so on.
<b>BUGS</b>
- The table format does not understand quoting conventions.
+ The table format does not understand quoting conventions.
<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
+ cidr_table(5) format of CIDR 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
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
Address mapping lookup table for sender and recipi-
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>
Address mapping lookup table for envelope and
header recipient addresses.
header sender addresses.
<b>masquerade</b><i>_</i><b>classes</b>
- List of address classes subject to masquerading:
- zero or more of <b>envelope</b><i>_</i><b>sender</b>, <b>envelope</b><i>_</i><b>recipi-</b>
+ List of address classes subject to masquerading:
+ zero or more of <b>envelope</b><i>_</i><b>sender</b>, <b>envelope</b><i>_</i><b>recipi-</b>
<b>ent</b>, <b>header</b><i>_</i><b>sender</b>, <b>header</b><i>_</i><b>recipient</b>.
<b>masquerade</b><i>_</i><b>domains</b>
- List of domains that hide their subdomain struc-
+ List of domains that hide their subdomain struc-
ture.
<b>masquerade</b><i>_</i><b>exceptions</b>
- List of user names that are not subject to address
+ List of user names that are not subject to address
masquerading.
<b>virtual</b><i>_</i><b>alias</b><i>_</i><b>maps</b>
<b>Resource</b> <b>controls</b>
<b>duplicate</b><i>_</i><b>filter</b><i>_</i><b>limit</b>
- Limits the number of envelope recipients that are
+ Limits the number of envelope recipients that are
remembered.
<b>header</b><i>_</i><b>address</b><i>_</i><b>token</b><i>_</i><b>limit</b>
a message header.
<b>header</b><i>_</i><b>size</b><i>_</i><b>limit</b>
- Limits the amount of memory in bytes used to pro-
+ Limits the amount of memory in bytes used to pro-
cess a message header.
<b>in</b><i>_</i><b>flow</b><i>_</i><b>delay</b>
Amount of time to pause before accepting a message,
- when the message arrival rate exceeds the message
+ when the message arrival rate exceeds the message
delivery rate.
<b>virtual</b><i>_</i><b>alias</b><i>_</i><b>expansion</b><i>_</i><b>limit</b>
- Limit the number of actual recipients produced by
- virtual alias expansion from each original recipi-
+ Limit the number of actual recipients produced by
+ virtual alias expansion from each original recipi-
ent.
<b>virtual</b><i>_</i><b>alias</b><i>_</i><b>recursion</b><i>_</i><b>limit</b>
- Limit the recursion depth of virtual alias expan-
+ Limit the recursion depth of virtual alias expan-
sion.
<b>SEE</b> <b>ALSO</b>
/etc/postfix/virtual*, virtual mapping table
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
<p>
-If you want to deliver the domain as a Postfix simulated <a
-href="virtual.8.html">virtual</a>(5) domain, then you should list
+If you want to deliver the domain as a <a href="virtual.8.html">
+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
<b>virtual_alias_domains</b> parameter instead.
<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.
<p>
Answer: Postfix logs the original recipient address in the
<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>
<a name="masquerade"><h3>Address masquerading with exceptions</h3></a>
pcre_table - format of Postfix PCRE tables
<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> <b>pcre:/etc/postfix/</b><i>filename</i> <<i>inputfile</i>
The general form of a PCRE table is:
<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
Empty lines and whitespace-only lines are ignored,
<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>
- matches. The <b>if</b>..<b>endif</b> can nest. Do not prepend
- whitespace to patterns inside <b>if</b>..<b>endif</b>.
+ matches (does not match). The <b>if</b>..<b>endif</b> can nest.
+ Do not prepend whitespace to patterns inside
+ <b>if</b>..<b>endif</b>.
Each pattern is a perl-like regular expression. The
- expression delimiter can be any character, except whites-
- pace or characters that have special meaning (tradition-
- ally the forward slash is used). The regular expression
+ expression delimiter can be any character, except whites-
+ pace or characters that have special meaning (tradition-
+ ally the forward slash is used). The regular expression
can contain whitespace.
By default, matching is case-insensitive, and newlines are
- not treated as special characters. The behavior is con-
- trolled by flags, which are toggled by appending one or
+ not treated as special characters. The behavior is con-
+ trolled by flags, which are toggled by appending one or
more of the following characters after the pattern:
<b>i</b> (default: on)
- Toggles the case sensitivity flag. By default,
+ Toggles the case sensitivity flag. By default,
matching is case insensitive.
<b>m</b> (default: off)
- Toggles the PCRE_MULTILINE flag. When this flag is
- on, the <b>^</b> and <b>$</b> metacharacters match immediately
- after and immediately before a newline character,
- respectively, in addition to matching at the start
+ Toggles the PCRE_MULTILINE flag. When this flag is
+ on, the <b>^</b> and <b>$</b> metacharacters match immediately
+ after and immediately before a newline character,
+ respectively, in addition to matching at the start
and end of the subject string.
<b>s</b> (default: on)
Toggles the PCRE_DOTALL flag. When this flag is on,
the <b>.</b> metacharacter matches the newline character.
- With Postfix versions prior to 20020528, The flag
+ With Postfix versions prior to 20020528, The flag
is off by default, which is inconvenient for multi-
line message header matching.
<b>x</b> (default: off)
- Toggles the pcre extended flag. When this flag is
- on, whitespace in the pattern (other than in a
+ Toggles the pcre extended flag. When this flag is
+ on, whitespace in the pattern (other than in a
character class) and characters between a <b>#</b> outside
- a character class and the next newline character
- are ignored. An escaping backslash can be used to
- include a whitespace or <b>#</b> character as part of the
+ a character class and the next newline character
+ are ignored. An escaping backslash can be used to
+ include a whitespace or <b>#</b> character as part of the
pattern.
<b>A</b> (default: off)
- Toggles the PCRE_ANCHORED flag. When this flag is
- on, the pattern is forced to be "anchored", that
+ Toggles the PCRE_ANCHORED flag. When this flag is
+ on, the pattern is forced to be "anchored", that
is, it is constrained to match only at the start of
- the string which is being searched (the "subject
- string"). This effect can also be achieved by
+ the string which is being searched (the "subject
+ string"). This effect can also be achieved by
appropriate constructs in the pattern itself.
<b>E</b> (default: off)
- Toggles the PCRE_DOLLAR_ENDONLY flag. When this
- flag is on, a <b>$</b> metacharacter in the pattern
- matches only at the end of the subject string.
- Without this flag, a dollar also matches immedi-
+ Toggles the PCRE_DOLLAR_ENDONLY flag. When this
+ flag is on, a <b>$</b> metacharacter in the pattern
+ matches only at the end of the subject string.
+ Without this flag, a dollar also matches immedi-
ately before the final character if it is a newline
character (but not before any other newline charac-
- ters). This flag is ignored if PCRE_MULTILINE flag
+ ters). This flag is ignored if PCRE_MULTILINE flag
is set.
<b>U</b> (default: off)
Toggles the ungreedy matching flag. When this flag
- is on, the pattern matching engine inverts the
- "greediness" of the quantifiers so that they are
- not greedy by default, but become greedy if fol-
- lowed by "?". This flag can also set by a (?U)
+ is on, the pattern matching engine inverts the
+ "greediness" of the quantifiers so that they are
+ not greedy by default, but become greedy if fol-
+ lowed by "?". This flag can also set by a (?U)
modifier within the pattern.
<b>X</b> (default: off)
Toggles the PCRE_EXTRA flag. When this flag is on,
- any backslash in a pattern that is followed by a
+ any backslash in a pattern that is followed by a
letter that has no special meaning causes an error,
thus reserving these combinations for future expan-
sion.
- Each pattern is applied to the entire lookup key string.
- Depending on the application, that string is an entire
+ Each pattern is applied to the entire lookup key string.
+ Depending on the application, that string is an entire
client hostname, an entire client IP address, or an entire
- mail address. Thus, no parent domain or parent network
- search is done, and <i>user@domain</i> mail addresses are not
- broken up into their <i>user</i> and <i>domain</i> constituent parts,
+ mail address. Thus, no parent domain or parent network
+ search is done, and <i>user@domain</i> mail addresses are not
+ broken up into their <i>user</i> and <i>domain</i> constituent parts,
nor is <i>user+foo</i> broken up into <i>user</i> and <i>foo</i>.
- Patterns are applied in the order as specified in the
- table, until a pattern is found that matches the search
+ Patterns are applied in the order as specified in the
+ table, until a pattern is found that matches the search
string.
- Substitution of substrings from the matched expression
- into the result string is possible using the conventional
- perl syntax ($1, $2, etc.). The macros in the result
- string may need to be written as ${n} or $(n) if they
- aren't followed by whitespace.
+ Substitution of substrings from the matched expression
+ into the result string is possible using the conventional
+ perl syntax ($1, $2, etc.). The macros in the result
+ string may need to be written as ${n} 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>
# Protect your outgoing majordomo exploders
<b>SEE</b> <b>ALSO</b>
<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>
The PCRE table lookup code was originally written by:
regexp_table - format of Postfix regular expression tables
<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> <b>regexp:/etc/postfix/</b><i>filename</i> <<i>inputfile</i>
Substitution of substrings from the matched expression
into the result 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 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>
# Disallow sender-specified routing. This is a must if you relay mail
<b>SEE</b> <b>ALSO</b>
<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>
The regexp table lookup code was originally written by:
href="virtual.5.html">virtual alias</a> table to redirect mail for all
recipients, local or remote. The mapping affects only envelope
recipients; it has no effect on message headers or envelope senders.
-Virtual alias lookups are useful to redirect mail for simulated
-virtual domains to real user mailboxes, and to redirect mail for
+Virtual alias lookups are useful to redirect mail for virtual
+alias domains to real user mailboxes, and to redirect mail for
domains that no longer exist. Virtual alias lookups can also be
used to transform <i> Firstname.Lastname </i> back into UNIX login
names, although it seems that local <a href="#aliases">aliases</a>
preference, and connects 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 ban-
ner, no error will cause it to proceed to the next address
on the mail exchanger list. Instead, the message is either
<b>SECURITY</b>
The SMTP client is moderately security-sensitive. It talks
- to SMTP servers and to DNS servers on the network. The
+ to SMTP servers and to DNS servers on the network. The
SMTP client can be run chrooted at fixed low privilege.
<b>STANDARDS</b>
<a href="http://www.faqs.org/rfcs/rfc2920.html">RFC 2920</a> (SMTP Pipelining)
<b>DIAGNOSTICS</b>
- Problems and transactions are logged to <b>syslogd</b>(8). Cor-
- rupted message files are marked so that the queue manager
+ Problems and transactions are logged to <b>syslogd</b>(8). Cor-
+ rupted message files are marked so that the queue manager
can move them to the <b>corrupt</b> queue for further inspection.
- Depending on the setting of the <b>notify</b><i>_</i><b>classes</b> parameter,
- the postmaster is notified of bounces, protocol problems,
+ Depending on the setting of the <b>notify</b><i>_</i><b>classes</b> parameter,
+ the postmaster is notified of bounces, protocol problems,
and of other trouble.
<b>BUGS</b>
<b>CONFIGURATION</b> <b>PARAMETERS</b>
- The following <b>main.cf</b> parameters are especially relevant
- to this program. See the Postfix <b>main.cf</b> file for syntax
- details and for default values. Use the <b>postfix</b> <b>reload</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this program. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
command after a configuration change.
<b>Miscellaneous</b>
<b>best</b><i>_</i><b>mx</b><i>_</i><b>transport</b>
- Name of the delivery transport to use when the
- local machine is the most-preferred mail exchanger
- (by default, a mailer loop is reported, and the
+ Name of the delivery transport to use when the
+ local machine is the most-preferred mail exchanger
+ (by default, a mailer loop is reported, and the
message is bounced).
<b>debug</b><i>_</i><b>peer</b><i>_</i><b>level</b>
- Verbose logging level increment for hosts that
+ Verbose logging level increment for hosts that
match a pattern in the <b>debug</b><i>_</i><b>peer</b><i>_</i><b>list</b> parameter.
<b>debug</b><i>_</i><b>peer</b><i>_</i><b>list</b>
- List of domain or network patterns. When a remote
- host matches a pattern, increase the verbose log-
- ging level by the amount specified in the
+ List of domain or network patterns. When a remote
+ host matches a pattern, increase the verbose log-
+ ging level by the amount specified in the
<b>debug</b><i>_</i><b>peer</b><i>_</i><b>level</b> parameter.
<b>disable</b><i>_</i><b>dns</b><i>_</i><b>lookups</b>
- Disable DNS lookups. This means that mail must be
+ Disable DNS lookups. This means that mail must be
forwarded via a smart relay host.
<b>smtp</b><i>_</i><b>host</b><i>_</i><b>lookup</b>
- What host lookup mechanism the SMTP client should
- use. Specify <b>dns</b> (use DNS lookup) and/or <b>native</b>
- (use the native naming service which also uses
- /etc/hosts). This setting is ignored when DNS
+ What host lookup mechanism the SMTP client should
+ use. Specify <b>dns</b> (use DNS lookup) and/or <b>native</b>
+ (use the native naming service which also uses
+ /etc/hosts). This setting is ignored when DNS
lookups are disabled.
<b>error</b><i>_</i><b>notice</b><i>_</i><b>recipient</b>
- Recipient of protocol/policy/resource/software
+ Recipient of protocol/policy/resource/software
error notices.
<b>fallback</b><i>_</i><b>relay</b>
- Hosts to hand off mail to if a message destination
+ Hosts to hand off mail to if a message destination
is not found or if a destination is unreachable.
<b>ignore</b><i>_</i><b>mx</b><i>_</i><b>lookup</b><i>_</i><b>error</b>
When a name server fails to respond to an MX query,
- search for an A record instead deferring mail
+ search for an A record instead deferring mail
delivery.
<b>inet</b><i>_</i><b>interfaces</b>
The network interface addresses that this mail sys-
- tem receives mail on. When any of those addresses
+ tem receives mail on. When any of those addresses
appears in the list of mail exchangers for a remote
- destination, the list is truncated to avoid mail
+ destination, the list is truncated to avoid mail
delivery loops. See also the <b>proxy</b><i>_</i><b>interfaces</b>
parameter.
<b>notify</b><i>_</i><b>classes</b>
- When this parameter includes the <b>protocol</b> class,
- send mail to the postmaster with transcripts of
+ When this parameter includes the <b>protocol</b> class,
+ send mail to the postmaster with transcripts of
SMTP sessions with protocol errors.
<b>proxy</b><i>_</i><b>interfaces</b>
- Network interfaces that this mail system receives
+ Network interfaces that this mail system receives
mail on by way of a proxy or network address trans-
- lator. When any of those addresses appears in the
- list of mail exchangers for a remote destination,
+ lator. When any of those addresses appears in the
+ list of mail exchangers for a remote destination,
the list is truncated to avoid mail delivery loops.
See also the <b>inet</b><i>_</i><b>interfaces</b> parameter.
Never send EHLO at the start of a connection.
<b>smtp</b><i>_</i><b>bind</b><i>_</i><b>address</b>
- Numerical source network address to bind to when
+ Numerical source network address to bind to when
making a connection.
<b>smtp</b><i>_</i><b>line</b><i>_</i><b>length</b><i>_</i><b>limit</b>
- Length limit for SMTP message content lines. Zero
- means no limit. Some SMTP servers misbehave on
+ Length limit for SMTP message content lines. Zero
+ means no limit. Some SMTP servers misbehave on
long lines.
<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>
Skip servers that greet us with a 4xx status code.
<b>reject</b><i>_</i><b>sender</b><i>_</i><b>login</b><i>_</i><b>mismatch</b> sender anti-spoofing
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>authorized</b><i>_</i><b>verp</b><i>_</i><b>clients</b>
Hostnames, domain names and/or addresses of clients
that are authorized to use the XVERP extension.
<b>debug</b><i>_</i><b>peer</b><i>_</i><b>level</b>
- Increment in verbose logging level when a remote
+ Increment in verbose logging level when a remote
host matches a pattern in the <b>debug</b><i>_</i><b>peer</b><i>_</i><b>list</b>
parameter.
<b>debug</b><i>_</i><b>peer</b><i>_</i><b>list</b>
- List of domain or network patterns. When a remote
- host matches a pattern, increase the verbose log-
- ging level by the amount specified in the
+ List of domain or network patterns. When a remote
+ host matches a pattern, increase the verbose log-
+ ging level by the amount specified in the
<b>debug</b><i>_</i><b>peer</b><i>_</i><b>level</b> parameter.
<b>default</b><i>_</i><b>verp</b><i>_</i><b>delimiters</b>
The default VERP delimiter characters that are used
- when the XVERP command is specified without
+ when the XVERP command is specified without
explicit delimiters.
<b>error</b><i>_</i><b>notice</b><i>_</i><b>recipient</b>
- Recipient of protocol/policy/resource/software
+ Recipient of protocol/policy/resource/software
error notices.
<b>hopcount</b><i>_</i><b>limit</b>
<b>notify</b><i>_</i><b>classes</b>
List of error classes. Of special interest are:
- <b>policy</b> When a client violates any policy, mail a
+ <b>policy</b> When a client violates any policy, mail a
transcript of the entire SMTP session to the
postmaster.
<b>protocol</b>
- When a client violates the SMTP protocol or
+ When a client violates the SMTP protocol or
issues an unimplemented command, mail a
transcript of the entire SMTP session to the
postmaster.
<b>smtpd</b><i>_</i><b>banner</b>
- Text that follows the <b>220</b> status code in the SMTP
+ Text that follows the <b>220</b> status code in the SMTP
greeting banner.
<b>smtpd</b><i>_</i><b>expansion</b><i>_</i><b>filter</b>
expansion of rbl template responses and other text.
<b>smtpd</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
- Restrict the number of recipients that the SMTP
+ Restrict the number of recipients that the SMTP
server accepts per message delivery.
<b>smtpd</b><i>_</i><b>timeout</b>
- Limit the time to send a server response and to
+ Limit the time to send a server response and to
receive a client request.
<b>soft</b><i>_</i><b>bounce</b>
- Change hard (5xx) reject responses into soft (4xx)
- reject responses. This can be useful for testing
+ Change hard (5xx) reject responses into soft (4xx)
+ reject responses. This can be useful for testing
purposes.
<b>verp</b><i>_</i><b>delimiter</b><i>_</i><b>filter</b>
- The characters that Postfix accepts as VERP delim-
+ The characters that Postfix accepts as VERP delim-
iter characters.
<b>Known</b> <b>versus</b> <b>unknown</b> <b>recipients</b>
<b>show</b><i>_</i><b>user</b><i>_</i><b>unknown</b><i>_</i><b>table</b><i>_</i><b>name</b>
- Whether or not to reveal the table name in the
- "User unknown" responses. The extra detail makes
- trouble shooting easier but also reveals informa-
+ Whether or not to reveal the table name in the
+ "User unknown" responses. The extra detail makes
+ trouble shooting easier but also reveals informa-
tion that is nobody elses business.
<b>unknown</b><i>_</i><b>local</b><i>_</i><b>recipient</b><i>_</i><b>reject</b><i>_</i><b>code</b>
The response code when a client specifies a recipi-
- ent whose domain matches <b>$mydestination</b> or
+ ent whose domain matches <b>$mydestination</b> or
<b>$inet</b><i>_</i><b>interfaces</b>, while <b>$local</b><i>_</i><b>recipient</b><i>_</i><b>maps</b> is
- non-empty and does not list the recipient address
+ non-empty and does not list the recipient address
or address local-part.
<b>unknown</b><i>_</i><b>relay</b><i>_</i><b>recipient</b><i>_</i><b>reject</b><i>_</i><b>code</b>
The response code when a client specifies a recipi-
ent whose domain matches <b>$relay</b><i>_</i><b>domains</b>, while
- <b>$relay</b><i>_</i><b>recipient</b><i>_</i><b>maps</b> is non-empty and does not
+ <b>$relay</b><i>_</i><b>recipient</b><i>_</i><b>maps</b> is non-empty and does not
list the recipient address.
<b>unknown</b><i>_</i><b>virtual</b><i>_</i><b>alias</b><i>_</i><b>reject</b><i>_</i><b>code</b>
The response code when a client specifies a recipi-
- ent whose domain matches <b>$virtual</b><i>_</i><b>alias</b><i>_</i><b>domains</b>,
- while the recipient is not listed in <b>$vir-</b>
+ ent whose domain matches <b>$virtual</b><i>_</i><b>alias</b><i>_</i><b>domains</b>,
+ while the recipient is not listed in <b>$vir-</b>
<b>tual</b><i>_</i><b>alias</b><i>_</i><b>maps</b>.
<b>unknown</b><i>_</i><b>virtual</b><i>_</i><b>mailbox</b><i>_</i><b>reject</b><i>_</i><b>code</b>
The response code when a client specifies a recipi-
- ent whose domain matches <b>$virtual</b><i>_</i><b>mailbox</b><i>_</i><b>domains</b>,
+ ent whose domain matches <b>$virtual</b><i>_</i><b>mailbox</b><i>_</i><b>domains</b>,
while the recipient is not listed in <b>$virtual</b><i>_</i><b>mail-</b>
<b>box</b><i>_</i><b>maps</b>.
<b>Resource</b> <b>controls</b>
<b>line</b><i>_</i><b>length</b><i>_</i><b>limit</b>
- Limit the amount of memory in bytes used for the
+ Limit the amount of memory in bytes used for the
handling of partial input lines.
<b>message</b><i>_</i><b>size</b><i>_</i><b>limit</b>
ing on-disk storage for envelope information.
<b>queue</b><i>_</i><b>minfree</b>
- Minimal amount of free space in bytes in the queue
- file system for the SMTP server to accept any mail
+ Minimal amount of free space in bytes in the queue
+ file system for the SMTP server to accept any mail
at all.
<b>smtpd</b><i>_</i><b>history</b><i>_</i><b>flush</b><i>_</i><b>threshold</b>
<b>smtpd</b><i>_</i><b>soft</b><i>_</i><b>error</b><i>_</i><b>limit</b>
When an SMTP client has made this number of errors,
- wait <i>error_count</i> seconds before responding to any
+ wait <i>error_count</i> seconds before responding to any
client request.
<b>smtpd</b><i>_</i><b>hard</b><i>_</i><b>error</b><i>_</i><b>limit</b>
- Disconnect after a client has made this number of
+ Disconnect after a client has made this number of
errors.
<b>smtpd</b><i>_</i><b>junk</b><i>_</i><b>command</b><i>_</i><b>limit</b>
Limit the number of times a client can issue a junk
- command such as NOOP, VRFY, ETRN or RSET in one
- SMTP session before it is penalized with tarpit
+ command such as NOOP, VRFY, ETRN or RSET in one
+ SMTP session before it is penalized with tarpit
delays.
<b>UCE</b> <b>control</b> <b>restrictions</b>
<b>parent</b><i>_</i><b>domain</b><i>_</i><b>matches</b><i>_</i><b>subdomains</b>
- List of Postfix features that use <i>domain.tld</i> pat-
- terns to match <i>sub.domain.tld</i> (as opposed to
+ List of Postfix features that use <i>domain.tld</i> pat-
+ terns to match <i>sub.domain.tld</i> (as opposed to
requiring <i>.domain.tld</i> patterns).
<b>smtpd</b><i>_</i><b>client</b><i>_</i><b>restrictions</b>
tem.
<b>smtpd</b><i>_</i><b>helo</b><i>_</i><b>required</b>
- Require that clients introduce themselves at the
+ Require that clients introduce themselves at the
beginning of an SMTP session.
<b>smtpd</b><i>_</i><b>helo</b><i>_</i><b>restrictions</b>
- Restrict what client hostnames are allowed in <b>HELO</b>
+ Restrict what client hostnames are allowed in <b>HELO</b>
and <b>EHLO</b> commands.
<b>smtpd</b><i>_</i><b>sender</b><i>_</i><b>restrictions</b>
- Restrict what sender addresses are allowed in <b>MAIL</b>
+ Restrict what sender addresses are allowed in <b>MAIL</b>
<b>FROM</b> commands.
<b>smtpd</b><i>_</i><b>recipient</b><i>_</i><b>restrictions</b>
- Restrict what recipient addresses are allowed in
+ Restrict what recipient addresses are allowed in
<b>RCPT</b> <b>TO</b> commands.
<b>smtpd</b><i>_</i><b>etrn</b><i>_</i><b>restrictions</b>
mands, and what clients may issue <b>ETRN</b> commands.
<b>smtpd</b><i>_</i><b>data</b><i>_</i><b>restrictions</b>
- Restrictions on the <b>DATA</b> command. Currently, the
- only restriction that makes sense here is
+ Restrictions on the <b>DATA</b> command. Currently, the
+ only restriction that makes sense here is
<b>reject</b><i>_</i><b>unauth</b><i>_</i><b>pipelining</b>.
<b>allow</b><i>_</i><b>untrusted</b><i>_</i><b>routing</b>
- Allow untrusted clients to specify addresses with
- sender-specified routing. Enabling this opens up
- nasty relay loopholes involving trusted backup MX
+ Allow untrusted clients to specify addresses with
+ sender-specified routing. Enabling this opens up
+ nasty relay loopholes involving trusted backup MX
hosts.
<b>smtpd</b><i>_</i><b>restriction</b><i>_</i><b>classes</b>
- Declares the name of zero or more parameters that
- contain a list of UCE restrictions. The names of
- these parameters can then be used instead of the
+ Declares the name of zero or more parameters that
+ contain a list of UCE restrictions. The names of
+ these parameters can then be used instead of the
restriction lists that they represent.
<b>smtpd</b><i>_</i><b>null</b><i>_</i><b>access</b><i>_</i><b>lookup</b><i>_</i><b>key</b>
- The lookup key to be used in SMTPD access tables
- instead of the null sender address. A null sender
+ The lookup key to be used in SMTPD access tables
+ instead of the null sender address. A null sender
address cannot be looked up.
<b>maps</b><i>_</i><b>rbl</b><i>_</i><b>domains</b> (deprecated)
- List of DNS domains that publish the addresses of
+ List of DNS domains that publish the addresses of
blacklisted hosts. This is used with the deprecated
<b>reject</b><i>_</i><b>maps</b><i>_</i><b>rbl</b> restriction.
<b>permit</b><i>_</i><b>mx</b><i>_</i><b>backup</b><i>_</i><b>networks</b>
- Only domains whose primary MX hosts match the
- listed networks are eligible for the <b>per-</b>
+ Only domains whose primary MX hosts match the
+ listed networks are eligible for the <b>per-</b>
<b>mit</b><i>_</i><b>mx</b><i>_</i><b>backup</b> feature.
<b>relay</b><i>_</i><b>domains</b>
- Restrict what domains this mail system will relay
- mail to. The domains are routed to the delivery
+ Restrict what domains this mail system will relay
+ mail to. The domains are routed to the delivery
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>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
be undeliverable.
<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="master.8.html">master(8)</a> process manager
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>
The Secure Mailer license must be distributed with this
relay hosts. The mapping is used by the <a href="trivial-rewrite.8.html"><b>trivial-rewrite</b>(8)</a>
daemon.
- 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
- result, an indexed file in <b>dbm</b> or <b>db</b> format, is used for
- fast searching by the mail system. Execute the command
- <b>postmap</b> <b>/etc/postfix/transport</b> in order to rebuild the
+ 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
+ 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
+ fast searching by the mail system. Execute the command
+ <b>postmap</b> <b>/etc/postfix/transport</b> in order to rebuild the
indexed file after changing the transport table.
- When the table is provided via other means such as NIS,
- LDAP or SQL, the same lookups are done as for ordinary
+ When the table is provided via other means such as NIS,
+ LDAP or SQL, the same lookups are done as for ordinary
indexed files.
- Alternatively, the table can be provided as a regular-
+ Alternatively, the table can be provided as a regular-
expression map where patterns are given as regular expres-
- sions. In that case, the lookups are done in a slightly
- different way as described in section "REGULAR EXPRESSION
+ sions. In that case, the lookups are done in a slightly
+ different way as described in section "REGULAR EXPRESSION
TABLES".
<b>TABLE</b> <b>FORMAT</b>
domain, use the corresponding <i>result</i>.
blank lines and comments
- Empty lines and whitespace-only lines are ignored,
- as are lines whose first non-whitespace character
+ 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-
+ A logical line starts with non-whitespace text. A
+ line that starts with whitespace continues a logi-
cal line.
- The <i>pattern</i> specifies an email address, a domain name, or
- a domain name hierarchy, as described in section "TABLE
+ The <i>pattern</i> specifies an email address, a domain name, or
+ a domain name hierarchy, as described in section "TABLE
LOOKUP".
- The <i>result</i> is of the form <i>transport</i><b>:</b><i>nexthop</i>. The <i>trans-</i>
- <i>port</i> field specifies a mail delivery transport such as
- <b>smtp</b> or <b>local</b>. The <i>nexthop</i> field specifies where and how
+ The <i>result</i> is of the form <i>transport</i><b>:</b><i>nexthop</i>. The <i>trans-</i>
+ <i>port</i> field specifies a mail delivery transport such as
+ <b>smtp</b> or <b>local</b>. The <i>nexthop</i> field specifies where and how
to deliver mail. More details are given in section "RESULT
FORMAT".
<b>TABLE</b> <b>LOOKUP</b>
With lookups from indexed files such as DB or DBM, or from
- networked tables such as NIS, LDAP or SQL, patterns are
+ networked tables such as NIS, LDAP or SQL, patterns are
tried in the order as listed below:
<i>user+extension@domain</i> <i>transport</i>:<i>nexthop</i>
to <i>nexthop</i>.
<i>domain</i> <i>transport</i>:<i>nexthop</i>
- Mail for <i>domain</i> is delivered through <i>transport</i> to
+ Mail for <i>domain</i> is delivered through <i>transport</i> to
<i>nexthop</i>.
<i>.domain</i> <i>transport</i>:<i>nexthop</i>
- Mail for any subdomain of <i>domain</i> is delivered
- through <i>transport</i> to <i>nexthop</i>. This applies only
+ Mail for any subdomain of <i>domain</i> is delivered
+ through <i>transport</i> to <i>nexthop</i>. This applies only
when the string <b>transport</b><i>_</i><b>maps</b> is not listed in the
<b>parent</b><i>_</i><b>domain</b><i>_</i><b>matches</b><i>_</i><b>subdomains</b> configuration set-
- ting. Otherwise, a domain name matches itself and
+ ting. Otherwise, a domain name matches itself and
its subdomains.
Note 1: the special pattern <b>*</b> represents any address (i.e.
it functions as the wild-card pattern).
- Note 2: the null recipient address is looked up as
+ Note 2: the null recipient address is looked up as
<b>$empty</b><i>_</i><b>address</b><i>_</i><b>recipient</b>@<b>$myhostname</b> (default: mailer-dae-
mon@hostname).
<b>RESULT</b> <b>FORMAT</b>
- The transport field specifies the name of a mail delivery
+ The transport field specifies the name of a mail delivery
transport (the first name of a mail delivery service entry
in the Postfix <b>master.cf</b> file).
- The interpretation of the nexthop field is transport
+ The interpretation of the nexthop field is transport
dependent. In the case of SMTP, specify <i>host</i>:<i>service</i> for a
- non-default server port, and use [<i>host</i>] or [<i>host</i>]:<i>port</i> in
- order to disable MX (mail exchanger) DNS lookups. The []
+ non-default server port, and use [<i>host</i>] or [<i>host</i>]:<i>port</i> in
+ order to disable MX (mail exchanger) DNS lookups. The []
form is required when you specify an IP address instead of
a hostname.
- A null <i>transport</i> and null <i>nexthop</i> result means "do not
- change": use the delivery transport and nexthop informa-
- tion that would be used when the entire transport table
+ A null <i>transport</i> and null <i>nexthop</i> result means "do not
+ change": use the delivery transport and nexthop informa-
+ tion that would be used when the entire transport table
did not exist.
- A non-null <i>transport</i> field with a null <i>nexthop</i> field
+ A non-null <i>transport</i> field with a null <i>nexthop</i> field
resets the nexthop information to the recipient domain.
- A null <i>transport</i> field with non-null <i>nexthop</i> field does
+ A null <i>transport</i> field with non-null <i>nexthop</i> field does
not modify the transport information.
<b>EXAMPLES</b>
- In order to deliver internal mail directly, while using a
- mail relay for all other mail, specify a null entry for
- internal destinations (do not change the delivery trans-
- port or the nexthop information) and specify a wildcard
+ In order to deliver internal mail directly, while using a
+ mail relay for all other mail, specify a null entry for
+ internal destinations (do not change the delivery trans-
+ port or the nexthop information) and specify a wildcard
for all other destinations.
<b>my.domain</b> <b>:</b>
<b>.my.domain</b> <b>:</b>
<b>*</b> <b>smtp:outbound-relay.my.domain</b>
- In order to send mail for <b>foo.org</b> and its subdomains via
+ In order to send mail for <b>foo.org</b> and its subdomains via
the <b>uucp</b> transport to the UUCP host named <b>foo</b>:
<b>foo.org</b> <b>uucp:foo</b>
<b>.foo.org</b> <b>uucp:foo</b>
- When no nexthop host name is specified, the destination
- domain name is used instead. For example, the following
- directs mail for <i>user</i>@<b>foo.org</b> via the <b>slow</b> transport to a
- mail exchanger for <b>foo.org</b>. The <b>slow</b> transport could be
- something that runs at most one delivery process at a
+ When no nexthop host name is specified, the destination
+ domain name is used instead. For example, the following
+ directs mail for <i>user</i>@<b>foo.org</b> via the <b>slow</b> transport to a
+ mail exchanger for <b>foo.org</b>. The <b>slow</b> transport could be
+ something that runs at most one delivery process at a
time:
<b>foo.org</b> <b>slow:</b>
When no transport is specified, Postfix uses the transport
that matches the address domain class (see TRANSPORT FIELD
- discussion above). The following sends all mail for
+ discussion above). The following sends all mail for
<b>foo.org</b> and its subdomains to host <b>gateway.foo.org</b>:
<b>foo.org</b> <b>:[gateway.foo.org]</b>
<b>.foo.org</b> <b>:[gateway.foo.org]</b>
- In the above example, the [] are used to suppress MX
- lookups. The result would likely point to your local
+ In the above example, the [] are used to suppress MX
+ lookups. The result would likely point to your local
machine.
- In the case of delivery via SMTP, one may specify <i>host-</i>
+ In the case of delivery via SMTP, one may specify <i>host-</i>
<i>name</i>:<i>service</i> instead of just a host:
<b>foo.org</b> <b>smtp:bar.org:2025</b>
- This directs mail for <i>user</i>@<b>foo.org</b> to host <b>bar.org</b> port
- <b>2025</b>. Instead of a numerical port a symbolic name may be
- used. Specify [] around the hostname in order to disable
+ This directs mail for <i>user</i>@<b>foo.org</b> to host <b>bar.org</b> port
+ <b>2025</b>. Instead of a numerical port a symbolic name may be
+ used. Specify [] around the hostname in order to disable
MX lookups.
The error mailer can be used to bounce mail:
- <b>.foo.org</b> <b>error:mail</b> <b>for</b> <b>*.foo.org</b> <b>is</b> <b>not</b> <b>deliv-</b>
+ <b>.foo.org</b> <b>error:mail</b> <b>for</b> <b>*.foo.org</b> <b>is</b> <b>not</b> <b>deliv-</b>
<b>erable</b>
- This causes all mail for <i>user</i>@<i>anything</i><b>.foo.org</b> to be
+ This causes all mail for <i>user</i>@<i>anything</i><b>.foo.org</b> to be
bounced.
<b>REGULAR</b> <b>EXPRESSION</b> <b>TABLES</b>
- This section describes how the table lookups change when
+ This section describes how the table lookups change when
the table is given in the form of regular expressions. For
- a description of regular expression lookup table syntax,
+ a description of regular expression lookup table syntax,
see <a href="regexp_table.5.html"><b>regexp</b><i>_</i><b>table</b>(5)</a> or <a href="pcre_table.5.html"><b>pcre</b><i>_</i><b>table</b>(5)</a>.
- Each pattern is a regular expression that is applied to
+ Each pattern is a regular expression that is applied to
the entire domain being looked up. Thus, <i>some.domain.hier-</i>
<i>archy</i> is not broken up into parent domains.
- Patterns are applied in the order as specified in the
- table, until a pattern is found that matches the search
+ Patterns are applied in the order as specified in the
+ table, until a pattern is found that matches the search
string.
- Results are the same as with indexed file lookups, with
- the additional feature that parenthesized substrings from
+ Results are the same as with indexed file lookups, with
+ the additional feature that parenthesized substrings from
the pattern can be interpolated as <b>$1</b>, <b>$2</b> and so on.
<b>CONFIGURATION</b> <b>PARAMETERS</b>
- The following <b>main.cf</b> parameters are especially relevant
- to this topic. See the Postfix <b>main.cf</b> file for syntax
- details and for default values. Use the <b>postfix</b> <b>reload</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this topic. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
command after a configuration change.
<b>empty</b><i>_</i><b>address</b><i>_</i><b>recipient</b>
- The address that is looked up instead of the null
+ The address that is looked up instead of the null
sender address.
<b>parent</b><i>_</i><b>domain</b><i>_</i><b>matches</b><i>_</i><b>subdomains</b>
- List of Postfix features that use <i>domain.tld</i> pat-
- terns to match <i>sub.domain.tld</i> (as opposed to
+ List of Postfix features that use <i>domain.tld</i> pat-
+ terns to match <i>sub.domain.tld</i> (as opposed to
requiring <i>.domain.tld</i> patterns).
<b>transport</b><i>_</i><b>maps</b>
<a href="regexp_table.5.html">regexp_table(5)</a> format of POSIX regular expression tables
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
Update the status of the specified address.
<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
- address. If the status is unknown, a probe is sent
- and a default status is returned.
+ Look up the <i>status</i>, <i>last</i> <i>update</i> <i>time</i> and <i>text</i> of
+ the specified address. If the status is unknown, a
+ probe is sent and a default status is returned.
The server reply status is one of:
man1/postmap.1 man1/sendmail.1 man1/mailq.1 man1/newaliases.1 \
man1/postqueue.1 man1/postsuper.1
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 \
man1/qmqp-source.1
man5/canonical.5: ../proto/canonical
../mantools/srctoman - $? >$@
+man5/cidr_table.5: ../proto/cidr_table
+ ../mantools/srctoman - $? >$@
+
man5/pcre_table.5: ../proto/pcre_table
../mantools/srctoman - $? >$@
man1/smtp-source.1: ../src/smtpstone/smtp-source.c
../mantools/srctoman $? >$@
+man5/tcp_table.5: ../proto/tcp_table
+ ../mantools/srctoman - $? >$@
+
man1/qmqp-sink.1: ../src/smtpstone/qmqp-sink.c
../mantools/srctoman $? >$@
.IP \fInet\fR
Matches any host address in the specified network. A network
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
.na
.nf
.SH SEE ALSO
.na
.nf
-postmap(1) create mapping table
+postmap(1) create lookup table
smtpd(8) smtp server
+cidr_table(5) format of CIDR tables
pcre_table(5) format of PCRE tables
regexp_table(5) format of POSIX regular expression tables
.SH LICENSE
--- /dev/null
+.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
.SH SYNOPSIS
.na
.nf
-\fBpcre:/etc/postfix/\fIfilename\fR
-
\fBpostmap -q "\fIstring\fB" pcre:/etc/postfix/\fIfilename\fR
\fBpostmap -q - pcre:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
The general form of a PCRE table is:
.IP "\fB/\fIpattern\fB/\fIflags result\fR"
-When \fIpattern\fR matches a search string, use the corresponding
-\fIresult\fR value.
+.IP "\fB!/\fIpattern\fB/\fIflags result\fR"
+When \fIpattern\fR matches (does not match) a search string, 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 `#'.
A logical line starts with non-whitespace text. A line that
starts with whitespace continues a logical line.
.IP "\fBif /\fIpattern\fB/\fIflags\fR"
+.IP "\fBif !/\fIpattern\fB/\fIflags\fR"
.IP "\fBendif\fR"
Examine the lines between \fBif\fR..\fBendif\fR only if
-\fIpattern\fR matches. The \fBif\fR..\fBendif\fR can nest.
-Do not prepend whitespace to patterns inside \fBif\fR..\fBendif\fR.
+\fIpattern\fR matches (does not match). The \fBif\fR..\fBendif\fR
+can nest. Do not prepend whitespace to patterns inside
+\fBif\fR..\fBendif\fR.
.PP
Each pattern is a perl-like regular expression. The expression
delimiter can be any character, except whitespace or characters
Substitution of substrings from the matched expression into the result
string is possible using the conventional perl syntax ($1, $2, etc.).
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
.na
.nf
.na
.nf
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)
.na
.nf
.SH SYNOPSIS
.na
.nf
-\fBregexp:/etc/postfix/\fIfilename\fR
-
\fBpostmap -q "\fIstring\fB" regexp:/etc/postfix/\fIfilename\fR
\fBpostmap -q - regexp:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
Substitution of substrings from the matched expression into the result
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
-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
.na
.nf
.na
.nf
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)
.na
.nf
--- /dev/null
+.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
addresses to message delivery transports and/or relay hosts. The
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
that serves as input to the \fBpostmap\fR(1) command.
The result, an indexed file in \fBdbm\fR or \fBdb\fR format, is used
.IP \fBcanonical_maps\fR
Address mapping lookup table for sender and recipient addresses
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
Address mapping lookup table for envelope and header recipient
addresses.
the destination host, sorts the list by preference, and connects
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
error will cause it to proceed to the next address on the mail
exchanger list. Instead, the message is either bounced, or its
Some SMTP servers misbehave on long lines.
.IP \fBsmtp_helo_name\fR
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
Skip servers that greet us with a 4xx status code.
.IP \fBsmtp_skip_5xx_greeting\fR
Maps that specify the SASL login name that owns a MAIL FROM sender
address. Used by the \fBreject_sender_login_mismatch\fR sender
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
.ad
.fi
Restrict what domains this mail system will relay
mail to. The domains are routed to the delivery agent
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"
.ad
.fi
.SH SEE ALSO
.na
.nf
-trivial-rewrite(8) address resolver
cleanup(8) message canonicalization
master(8) process manager
syslogd(8) system logging
+trivial-rewrite(8) address resolver
+verify(8) address verification service
.SH LICENSE
.na
.nf
.IP "\fBVRFY_ADDR_UPDATE\fI address status text\fR"
Update the status of the specified address.
.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
returned.
.PP
CONFIG = ../conf/access ../conf/aliases ../conf/canonical ../conf/relocated \
../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" }'
../conf/canonical: canonical
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
srctoman - $? | $(AWK) | nroff -man | col -bx | uniq | sed 's/^/# /' >$@
../conf/relocated: relocated
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
srctoman - $? | $(AWK) | nroff -man | col -bx | uniq | sed 's/^/# /' >$@
# .IP \fInet\fR
# Matches any host address in the specified network. A network
# 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
# .ad
# .fi
# BUGS
# The table format does not understand quoting conventions.
# SEE ALSO
-# postmap(1) create mapping table
+# postmap(1) create lookup table
# smtpd(8) smtp server
+# cidr_table(5) format of CIDR tables
# pcre_table(5) format of PCRE tables
# regexp_table(5) format of POSIX regular expression tables
# LICENSE
--- /dev/null
+#++
+# 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
+#--
# SUMMARY
# format of Postfix PCRE tables
# SYNOPSIS
-# \fBpcre:/etc/postfix/\fIfilename\fR
-#
# \fBpostmap -q "\fIstring\fB" pcre:/etc/postfix/\fIfilename\fR
#
# \fBpostmap -q - pcre:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
#
# The general form of a PCRE table is:
# .IP "\fB/\fIpattern\fB/\fIflags result\fR"
-# When \fIpattern\fR matches a search string, use the corresponding
-# \fIresult\fR value.
+# .IP "\fB!/\fIpattern\fB/\fIflags result\fR"
+# When \fIpattern\fR matches (does not match) a search string, 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 `#'.
# A logical line starts with non-whitespace text. A line that
# starts with whitespace continues a logical line.
# .IP "\fBif /\fIpattern\fB/\fIflags\fR"
+# .IP "\fBif !/\fIpattern\fB/\fIflags\fR"
# .IP "\fBendif\fR"
# Examine the lines between \fBif\fR..\fBendif\fR only if
-# \fIpattern\fR matches. The \fBif\fR..\fBendif\fR can nest.
-# Do not prepend whitespace to patterns inside \fBif\fR..\fBendif\fR.
+# \fIpattern\fR matches (does not match). The \fBif\fR..\fBendif\fR
+# can nest. Do not prepend whitespace to patterns inside
+# \fBif\fR..\fBendif\fR.
# .PP
# Each pattern is a perl-like regular expression. The expression
# delimiter can be any character, except whitespace or characters
# Substitution of substrings from the matched expression into the result
# string is possible using the conventional perl syntax ($1, $2, etc.).
# 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
# # Protect your outgoing majordomo exploders
# /^(?!owner-)(.*)-outgoing@(.*)/ 550 Use ${1}@${2} instead
# # Put your own body patterns here.
# SEE ALSO
# 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)
# The PCRE table lookup code was originally written by:
# Andrew McNamara
# SUMMARY
# format of Postfix regular expression tables
# SYNOPSIS
-# \fBregexp:/etc/postfix/\fIfilename\fR
-#
# \fBpostmap -q "\fIstring\fB" regexp:/etc/postfix/\fIfilename\fR
#
# \fBpostmap -q - regexp:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
# Substitution of substrings from the matched expression into the result
# 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
-# 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
# # Disallow sender-specified routing. This is a must if you relay mail
# # for other domains.
# # Put your own body patterns here.
# SEE ALSO
# 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)
# The regexp table lookup code was originally written by:
# LaMont Jones
--- /dev/null
+#++
+# 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
+#--*/
# addresses to message delivery transports and/or relay hosts. The
# 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
# that serves as input to the \fBpostmap\fR(1) command.
# The result, an indexed file in \fBdbm\fR or \fBdb\fR format, is used
bounce_notify_util.o: ../../include/vbuf.h
bounce_notify_util.o: ../../include/vstream.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/quote_822_local.h
bounce_notify_util.o: ../../include/quote_flags.h
#include <vstring.h>
#include <vstream.h>
#include <line_wrap.h>
+#include <stringops.h>
+#include <xtext.h>
/* Global library. */
}
bounce_info->flush = flush;
bounce_info->buf = vstring_alloc(100);
+ bounce_info->sender = vstring_alloc(100);
bounce_info->arrival_time = 0;
bounce_info->orig_offs = 0;
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
* ID and a hostname contain acceptable characters for a boundary string,
if (rec_type == REC_TYPE_TIME && bounce_info->arrival_time == 0) {
if ((bounce_info->arrival_time = atol(STR(bounce_info->buf))) < 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) {
+ /* 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);
break;
}
bounce_info->queue_id, bounce_info->queue_name,
bounce_info->queue_id);
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);
}
#if 0
post_mail_fprintf(bounce, "Received-From-MTA: dns; %s", "whatever");
#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)
post_mail_fprintf(bounce, "Arrival-Date: %s",
mail_date(bounce_info->arrival_time));
int bounce_recipient_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
{
- char *fixed_mail_name;
-
post_mail_fputs(bounce, "");
post_mail_fprintf(bounce, "Final-Recipient: rfc822; %s",
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",
- bounce_info->log_handle->orig_rcpt);
+ STR(bounce_info->buf));
+ }
post_mail_fprintf(bounce, "Action: %s",
bounce_info->flush == BOUNCE_MSG_FAIL ?
"failed" : bounce_info->log_handle->dsn_action);
post_mail_fprintf(bounce, "Status: %s",
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",
- fixed_mail_name, bounce_info->log_handle->text);
- myfree(fixed_mail_name);
+ bounce_info->mail_name, bounce_info->log_handle->text);
#if 0
post_mail_fprintf(bounce, "Last-Attempt-Date: %s",
bounce_info->log_handle->log_time);
const char *mime_boundary; /* for MIME */
int flush; /* 0=defer, other=bounce */
VSTRING *buf; /* scratch pad */
+ VSTRING *sender; /* envelope sender */
VSTREAM *orig_fp; /* open queue file */
long orig_offs; /* start of content */
time_t arrival_time; /* time of arrival */
BOUNCE_LOG *log_handle; /* open logfile */
+ char *mail_name; /* $mail_name, cooked */
} BOUNCE_INFO;
extern BOUNCE_INFO *bounce_mail_init(const char *, const char *, const char *, const char *, int);
/* .IP \fBcanonical_maps\fR
/* Address mapping lookup table for sender and recipient addresses
/* 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
/* Address mapping lookup table for envelope and header recipient
/* addresses.
*/
single_server_main(argc, argv, cleanup_service,
MAIL_SERVER_INT_TABLE, cleanup_int_table,
+ MAIL_SERVER_BOOL_TABLE, cleanup_bool_table,
MAIL_SERVER_STR_TABLE, cleanup_str_table,
MAIL_SERVER_TIME_TABLE, cleanup_time_table,
MAIL_SERVER_PRE_INIT, cleanup_pre_jail,
extern void cleanup_pre_jail(char *, char **);
extern void cleanup_post_jail(char *, char **);
extern CONFIG_INT_TABLE cleanup_int_table[];
+extern CONFIG_BOOL_TABLE cleanup_bool_table[];
extern CONFIG_STR_TABLE cleanup_str_table[];
extern CONFIG_TIME_TABLE cleanup_time_table[];
/*
/* CONFIG_INT_TABLE cleanup_int_table[];
/*
+/* CONFIG_BOOL_TABLE cleanup_bool_table[];
+/*
/* CONFIG_STR_TABLE cleanup_str_table[];
/*
/* CONFIG_TIME_TABLE cleanup_time_table[];
char *var_nesthdr_checks; /* nested header checks */
char *var_body_checks; /* any body checks */
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 */
int var_delay_warn_time; /* delay that triggers warning */
char *var_prop_extension; /* propagate unmatched extension */
0,
};
+CONFIG_BOOL_TABLE cleanup_bool_table[] = {
+ VAR_ENABLE_ORCPT, DEF_ENABLE_ORCPT, &var_enable_orcpt,
+ 0,
+};
+
CONFIG_TIME_TABLE cleanup_time_table[] = {
VAR_DELAY_WARN_TIME, DEF_DELAY_WARN_TIME, &var_delay_warn_time, 0, 0,
0,
ARGV *argv;
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
* onto the same mailbox. The recipient will use our original recipient
off_cvt quote_822_local rec2stream recdump resolve_clnt \
resolve_local rewrite_clnt stream2rec string_list tok822_parse \
quote_821_local mail_conf_time mime_state strip_addr \
- virtual8_maps verify_clnt
+ virtual8_maps verify_clnt xtext
LIBS = ../../lib/libutil.a
LIB_DIR = ../../lib
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
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 \
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_parse <tok822_parse.in >tok822_parse.tmp 2>&1
diff virtual8.ref virtual8.tmp
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
rewrite_clnt_test: rewrite_clnt rewrite_clnt.in rewrite_clnt.ref
virtual8_maps.o: virtual8_maps.h
xtext.o: xtext.c
xtext.o: ../../include/sys_defs.h
-xtext.o: ../../include/vstream.h
-xtext.o: ../../include/vbuf.h
+xtext.o: ../../include/msg.h
xtext.o: ../../include/vstring.h
+xtext.o: ../../include/vbuf.h
xtext.o: xtext.h
#define CLEANUP_STAT_CONT (1<<3) /* Message content rejected */
#define CLEANUP_STAT_HOPS (1<<4) /* Too many hops */
#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.
if (ch == '"')
break;
if (ch == '\n') { /* unfold */
- len = LEN(token_buffer);
- while (len > 0 && IS_SPACE_TAB_CR_LF(STR(token_buffer)[len - 1]))
- len--;
- if (len < LEN(token_buffer))
- vstring_truncate(token_buffer, len);
+ if (tok_count < token_len) {
+ len = LEN(token_buffer);
+ while (len > 0
+ && IS_SPACE_TAB_CR_LF(STR(token_buffer)[len - 1]))
+ len--;
+ if (len < LEN(token_buffer))
+ vstring_truncate(token_buffer, len);
+ }
continue;
}
if (ch == '\\') {
int delay = time((time_t *) 0) - entry;
vstring_vsprintf(why, fmt, ap);
- if (orig_rcpt == 0)
- orig_rcpt = "";
- if (strcasecmp(recipient, orig_rcpt) != 0)
+ if (orig_rcpt && *orig_rcpt && strcasecmp(recipient, orig_rcpt) != 0)
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));
else
static void check_mail_conf_int(const char *name, int intval, int min, int max)
{
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)
- 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 */
int len = strlen(strval);
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);
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);
}
if (flags & MAIL_COPY_ORIG_RCPT) {
if (orig_rcpt == 0)
msg_panic("%s: null orig_rcpt", myname);
- quote_822_local(buf, orig_rcpt);
- vstream_fprintf(dst, "X-Original-To: %s%s", vstring_str(buf), eol);
+
+ /*
+ * An empty original recipient record almost certainly means that
+ * original recipient processing was disabled.
+ */
+ if (*orig_rcpt) {
+ quote_822_local(buf, orig_rcpt);
+ vstream_fprintf(dst, "X-Original-To: %s%s", vstring_str(buf), eol);
+ }
}
if (flags & MAIL_COPY_DELIVERED) {
if (delivered == 0)
#define DEF_DELIVER_HDR "command, file, forward"
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 DEF_EXP_OWN_ALIAS 0
extern bool var_exp_own_alias;
#define DEF_SMTP_QUIT_TMOUT "300s"
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 DEF_SMTP_SKIP_4XX 1
extern bool var_smtp_skip_4xx_greeting;
#define DEF_VERIFY_SENDER "postmaster"
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 DEF_VRFY_LOCAL_XPORT "$" VAR_LOCAL_TRANSPORT
extern char *var_vrfy_local_xport;
#define DEF_OLDLOG_COMPAT 1
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
/* .ad
/* .fi
* Patches change the patchlevel and the release date. Snapshots change the
* 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 DEF_MAIL_VERSION "2.0.12-" MAIL_RELEASE_DATE
+#define DEF_MAIL_VERSION "2.0.13-" MAIL_RELEASE_DATE
extern char *var_mail_version;
/*
/* .IP RESOLVE_CLASS_LOCAL
/* The address domain matches $mydestination or $inet_interfaces.
/* .IP RESOLVE_CLASS_ALIAS
-/* The address domain matches $virtual_alias_domains (simulated
-/* virtual domains, where each address is redirected to a real
+/* The address domain matches $virtual_alias_domains (virtual
+/* alias domains, where each address is redirected to a real
/* local or remote address).
/* .IP RESOLVE_CLASS_VIRTUAL
/* The address domain matches $virtual_mailbox_domains (true
-#ifndef _VRFY_STAT_H_INCLUDED_
-#define _VRFY_STAT_H_INCLUDED_
+#ifndef _VRFY_CLNT_H_INCLUDED_
+#define _VRFY_CLNT_H_INCLUDED_
/*++
/* NAME
-/* mail_proto 3h
+/* verify_clnt 3h
/* SUMMARY
-/* mail internal IPC support
+/* address verification client interface
/* SYNOPSIS
-/* #include <mail_proto.h>
+/* #include <verify_clnt.h>
/* DESCRIPTION
/* .nf
/* NAME
/* xtext 3
/* SUMMARY
-/* translate characters according to RFC 1894
+/* quote/unquote text, HTTP style.
/* SYNOPSIS
/* #include <xtext.h>
/*
-/* VSTRING *xtext(result, original)
-/* VSTRING *result;
-/* const char *original;
+/* VSTRING *xtext_quote(quoted, unquoted, special)
+/* VSTRING *quoted;
+/* const char *unquoted;
+/* const char *special;
+/*
+/* VSTRING *xtext_unquote(unquoted, quoted)
+/* VSTRING *unquoted;
+/* const char *quoted;
/* DESCRIPTION
-/* xtext() takes a null-terminated string, and produces a translation
-/* according to RFC 1894 (DSN).
-/* BUGS
-/* Cannot replace null characters.
+/* 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.
/*
-/* Does not insert CR LF SPACE to limit output line length.
-/* SEE ALSO
-/* RFC 1894, Delivery Status Notifications
+/* 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
/* .ad
/* .fi
/* System library. */
-#include "sys_defs.h"
-#include <vstream.h>
+#include <sys_defs.h>
+#include <string.h>
+#include <ctype.h>
/* 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;
int ch;
- /*
- * Preliminary implementation. ASCII specific!!
- */
- VSTRING_RESET(result);
- for (cp = original; (ch = *(unsigned char *) cp) != 0; cp++) {
- if (ch == '+' || ch == '\\' || ch == '(' || ch < 33 || ch > 126)
- vstring_sprintf_append(result, "+%02X", ch);
- else
- VSTRING_ADDCH(result, ch);
+ VSTRING_RESET(quoted);
+ for (cp = unquoted; (ch = *(unsigned const char *) cp) != 0; cp++) {
+ if (ch != '+' && ch > 32 && ch < 127 && strchr(special, ch) == 0) {
+ VSTRING_ADDCH(quoted, ch);
+ } else {
+ vstring_sprintf_append(quoted, "+%02X", ch);
+ }
}
- VSTRING_TERMINATE(result);
+ VSTRING_TERMINATE(quoted);
+ return (quoted);
+}
+
+/* xtext_unquote - quoted data to unquoted */
+
+VSTRING *xtext_unquote(VSTRING *unquoted, const char *quoted)
+{
+ const char *cp;
+ int ch;
- return (result);
+ 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
-#define STR(x) vstring_str(x)
-
+ /*
+ * Proof-of-concept test program: convert to quoted and back.
+ */
#include <vstream.h>
-int main(int unused_argc, char **unused_argv)
+#define BUFLEN 1024
+
+static int read_buf(VSTREAM *fp, VSTRING *buf)
{
- VSTRING *ibuf = vstring_alloc(100);
- VSTRING *obuf = vstring_alloc(100);
+ 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);
+}
- while (vstring_fgets(ibuf, VSTREAM_IN)) {
- vstream_fputs(STR(xtext(obuf, STR(ibuf))));
- vstream_fflush(VSTREAM_OUT);
+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);
- vstring_free(obuf);
+ vstream_fflush(VSTREAM_OUT);
+ vstring_free(unquoted);
+ vstring_free(quoted);
return (0);
}
+#ifndef _XTEXT_H_INCLUDED_
+#define _XTEXT_H_INCLUDED_
+
/*++
/* NAME
/* xtext 3h
/* SUMMARY
-/* translate characters according to RFC 1894
+/* quote/unquote text, xtext style.
/* SYNOPSIS
/* #include <xtext.h>
/* DESCRIPTION
/*
* Utility library.
- */
+ */
#include <vstring.h>
/*
* 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
/* .ad
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
+
+#endif
if (message->rcpt_offset == 0) {
message->rcpt_unread--;
qmgr_rcpt_list_add(&message->rcpt_list, curr_offset,
- orig_rcpt ? orig_rcpt : "unknown", start);
+ orig_rcpt ? orig_rcpt : "", start);
if (orig_rcpt) {
myfree(orig_rcpt);
orig_rcpt = 0;
* See if this is a plausible file.
*/
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));
return;
}
#define FUDGE(x) ((x) * (var_qmgr_fudge / 100.0))
if (message->rcpt_offset == 0) {
qmgr_rcpt_list_add(&message->rcpt_list, curr_offset,
- orig_rcpt ? orig_rcpt : "unknown", start);
+ orig_rcpt ? orig_rcpt : "", start);
if (orig_rcpt) {
myfree(orig_rcpt);
orig_rcpt = 0;
/* showq_report - report status of sender and recipients */
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 *printable_quoted_addr = vstring_alloc(100);
printable(STR(printable_quoted_addr), '?');
vstream_fprintf(client, DATA_FORMAT, id, status,
msg_size > 0 ? msg_size : size, arrival_time > 0 ?
- asctime(localtime(&arrival_time)) : "??",
+ asctime(localtime(&arrival_time)) :
+ asctime(localtime(&mtime)),
STR(printable_quoted_addr));
break;
case REC_TYPE_RCPT:
vstream_fprintf(client, "\n");
if ((qfile = mail_queue_open(qp->name, id, O_RDONLY, 0)) != 0) {
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))
msg_warn("close file %s %s: %m", qp->name, id);
} else if (strcmp(qp->name, MAIL_QUEUE_MAILDROP) == 0) {
/* the destination host, sorts the list by preference, and connects
/* 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
/* error will cause it to proceed to the next address on the mail
/* exchanger list. Instead, the message is either bounced, or its
/* Some SMTP servers misbehave on long lines.
/* .IP \fBsmtp_helo_name\fR
/* 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
/* Skip servers that greet us with a 4xx status code.
/* .IP \fBsmtp_skip_5xx_greeting\fR
int var_smtp_line_limit;
char *var_smtp_helo_name;
char *var_smtp_host_lookup;
+int var_smtp_quote_821_env;
/*
* Global variables. smtp_errno is set by the address lookup routines and by
VAR_SMTP_NEVER_EHLO, DEF_SMTP_NEVER_EHLO, &var_smtp_never_ehlo,
VAR_SMTP_SASL_ENABLE, DEF_SMTP_SASL_ENABLE, &var_smtp_sasl_enable,
VAR_SMTP_RAND_ADDR, DEF_SMTP_RAND_ADDR, &var_smtp_rand_addr,
+ VAR_SMTP_QUOTE_821_ENV, DEF_SMTP_QUOTE_821_ENV, &var_smtp_quote_821_env,
0,
};
* Macros for readability.
*/
#define REWRITE_ADDRESS(dst, mid, src) do { \
- if (*(src)) { \
+ if (*(src) && var_smtp_quote_821_env) { \
quote_821_local(mid, src); \
smtp_unalias_addr(dst, vstring_str(mid)); \
} else { \
} while (0)
#define QUOTE_ADDRESS(dst, src) do { \
- if (*(src)) { \
+ if (*(src) && var_smtp_quote_821_env) { \
quote_821_local(dst, src); \
} else { \
vstring_strcpy(dst, src); \
if (resp->code == 552)
resp->code = 452;
#endif
+ rcpt = request->rcpt_list.info + recv_rcpt;
if (resp->code / 100 == 2) {
++nrcpt;
/* If trace-only, mark the recipient done. */
rcpt->offset = 0; /* in case deferred */
}
} else {
- rcpt = request->rcpt_list.info + recv_rcpt;
smtp_rcpt_fail(state, resp->code, rcpt,
"host %s said: %s (in reply to %s)",
session->namaddr,
SHELL = /bin/sh
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 \
- 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 \
- smtpd_sasl_glue.h
+ smtpd_sasl_glue.h smtpd_proxy.h
TESTSRC = smtpd_token_test.c
WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
-Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
smtpd.o: smtpd_chat.h
smtpd.o: smtpd_sasl_proto.h
smtpd.o: smtpd_sasl_glue.h
+smtpd.o: smtpd_proxy.h
smtpd_chat.o: smtpd_chat.c
smtpd_chat.o: ../../include/sys_defs.h
smtpd_chat.o: ../../include/msg.h
smtpd_peer.o: ../../include/vstream.h
smtpd_peer.o: ../../include/argv.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: ../../include/sys_defs.h
smtpd_sasl_glue.o: ../../include/msg.h
/* Maps that specify the SASL login name that owns a MAIL FROM sender
/* address. Used by the \fBreject_sender_login_mismatch\fR sender
/* 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
/* .ad
/* .fi
/* Restrict what domains this mail system will relay
/* mail to. The domains are routed to the delivery agent
/* 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"
/* .ad
/* .fi
/* .IP \fBunverified_recipient_reject_code\fR
/* Response code when a recipient address is known to be undeliverable.
/* SEE ALSO
-/* trivial-rewrite(8) address resolver
/* cleanup(8) message canonicalization
/* master(8) process manager
/* syslogd(8) system logging
+/* trivial-rewrite(8) address resolver
+/* verify(8) address verification service
/* LICENSE
/* .ad
/* .fi
/* Application-specific */
-#include "smtpd_token.h"
-#include "smtpd.h"
-#include "smtpd_check.h"
-#include "smtpd_chat.h"
-#include "smtpd_sasl_proto.h"
-#include "smtpd_sasl_glue.h"
+#include <smtpd_token.h>
+#include <smtpd.h>
+#include <smtpd_check.h>
+#include <smtpd_chat.h>
+#include <smtpd_sasl_proto.h>
+#include <smtpd_sasl_glue.h>
+#include <smtpd_proxy.h>
/*
* Tunable parameters. Make sure that there is some bound on the length of
int var_relay_rcpt_code;
char *var_verp_clients;
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.
smtpd_chat_reply(state, "503 Error: send HELO/EHLO first");
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;
smtpd_chat_reply(state, "503 Error: nested MAIL command");
return (-1);
smtpd_chat_reply(state, "%s", err);
return (-1);
}
- if ((err = smtpd_check_size(state, state->msg_size)) != 0) {
- smtpd_chat_reply(state, "%s", err);
- 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) {
+ smtpd_chat_reply(state, "%s", err);
+ return (-1);
+ }
- /*
- * Open queue file or IPC stream.
- */
- mail_open_stream(state);
+ /*
+ * Open queue file or IPC stream.
+ */
+ mail_open_stream(state);
#ifdef USE_SASL_AUTH
- if (var_smtpd_sasl_enable)
- smtpd_sasl_mail_log(state);
- else
+ if (var_smtpd_sasl_enable)
+ smtpd_sasl_mail_log(state);
+ else
#endif
- msg_info("%s: client=%s[%s]", state->queue_id, state->name, state->addr);
-
- /*
- * Record the time of arrival and the sender envelope address.
- */
- if (SMTPD_STAND_ALONE(state) == 0) {
- rec_fprintf(state->cleanup, REC_TYPE_TIME, "%ld",
- (long) time((time_t *) 0));
- if (*var_filter_xport)
- rec_fprintf(state->cleanup, REC_TYPE_FILT, "%s", var_filter_xport);
- }
- rec_fputs(state->cleanup, REC_TYPE_FROM, argv[2].strval);
- if (encoding != 0)
- rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
- MAIL_ATTR_ENCODING, encoding);
- if (SMTPD_STAND_ALONE(state) == 0) {
- rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
- MAIL_ATTR_CLIENT_NAME, state->name);
- rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
- MAIL_ATTR_CLIENT_ADDR, state->addr);
- rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
- MAIL_ATTR_ORIGIN, state->namaddr);
- if (state->helo_name != 0)
+ msg_info("%s: client=%s[%s]", state->queue_id, state->name, state->addr);
+
+ /*
+ * Record the time of arrival and the sender envelope address.
+ */
+ if (SMTPD_STAND_ALONE(state) == 0) {
+ rec_fprintf(state->cleanup, REC_TYPE_TIME, "%ld",
+ (long) time((time_t *) 0));
+ if (*var_filter_xport)
+ rec_fprintf(state->cleanup, REC_TYPE_FILT, "%s", var_filter_xport);
+ }
+ rec_fputs(state->cleanup, REC_TYPE_FROM, argv[2].strval);
+ if (encoding != 0)
+ rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
+ MAIL_ATTR_ENCODING, encoding);
+ if (SMTPD_STAND_ALONE(state) == 0) {
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
- MAIL_ATTR_HELO_NAME, state->helo_name);
- rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
- MAIL_ATTR_PROTO_NAME, state->protocol);
+ MAIL_ATTR_CLIENT_NAME, state->name);
+ rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
+ MAIL_ATTR_CLIENT_ADDR, state->addr);
+ rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
+ MAIL_ATTR_ORIGIN, state->namaddr);
+ if (state->helo_name != 0)
+ rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
+ MAIL_ATTR_HELO_NAME, state->helo_name);
+ rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
+ MAIL_ATTR_PROTO_NAME, state->protocol);
+ }
+ if (verp_delims)
+ rec_fputs(state->cleanup, REC_TYPE_VERP, verp_delims);
}
- if (verp_delims)
- rec_fputs(state->cleanup, REC_TYPE_VERP, verp_delims);
state->sender = mystrdup(argv[2].strval);
smtpd_chat_reply(state, "250 Ok");
return (0);
smtpd_sasl_mail_reset(state);
#endif
state->discard = 0;
+ if (state->proxy)
+ smtpd_proxy_close(state);
}
/* rcpt_cmd - process RCPT TO command */
* command with a 501 response. So much for the principle of "be liberal
* 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;
smtpd_chat_reply(state, "503 Error: need MAIL command");
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.
state->rcpt_count++;
if (state->recipient == 0)
state->recipient = mystrdup(argv[2].strval);
- rec_fputs(state->cleanup, REC_TYPE_RCPT, argv[2].strval);
+ if (state->cleanup)
+ rec_fputs(state->cleanup, REC_TYPE_RCPT, argv[2].strval);
smtpd_chat_reply(state, "250 Ok");
return (0);
}
int first = 1;
VSTRING *why = 0;
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
* error.
*/
if (state->rcpt_count == 0) {
- if (state->cleanup == 0) {
+ if (!IN_MAIL_TRANSACTION(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 Error: need RCPT command");
} else {
smtpd_chat_reply(state, "%s", err);
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
* segment, and prepend our own Received: header. If there is only one
* recipient, list the recipient address.
*/
- rec_fputs(state->cleanup, REC_TYPE_MESG, "");
- rec_fprintf(state->cleanup, REC_TYPE_NORM,
+ if (state->cleanup)
+ rec_fputs(state->cleanup, REC_TYPE_MESG, "");
+ out_fprintf(out_stream, REC_TYPE_NORM,
"Received: from %s (%s [%s])",
state->helo_name ? state->helo_name : state->name,
state->name, state->addr);
if (state->rcpt_count == 1 && state->recipient) {
- rec_fprintf(state->cleanup, REC_TYPE_NORM,
- "\tby %s (%s) with %s id %s",
+ out_fprintf(out_stream, REC_TYPE_NORM,
+ state->cleanup ? "\tby %s (%s) with %s id %s" :
+ "\tby %s (%s) with %s",
var_myhostname, var_mail_name,
state->protocol, state->queue_id);
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));
} else {
- rec_fprintf(state->cleanup, REC_TYPE_NORM,
- "\tby %s (%s) with %s",
- var_myhostname, var_mail_name, state->protocol);
- rec_fprintf(state->cleanup, REC_TYPE_NORM,
- "\tid %s; %s", state->queue_id, mail_date(state->time));
+ out_fprintf(out_stream, REC_TYPE_NORM,
+ state->cleanup ? "\tby %s (%s) with %s id %s;" :
+ "\tby %s (%s) with %s;",
+ var_myhostname, var_mail_name,
+ state->protocol, state->queue_id);
+ out_fprintf(out_stream, REC_TYPE_NORM,
+ "\t%s", mail_date(state->time));
}
#ifdef RECEIVED_ENVELOPE_FROM
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));
#endif
smtpd_chat_reply(state, "354 End data with <CR><LF>.<CR><LF>");
* XXX Deal with UNIX-style From_ lines at the start of message content
* 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) {
if (smtp_get(state->buffer, state->client, var_line_limit) == '\n')
curr_rec_type = REC_TYPE_NORM;
len = VSTRING_LEN(state->buffer);
if (first) {
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);
continue;
}
first = 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
- && *start == '.' && (++start, --len) == 0)
+ if (prev_rec_type != REC_TYPE_CONT && *start == '.'
+ && (state->proxy == 0 ? (++start, --len) == 0 : len == 1))
break;
if (state->err == CLEANUP_STAT_OK
- && rec_put(state->cleanup, curr_rec_type, start, len) < 0)
- state->err = CLEANUP_STAT_WRITE;
+ && out_record(out_stream, curr_rec_type, start, len) < 0)
+ state->err = out_error;
}
/*
* Send the end-of-segment markers.
*/
- if (state->err == CLEANUP_STAT_OK)
- if (rec_fputs(state->cleanup, REC_TYPE_XTRA, "") < 0
- || rec_fputs(state->cleanup, REC_TYPE_END, "") < 0
- || vstream_fflush(state->cleanup))
- state->err = CLEANUP_STAT_WRITE;
-
- /*
- * Finish the queue file or finish the cleanup conversation.
- */
- if (state->err == 0)
- state->err = mail_stream_finish(state->dest, why = vstring_alloc(10));
- else
- mail_stream_cleanup(state->dest);
- state->dest = 0;
- state->cleanup = 0;
+ 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 (rec_fputs(state->cleanup, REC_TYPE_XTRA, "") < 0
+ || rec_fputs(state->cleanup, REC_TYPE_END, "") < 0
+ || vstream_fflush(state->cleanup))
+ state->err = CLEANUP_STAT_WRITE;
+
+ /*
+ * Finish the queue file or finish the cleanup conversation.
+ */
+ if (state->err == 0)
+ state->err = mail_stream_finish(state->dest, why = vstring_alloc(10));
+ else
+ mail_stream_cleanup(state->dest);
+ state->dest = 0;
+ state->cleanup = 0;
+ }
/*
* Handle any errors. One message may suffer from multiple errors, so
state->error_count = 0;
state->error_mask = 0;
state->junk_cmds = 0;
- smtpd_chat_reply(state, "250 Ok: queued as %s", state->queue_id);
+ if (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) {
state->error_mask |= MAIL_ERROR_SOFTWARE;
smtpd_chat_reply(state, "451 Error: internal error %d", state->err);
} else if ((state->err & CLEANUP_STAT_WRITE) != 0) {
state->error_mask |= MAIL_ERROR_RESOURCE;
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 {
state->error_mask |= MAIL_ERROR_SOFTWARE;
smtpd_chat_reply(state, "451 Error: internal error %d", state->err);
smtpd_chat_reply(state, "503 Error: send HELO/EHLO first");
return (-1);
}
- if (state->cleanup != 0) {
+ if (IN_MAIL_TRANSACTION(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 Error: MAIL transaction in progress");
return (-1);
} SMTPD_CMD;
#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[] = {
"HELO", helo_cmd, SMTPD_CMD_FLAG_LIMIT,
"VRFY", vrfy_cmd, SMTPD_CMD_FLAG_LIMIT,
"ETRN", etrn_cmd, SMTPD_CMD_FLAG_LIMIT,
"QUIT", quit_cmd, 0,
- "Received:", 0, SMTPD_CMD_FLAG_HEADER,
- "Reply-To:", 0, SMTPD_CMD_FLAG_HEADER,
- "Message-ID:", 0, SMTPD_CMD_FLAG_HEADER,
- "Subject:", 0, SMTPD_CMD_FLAG_HEADER,
- "From:", 0, SMTPD_CMD_FLAG_HEADER,
+ "Received:", 0, SMTPD_CMD_FLAG_FORBIDDEN,
+ "Reply-To:", 0, SMTPD_CMD_FLAG_FORBIDDEN,
+ "Message-ID:", 0, SMTPD_CMD_FLAG_FORBIDDEN,
+ "Subject:", 0, SMTPD_CMD_FLAG_FORBIDDEN,
+ "From:", 0, SMTPD_CMD_FLAG_FORBIDDEN,
+ "CONNECT", 0, SMTPD_CMD_FLAG_FORBIDDEN,
+ "User-Agent:", 0, SMTPD_CMD_FLAG_FORBIDDEN,
0,
};
state->error_count++;
continue;
}
- if (cmdp->flags & SMTPD_CMD_FLAG_HEADER) {
- msg_warn("%s sent %s header instead of SMTP command: %.100s",
+ if (cmdp->flags & SMTPD_CMD_FLAG_FORBIDDEN) {
+ msg_warn("%s sent %s instead of SMTP command: %.100s",
state->namaddr, cmdp->name, vstring_str(state->buffer));
smtpd_chat_reply(state, "221 Error: I can break rules, too. Goodbye.");
break;
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_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,
};
static CONFIG_TIME_TABLE time_table[] = {
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_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,
};
static CONFIG_BOOL_TABLE bool_table[] = {
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_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,
};
static CONFIG_RAW_TABLE raw_table[] = {
int defer_if_permit_sender; /* force permit into warning */
int discard; /* discard message */
VSTRING *expand_buf; /* scratch space for $name expansion */
+ VSTREAM *proxy; /* proxy handle */
+ VSTRING *proxy_buffer; /* proxy query/reply buffer */
} SMTPD_STATE;
extern void smtpd_state_init(SMTPD_STATE *, VSTREAM *);
if (once == 0) {
once = 1;
- msg_warn("the \"%s\" restriction is going away; use \"%s\" instead",
- CHECK_RELAY_DOMAINS, REJECT_UNAUTH_DEST);
+ msg_warn("support for restriction \"%s\" will be removed from %s; "
+ "use \"%s\" instead",
+ CHECK_RELAY_DOMAINS, var_mail_name, REJECT_UNAUTH_DEST);
}
#endif
/*
* 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);
if (verify_status != VRFY_STAT_OK || rcpt_status != DEL_RCPT_STAT_TODO)
break;
- if (count >= 2)
+ if (++count >= var_verify_poll_count)
break;
- sleep(3);
+ sleep(var_verify_poll_delay);
}
if (verify_status != VRFY_STAT_OK) {
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",
reply_name, reply_class);
rqst_status = SMTPD_CHECK_DUNNO;
if (warned == 0) {
warned++;
- msg_warn("restriction %s is going away. Please use %s <domain> instead",
- REJECT_MAPS_RBL, REJECT_RBL_CLIENT);
+ msg_warn("support for restriction \"%s\" will be removed from %s; "
+ "use \"%s <domain-name>\" instead",
+ REJECT_MAPS_RBL, var_mail_name, REJECT_RBL_CLIENT);
}
while ((rbl_domain = mystrtok(&bp, " \t\r\n,")) != 0) {
result = reject_rbl_addr(state, rbl_domain, state->addr,
int saved_recursion = state->recursion++;
if (msg_verbose)
- msg_info("%s: START", myname);
+ msg_info(">>> START %s RESTRICTIONS <<<", reply_class);
for (cpp = restrictions->argv; (name = *cpp) != 0; cpp++) {
break;
}
if (msg_verbose && name == 0)
- msg_info("%s: END", myname);
+ msg_info(">>> END %s RESTRICTIONS <<<", reply_class);
state->recursion = saved_recursion;
return (0);
state->rcptmap_checked = 1;
+ if (msg_verbose)
+ msg_info(">>> CHECKING RECIPIENT MAPS <<<");
+
/*
* Resolve the address.
*/
int var_virt_mailbox_code;
int var_virt_alias_code;
int var_show_unk_rcpt_table;
+int var_verify_poll_count;
+int var_verify_poll_delay;
static INT_TABLE int_table[] = {
"msg_verbose", 0, &msg_verbose,
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_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,
};
#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 */
VSTRING *canon_addr_internal(VSTRING *result, const char *addr)
/* 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;
--- /dev/null
+/*++
+/* 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;
+}
--- /dev/null
+/*++
+/* 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
+/*--*/
state->defer_if_permit.reason = 0;
state->discard = 0;
state->expand_buf = 0;
+ state->proxy = 0;
+ state->proxy_buffer = 0;
#ifdef USE_SASL_AUTH
if (SMTPD_STAND_ALONE(state))
vstring_free(state->defer_if_reject.reason);
if (state->expand_buf)
vstring_free(state->expand_buf);
+ if (state->proxy_buffer)
+ vstring_free(state->proxy_buffer);
#ifdef USE_SASL_AUTH
if (var_smtpd_sasl_enable)
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 \
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_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 \
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 \
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_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 \
write_buf.o write_wait.o $(STRCASE)
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 \
- 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_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 \
tests: valid_hostname_test mac_expand_test dict_test unescape_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 <valid_hostname.in 2>valid_hostname.tmp
diff dict_regexp.ref 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 <host_port.in >host_port.tmp 2>&1
diff host_port.ref host_port.tmp
dict_alloc.o: vstream.h
dict_alloc.o: vbuf.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: sys_defs.h
dict_db.o: msg.h
dict_open.o: dict_pcre.h
dict_open.o: dict_regexp.h
dict_open.o: dict_static.h
+dict_open.o: dict_cidr.h
dict_open.o: stringops.h
dict_open.o: vstring.h
dict_open.o: split_at.h
--- /dev/null
+/*++
+/* 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));
+}
--- /dev/null
+#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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+./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
/* If you must bind to the server, do it with this distinguished name ...
/* .IP \fIldapsource_\fRbind_pw
/* \&... and this password.
-/* .IP \fIldapsource_\fRcache
+/* .IP \fIldapsource_\fRcache (no longer supported)
/* 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.
-/* .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.
+/* .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
/* How to handle LDAP aliases. See ldap.h or ldap_open(3) man page.
/* .IP \fIldapsource_\fRdebuglevel
char *bind_dn;
char *bind_pw;
int timeout;
- int cache;
- long cache_expiry;
- long cache_size;
int dereference;
+ long recursion_limit;
+ long expansion_limit;
+ long size_limit;
int chase_referrals;
int debuglevel;
int version;
char *myname = "dict_ldap_connect";
int rc = 0;
-#ifdef LDAP_API_FEATURE_X_MEMCACHE
- LDAPMemCache *dircache;
-
-#endif
-
#ifdef LDAP_OPT_NETWORK_TIMEOUT
struct timeval mytimeval;
}
#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
* Mattice for this, and to Hery Rakotoarisoa for the v3 update.
msg_info("%s: Successful bind to server %s as %s ",
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)
msg_info("%s: Cached connection handle for LDAP source %s",
myname, dict_ldap->ldapsource);
/*
* 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 *sub,
vstring_strcat(out, u);
break;
default:
- msg_warn
- ("%s: Invalid filter substitution format '%%%c'!",
- myname, *(sub + 1));
+ msg_warn("%s: %s: Invalid filter substitution format '%%%c'!",
+ myname, ldapsource, *(sub + 1));
/* fall through */
case 's':
vstring_strcat(out, u);
static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
VSTRING *result)
{
+ static int recursion = 0;
+ static int expansion;
+ long entries = 0;
long i = 0;
int rc = 0;
LDAPMessage *resloop = 0;
tv.tv_sec = dict_ldap->timeout;
tv.tv_usec = 0;
+ if (++recursion == 1)
+ expansion = 0;
+
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));
for (entry = ldap_first_entry(dict_ldap->ld, res); entry != NULL;
entry = ldap_next_entry(dict_ldap->ld, entry)) {
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);
attr != NULL;
ldap_memfree(attr), attr = ldap_next_attribute(dict_ldap->ld,
vals = ldap_get_values(dict_ldap->ld, entry, attr);
if (vals == NULL) {
if (msg_verbose)
- msg_info("%s: Entry doesn't have any values for %s",
- myname, attr);
+ msg_info("%s[%d]: Entry doesn't have any values for %s",
+ myname, recursion, attr);
+ continue;
+ }
+
+ /*
+ * If we previously encountered an error, we still continue
+ * through the loop, to avoid memory leaks, but we don't waste
+ * time accumulating any further results.
+ *
+ * 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) {
- if (msg_verbose)
- msg_info("%s: search returned %ld value(s) for requested result attribute %s", myname, i, attr);
+ if (strcasecmp(dict_ldap->result_attributes->argv[i], attr) == 0)
break;
- }
}
/*
* recursing (for dn or url attributes).
*/
if (i < dict_ldap->num_attributes) {
+ /* Ordinary result attribute */
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)
vstring_strcat(result, ",");
if (dict_ldap->result_filter == NULL)
vstring_strcat(result, vals[i]);
else
- dict_ldap_expand_filter(dict_ldap->result_filter,
+ dict_ldap_expand_filter(dict_ldap->ldapsource,
+ dict_ldap->result_filter,
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++) {
if (ldap_is_ldap_url(vals[i])) {
if (msg_verbose)
- msg_info("%s: looking up URL %s", myname,
- vals[i]);
+ msg_info("%s[%d]: looking up URL %s", myname,
+ recursion, vals[i]);
rc = ldap_url_parse(vals[i], &url);
if (rc == 0) {
rc = ldap_search_st(dict_ldap->ld, url->lud_dn,
}
} else {
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],
LDAP_SCOPE_BASE, "objectclass=*",
dict_ldap->result_attributes->argv,
* Go ahead and treat this as though the DN existed
* and just didn't have any result attributes.
*/
- msg_warn("%s: DN %s not found, skipping ", myname,
- vals[i]);
+ msg_warn("%s[%d]: DN %s not found, skipping ", myname,
+ recursion, vals[i]);
break;
default:
- msg_warn("%s: search error %d: %s ", myname, rc,
- ldap_err2string(rc));
+ msg_warn("%s[%d]: search error %d: %s ", myname,
+ recursion, rc, ldap_err2string(rc));
dict_errno = DICT_ERR_RETRY;
break;
}
if (resloop != 0)
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);
}
if (ber)
ber_free(ber, 0);
}
+
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 */
/*
* No, log the fact and continue.
*/
- msg_warn("%s: Fixed query_filter %s is probably useless", myname,
- dict_ldap->query_filter);
+ msg_warn("%s: %s: Fixed query_filter %s is probably useless",
+ myname, dict_ldap->ldapsource, dict_ldap->query_filter);
vstring_strcpy(filter_buf, dict_ldap->query_filter);
} 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);
}
char *domainlist;
char *scope;
char *attr;
+ int tmp;
if (msg_verbose)
msg_info("%s: Using LDAP source %s", myname, ldapsource);
* get configured value of "ldapsource_cache"; default to false
*/
vstring_sprintf(config_param, "%s_cache", ldapsource);
- dict_ldap->cache = get_mail_conf_bool(vstring_str(config_param), 0);
- if (msg_verbose)
- msg_info("%s: %s is %d", myname, vstring_str(config_param),
- dict_ldap->cache);
+ tmp = get_mail_conf_bool(vstring_str(config_param), 0);
+ if (tmp)
+ msg_warn("%s: ignoring %s", myname, vstring_str(config_param));
/*
* get configured value of "ldapsource_cache_expiry"; default to 30
* seconds
*/
vstring_sprintf(config_param, "%s_cache_expiry", ldapsource);
- dict_ldap->cache_expiry = get_mail_conf_int(vstring_str(config_param),
- 30, 0, 0);
- if (msg_verbose)
- msg_info("%s: %s is %ld", myname, vstring_str(config_param),
- dict_ldap->cache_expiry);
+ tmp = get_mail_conf_int(vstring_str(config_param), -1, 0, 0);
+ if (tmp >= 0)
+ msg_warn("%s: ignoring %s", myname, vstring_str(config_param));
/*
* get configured value of "ldapsource_cache_size"; default to 32k
*/
vstring_sprintf(config_param, "%s_cache_size", ldapsource);
- dict_ldap->cache_size = get_mail_conf_int(vstring_str(config_param),
- 32768, 0, 0);
+ tmp = get_mail_conf_int(vstring_str(config_param), -1, 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)
+ msg_info("%s: %s is %ld", myname, vstring_str(config_param),
+ 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->cache_size);
+ dict_ldap->size_limit);
/*
* Alias dereferencing suggested by Mike Mattice.
#include <dict_pcre.h>
#include <dict_regexp.h>
#include <dict_static.h>
+#include <dict_cidr.h>
#include <stringops.h>
#include <split_at.h>
#include <htable.h>
static DICT_OPEN_INFO dict_open_info[] = {
DICT_TYPE_ENVIRON, dict_env_open,
DICT_TYPE_UNIX, dict_unix_open,
-#if 0
DICT_TYPE_TCP, dict_tcp_open,
-#endif
#ifdef HAS_DBM
DICT_TYPE_DBM, dict_dbm_open,
#endif
DICT_TYPE_REGEXP, dict_regexp_open,
#endif
DICT_TYPE_STATIC, dict_static_open,
+ DICT_TYPE_CIDR, dict_cidr_open,
0,
};
typedef struct {
char *regexp; /* regular expression */
int options; /* options */
+ int match; /* positive or negative match */
} DICT_PCRE_REGEXP;
typedef struct {
pcre *pattern; /* compiled pattern */
pcre_extra *hints; /* hints to speed pattern execution */
char *replacement; /* replacement string */
+ int match; /* positive or negative match */
} DICT_PCRE_MATCH_RULE;
typedef struct {
DICT_PCRE_RULE rule; /* generic members */
pcre *pattern; /* compiled pattern */
pcre_extra *hints; /* hints to speed pattern execution */
+ int match; /* positive or negative match */
} DICT_PCRE_IF_RULE;
/*
const char *mapname; /* name of regexp map */
int lineno; /* where in file */
int flags; /* dict_flags */
+ size_t max_sub; /* Largest $n seen */
} DICT_PCRE_PRESCAN_CONTEXT;
/*
lookup_string, lookup_len,
NULL_STARTOFFSET, NULL_EXEC_OPTIONS,
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);
- 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.
*/
lookup_string, lookup_len,
NULL_STARTOFFSET, NULL_EXEC_OPTIONS,
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);
- continue;
+ continue; /* pcre_exec failed */
}
nesting++;
continue;
char *p = *bufp;
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++;
pattern->regexp = p;
if (type == MAC_PARSE_VARNAME) {
if (ctxt->flags & DICT_FLAG_NO_REGSUB) {
msg_warn("pcre map %s, line %d: "
- "regular expression substitution is not allowed",
- ctxt->mapname, ctxt->lineno);
+ "regular expression substitution is not allowed",
+ ctxt->mapname, ctxt->lineno);
return (MAC_PARSE_ERROR);
}
-
if (!alldig(vstring_str(buf))) {
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));
return (MAC_PARSE_ERROR);
}
+ if (n > ctxt->max_sub)
+ ctxt->max_sub = n;
}
return (MAC_PARSE_OK);
}
prescan_context.mapname = mapname;
prescan_context.lineno = lineno;
prescan_context.flags = dict_flags;
+ prescan_context.max_sub = 0;
if (mac_parse(p, dict_pcre_prescan, (char *) &prescan_context)
& MAC_PARSE_ERROR) {
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.
*/
match_rule = (DICT_PCRE_MATCH_RULE *)
dict_pcre_rule_alloc(DICT_PCRE_OP_MATCH, nesting, lineno,
sizeof(DICT_PCRE_MATCH_RULE));
+ match_rule->match = regexp.match;
match_rule->replacement = mystrdup(p);
match_rule->pattern = engine.pattern;
match_rule->hints = engine.hints;
/*
* Warn about out-of-place text.
*/
+ while (*p && ISSPACE(*p))
+ ++p;
if (*p)
msg_warn("pcre map %s, line %d: ignoring extra text after IF",
mapname, lineno);
if_rule = (DICT_PCRE_IF_RULE *)
dict_pcre_rule_alloc(DICT_PCRE_OP_IF, nesting, lineno,
sizeof(DICT_PCRE_IF_RULE));
+ if_rule->match = regexp.match;
if_rule->pattern = engine.pattern;
if_rule->hints = engine.hints;
return ((DICT_PCRE_RULE *) if_rule);
/*
* Warn about out-of-place text.
*/
+ while (*p && ISSPACE(*p))
+ ++p;
if (*p)
msg_warn("pcre map %s, line %d: ignoring extra text after ENDIF",
mapname, lineno);
get d
get 1234
get 123
+get bar/find
+get bar/whynot
+get bar/elbereth
+get say/elbereth
/(1)(2)(3)(5)/ ($1)($2)($3)($4)($5)
/(1)(2)(3)(4)/ ($1)($2)($3)($4)
/(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
./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: 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
true1=1
true2: not found
d: not found
1234=(1)(2)(3)(4)
123=(1)(2)(3)
+bar/find: not found
+bar/whynot=Don't have a liquor license
+bar/elbereth=(elbereth)
+say/elbereth: not found
first_pat.options |= REG_NOSUB;
} else if (dict_flags & DICT_FLAG_NO_REGSUB) {
msg_warn("regexp map %s, line %d: "
- "regular expression substitution is not allowed: "
- "skipping this rule", mapname, lineno);
- return(0);
+ "regular expression substitution is not allowed: "
+ "skipping this rule", mapname, lineno);
+ return (0);
}
if ((first_exp = dict_regexp_compile_pat(mapname, lineno,
&first_pat)) == 0)
p++;
if (!dict_regexp_get_pat(mapname, lineno, &p, &pattern))
return (0);
+ while (*p && ISSPACE(*p))
+ ++p;
if (*p)
msg_warn("regexp map %s, line %d: ignoring extra text after IF",
mapname, lineno);
mapname, lineno);
return (0);
}
+ while (*p && ISSPACE(*p))
+ ++p;
if (*p)
msg_warn("regexp map %s, line %d: ignoring extra text after ENDIF",
mapname, lineno);
get 1235
get 1234
get 123
+get bar/find
+get bar/whynot
+get bar/elbereth
+get say/elbereth
/(1)(2)(3)(5)/ ($1)($2)($3)($4)($5)
/(1)(2)(3)(4)/ ($1)($2)($3)($4)
/(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
./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 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
true1=1
true2: not found
1235=(1)(2)(3)
1234=(1)(2)(3)(4)
123=(1)(2)(3)
+bar/find: not found
+bar/whynot=Don't have a liquor license
+bar/elbereth=(elbereth)
+say/elbereth: not found
/* ENCODING
/* .ad
/* .fi
-/* In request and reply parameters, the character % and any non-printable
-/* characters (including whitespace) are replaced by %XX, XX being the
+/* 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
/* REPLY FORMAT
/* .ad
/* .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"
/* In case of a lookup request, the requested data does not exist.
/* In case of an update request, the request was rejected.
/* Utility library. */
-#include "msg.h"
-#include "mymalloc.h"
-#include "vstring.h"
-#include "vstream.h"
-#include "vstring_vstream.h"
-#include "connect.h"
-#include "hex_quote.h"
-#include "dict.h"
-#include "stringops.h"
-#include "dict_tcp.h"
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <connect.h>
+#include <hex_quote.h>
+#include <dict.h>
+#include <stringops.h>
+#include <dict_tcp.h>
/* Application-specific. */
VSTREAM *fp; /* I/O stream */
} DICT_TCP;
-#define DICT_TCP_MAXTRY 10
-#define DICT_TCP_TMOUT 100
+#define DICT_TCP_MAXTRY 10 /* attempts before giving up */
+#define DICT_TCP_TMOUT 100 /* connect/read/write timeout */
+#define DICT_TCP_MAXLEN 4096 /* server reply size limit */
#define STR(x) vstring_str(x)
int fd;
/*
- * Connect to the server. Enforce a time limit on read/write operations
- * so that we do not get stuck.
+ * Connect to the server. Enforce a time limit on all operations so that
+ * 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);
return (-1);
}
char *myname = "dict_tcp_lookup";
int tries;
char *start;
+ int last_ch;
#define RETURN(errval, result) { dict_errno = errval; return (result); }
*/
hex_quote(dict_tcp->hex_buf, key);
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;
/*
* Disconnect from the server if it can't talk to us.
*/
- msg_warn("read TCP map reply from %s: unexpected EOF (%m)",
- dict_tcp->dict.name);
+ if (last_ch < 0)
+ msg_warn("read TCP map reply from %s: unexpected EOF (%m)",
+ 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);
}
*/
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
|| !ISDIGIT(start[2]) || !ISSPACE(start[3])
|| !hex_unquote(dict_tcp->raw_buf, start + 4)) {
msg_warn("read TCP map reply from %s: malformed reply %.100s",
- dict_tcp->dict.name, printable(STR(dict_tcp->hex_buf), '_'));
+ dict_tcp->dict.name, printable(STR(dict_tcp->hex_buf), '_'));
dict_tcp_disconnect(dict_tcp);
RETURN(DICT_ERR_RETRY, 0);
}
switch (start[0]) {
default:
msg_warn("read TCP map reply from %s: bad status code %.100s",
- dict_tcp->dict.name, printable(STR(dict_tcp->hex_buf), '_'));
+ dict_tcp->dict.name, printable(STR(dict_tcp->hex_buf), '_'));
dict_tcp_disconnect(dict_tcp);
RETURN(DICT_ERR_RETRY, 0);
case '4':
dict_tcp->dict.lookup = dict_tcp_lookup;
dict_tcp->dict.close = dict_tcp_close;
dict_tcp->dict.flags = dict_flags | DICT_FLAG_FIXED;
- return (DICT_DEBUG(&dict_tcp->dict));
+ return (DICT_DEBUG (&dict_tcp->dict));
}
/* const char *hex;
/* DESCRIPTION
/* 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
/* value is the hex argument.
/*
VSTRING_RESET(hex);
for (cp = raw; (ch = *(unsigned const char *) cp) != 0; cp++) {
- if (ch != '%' && ISPRINT(ch)) {
+ if (ch != '%' && !ISSPACE(ch) && ISPRINT(ch)) {
VSTRING_ADDCH(hex, ch);
} else {
vstring_sprintf_append(hex, "%%%02X", ch);
#endif
#ifndef INADDR_NONE
-#define INADDR_NONE 0xffffff
+#define INADDR_NONE 0xffffffff
#endif
/* Utility library. */
/*
* Open an existing file or create a new one, carefully. When opening
* 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:
- if ((fp = safe_open_exist(path, flags, st, why)) == 0)
- if (errno == ENOENT)
- fp = safe_open_create(path, flags, mode, st, user, group, why);
+ fp = safe_open_exist(path, flags, st, why);
+ if (fp == 0 && errno == ENOENT) {
+ 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);
/*
msg_warn("%s: unexpected text after ']': %.100s", myname, addr);
return (0);
}
- if (last - addr >= sizeof(buf)) {
+ if (last >= addr + sizeof(buf)) {
if (gripe)
msg_warn("%s: too much text: %.100s", myname, addr);
return (0);
/* .IP "\fBVRFY_ADDR_UPDATE\fI address status text\fR"
/* Update the status of the specified address.
/* .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
/* returned.
/* .PP