2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-31 14:17:41 +00:00

postfix-2.3-20050308

This commit is contained in:
Wietse Venema
2005-03-08 00:00:00 -05:00
committed by Viktor Dukhovni
parent 9bf2b1e43e
commit 9228cc5a29
37 changed files with 3562 additions and 1647 deletions

View File

@@ -10341,6 +10341,13 @@ Apologies for any names omitted.
Cleanup: documented the myorigin/mydomain address rewriting
in canonical, generic and virtual alias maps.
Feature: updated LDAP and *SQL query interfaces using a
common infrastructure so that all have the same feature set
where possible. Victor Duchovni and many others. This code
was tested separately and was merged into the main stream
20050308. Files: global/db_common.[hc], global/dict_ldap.c,
global/dict_mysql.c, global/dict_pgsql.c, plus documentation.
20050210
Bugfix: spurious fallback_relay warnings after 20050202.
@@ -10445,6 +10452,11 @@ Apologies for any names omitted.
webpage. As proof of authenticity the new PGP key is signed
with Wietse's old PGP key.
Cleanup: check_mumble_{ns,mx}_access no longer attempt to
do MX or NS lookups for address literals. An address literal
is treated as its own MX host; there is no meaningful
equivalent for NS access control. File: smtpd/smtpd_check.c.
Open problems:
Med: disable header address rewriting after XCLIENT?

View File

@@ -129,58 +129,59 @@ this document for the first time, skip forward to "Address rewriting when mail
is received". Once you've finished reading the remainder of this document, the
table will help you to quickly find what you need.
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|AAddddrreessss |SSccooppee |DDaaeemmoonn |GGlloobbaall ttuurrnn--oonn |SSeelleeccttiivvee ttuurrnn--ooffff |
|mmaanniippuullaattiioonn| | |ccoonnttrrooll |ccoonnttrrooll |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Rewrite | |trivial-|append_at_myorigin, | |
|addresses to|all mail|rewrite |append_dot_mydomain,|none |
|standard | |(8) |swap_bangpath, | |
|form | | |allow_percent_hack | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Canonical | |cleanup | | |
|address |all mail|(8) |canonical_maps |receive_override_options|
|mapping | | | | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Address |all mail|cleanup |masquerade_domains |receive_override_options|
|masquerading| |(8) | | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Automatic | |cleanup |always_bcc, | |
|BCC |new mail|(8) |sender_bcc_maps, |receive_override_options|
|recipients | | |recipient_bcc_maps | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Virtual |all mail|cleanup |virtual_alias_maps |receive_override_options|
|aliasing | |(8) | | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Resolve | |trivial-| | |
|address to |all mail|rewrite |none |none |
|destination | |(8) | | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Mail | |trivial-| | |
|transport |all mail|rewrite |transport_maps |none |
|switch | |(8) | | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Relocated | |trivial-| | |
|users table |all mail|rewrite |relocated_maps |none |
| | |(8) | | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Generic |outgoing| | | |
|mapping |SMTP |smtp(8) |smtp_generic_maps |none |
|table |mail | | | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Local alias |local | | | |
|database |mail |local(8)|alias_maps |none |
| |only | | | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Local per- |local | | | |
|user |mail |local(8)|forward_path |none |
|.forward |only | | | |
|files | | | | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Local catch-|local | | | |
|all address |mail |local(8)|luser_relay |none |
| |only | | | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|AAddddrreessss |SSccooppee |DDaaeemmoonn |GGlloobbaall ttuurrnn--oonn |SSeelleeccttiivvee ttuurrnn--ooffff ccoonnttrrooll |
|mmaanniippuullaattiioonn| | |ccoonnttrrooll | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Rewrite | |trivial-|append_at_myorigin, | |
|addresses to|all mail|rewrite |append_dot_mydomain,|local_header_rewrite_clients,|
|standard | |(8) |swap_bangpath, |remote_header_rewrite_domain |
|form | | |allow_percent_hack | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Canonical | |cleanup | |receive_override_options, |
|address |all mail|(8) |canonical_maps |local_header_rewrite_clients,|
|mapping | | | |remote_header_rewrite_domain |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Address | |cleanup | |receive_override_options, |
|masquerading|all mail|(8) |masquerade_domains |local_header_rewrite_clients,|
| | | | |remote_header_rewrite_domain |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Automatic | |cleanup |always_bcc, | |
|BCC |new mail|(8) |sender_bcc_maps, |receive_override_options |
|recipients | | |recipient_bcc_maps | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Virtual |all mail|cleanup |virtual_alias_maps |receive_override_options |
|aliasing | |(8) | | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Resolve | |trivial-| | |
|address to |all mail|rewrite |none |none |
|destination | |(8) | | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Mail | |trivial-| | |
|transport |all mail|rewrite |transport_maps |none |
|switch | |(8) | | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Relocated | |trivial-| | |
|users table |all mail|rewrite |relocated_maps |none |
| | |(8) | | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Generic |outgoing| | | |
|mapping |SMTP |smtp(8) |smtp_generic_maps |none |
|table |mail | | | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Local alias |local | | | |
|database |mail |local(8)|alias_maps |none |
| |only | | | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Local per- |local | | | |
|user |mail |local(8)|forward_path |none |
|.forward |only | | | |
|files | | | | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|Local catch-|local | | | |
|all address |mail |local(8)|luser_relay |none |
| |only | | | |
|_ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
AAddddrreessss rreewwrriittiinngg wwhheenn mmaaiill iiss rreecceeiivveedd
@@ -213,11 +214,11 @@ table, it first rewrites the address to the standard
rewrite(8) daemon. The purpose of rewriting to standard form is to reduce the
number of entries needed in lookup tables.
Postfix versions 2.2 and later do not rewrite message headers from remote SMTP
clients at all, unless a non-empty domain name is specified with the
remote_header_rewrite_domain configuration parameter. The
local_header_rewrite_clients parameter controls what SMTP clients Postfix
considers local.
NOTE: Postfix versions 2.2 and later rewrite message headers from remote SMTP
clients only if the client matches the local_header_rewrite_clients parameter,
or if the remote_header_rewrite_domain configuration parameter specifies a non-
empty value. To get the behavior before Postfix 2.2, specify
"local_header_rewrite_clients = static:all".
The Postfix trivial-rewrite(8) daemon implements the following hard-coded
address manipulations:
@@ -246,10 +247,13 @@ address manipulations:
of Postfix components expect that all addresses have the form
"user@domain".
Postfix versions 2.2 and later either do not rewrite message headers
from remote SMTP clients at all, or they append the domain name
specified with the remote_header_rewrite_domain configuration
parameter.
NOTE: Postfix versions 2.2 and later rewrite message headers from
remote SMTP clients only if the client matches the
local_header_rewrite_clients parameter; otherwise they append the
domain name specified with the remote_header_rewrite_domain
configuration parameter, if one is specified. To get the behavior
before Postfix 2.2, specify "local_header_rewrite_clients = static:
all".
If your machine is not the main machine for $myorigin and you wish to
have some users delivered locally without going via that main machine,
@@ -262,9 +266,13 @@ address manipulations:
(default: yes). The purpose is to get consistent treatment of different
forms of the same hostname.
Postfix versions 2.2 and later either do not rewrite message headers
from remote clients at all, or they append the domain name specified
with the remote_header_rewrite_domain configuration parameter.
NOTE: Postfix versions 2.2 and later rewrite message headers from
remote SMTP clients only if the client matches the
local_header_rewrite_clients parameter; otherwise they append the
domain name specified with the remote_header_rewrite_domain
configuration parameter, if one is specified. To get the behavior
before Postfix 2.2, specify "local_header_rewrite_clients = static:
all".
Some will argue that rewriting "host" to "host.domain" is bad. That is
why it can be turned off. Others like the convenience of having
@@ -281,11 +289,11 @@ message envelopes and in message headers. By default all header and envelope
addresses are rewritten; this is controlled with the canonical_classes
configuration parameter.
Postfix versions 2.2 and later do not rewrite message headers from remote
clients at all, unless a non-empty domain name is specified with the
remote_header_rewrite_domain configuration parameter. The
local_header_rewrite_clients parameter controls what SMTP clients Postfix
considers local.
NOTE: Postfix versions 2.2 and later rewrite message headers from remote SMTP
clients only if the client matches the local_header_rewrite_clients parameter,
or if the remote_header_rewrite_domain configuration parameter specifies a non-
empty value. To get the behavior before Postfix 2.2, specify
"local_header_rewrite_clients = static:all".
Address rewriting is done for local and remote addresses. The mapping is useful
to replace login names by "Firstname.Lastname" style addresses, or to clean up
@@ -345,11 +353,11 @@ Address masquerading is a method to hide hosts inside a domain behind their
mail gateway, and to make it appear as if the mail comes from the gateway
itself, instead of from individual machines.
Postfix versions 2.2 and later do not rewrite message headers from remote SMTP
clients at all, unless a non-empty domain name is specified with the
remote_header_rewrite_domain configuration parameter. The
local_header_rewrite_clients parameter controls what SMTP clients Postfix
considers local.
NOTE: Postfix versions 2.2 and later rewrite message headers from remote SMTP
clients only if the client matches the local_header_rewrite_clients parameter,
or if the remote_header_rewrite_domain configuration parameter specifies a non-
empty value. To get the behavior before Postfix 2.2, specify
"local_header_rewrite_clients = static:all".
Address masquerading is disabled by default, and is implemented by the cleanup
(8) server. To enable, edit the masquerade_domains parameter in the main.cf

View File

@@ -180,7 +180,7 @@ NNootteess aanndd tthhiinnggss ttoo tthhiinnkk aabboouu
* If you use an LDAP map for lookups other than aliases, you may have to make
sure the lookup makes sense. In the case of virtual lookups, maildrops
other than mail addresses are pretty useless, because Postfix can't know
how to set the ownership for program or file delivery. Your query_filter
how to set the ownership for program or file delivery. Your qquueerryy__ffiilltteerr
should probably look something like this:
query_filter = (&(mailacceptinggeneralid=%s)(!(|(maildrop="*|*")
@@ -196,7 +196,7 @@ NNootteess aanndd tthhiinnggss ttoo tthhiinnkk aabboouu
some thought on your part to implement safely, considering the
ramifications of this type of delivery. You may decide it's not worth the
bother to allow any of that nonsense in LDAP lookups, ban it in the
query_filter, and keep things like majordomo lists in local alias
qquueerryy__ffiilltteerr, and keep things like majordomo lists in local alias
databases.
query_filter = (&(mailacceptinggeneralid=%s)(!(|(maildrop="*|*")
@@ -236,12 +236,17 @@ CCrreeddiittss
the work on RFC 2254 escaping in queries. Spotted a bug in binding.
* Sami Haahtinen: Referral chasing and v3 support.
* Victor Duchovni: ldap_bind() timeout. With fixes from LaMont Jones:
OpenLDAP cache deprecation. Limits on recursion, expansion and query
OpenLDAP cache deprecation. Limits on recursion, expansion and search
results size. LDAP connection sharing for maps differing only in the query
parameters.
* Liviu Daia: Support for SSL/STARTTLS. Support for storing map definitions
in external files (ldap:/path/ldap.cf) needed to securely store passwords
for plain auth.
* Liviu Daia revised the configuration interface and added the main.cf
configuration feature.
* Liviu Daia with further refinements from Jose Luis Tallon and Victor
Duchovni developed the common query, result_format, domain and
expansion_limit interface for LDAP, MySQL and PosgreSQL.
And of course Wietse.

View File

@@ -62,22 +62,16 @@ password = some_password
# The database name on the servers.
dbname = customer_database
# The table name.
table = mxaliases
# For Postfix 2.2 and later The SQL query template.
# See mysql_table(5) for details.
query = SELECT forw_addr FROM mxaliases WHERE alias='%s' AND status='paid'
# Query components, see below.
# For Postfix releases prior to 2.2. See mysql_table(5) for details.
select_field = forw_addr
table = mxaliases
where_field = alias
# You may specify additional_conditions or leave this empty.
additional_conditions = and status = 'paid'
# The above variables will result in a query of the form:
#
# select forw_addr from mxaliases where alias = '$lookup' and status = 'paid'
#
# ($lookup is escaped so if it contains single quotes or other odd
# characters, it will not cause trouble).
# Don't forget the leading "AND"!
additional_conditions = AND status = 'paid'
AAddddiittiioonnaall nnootteess
@@ -100,4 +94,7 @@ CCrreeddiittss
Group, Inc.
* Liviu Daia revised the configuration interface and added the main.cf
configuration feature.
* Liviu Daia with further refinements from Jose Luis Tallon and Victor
Duchovni developed the common query, result_format, domain and
expansion_limit interface for LDAP, MySQL and PosgreSQL.

View File

@@ -63,25 +63,15 @@ password = some_password
# The database name on the servers.
dbname = customer_database
# The table name.
table = mxaliases
# Postfix 2.2 and later The SQL query template. See pgsql_table(5).
query = SELECT forw_addr FROM mxaliases WHERE alias='%s' AND status='paid'
# Query components, see below.
# For Postfix releases prior to 2.2. See pgsql_table(5) for details.
select_field = forw_addr
table = mxaliases
where_field = alias
# You may specify additional_conditions or leave this empty.
additional_conditions = and status = 'paid'
# The above variables will result in a query of the form:
#
# select forw_addr from mxaliases where alias = '$lookup' and status = 'paid'
#
# ($lookup is escaped so if it contains single quotes or other odd
# characters, it will not cause problems).
#
# You may also override the built-in SELECT template. See pgsql_table(5)
# for details.
# Don't forget the leading "AND"!
additional_conditions = AND status = 'paid'
UUssiinngg mmiirrrroorreedd ddaattaabbaasseess
@@ -107,4 +97,9 @@ CCrreeddiittss
* LaMont Jones was the initial Postfix pgsql maintainer.
* Liviu Daia revised the configuration interface and added the main.cf
configuration feature.
* Liviu Daia revised the configuration interface and added the main.cf
configuration feature.
* Liviu Daia with further refinements from Jose Luis Tallon and Victor
Duchovni developed the common query, result_format, domain and
expansion_limit interface for LDAP, MySQL and PosgreSQL.

View File

@@ -20,13 +20,17 @@ the following sections of this document.
- TLS and IPv6 support are now built into Postfix, based on code
from third-party patches.
- Extended query interface for LDAP, MySQL and PostgreSQL with free
form SQL queries, and domain filters to reduce unnecessary lookups.
- SMTP client-side connection reuse. This can dramatically speed
up deliveries to high-volume destinations that have some servers
that respond, and some non-responding mail servers.
- By default, message header address rewriting is now disabled for
SMTP mail from other systems. Thus, spam from poorly written
software no longer looks like it came from a local user.
SMTP mail from other systems (including masquerading and canonical
mapping). Thus, spam from poorly written software no longer looks
like it came from a local user.
- When your machine does not have its own domain name, Postfix can
now replace your "home network" email address by your ISP account
@@ -273,6 +277,12 @@ it does not turn off the actual features in the SMTP server.
Major changes - database support
--------------------------------
[Feature 20050209] Extended LDAP, MySQL and PgSQL query interface
with free form SQL queries, the domain filter optimization that was
already available with LDAP and more. This code was worked on by
many people but Victor Duchovni took the lead. See the respective
{LDAP,MYSQL,PGSQL}_README and {ldap,mysql,pgsql}_table documents.
[Feature 20041210] You can now dump an entire database with the new
postmap/postalias "-s" option. This works only for database types
with Postfix sequence operator support: hash, btree, dbm, and sdbm.

View File

@@ -1,242 +0,0 @@
#!/bin/sh
# postfinger - captures Postfix configuration for reporting errors
#
# Inspired by comments on the postfix-users mailing list.
# Copyright (C) 2003 Simon J. Mudd (sjmudd@pobox.com)
# With help from:
# Matthias Andree <ma@dt.e-technik.uni-dortmund.de>
# Victor Duchovni <Victor.Duchovni@morganstanley.com>
# Sasa Babic <sasab@hygia.pharmacy.bg.ac.yu>
# I<>aki Arenaza <iarenaza@escomposlinux.org>
# Jorge Gordoy <gordoy@g2ctech.com>
# $Revision: 1.29 $
#
# License:
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You may have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
# USA.
#
# An on-line copy of the GNU General Public License can be found
# http://www.fsf.org/copyleft/gpl.html.
version_number=1.29 # don't use rcs version here
version="version: ${version_number}"
BACKUP_IFS=$IFS
usage="postfinger ${version}: a Postfix configuration extraction utility
Usage: postfinger [options]
Options can be any of:
--all Show all configuration information
--system Show basic system environment (os/kernel/...) [default]
--package Show packaging information [default]
--locking Show mailbox locking methods
--tables Show supported lookup tables
--main Show main.cf non-default configuration values [default]
--defaultsinmain Show main.cf defined values which are identical to defaults
--master Show master.cf configuration [default]
--permissions Show some of the spool_directory permissions
--libraries Show the Postfix libraries dependencies
--nosystem Do not show basic system environment (os/kernel/...)
--nomain Do not show main.cf non-default configuration values
--nomaster Do not show master.cf configuration
--nowarn Do not warn about private information being leaked to
outsiders
--version print the version of postfinger being used and exit
Mail bug reports and suggestions to <postfinger@WL0.org>".
system=1; package=1; locking=; tables=; main=1; master=1; permissions=; libraries=;warn=1;defaultsinmain=
for arg
do
case $arg in
--version) echo "postfinger ${version}"; exit 0;;
--all) system=1; package=1; locking=1; tables=1; main=1; master=1; permissions=1; libraries=1; warn=1;;
--system) system=1;;
--package) package=1;;
--locking) locking=1;;
--tables) tables=1;;
--main) main=1;;
--defaultsinmain) defaultsinmain=1;;
--master) master=1;;
--permissions) permissions=1;;
--libraries) libraries=1;;
--nosystem) system=;;
--nomain) main=;;
--nomaster) master=;;
--nowarn) warn=;;
--help) echo "${usage}"; exit 0;;
*) echo "Error: ${usage}" 1>&2; exit 1;;
esac
shift
done
echo "postfinger - postfix configuration on `LANG=C date`"
echo ${version}
echo ''
[ "${warn}" = 1 ] && {
cat <<END
Warning: postfinger output may show private configuration information,
such as ip addresses and/or domain names which you do not want to show
to the public. If this is the case it is your responsibility to modify
the output to hide this private information. [Remove this warning with
the --nowarn option.]
END
}
# Look for postconf, using environment variable if given
[ -n "${POSTCONF}" ] && [ ! -x "${POSTCONF}" ] && POSTCONF=
[ -z "${POSTCONF}" ] && [ -x /usr/sbin/postconf ] && POSTCONF=/usr/sbin/postconf
[ -z "${POSTCONF}" ] && [ -x /usr/local/sbin/postconf ] && POSTCONF=/usr/local/sbin/postconf
[ -z "${POSTCONF}" ] && {
echo "$0: can not find postconf"
echo "set POSTCONF to postconf's location and try again"
exit 1
}
# Look for smtpd, using environment variable if given
[ -z "${SMTPD}" ] && {
SMTPD=`${POSTCONF} -h daemon_directory`/smtpd
[ -d "${SMTPD}" -o ! -x "${SMTPD}" ] && SMTPD=
}
[ -z "${SMTPD}" ] && {
echo "$0: can not find smtpd"
echo "set SMTPD to smtpd's location and try again"
exit 1
}
[ "${system}" = 1 ] && {
echo '--System Parameters--'
${POSTCONF} -d mail_version
echo "hostname = `hostname`"
echo "uname = `uname -a`"
echo ""
}
# check for different packaging systems and try to identify if this postfix
# (smtpd) comes from a package.
# I would appreciate help in adapting this part to include other packaging
# systems.
[ "${package}" = 1 ] && {
echo "--Packaging information--"
DPKG=
[ -x /usr/bin/dpkg ] && DPKG=/usr/bin/dpkg
[ -z "${DPKG}" ] && [ -x /usr/local/bin/dpkg ] && DPKG=/usr/local/bin/dpkg
[ -n "${DPKG}" ] && {
${DPKG} -S ${SMTPD} >/dev/null 2>/dev/null && {
package=`${DPKG} -S ${SMTPD} | awk -F: '{print $1}' | head -n 1`
package_ver=`COLUMNS=132 ${DPKG} -l ${package} | grep ii | grep -v "documentation" | awk '{print $3}'`
echo "looks like this postfix comes from deb package: ${package}-${package_ver}"
}
}
RPM=
[ -x /bin/rpm ] && RPM=/bin/rpm
[ -z "${RPM}" ] && [ -x /usr/local/bin/rpm ] && RPM=/usr/local/bin/rpm
[ -n "${RPM}" ] && {
${RPM} -qf ${SMTPD} >/dev/null 2>/dev/null && \
echo "looks like this postfix comes from RPM package: `${RPM} -qf ${SMTPD}`"
}
BSDPKG=
[ -x /usr/sbin/pkg_info ] && BSDPKG=/usr/sbin/pkg_info
[ -n "${BSDPKG}" ] && {
${BSDPKG} -q -W ${SMTPD} >/dev/null 2>/dev/null && \
echo "looks like this postfix comes from BSD package: `${BSDPKG} -q -W ${SMTPD}`"
}
echo ""
}
IFS="
"
[ "${locking}" = 1 ] && {
echo "--Mailbox locking methods--"
locking_methods=`${POSTCONF} -l`
echo $locking_methods
echo ""
}
[ "${tables}" = 1 ] && {
echo "--Supported Lookup tables--"
lookup_tables=`${POSTCONF} -m`
echo $lookup_tables
echo ""
}
[ "${main}" = 1 -o "${defaultsinmain}" = 1 ] && {
if [ "x`find . -prune \( -perm 020 -o -perm 002 \) -print`" != "x" ]
then
echo 2>&2 "Do not run this in a public- or group-writable directory"
exit 1
fi
rm -f postfinger.$$.d postfinger.$$.n
${POSTCONF} -d | tr -s [:blank:] | sort > postfinger.$$.d
${POSTCONF} -n | tr -s [:blank:] | sort > postfinger.$$.n
[ "$main" = 1 ] && {
echo "--main.cf non-default parameters--"
comm -13 postfinger.$$.d postfinger.$$.n
echo ""
}
[ "${defaultsinmain}" = 1 ] && {
echo "--main.cf parameters defined as per defaults--"
comm -12 postfinger.$$.d postfinger.$$.n
echo ""
}
rm -f postfinger.$$.d postfinger.$$.n
}
[ "${master}" = 1 ] && {
echo "--master.cf--"
# Remove blank and commented lines to reduce the output
# Note: the second grep contains a space followed by a tab character
cat `${POSTCONF} -h config_directory`/master.cf | \
grep -v '^#' | \
grep -v '^[ ]*$'
echo ""
}
[ "${permissions}" = 1 ] && {
echo "--Specific file and directory permissions--"
ls -ld `${POSTCONF} -h queue_directory`/maildrop
ls -ld `${POSTCONF} -h queue_directory`/public
ls -l `${POSTCONF} -h queue_directory`/public 2>/dev/null || {
echo 'WARNING: No access to $queue_directory/public'
echo ' Try running postfinger as user root or postfix'
}
ls -ld `${POSTCONF} -h queue_directory`/private
ls -l `${POSTCONF} -h queue_directory`/private 2>/dev/null || {
echo 'WARNING: No access to $queue_directory/private'
echo ' Try running postfinger as user root or postfix'
}
ls -l `${POSTCONF} -h command_directory`/postdrop
ls -l `${POSTCONF} -h command_directory`/postqueue
echo ""
}
[ "${libraries}" = 1 ] && {
echo "--Library dependencies--"
echo "${SMTPD}:"
ldd ${SMTPD} || echo "WARNING: Can not find ldd. Check you have it installed and in your path"
}
echo "-- end of postfinger output --"

View File

@@ -594,7 +594,7 @@ EOF
if [ -z "$has_lrm" -a -z "$has_lrjc" ]
then
echo SAFETY: editing main.cf, setting $unknown_local=450.
echo See the RELEASE_NOTES and LOCAL_RECIPIENT_README files for details.
echo See the LOCAL_RECIPIENT_README file for details.
$POSTCONF -e "$unknown_local = 450" || exit 1
fi

View File

@@ -317,15 +317,18 @@ turn-off control </th> </tr>
<tr> <td> <a href="#standard"> Rewrite addresses to standard form</a>
</td> <td nowrap> all mail </td> <td> <a href="trivial-rewrite.8.html">trivial-<br>rewrite(8)</a> </td>
<td> <a href="postconf.5.html#append_at_myorigin">append_at_myorigin</a>, <a href="postconf.5.html#append_dot_mydomain">append_dot_mydomain</a>, <a href="postconf.5.html#swap_bangpath">swap_bangpath</a>,
<a href="postconf.5.html#allow_percent_hack">allow_percent_hack</a> </td> <td> none </td> </tr>
<a href="postconf.5.html#allow_percent_hack">allow_percent_hack</a> </td> <td> <a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a>,
<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> </td> </tr>
<tr> <td> <a href="#canonical"> Canonical address mapping </a>
</td> <td nowrap> all mail </td> <td> <a href="cleanup.8.html">cleanup(8)</a> </td> <td>
<a href="postconf.5.html#canonical_maps">canonical_maps</a> </td> <td> <a href="postconf.5.html#receive_override_options">receive_override_options</a> </td> </tr>
<tr> <td> <a href="#canonical"> Canonical address mapping </a> </td>
<td nowrap> all mail </td> <td> <a href="cleanup.8.html">cleanup(8)</a> </td> <td> <a href="postconf.5.html#canonical_maps">canonical_maps</a>
</td> <td> <a href="postconf.5.html#receive_override_options">receive_override_options</a>, <a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a>,
<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> </td> </tr>
<tr> <td> <a href="#canonical"> Address masquerading </a> </td>
<td nowrap> all mail </td> <td> <a href="cleanup.8.html">cleanup(8)</a> </td> <td> <a href="postconf.5.html#masquerade_domains">masquerade_domains</a>
</td> <td> <a href="postconf.5.html#receive_override_options">receive_override_options</a> </td> </tr>
<tr> <td> <a href="#masquerade"> Address masquerading </a> </td> <td
nowrap> all mail </td> <td> <a href="cleanup.8.html">cleanup(8)</a> </td> <td> <a href="postconf.5.html#masquerade_domains">masquerade_domains</a>
</td> <td> <a href="postconf.5.html#receive_override_options">receive_override_options</a>, <a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a>,
<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> </td> </tr>
<tr> <td> <a href="#auto_bcc"> Automatic BCC recipients </a> </td>
<td nowrap> new mail </td> <td> <a href="cleanup.8.html">cleanup(8)</a> </td> <td> <a href="postconf.5.html#always_bcc">always_bcc</a>,
@@ -411,11 +414,12 @@ mapping lookup table, it first rewrites the address to the standard
form is to reduce the number of entries needed in lookup tables.
</p>
<p> Postfix versions 2.2 and later do not rewrite message headers
from remote SMTP clients at all, unless a non-empty domain name is
specified with the <a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> configuration
parameter. The <a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> parameter controls
what SMTP clients Postfix considers local. </p>
<p> NOTE: Postfix versions 2.2 and later rewrite message headers
from remote SMTP clients only if the client matches the
<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> parameter, or if the
<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> configuration parameter specifies a
non-empty value. To get the behavior before Postfix 2.2, specify
"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all". </p>
<p> The Postfix <a href="trivial-rewrite.8.html">trivial-rewrite(8)</a> daemon implements the following
hard-coded address manipulations: </p>
@@ -455,10 +459,13 @@ parameter (default: yes). You should never turn off this feature,
because a lot of Postfix components expect that all addresses have
the form "user@domain". </p>
<p> Postfix versions 2.2 and later either do not rewrite message
headers from remote SMTP clients at all, or they append the domain
name specified with the <a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> configuration
parameter. </p>
<p> NOTE: Postfix versions 2.2 and later rewrite message headers
from remote SMTP clients only if the client matches the
<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> parameter; otherwise they append the
domain name specified with the <a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a>
configuration parameter, if one is specified. To get the behavior
before Postfix 2.2, specify "<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> =
static:all". </p>
<p> If your machine is not the main machine for $<a href="postconf.5.html#myorigin">myorigin</a> and you
wish to have some users delivered locally without going via that
@@ -476,10 +483,13 @@ Rewrite "user@host" to "user@host.$<a href="postconf.5.html#mydomain">mydomain</
parameter (default: yes). The purpose is to get consistent treatment
of different forms of the same hostname. </p>
<p> Postfix versions 2.2 and later either do not rewrite message
headers from remote clients at all, or they append the domain name
specified with the <a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> configuration
parameter. </p>
<p> NOTE: Postfix versions 2.2 and later rewrite message headers
from remote SMTP clients only if the client matches the
<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> parameter; otherwise they append the
domain name specified with the <a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a>
configuration parameter, if one is specified. To get the behavior
before Postfix 2.2, specify "<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> =
static:all". </p>
<p> Some will argue that rewriting "host" to "host.domain"
is bad. That is why it can be turned off. Others like the convenience
@@ -502,11 +512,12 @@ addresses in message envelopes and in message headers. By default
all header and envelope addresses are rewritten; this is controlled
with the <a href="postconf.5.html#canonical_classes">canonical_classes</a> configuration parameter. </p>
<p> Postfix versions 2.2 and later do not rewrite message headers
from remote clients at all, unless a non-empty domain name is
specified with the <a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> configuration
parameter. The <a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> parameter controls
what SMTP clients Postfix considers local. </p>
<p> NOTE: Postfix versions 2.2 and later rewrite message headers
from remote SMTP clients only if the client matches the
<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> parameter, or if the
<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> configuration parameter specifies a
non-empty value. To get the behavior before Postfix 2.2, specify
"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all". </p>
<p> Address rewriting is
done for local and remote addresses. The mapping is useful to
@@ -585,11 +596,12 @@ behind their mail gateway, and to make it appear as if the mail
comes from the gateway itself, instead of from individual machines.
</p>
<p> Postfix versions 2.2 and later do not rewrite message headers
from remote SMTP clients at all, unless a non-empty domain name is
specified with the <a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> configuration
parameter. The <a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> parameter controls
what SMTP clients Postfix considers local. </p>
<p> NOTE: Postfix versions 2.2 and later rewrite message headers
from remote SMTP clients only if the client matches the
<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> parameter, or if the
<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> configuration parameter specifies a
non-empty value. To get the behavior before Postfix 2.2, specify
"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all". </p>
<p> Address masquerading is disabled by default, and is implemented
by the <a href="cleanup.8.html">cleanup(8)</a> server. To enable, edit the <a href="postconf.5.html#masquerade_domains">masquerade_domains</a>

View File

@@ -257,7 +257,7 @@ maildrop: this, that, theother
make sure the lookup makes sense. In the case of virtual lookups,
maildrops other than mail addresses are pretty useless, because
Postfix can't know how to set the ownership for program or file
delivery. Your query_filter should probably look something like this: </p>
delivery. Your <b>query_filter</b> should probably look something like this: </p>
<blockquote>
<pre>
@@ -276,7 +276,7 @@ query_filter = (&amp;(mailacceptinggeneralid=%s)(!(|(maildrop="*|*")(maildrop="*
require some thought on your part to implement safely, considering the
ramifications of this type of delivery. You may decide it's not worth
the bother to allow any of that nonsense in LDAP lookups, ban it in
the query_filter, and keep things like majordomo lists in local alias
the <b>query_filter</b>, and keep things like majordomo lists in local alias
databases. </p>
<blockquote>
@@ -334,13 +334,20 @@ contents, please include the applicable bits of some directory entries. </p>
<li>Victor Duchovni: ldap_bind() timeout. With fixes from LaMont Jones:
OpenLDAP cache deprecation. Limits on recursion, expansion
and query results size. LDAP connection sharing for maps
and search results size. LDAP connection sharing for maps
differing only in the query parameters.
<li>Liviu Daia: Support for SSL/STARTTLS. Support for storing map definitions in
external files (<a href="ldap_table.5.html">ldap</a>:/path/ldap.cf) needed to securely store
passwords for plain auth.
<li>Liviu Daia revised the configuration interface and added the main.cf
configuration feature.</li>
<li>Liviu Daia with further refinements from Jose Luis Tallon and
Victor Duchovni developed the common query, result_format, domain and
expansion_limit interface for LDAP, MySQL and PosgreSQL.</li>
</ul>
And of course Wietse.

View File

@@ -89,22 +89,16 @@ password = some_password
# The database name on the servers.
dbname = customer_database
# The table name.
table = mxaliases
# For Postfix 2.2 and later The SQL query template.
# See <a href="mysql_table.5.html">mysql_table(5)</a> for details.
query = SELECT forw_addr FROM mxaliases WHERE alias='%s' AND status='paid'
# Query components, see below.
# For Postfix releases prior to 2.2. See <a href="mysql_table.5.html">mysql_table(5)</a> for details.
select_field = forw_addr
table = mxaliases
where_field = alias
# You may specify additional_conditions or leave this empty.
additional_conditions = and status = 'paid'
# The above variables will result in a query of the form:
#
# select forw_addr from mxaliases where alias = '$lookup' and status = 'paid'
#
# ($lookup is escaped so if it contains single quotes or other odd
# characters, it will not cause trouble).
# Don't forget the leading "AND"!
additional_conditions = AND status = 'paid'
</pre>
<h2>Additional notes</h2>
@@ -129,10 +123,14 @@ will be deferred until at least one of those hosts is reachable.
<ul>
<li> The initial version was contributed by Scott Cotton and Joshua
Marcus, IC Group, Inc.
Marcus, IC Group, Inc.</li>
<li>Liviu Daia revised the configuration interface and added the
main.cf configuration feature.
<li> Liviu Daia revised the configuration interface and added the
main.cf configuration feature.</li>
<li> Liviu Daia with further refinements from Jose Luis Tallon and
Victor Duchovni developed the common query, result_format, domain and
expansion_limit interface for LDAP, MySQL and PosgreSQL.</li>
</ul>

View File

@@ -88,25 +88,15 @@ password = some_password
# The database name on the servers.
dbname = customer_database
# The table name.
table = mxaliases
# Postfix 2.2 and later The SQL query template. See <a href="pgsql_table.5.html">pgsql_table(5)</a>.
query = SELECT forw_addr FROM mxaliases WHERE alias='%s' AND status='paid'
# Query components, see below.
# For Postfix releases prior to 2.2. See <a href="pgsql_table.5.html">pgsql_table(5)</a> for details.
select_field = forw_addr
table = mxaliases
where_field = alias
# You may specify additional_conditions or leave this empty.
additional_conditions = and status = 'paid'
# The above variables will result in a query of the form:
#
# select forw_addr from mxaliases where alias = '$lookup' and status = 'paid'
#
# ($lookup is escaped so if it contains single quotes or other odd
# characters, it will not cause problems).
#
# You may also override the built-in SELECT template. See <a href="pgsql_table.5.html">pgsql_table(5)</a>
# for details.
# Don't forget the leading "AND"!
additional_conditions = AND status = 'paid'
</pre>
<h2>Using mirrored databases</h2>
@@ -130,17 +120,24 @@ those hosts is reachable. </p>
<ul>
<li> This code is based upon the Postfix mysql map by Scott Cotton
and Joshua Marcus, IC Group, Inc.
and Joshua Marcus, IC Group, Inc.</li>
<li> The PostgreSQL changes were done by Aaron Sethman.
<li> The PostgreSQL changes were done by Aaron Sethman.</li>
<li> Updates for Postfix 1.1.x and PostgreSQL 7.1+ and support for
calling stored procedures were added by Philip Warner.
calling stored procedures were added by Philip Warner.</li>
<li> LaMont Jones was the initial Postfix pgsql maintainer.
<li> LaMont Jones was the initial Postfix pgsql maintainer.</li>
<li> Liviu Daia revised the configuration interface and added the
main.cf configuration feature.
main.cf configuration feature.</li>
<li> Liviu Daia revised the configuration interface and added the main.cf
configuration feature.</li>
<li> Liviu Daia with further refinements from Jose Luis Tallon and
Victor Duchovni developed the common query, result_format, domain and
expansion_limit interface for LDAP, MySQL and PosgreSQL.</li>
</ul>

View File

@@ -54,6 +54,20 @@ LDAP_TABLE(5) LDAP_TABLE(5)
Support for this form will be removed in a future Postfix
version.
Postfix 2.2 has enhanced query interfaces for MySQL and
PostgreSQL, these now include features previously avail-
able only in the Postfix LDAP client. This work also cre-
ated an opportunity for improvements in the LDAP inter-
face. The primary compatibility issue is that <b>result_fil-</b>
<b>ter</b> (a name that has caused some confusion as to its mean-
ing in the past) has been renamed to <b>result_format</b>. For
backwards compatibility with the pre 2.2 LDAP client,
<b>result_filter</b> can for now be used instead of <b>result_for-</b>
<b>mat</b>, when the latter parameter is not also set. The new
name better reflects the function of the parameter. This
compatibility interface may be removed in a future
release.
<b>LIST MEMBERSHIP</b>
When using LDAP to store lists such as $<a href="postconf.5.html#mynetworks">mynetworks</a>,
$<a href="postconf.5.html#mydestination">mydestination</a>, $<a href="postconf.5.html#relay_domains">relay_domains</a>, $<a href="postconf.5.html#local_recipient_maps">local_recipient_maps</a>,
@@ -114,15 +128,57 @@ LDAP_TABLE(5) LDAP_TABLE(5)
The port the LDAP server listens on, e.g.
server_port = 778
<b>timeout (default: 10 seconds)</b>
The number of seconds a search can take before tim-
ing out, e.g.
timeout = 5
<b>search_base (No default; you must configure this)</b>
The <a href="http://www.faqs.org/rfcs/rfc2253.html">RFC2253</a> base DN at which to conduct the search,
e.g.
search_base = dc=your, dc=com
<b>timeout (default: 10 seconds)</b>
The number of seconds a search can take before tim-
ing out, e.g.
timeout = 5
With Postfix 2.2 and later this parameter supports
the following '%' expansions:
<b>%%</b> This is replaced by a literal '%' character.
<b>%s</b> This is replaced by the input key. <a href="http://www.faqs.org/rfcs/rfc2253.html">RFC 2253</a>
quoting is used to make sure that the input
key does not add unexpected metacharacters.
<b>%u</b> When the input key is an address of the form
user@domain, <b>%u</b> is replaced by the (RFC
2253) quoted local part of the address.
Otherwise, <b>%u</b> is replaced by the entire
search string. If the localpart is empty,
the search is suppressed and returns no
results.
<b>%d</b> When the input key is an address of the form
user@domain, <b>%d</b> is replaced by the (RFC
2253) quoted domain part of the address.
Otherwise, the search is suppressed and
returns no results.
<b>%[SUD]</b> For the <b>search_base</b> parameter, the upper-
case equivalents of the above expansions
behave identically to their lower-case
counter-parts. With the <b>result_format</b> param-
eter (previously called <b>result_filter</b> see
the COMPATIBILITY section and below), they
expand to the corresponding components of
input key rather than the result value.
<b>%[1-9]</b> The patterns %1, %2, ... %9 are replaced by
the corresponding most significant component
of the input key's domain. If the input key
is <i>user@mail.example.com</i>, then %1 is <b>com</b>, %2
is <b>example</b> and %3 is <b>mail</b>. If the input key
is unqualified or does not have enough
domain components to satisfy all the speci-
fied patterns, the search is suppressed and
returns no results.
<b>query_filter (default: mailacceptinggeneralid=%s)</b>
The <a href="http://www.faqs.org/rfcs/rfc2254.html">RFC2254</a> filter used to search the directory,
@@ -133,21 +189,51 @@ LDAP_TABLE(5) LDAP_TABLE(5)
This parameter supports the following '%' expan-
sions:
<b>%s</b> This is replaced by the input key. <a href="http://www.faqs.org/rfcs/rfc2254.html">RFC 2254</a>
quoting is used to make sure that the input
key does not add unexpected metacharacters.
<b>%%</b> This is replaced by a literal '%' character.
(Postfix 2.2 and later).
<b>%s</b> This is replaced by the input key. <a href="http://www.faqs.org/rfcs/rfc2254.html">RFC 2254</a>
quoting is used to make sure that the input
key does not add unexpected metacharacters.
<b>%u</b> When the input key is an address of the form
user@domain, <b>%u</b> is replaced by the (RFC
2254) quoted local part of the address. Oth-
erwise, <b>%u</b> is replaced by the entire search
string.
user@domain, <b>%u</b> is replaced by the (RFC
2254) quoted local part of the address.
Otherwise, <b>%u</b> is replaced by the entire
search string. If the localpart is empty,
the search is suppressed and returns no
results.
<b>%d</b> When the input key is an address of the form
user@domain, <b>%d</b> is replaced by the (RFC
2254) quoted domain part of the address.
Otherwise, <b>%d</b> is replaced by the entire
search string.
user@domain, <b>%d</b> is replaced by the (RFC
2254) quoted domain part of the address.
Otherwise, the search is suppressed and
returns no results.
<b>%[SUD]</b> The upper-case equivalents of the above
expansions behave in the <b>query_filter</b> param-
eter identically to their lower-case
counter-parts. With the <b>result_format</b> param-
eter (previously called <b>result_filter</b> see
the COMPATIBILITY section and below), they
expand to the corresponding components of
input key rather than the result value.
The above %S, %U and %D expansions are
available with Postfix 2.2 and later.
<b>%[1-9]</b> The patterns %1, %2, ... %9 are replaced by
the corresponding most significant component
of the input key's domain. If the input key
is <i>user@mail.example.com</i>, then %1 is <b>com</b>, %2
is <b>example</b> and %3 is <b>mail</b>. If the input key
is unqualified or does not have enough
domain components to satisfy all the speci-
fied patterns, the saerch is suppressed and
returns no results.
The above %1, ..., %9 expansions are avail-
able with Postfix 2.2 and later.
The "domain" parameter described below limits the
input keys to addresses in matching domains. When
@@ -156,34 +242,53 @@ LDAP_TABLE(5) LDAP_TABLE(5)
matching domains are suppressed and return no
results.
NOTE: DO NOT put quotes around the query filter.
NOTE: DO NOT put quotes around the <b>query_filter</b>
parameter.
<b>result_filter (default: %s</b>)
Format template applied to result attributes. Sup-
ports the same expansions as the query_filter, and
can be easily used to append (or prepend) text.
This parameter supports the following '%' expan-
sions:
<b>result_format (default: %s</b>)
Called <b>result_filter</b> in Postfix releases prior to
2.2. Format template applied to result attributes.
Most commonly used to append (or prepend) text to
the result. This parameter supports the following
'%' expansions:
<b>%%</b> This is replaced by a literal '%' character.
(Postfix 2.2 and later).
<b>%s</b> This is replaced by the value of the result
attribute.
attribute. When result is empty it is
skipped.
<b>%u</b> When the result attribute value is an
<b>%u</b> When the result attribute value is an
address of the form user@domain, <b>%u</b> is
replaced by the local part of the address.
Otherwise, <b>%u</b> is replaced by the entire
attribute value.
replaced by the local part of the address.
When the result has an empty localpart it is
skipped.
<b>%d</b> When a result attribute value is an address
of the form user@domain, <b>%d</b> is replaced by
the domain part of the attribute value.
Otherwise, <b>%d</b> is replaced by the entire
attribute value.
<b>%d</b> When a result attribute value is an address
of the form user@domain, <b>%d</b> is replaced by
the domain part of the attribute value. When
the result is unqualified it is skipped.
For example, using "result_filter = <a href="smtp.8.html">smtp</a>:[%s]"
<b>%[SUD1-9]</b>
The upper-case and decimal digit expansions
interpolate the parts of the input key
rather than the result. Their behaviour is
identical to that described with <b>query_fil-</b>
<b>ter</b>, and in fact because the input key is
known in advance, lookups whose key does not
contain all the information specified in the
result template are suppressed and return no
results.
The above %S, %U, %D and %1, ..., %9 expan-
sions are available with Postfix 2.2 and
later.
For example, using "result_format = <a href="smtp.8.html">smtp</a>:[%s]"
allows one to use a mailHost attribute as the basis
of a <a href="transport.5.html">transport(5)</a> table. After applying the result
filter, multiple values are concatenated as comma
format, multiple values are concatenated as comma
separated strings. The expansion_limit and
size_limit parameters explained below allow one to
restrict the number of values in the result, which
@@ -193,77 +298,84 @@ LDAP_TABLE(5) LDAP_TABLE(5)
The default value <b>%s</b> specifies that each attribute
value should be used as is.
NOTE: DO NOT put quotes around the result filter!
This parameter was called <b>result_filter</b> in Postfix
releases prior to 2.2. If no "result_format" is
specified, the value of "result_filter" will be
used instead before resorting to the default value.
This provides compatibility with old configuration
files.
NOTE: DO NOT put quotes around the result format!
<b>domain (default: no domain list)</b>
This is a list of domain names, paths to files, or
dictionaries. When specified, only fully qualified
search keys with a *non-empty* localpart and a
matching domain are eligible for lookup: 'user'
lookups, bare domain lookups and "@domain" lookups
are not performed. This can significantly reduce
This is a list of domain names, paths to files, or
dictionaries. When specified, only fully qualified
search keys with a *non-empty* localpart and a
matching domain are eligible for lookup: 'user'
lookups, bare domain lookups and "@domain" lookups
are not performed. This can significantly reduce
the query load on the LDAP server.
domain = postfix.org, hash:/etc/postfix/search-
domains
It is best not to use LDAP to store the domains
It is best not to use LDAP to store the domains
eligible for LDAP lookups.
NOTE: DO NOT define this parameter for <a href="local.8.html">local(8)</a>
NOTE: DO NOT define this parameter for <a href="local.8.html">local(8)</a>
aliases.
<b>result_attribute (default: maildrop)</b>
The attribute(s) Postfix will read from any direc-
The attribute(s) Postfix will read from any direc-
tory entries returned by the lookup, to be resolved
to an email address.
result_attribute = mailbox,maildrop
result_attribute = mailbox, maildrop
<b>special_result_attribute (No default)</b>
The attribute(s) of directory entries that can con-
tain DNs or URLs. If found, a recursive subsequent
tain DNs or URLs. If found, a recursive subsequent
search is done using their values.
special_result_attribute = member
DN recursion retrieves the same result_attributes
DN recursion retrieves the same result_attributes
as the main query, including the special attributes
for further recursion. URI processing retrieves
only those attributes that are included in the URI
definition and are *also* listed in
"result_attribute". If the URI lists any of the
map's special result attributes, these are also
for further recursion. URI processing retrieves
only those attributes that are included in the URI
definition and are *also* listed in
"result_attribute". If the URI lists any of the
map's special result attributes, these are also
retrieved and used recursively.
<b>scope (default: sub)</b>
The LDAP search scope: <b>sub</b>, <b>base</b>, or <b>one</b>. These
The LDAP search scope: <b>sub</b>, <b>base</b>, or <b>one</b>. These
translate into LDAP_SCOPE_SUBTREE, LDAP_SCOPE_BASE,
and LDAP_SCOPE_ONELEVEL.
<b>bind (default: yes)</b>
Whether or not to bind to the LDAP server. Newer
Whether or not to bind to the LDAP server. Newer
LDAP implementations don't require clients to bind,
which saves time. Example:
bind = no
If you do need to bind, you might consider config-
uring Postfix to connect to the local machine on a
port that's an SSL tunnel to your LDAP server. If
your LDAP server doesn't natively support SSL, put
If you do need to bind, you might consider config-
uring Postfix to connect to the local machine on a
port that's an SSL tunnel to your LDAP server. If
your LDAP server doesn't natively support SSL, put
a tunnel (wrapper, proxy, whatever you want to call
it) on that system too. This should prevent the
password from traversing the network in the clear.
it) on that system too. This should prevent the
password from traversing the network in the clear.
<b>bind_dn (default: empty)</b>
If you do have to bind, do it with this distin-
If you do have to bind, do it with this distin-
guished name. Example:
bind_dn = uid=postfix, dc=your, dc=com
<b>bind_pw (default: empty)</b>
The password for the distinguished name above. If
The password for the distinguished name above. If
you have to use this, you probably want to make the
map configuration file readable only by the Postfix
user. When using the obsolete <a href="ldap_table.5.html">ldap</a>:ldapsource syn-
user. When using the obsolete <a href="ldap_table.5.html">ldap</a>:ldapsource syn-
tax, with map parameters in main.cf, it is not pos-
sible to securely store the bind password. This is
sible to securely store the bind password. This is
because main.cf needs to be world readable to allow
local accounts to submit mail via the sendmail com-
mand. Example:
@@ -274,43 +386,43 @@ LDAP_TABLE(5) LDAP_TABLE(5)
<b>cache_expiry (IGNORED with a warning)</b>
<b>cache_size (IGNORED with a warning)</b>
The above parameters are NO LONGER SUPPORTED by
The above parameters are NO LONGER SUPPORTED by
Postfix. Cache support has been dropped from
OpenLDAP as of release 2.1.13.
<b>recursion_limit (default: 1000)</b>
A limit on the nesting depth of DN and URL special
result attribute evaluation. The limit must be a
A limit on the nesting depth of DN and URL special
result attribute evaluation. The limit must be a
non-zero positive number.
<b>expansion_limit (default: 0)</b>
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
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.
<b>size_limit (default: $expansion_limit)</b>
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. Expan-
sion of DN and URL references involves nested LDAP
queries, each of which is separately subjected to
A limit on the number of LDAP entries returned by
any single LDAP search performed as part of the
lookup. A setting of 0 disables the limit. Expan-
sion 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 multi-
ple 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".
Note: even a single LDAP entry can generate multi-
ple lookup results, via multiple result attributes
and/or multi-valued result attributes. This limit
caps the per search resource utilization on the
LDAP server, not the final multiplicity of the
lookup result. It is analogous to the "-z" option
of "ldapsearch".
<b>dereference (default: 0)</b>
When to dereference LDAP aliases. (Note that this
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
values are those legal for the OpenLDAP/UM LDAP
implementations:
0 never
@@ -322,99 +434,99 @@ LDAP_TABLE(5) LDAP_TABLE(5)
3 always
See ldap.h or the ldap_open(3) or ldapsearch(1) man
pages for more information. And if you're using an
pages for more information. And if you're using an
LDAP package that has other possible values, please
bring it to the attention of the postfix-
bring it to the attention of the postfix-
users@postfix.org mailing list.
<b>chase_referrals (default: 0)</b>
Sets (or clears) LDAP_OPT_REFERRALS (requires LDAP
Sets (or clears) LDAP_OPT_REFERRALS (requires LDAP
version 3 support).
<b>version (default: 2)</b>
Specifies the LDAP protocol version to use.
<b>debuglevel (default: 0)</b>
What level to set for debugging in the OpenLDAP
What level to set for debugging in the OpenLDAP
libraries.
<b>LDAP SSL AND STARTTLS PARAMETERS</b>
If you're using the OpenLDAP libraries compiled with SSL
support, Postfix can connect to LDAP SSL servers and can
If you're using the OpenLDAP libraries compiled with SSL
support, Postfix can connect to LDAP SSL servers and can
issue the STARTTLS command.
LDAP SSL service can be requested by using a LDAP SSL URL
LDAP SSL service can be requested by using a LDAP SSL URL
in the server_host parameter:
server_host = ldaps://ldap.example.com:636
STARTTLS can be turned on with the start_tls parameter:
start_tls = yes
Both forms require LDAP protocol version 3, which has to
Both forms require LDAP protocol version 3, which has to
be set explicitly with:
version = 3
If any of the Postfix programs querying the map is config-
ured in master.cf to run chrooted, all the certificates
ured in master.cf to run chrooted, all the certificates
and keys involved have to be copied to the chroot jail. Of
course, the private keys should only be readable by the
course, the private keys should only be readable by the
user "postfix".
The following parameters are relevant to LDAP SSL and
The following parameters are relevant to LDAP SSL and
STARTTLS:
<b>start_tls (default: no)</b>
Whether or not to issue STARTTLS upon connection to
the server. Don't set this with LDAP SSL (the SSL
the server. Don't set this with LDAP SSL (the SSL
session is setup automatically when the TCP connec-
tion is opened).
<b>tls_ca_cert_dir (No default; set either this or</b>
<b>tls_ca_cert_dir (No default; set either this or</b>
<b>tls_ca_cert_file)</b>
Directory containing X509 Certificate Authority
certificates in PEM format which are to be recog-
nized by the client in SSL/TLS connections. The
files each contain one CA certificate. The files
are looked up by the CA subject name hash value,
which must hence be available. If more than one CA
certificate with the same name hash value exist,
the extension must be different (e.g. 9d66eef0.0,
9d66eef0.1 etc). The search is performed in the
ordering of the extension number, regardless of
certificates in PEM format which are to be recog-
nized by the client in SSL/TLS connections. The
files each contain one CA certificate. The files
are looked up by the CA subject name hash value,
which must hence be available. If more than one CA
certificate with the same name hash value exist,
the extension must be different (e.g. 9d66eef0.0,
9d66eef0.1 etc). The search is performed in the
ordering of the extension number, regardless of
other properties of the certificates. Use the
c_rehash utility (from the OpenSSL distribution) to
create the necessary links.
<b>tls_ca_cert_file (No default; set either this or</b>
<b>tls_ca_cert_file (No default; set either this or</b>
<b>tls_ca_cert_dir)</b>
File containing the X509 Certificate Authority cer-
tificates in PEM format which are to be recognized
by the client in SSL/TLS connections. This setting
tificates in PEM format which are to be recognized
by the client in SSL/TLS connections. This setting
takes precedence over tls_ca_cert_dir.
<b>tls_cert (No default; you must set this)</b>
File containing client's X509 certificate to be
File containing client's X509 certificate to be
used by the client in SSL/ TLS connections.
<b>tls_key (No default; you must set this)</b>
File containing the private key corresponding to
File containing the private key corresponding to
the above tls_cert.
<b>tls_require_cert (default: no)</b>
Whether or not to request server's X509 certificate
and check its validity when establishing SSL/TLS
and check its validity when establishing SSL/TLS
connections.
<b>tls_random_file (No default)</b>
Path of a file to obtain random bits from when
/dev/[u]random is not available, to be used by the
Path of a file to obtain random bits from when
/dev/[u]random is not available, to be used by the
client in SSL/TLS connections.
<b>tls_cipher_suite (No default)</b>
Cipher suite to use in SSL/TLS negotiations.
<b>EXAMPLE</b>
Here's a basic example for using LDAP to look up <a href="local.8.html">local(8)</a>
Here's a basic example for using LDAP to look up <a href="local.8.html">local(8)</a>
aliases. Assume that in main.cf, you have:
<a href="postconf.5.html#alias_maps">alias_maps</a> = hash:/etc/aliases,
<a href="ldap_table.5.html">ldap</a>:/etc/postfix/ldap-aliases.cf
@@ -423,14 +535,14 @@ LDAP_TABLE(5) LDAP_TABLE(5)
server_host = ldap.my.com
search_base = dc=my, dc=com
Upon receiving mail for a local address "ldapuser" that
isn't found in the /etc/aliases database, Postfix will
search the LDAP server listening at port 389 on
ldap.my.com. It will bind anonymously, search for any
directory entries whose mailacceptinggeneralid attribute
is "ldapuser", read the "maildrop" attributes of those
found, and build a list of their maildrops, which will be
treated as <a href="http://www.faqs.org/rfcs/rfc822.html">RFC822</a> addresses to which the message will be
Upon receiving mail for a local address "ldapuser" that
isn't found in the /etc/aliases database, Postfix will
search the LDAP server listening at port 389 on
ldap.my.com. It will bind anonymously, search for any
directory entries whose mailacceptinggeneralid attribute
is "ldapuser", read the "maildrop" attributes of those
found, and build a list of their maildrops, which will be
treated as <a href="http://www.faqs.org/rfcs/rfc822.html">RFC822</a> addresses to which the message will be
delivered.
<b>SEE ALSO</b>
@@ -444,13 +556,13 @@ LDAP_TABLE(5) LDAP_TABLE(5)
<a href="LDAP_README.html">LDAP_README</a>, Postfix LDAP client guide
<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>
Carsten Hoeger, Hery Rakotoarisoa, John Hensley, Keith
Stevenson, LaMont Jones, Liviu Daia, Manuel Guesdon, Mike
Mattice, Prabhat K Singh, Sami Haahtinen, Samuel Tardieu,
Carsten Hoeger, Hery Rakotoarisoa, John Hensley, Keith
Stevenson, LaMont Jones, Liviu Daia, Manuel Guesdon, Mike
Mattice, Prabhat K Singh, Sami Haahtinen, Samuel Tardieu,
Victor Duchovni, and many others.
LDAP_TABLE(5)

View File

@@ -28,7 +28,7 @@ MYSQL_TABLE(5) MYSQL_TABLE(5)
as the Postfix main.cf file, and can specify the parame-
ters described below.
<b>ALTERNATIVE CONFIGURATION</b>
<b>BACKWARDS COMPATIBILITY</b>
For compatibility with other Postfix lookup tables, MySQL
parameters can also be defined in main.cf. In order to do
that, specify as MySQL source a name that doesn't begin
@@ -44,6 +44,27 @@ MYSQL_TABLE(5) MYSQL_TABLE(5)
Support for this form will be removed in a future Postfix
version.
Postfix 2.2 has enhanced query interfaces for MySQL and
PostreSQL, these include features previously available
only in the Postfix LDAP client. In the new interface the
SQL query is specified via a single <b>query</b> parameter
(described in more detail below). When the new <b>query</b>
parameter is not specified in the map definition, Postfix
reverts to the old interface, with the SQL query con-
structed from the <b>select_field</b>, <b>table</b>, <b>where_field</b> and
<b>additional_conditions</b> parameters. The old interface will
be gradually phased out. To migrate to the new interface
set:
<b>query</b> = SELECT [<i>select</i><b>_</b><i>field</i>]
FROM [<i>table</i>]
WHERE [<i>where</i><b>_</b><i>field</i>] = '%s'
[<i>additional</i><b>_</b><i>conditions</i>]
Insert the value, not the name, of each legacy parameter.
Note that the <b>additional_conditions</b> parameter is optional
and if not empty, will always start with <b>AND</b>.
<b>LIST MEMBERSHIP</b>
When using SQL to store lists such as $<a href="postconf.5.html#mynetworks">mynetworks</a>, $<a href="postconf.5.html#mydestination">mydes</a>-
<a href="postconf.5.html#mydestination">tination</a>, $<a href="postconf.5.html#relay_domains">relay_domains</a>, $<a href="postconf.5.html#local_recipient_maps">local_recipient_maps</a>, etc., it
@@ -91,29 +112,189 @@ MYSQL_TABLE(5) MYSQL_TABLE(5)
<b>dbname</b> The database name on the servers. Example:
dbname = customer_database
The following parameters are used to fill in a SELECT
query template of the form:
select [<b>select_field</b>] from [<b>table</b>] where
[<b>where_field</b>] = '$lookup' [<b>additional_conditions</b>]
<b>query</b> The SQL query template used to search the database,
where <b>%s</b> is a substitute for the address Postfix is
trying to resolve, e.g.
query = SELECT replacement FROM aliases WHERE
mailbox = '%s'
$lookup contains the search string, and is escaped so if
it contains single quotes or other odd characters, it will
not cause a parse error, or worse, a security problem.
This parameter supports the following '%' expan-
sions:
<b>%%</b> This is replaced by a literal '%' character.
<b>%s</b> This is replaced by the input key. SQL
quoting is used to make sure that the input
key does not add unexpected metacharacters.
<b>%u</b> When the input key is an address of the form
user@domain, <b>%u</b> is replaced by the SQL
quoted local part of the address. Other-
wise, <b>%u</b> is replaced by the entire search
string. If the localpart is empty, the
query is suppressed and returns no results.
<b>%d</b> When the input key is an address of the form
user@domain, <b>%d</b> is replaced by the SQL
quoted domain part of the address. Other-
wise, the query is suppressed and returns no
results.
<b>%[SUD]</b> The upper-case equivalents of the above
expansions behave in the <b>query</b> parameter
identically to their lower-case counter-
parts. With the <b>result_format</b> parameter
(see below), they expand the input key
rather than the result value.
<b>%[1-9]</b> The patterns %1, %2, ... %9 are replaced by
the corresponding most significant component
of the input key's domain. If the input key
is <i>user@mail.example.com</i>, then %1 is <b>com</b>, %2
is <b>example</b> and %3 is <b>mail</b>. If the input key
is unqualified or does not have enough
domain components to satisfy all the speci-
fied patterns, the query is suppressed and
returns no results.
The <b>domain</b> parameter described below limits the
input keys to addresses in matching domains. When
the <b>domain</b> parameter is non-empty, SQL queries for
unqualified addresses or addresses in non-matching
domains are suppressed and return no results.
This parameter is available with Postfix 2.2. In
prior releases the SQL query was built from the
separate parameters: <b>select_field</b>, <b>table</b>,
<b>where_field</b> and <b>additional_conditions</b>. The mapping
from the old parameters to the equivalent query is:
SELECT [<b>select_field</b>]
FROM [<b>table</b>]
WHERE [<b>where_field</b>] = '%s'
[<b>additional_conditions</b>]
The '%s' in the <b>WHERE</b> clause expands to the escaped
search string. With Postfix 2.2 these legacy
parameters are used if the <b>query</b> parameter is not
specified.
NOTE: DO NOT put quotes around the query parameter.
<b>result_format (default: %s</b>)
Format template applied to result attributes. Most
commonly used to append (or prepend) text to the
result. This parameter supports the following '%'
expansions:
<b>%%</b> This is replaced by a literal '%' character.
<b>%s</b> This is replaced by the value of the result
attribute. When result is empty it is
skipped.
<b>%u</b> When the result attribute value is an
address of the form user@domain, <b>%u</b> is
replaced by the local part of the address.
When the result has an empty localpart it is
skipped.
<b>%d</b> When a result attribute value is an address
of the form user@domain, <b>%d</b> is replaced by
the domain part of the attribute value. When
the result is unqualified it is skipped.
<b>%[SUD1-9]</b>
The upper-case and decimal digit expansions
interpolate the parts of the input key
rather than the result. Their behaviour is
identical to that described with <b>query</b>, and
in fact because the input key is known in
advance, queries whose key does not contain
all the information specified in the result
template are suppressed and return no
results.
For example, using "result_format = <a href="smtp.8.html">smtp</a>:[%s]"
allows one to use a mailHost attribute as the basis
of a <a href="transport.5.html">transport(5)</a> table. After applying the result
format, multiple values are concatenated as comma
separated strings. The expansion_limit and parame-
ter explained below allows one to restrict the num-
ber of values in the result, which is especially
useful for maps that must return at most one value.
The default value <b>%s</b> specifies that each result
value should be used as is.
This parameter is available with Postfix 2.2 and
later.
NOTE: DO NOT put quotes around the result format!
<b>domain (default: no domain list)</b>
This is a list of domain names, paths to files, or
dictionaries. When specified, only fully qualified
search keys with a *non-empty* localpart and a
matching domain are eligible for lookup: 'user'
lookups, bare domain lookups and "@domain" lookups
are not performed. This can significantly reduce
the query load on the MySQL server.
domain = postfix.org, hash:/etc/postfix/search-
domains
It is best not to use SQL to store the domains eli-
gible for SQL lookups.
This parameter is available with Postfix 2.2 and
later.
NOTE: DO NOT define this parameter for <a href="local.8.html">local(8)</a>
aliases, because the input keys are always unquali-
fied.
<b>expansion_limit (default: 0)</b>
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.
The following parameters can be used to fill in a SELECT
template statement of the form:
SELECT [<b>select_field</b>]
FROM [<b>table</b>]
WHERE [<b>where_field</b>] = '%s'
[<b>additional_conditions</b>]
The specifier %s is replaced by the search string, and is
escaped so if it contains single quotes or other odd char-
acters, it will not cause a parse error, or worse, a secu-
rity problem.
As of Postfix 2.2 this interface is obsolete, it is
replaced by the more general <b>query</b> interface described
above. If the <b>query</b> parameter is defined, the legacy
parameters are ignored. Please migrate to the new inter-
face as the legacy interface may be removed in a future
release.
<b>select_field</b>
The SQL "select" parameter. Example:
select_field = forw_addr
<b>select_field</b> = forw_addr
<b>table</b> The SQL "select .. from" table name. Example:
table = mxaliases
<b>table</b> = mxaliases
<b>where_field</b>
The SQL "select .. where" parameter. Example:
where_field = alias
<b>where_field</b> = alias
<b>additional_conditions</b>
Additional conditions to the SQL query. Example:
additional_conditions = and status = 'paid'
<b>additional_conditions</b> = AND status = 'paid'
<b>SEE ALSO</b>
<a href="postmap.1.html">postmap(1)</a>, Postfix lookup table maintenance
@@ -126,7 +307,7 @@ MYSQL_TABLE(5) MYSQL_TABLE(5)
<a href="MYSQL_README.html">MYSQL_README</a>, Postfix MYSQL client guide
<b>LICENSE</b>
The Secure Mailer license must be distributed with this
The Secure Mailer license must be distributed with this
software.
<b>HISTORY</b>

View File

@@ -29,7 +29,7 @@ PGSQL_TABLE(5) PGSQL_TABLE(5)
as the Postfix main.cf file, and can specify the parame-
ters described below.
<b>ALTERNATIVE CONFIGURATION</b>
<b>BACKWARDS COMPATIBILITY</b>
For compatibility with other Postfix lookup tables, Post-
greSQL parameters can also be defined in main.cf. In
order to do that, specify as PostgreSQL source a name that
@@ -45,44 +45,72 @@ PGSQL_TABLE(5) PGSQL_TABLE(5)
readable. Support for this form will be removed in a
future Postfix version.
Postfix 2.2 has enhanced query interfaces for MySQL and
PostgreSQL, these include features previously available
only in the Postfix LDAP client. In the new interface the
SQL query is specified via a single <b>query</b> parameter
(described in more detail below). In Postfix 2.1 the
parameter precedence was, from highest to lowest,
<b>select_function</b>, <b>query</b> and finally <b>select_field</b>, ...
With Postfix 2.2 the <b>query</b> parameter has highest prece-
dence, and is used in preference to the still supported,
but slated to be phased out, <b>select_function</b>,
<b>select_field</b>, <b>table</b>, <b>where_field</b> and <b>additional_conditions</b>
parameters. To migrate to the new interface set:
<b>query</b> = SELECT <i>select</i><b>_</b><i>function</i>('%s')
or in the absense of <b>selection_function</b>, the lower prece-
dence:
<b>query</b> = SELECT <i>select</i><b>_</b><i>field</i>
FROM <i>table</i>
WHERE <i>where</i><b>_</b><i>field</i> = '%s'
<i>additional</i><b>_</b><i>conditions</i>
Use the value, not the name, of each legacy parameter.
Note that the <b>additional_conditions</b> parameter is optional
and if not empty, will always start with <b>AND</b>.
<b>LIST MEMBERSHIP</b>
When using SQL to store lists such as $<a href="postconf.5.html#mynetworks">mynetworks</a>, $<a href="postconf.5.html#mydestination">mydes</a>-
<a href="postconf.5.html#mydestination">tination</a>, $<a href="postconf.5.html#relay_domains">relay_domains</a>, $<a href="postconf.5.html#local_recipient_maps">local_recipient_maps</a>, etc., it
is important to understand that the table must store each
list member as a separate key. The table lookup verifies
the *existence* of the key. See "Postfix lists versus
tables" in the <a href="DATABASE_README.html">DATABASE_README</a> document for a discussion.
<a href="postconf.5.html#mydestination">tination</a>, $<a href="postconf.5.html#relay_domains">relay_domains</a>, $<a href="postconf.5.html#local_recipient_maps">local_recipient_maps</a>, etc., it
is important to understand that the table must store each
list member as a separate key. The table lookup verifies
the *existence* of the key. See "Postfix lists versus
tables" in the <a href="DATABASE_README.html">DATABASE_README</a> document for a discussion.
Do NOT create tables that return the full list of domains
in $<a href="postconf.5.html#mydestination">mydestination</a> or $<a href="postconf.5.html#relay_domains">relay_domains</a> etc., or IP addresses
Do NOT create tables that return the full list of domains
in $<a href="postconf.5.html#mydestination">mydestination</a> or $<a href="postconf.5.html#relay_domains">relay_domains</a> etc., or IP addresses
in $<a href="postconf.5.html#mynetworks">mynetworks</a>.
DO create tables with each matching item as a key and with
an arbitrary value. With SQL databases it is not uncommon
an arbitrary value. With SQL databases it is not uncommon
to return the key itself or a constant value.
<b>PGSQL PARAMETERS</b>
<b>hosts</b> The hosts that Postfix will try to connect to and
<b>hosts</b> The hosts that Postfix will try to connect to and
query from. Specify <i>unix:</i> for UNIX-domain sockets,
<i>inet:</i> for TCP connections (default). Example:
hosts = host1.some.domain host2.some.domain
hosts = unix:/file/name
The hosts are tried in random order, with all con-
The hosts are tried in random order, with all con-
nections over UNIX domain sockets being tried
before those over TCP. The connections are auto-
matically closed after being idle for about 1
before those over TCP. The connections are auto-
matically closed after being idle for about 1
minute, and are re-opened as necessary.
NOTE: the <i>unix:</i> and <i>inet:</i> prefixes are accepted for
backwards compatibility reasons, but are actually
backwards compatibility reasons, but are actually
ignored. The PostgreSQL client library will always
try to connect to an UNIX socket if the name starts
with a slash, and will try a TCP connection other-
with a slash, and will try a TCP connection other-
wise.
<b>user, password</b>
The user name and password to log into the pgsql
The user name and password to log into the pgsql
server. Example:
user = someone
password = some_password
@@ -90,58 +118,155 @@ PGSQL_TABLE(5) PGSQL_TABLE(5)
<b>dbname</b> The database name on the servers. Example:
dbname = customer_database
The following parameters can be used to fill in a SELECT
template statement of the form:
select [<b>select_field</b>] from [<b>table</b>] where
[<b>where_field</b>] = '$lookup' [<b>additional_conditions</b>]
<b>query</b> The SQL query template used to search the database,
where <b>%s</b> is a substitute for the address Postfix is
trying to resolve, e.g.
query = SELECT replacement FROM aliases WHERE
mailbox = '%s'
$lookup contains the search string, and is escaped so if
it contains single quotes or other odd characters, it will
not cause a parse error, or worse, a security problem.
<b>select_field</b>
The SQL "select" parameter. Example:
select_field = forw_addr
<b>table</b> The SQL "select .. from" table name. Example:
table = mxaliases
<b>where_field</b>
The SQL "select .. where" parameter. Example:
where_field = alias
<b>additional_conditions</b>
Additional conditions to the SQL query. Example:
additional_conditions = and status = 'paid'
The following parameters provide ways to override the
default SELECT statement. Setting them will instruct
Postfix to ignore the above <b>table</b>, <b>select_field</b>,
<b>where_field</b> and <b>additional_conditions</b> parameters:
<b>query</b> This parameter specifies a complete SQL query.
Example:
query = select forw_addr from mxaliases where
alias = '%s' and status = 'paid'
This parameter supports the following '%' expan-
This parameter supports the following '%' expan-
sions:
<b>%s</b> This is replaced by the input key. Quoting
is used to make sure that the input key does
not add unexpected metacharacters.
<b>%%</b> This is replaced by a literal '%' character.
(Postfix 2.2 and later)
<b>%s</b> This is replaced by the input key. SQL
quoting is used to make sure that the input
key does not add unexpected metacharacters.
<b>%u</b> When the input key is an address of the form
user@domain, <b>%u</b> is replaced by the quoted
local part of the address. If no domain is
specified, <b>%u</b> is replaced by the entire
search string.
user@domain, <b>%u</b> is replaced by the SQL
quoted local part of the address. Other-
wise, <b>%u</b> is replaced by the entire search
string. If the localpart is empty, the
query is suppressed and returns no results.
<b>%d</b> When the input key is an address of the form
user@domain, <b>%d</b> is replaced by the quoted
domain part of the address. When the input
key has no domain qualifier, <b>%d</b> is replaced
by the entire search string.
user@domain, <b>%d</b> is replaced by the SQL
quoted domain part of the address. Other-
wise, the query is suppressed and returns no
results.
<b>%[SUD]</b> The upper-case equivalents of the above
expansions behave in the <b>query</b> parameter
identically to their lower-case counter-
parts. With the <b>result_format</b> parameter
(see below), they expand the input key
rather than the result value.
The above %S, %U and %D expansions are
available with Postfix 2.2 and later
<b>%[1-9]</b> The patterns %1, %2, ... %9 are replaced by
the corresponding most significant component
of the input key's domain. If the input key
is <i>user@mail.example.com</i>, then %1 is <b>com</b>, %2
is <b>example</b> and %3 is <b>mail</b>. If the input key
is unqualified or does not have enough
domain components to satisfy all the speci-
fied patterns, the query is suppressed and
returns no results.
The above %1, ... %9 expansions are avail-
able with Postfix 2.2 and later
The <b>domain</b> parameter described below limits the
input keys to addresses in matching domains. When
the <b>domain</b> parameter is non-empty, SQL queries for
unqualified addresses or addresses in non-matching
domains are suppressed and return no results.
The precedence of this parameter has changed with
Postfix 2.2, in prior releases the precedence was,
from highest to lowest, <b>select_function</b>, <b>query</b>,
<b>select_field</b>, ...
With Postfix 2.2 the <b>query</b> parameter has highest
precedence, see COMPATIBILITY above.
NOTE: DO NOT put quotes around the <b>query</b> parameter.
<b>result_format (default: %s</b>)
Format template applied to result attributes. Most
commonly used to append (or prepend) text to the
result. This parameter supports the following '%'
expansions:
<b>%%</b> This is replaced by a literal '%' character.
<b>%s</b> This is replaced by the value of the result
attribute. When result is empty it is
skipped.
<b>%u</b> When the result attribute value is an
address of the form user@domain, <b>%u</b> is
replaced by the local part of the address.
When the result has an empty localpart it is
skipped.
<b>%d</b> When a result attribute value is an address
of the form user@domain, <b>%d</b> is replaced by
the domain part of the attribute value. When
the result is unqualified it is skipped.
<b>%[SUD1-9]</b>
The upper-case and decimal digit expansions
interpolate the parts of the input key
rather than the result. Their behaviour is
identical to that described with <b>query</b>, and
in fact because the input key is known in
advance, queries whose key does not contain
all the information specified in the result
template are suppressed and return no
results.
For example, using "result_format = <a href="smtp.8.html">smtp</a>:[%s]"
allows one to use a mailHost attribute as the basis
of a <a href="transport.5.html">transport(5)</a> table. After applying the result
format, multiple values are concatenated as comma
separated strings. The expansion_limit and parame-
ter explained below allows one to restrict the num-
ber of values in the result, which is especially
useful for maps that must return at most one value.
The default value <b>%s</b> specifies that each result
value should be used as is.
This parameter is available with Postfix 2.2 and
later.
NOTE: DO NOT put quotes around the result format!
<b>domain (default: no domain list)</b>
This is a list of domain names, paths to files, or
dictionaries. When specified, only fully qualified
search keys with a *non-empty* localpart and a
matching domain are eligible for lookup: 'user'
lookups, bare domain lookups and "@domain" lookups
are not performed. This can significantly reduce
the query load on the PostgreSQL server.
domain = postfix.org, hash:/etc/postfix/search-
domains
It is best not to use SQL to store the domains eli-
gible for SQL lookups.
This parameter is available with Postfix 2.2 and
later.
NOTE: DO NOT define this parameter for <a href="local.8.html">local(8)</a>
aliases, because the input keys are always unquali-
fied.
<b>expansion_limit (default: 0)</b>
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.
Pre-Postfix 2.2 legacy interfaces:
<b>select_function</b>
This parameter specifies a database function name.
@@ -149,18 +274,51 @@ PGSQL_TABLE(5) PGSQL_TABLE(5)
select_function = my_lookup_user_alias
This is equivalent to:
query = select my_lookup_user_alias('%s')
query = SELECT my_lookup_user_alias('%s')
and overrides both the <b>query</b> parameter and the ta-
ble-related fields above.
This parameter overrides the legacy table-related
fields (described below). With Postfix versions
prior to 2.2, it also overrides the <b>query</b> parame-
ter. Starting with Postfix 2.2, the <b>query</b> parameter
has highest precedence, and this parameter is dep-
recated. Please migrate to the new <b>query</b> interface
as this interface is slated to be phased out.
As of June 2002, if the function returns a single
row and a single column AND that value is NULL,
then the result will be treated as if the key was
not in the dictionary.
The following parameters (with lower precedence than the
<b>select_function</b> interface described above) can be used to
build the SQL select statement as follows:
Future versions will allow functions to return
result sets.
SELECT [<b>select_field</b>]
FROM [<b>table</b>]
WHERE [<b>where_field</b>] = '%s'
[<b>additional_conditions</b>]
The specifier %s is replaced with each lookup by the
lookup key and is escaped so if it contains single quotes
or other odd characters, it will not cause a parse error,
or worse, a security problem.
Starting with Postfix 2.2, this interface is obsoleted by
the more general <b>query</b> interface described above. If
higher precedence the <b>query</b> or <b>select_function</b> parameters
described above are defined, these parameters are ignored.
Please migrate to the new <b>query</b> interface as this inter-
face is slated to be phased out.
<b>select_field</b>
The SQL "select" parameter. Example:
<b>select_field</b> = forw_addr
<b>table</b> The SQL "select .. from" table name. Example:
<b>table</b> = mxaliases
<b>where_field</b>
The SQL "select .. where" parameter. Example:
<b>where_field</b> = alias
<b>additional_conditions</b>
Additional conditions to the SQL query. Example:
<b>additional_conditions</b> = AND status = 'paid'
<b>SEE ALSO</b>
<a href="postmap.1.html">postmap(1)</a>, Postfix lookup table manager
@@ -173,7 +331,7 @@ PGSQL_TABLE(5) PGSQL_TABLE(5)
<a href="PGSQL_README.html">PGSQL_README</a>, Postfix PostgreSQL client guide
<b>LICENSE</b>
The Secure Mailer license must be distributed with this
The Secure Mailer license must be distributed with this
software.
<b>HISTORY</b>

View File

@@ -559,6 +559,24 @@ Enable the rewriting of the form "user%domain" to "user@domain".
This is enabled by default.
</p>
<p> Note: With Postfix version 2.2, message header address rewriting
happens only when one of the following conditions is true: </p>
<ul>
<li> The message is received with the Postfix <a href="sendmail.1.html">sendmail(1)</a> command,
<li> The message is received from a network client that matches
$<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a>,
<li> The message is received from the network, and the
<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> parameter specifies a non-empty value.
</ul>
<p> To get the behavior before Postfix 2.2, specify
"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all". </p>
<p>
Example:
</p>
@@ -689,10 +707,28 @@ append the string "@$<a href="postconf.5.html#remote_header_rewrite_domain">remo
</p>
<p>
This feature is enabled by default and must not be turned off.
Note 1: This feature is enabled by default and must not be turned off.
Postfix does not support domain-less addresses.
</p>
<p> Note 2: With Postfix version 2.2, message header address rewriting
happens only when one of the following conditions is true: </p>
<ul>
<li> The message is received with the Postfix <a href="sendmail.1.html">sendmail(1)</a> command,
<li> The message is received from a network client that matches
$<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a>,
<li> The message is received from the network, and the
<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> parameter specifies a non-empty value.
</ul>
<p> To get the behavior before Postfix 2.2, specify
"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all". </p>
</DD>
@@ -707,11 +743,29 @@ instead.
</p>
<p>
This feature is enabled by default. If disabled, users will not be
Note 1: This feature is enabled by default. If disabled, users will not be
able to send mail to "user@partialdomainname" but will have to
specify full domain names instead.
</p>
<p> Note 2: With Postfix version 2.2, message header address rewriting
happens only when one of the following conditions is true: </p>
<ul>
<li> The message is received with the Postfix <a href="sendmail.1.html">sendmail(1)</a> command,
<li> The message is received from a network client that matches
$<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a>,
<li> The message is received from the network, and the
<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> parameter specifies a non-empty value.
</ul>
<p> To get the behavior before Postfix 2.2, specify
"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all". </p>
</DD>
@@ -1105,6 +1159,24 @@ will become visible after a minute or so. Use "<b>postfix reload</b>"
to eliminate the delay.
</p>
<p> Note: with Postfix version 2.2, message header address mapping
happens only when message header address rewriting is enabled: </p>
<ul>
<li> The message is received with the Postfix <a href="sendmail.1.html">sendmail(1)</a> command,
<li> The message is received from a network client that matches
$<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a>,
<li> The message is received from the network, and the
<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> parameter specifies a non-empty value.
</ul>
<p> To get the behavior before Postfix 2.2, specify
"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all". </p>
<p>
Examples:
</p>
@@ -2659,7 +2731,8 @@ Examples:
<p> The Internet protocols Postfix will attempt to use when making
or accepting connections. Specify one or more of "ipv4"
or "ipv6", separated by whitespace or commas. The form
"all" is equivalent to "ipv4, ipv6". </p>
"all" is equivalent to "ipv4, ipv6" or "ipv4", depending
on whether the operating system implements IPv6. </p>
<p> This feature is available in Postfix version 2.2 and later. </p>
@@ -3881,6 +3954,24 @@ does not change "user@any.thing.foo.example.com" or "user@foo.example.com",
but strips "user@any.thing.else.example.com" to "user@example.com".
</p>
<p> Note: with Postfix version 2.2, message header address masquerading
happens only when message header address rewriting is enabled: </p>
<ul>
<li> The message is received with the Postfix <a href="sendmail.1.html">sendmail(1)</a> command,
<li> The message is received from a network client that matches
$<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a>,
<li> The message is received from the network, and the
<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> parameter specifies a non-empty value.
</ul>
<p> To get the behavior before Postfix 2.2, specify
"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all". </p>
<p>
Example:
</p>
@@ -9044,6 +9135,24 @@ necessary if your machine is connected to UUCP networks. It is
enabled by default.
</p>
<p> Note: With Postfix version 2.2, message header address rewriting
happens only when one of the following conditions is true: </p>
<ul>
<li> The message is received with the Postfix <a href="sendmail.1.html">sendmail(1)</a> command,
<li> The message is received from a network client that matches
$<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a>,
<li> The message is received from the network, and the
<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> parameter specifies a non-empty value.
</ul>
<p> To get the behavior before Postfix 2.2, specify
"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all". </p>
<p>
Example:
</p>

View File

@@ -53,6 +53,18 @@ parameter below would be defined in main.cf as
Note: with this form, the passwords for the LDAP sources are
written in main.cf, which is normally world-readable. Support
for this form will be removed in a future Postfix version.
Postfix 2.2 has enhanced query interfaces for MySQL and PostgreSQL,
these now include features previously available only in the
Postfix LDAP client. This work also created an opportunity for
improvements in the LDAP interface. The primary compatibility
issue is that \fBresult_filter\fR (a name that has caused some
confusion as to its meaning in the past) has been renamed to
\fBresult_format\fR. For backwards compatibility with the pre
2.2 LDAP client, \fBresult_filter\fR can for now be used instead
of \fBresult_format\fR, when the latter parameter is not also set.
The new name better reflects the function of the parameter. This
compatibility interface may be removed in a future release.
.SH "LIST MEMBERSHIP"
.na
.nf
@@ -128,14 +140,50 @@ server_host = ldapi://%2Fsome%2Fpath
The port the LDAP server listens on, e.g.
.ti +4
server_port = 778
.IP "\fBsearch_base (No default; you must configure this)\fR"
The RFC2253 base DN at which to conduct the search, e.g.
.ti +4
search_base = dc=your, dc=com
.IP "\fBtimeout (default: 10 seconds)\fR"
The number of seconds a search can take before timing out, e.g.
.ti +4
timeout = 5
.IP "\fBsearch_base (No default; you must configure this)\fR"
The RFC2253 base DN at which to conduct the search, e.g.
.ti +4
search_base = dc=your, dc=com
.IP
With Postfix 2.2 and later this parameter supports the
following '%' expansions:
.RS
.IP "\fB\fB%%\fR\fR"
This is replaced by a literal '%' character.
.IP "\fB\fB%s\fR\fR"
This is replaced by the input key.
RFC 2253 quoting is used to make sure that the input key
does not add unexpected metacharacters.
.IP "\fB\fB%u\fR\fR"
When the input key is an address of the form user@domain, \fB%u\fR
is replaced by the (RFC 2253) quoted local part of the address.
Otherwise, \fB%u\fR is replaced by the entire search string.
If the localpart is empty, the search is suppressed and returns
no results.
.IP "\fB\fB%d\fR\fR"
When the input key is an address of the form user@domain, \fB%d\fR
is replaced by the (RFC 2253) quoted domain part of the address.
Otherwise, the search is suppressed and returns no results.
.IP "\fB\fB%[SUD]\fR\fR"
For the \fBsearch_base\fR parameter, the upper-case equivalents
of the above expansions behave identically to their lower-case
counter-parts. With the \fBresult_format\fR parameter (previously
called \fBresult_filter\fR see the COMPATIBILITY section and below),
they expand to the corresponding components of input key rather
than the result value.
.IP "\fB\fB%[1-9]\fR\fR"
The patterns %1, %2, ... %9 are replaced by the corresponding
most significant component of the input key's domain. If the
input key is \fIuser@mail.example.com\fR, then %1 is \fBcom\fR,
%2 is \fBexample\fR and %3 is \fBmail\fR. If the input key is
unqualified or does not have enough domain components to satisfy
all the specified patterns, the search is suppressed and returns
no results.
.RE
.IP "\fBquery_filter (default: mailacceptinggeneralid=%s)\fR"
The RFC2254 filter used to search the directory, where \fB%s\fR
is a substitute for the address Postfix is trying to resolve,
@@ -145,20 +193,43 @@ query_filter = (&(mail=%s)(paid_up=true))
This parameter supports the following '%' expansions:
.RS
.IP "\fB\fB%%\fR\fR"
This is replaced by a literal '%' character. (Postfix 2.2 and later).
.IP "\fB\fB%s\fR\fR"
This is replaced by the input key. RFC 2254 quoting is used
to make sure that the input key does not add unexpected
metacharacters.
This is replaced by the input key.
RFC 2254 quoting is used to make sure that the input key
does not add unexpected metacharacters.
.IP "\fB\fB%u\fR\fR"
When the input key is an address of the form user@domain,
\fB%u\fR is replaced by the (RFC 2254) quoted local part of the
address. Otherwise, \fB%u\fR is replaced by the entire
search string.
When the input key is an address of the form user@domain, \fB%u\fR
is replaced by the (RFC 2254) quoted local part of the address.
Otherwise, \fB%u\fR is replaced by the entire search string.
If the localpart is empty, the search is suppressed and returns
no results.
.IP "\fB\fB%d\fR\fR"
When the input key is an address of the form user@domain,
\fB%d\fR is replaced by the (RFC 2254) quoted domain part of the
address. Otherwise, \fB%d\fR is replaced by the entire
search string.
When the input key is an address of the form user@domain, \fB%d\fR
is replaced by the (RFC 2254) quoted domain part of the address.
Otherwise, the search is suppressed and returns no results.
.IP "\fB\fB%[SUD]\fR\fR"
The upper-case equivalents of the above expansions behave in the
\fBquery_filter\fR parameter identically to their lower-case
counter-parts. With the \fBresult_format\fR parameter (previously
called \fBresult_filter\fR see the COMPATIBILITY section and below),
they expand to the corresponding components of input key rather
than the result value.
.IP
The above %S, %U and %D expansions are available with Postfix 2.2
and later.
.IP "\fB\fB%[1-9]\fR\fR"
The patterns %1, %2, ... %9 are replaced by the corresponding
most significant component of the input key's domain. If the
input key is \fIuser@mail.example.com\fR, then %1 is \fBcom\fR,
%2 is \fBexample\fR and %3 is \fBmail\fR. If the input key is
unqualified or does not have enough domain components to satisfy
all the specified patterns, the saerch is suppressed and returns
no results.
.IP
The above %1, ..., %9 expansions are available with Postfix 2.2
and later.
.RE
.IP
The "domain" parameter described below limits the input
@@ -167,30 +238,42 @@ parameter is non-empty, LDAP queries for unqualified
addresses or addresses in non-matching domains are suppressed
and return no results.
NOTE: DO NOT put quotes around the query filter.
.IP "\fBresult_filter (default: \fB%s\fR)\fR"
Format template applied to result attributes. Supports the
same expansions as the query_filter, and can be easily used
to append (or prepend) text. This parameter supports the
following '%' expansions:
NOTE: DO NOT put quotes around the \fBquery_filter\fR parameter.
.IP "\fBresult_format (default: \fB%s\fR)\fR"
Called \fBresult_filter\fR in Postfix releases prior to 2.2.
Format template applied to result attributes. Most commonly used
to append (or prepend) text to the result. This parameter supports
the following '%' expansions:
.RS
.IP "\fB\fB%%\fR\fR"
This is replaced by a literal '%' character. (Postfix 2.2 and later).
.IP "\fB\fB%s\fR\fR"
This is replaced by the value of the result attribute.
This is replaced by the value of the result attribute. When
result is empty it is skipped.
.IP "\fB%u\fR
When the result attribute value is an address of the form
user@domain, \fB%u\fR is replaced by the local part of the
address. Otherwise, \fB%u\fR is replaced by the entire
attribute value.
address. When the result has an empty localpart it is skipped.
.IP "\fB\fB%d\fR\fR"
When a result attribute value is an address of the form
user@domain, \fB%d\fR is replaced by the domain part of
the attribute value. Otherwise, \fB%d\fR is replaced by
the entire attribute value.
the attribute value. When the result is unqualified it
is skipped.
.IP "\fB\fB%[SUD1-9]\fR\fB"
The upper-case and decimal digit expansions interpolate
the parts of the input key rather than the result. Their
behaviour is identical to that described with \fBquery_filter\fR,
and in fact because the input key is known in advance, lookups
whose key does not contain all the information specified in
the result template are suppressed and return no results.
.IP
The above %S, %U, %D and %1, ..., %9 expansions are available with
Postfix 2.2 and later.
.RE
.IP
For example, using "result_filter = smtp:[%s]" allows one
For example, using "result_format = smtp:[%s]" allows one
to use a mailHost attribute as the basis of a transport(5)
table. After applying the result filter, multiple values
table. After applying the result format, multiple values
are concatenated as comma separated strings. The expansion_limit
and size_limit parameters explained below allow one to
restrict the number of values in the result, which is
@@ -200,7 +283,13 @@ value.
The default value \fB%s\fR specifies that each
attribute value should be used as is.
NOTE: DO NOT put quotes around the result filter!
This parameter was called \fBresult_filter\fR in Postfix
releases prior to 2.2. If no "result_format" is specified,
the value of "result_filter" will be used instead before
resorting to the default value. This provides compatibility
with old configuration files.
NOTE: DO NOT put quotes around the result format!
.IP "\fBdomain (default: no domain list)\fR"
This is a list of domain names, paths to files, or
dictionaries. When specified, only fully qualified search
@@ -220,7 +309,7 @@ The attribute(s) Postfix will read from any directory
entries returned by the lookup, to be resolved to an email
address.
.ti +4
result_attribute = mailbox,maildrop
result_attribute = mailbox, maildrop
.IP "\fBspecial_result_attribute (No default)\fR"
The attribute(s) of directory entries that can contain DNs
or URLs. If found, a recursive subsequent search is done
@@ -287,14 +376,14 @@ limit to 1 ensures that lookups do not return multiple
values.
.IP "\fBsize_limit (default: $expansion_limit)\fR"
A limit on the number of LDAP entries returned by any single
LDAP query performed as part of the lookup. A setting of
LDAP search 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
result attributes. This limit caps the per search resource
utilization on the LDAP server, not the final multiplicity
of the lookup result. It is analogous to the "-z" option
of "ldapsearch".

View File

@@ -27,7 +27,7 @@ alias_maps = mysql:/etc/mysql-aliases.cf
The file /etc/postfix/mysql-aliases.cf has the same format as
the Postfix main.cf file, and can specify the parameters
described below.
.SH "ALTERNATIVE CONFIGURATION"
.SH "BACKWARDS COMPATIBILITY"
.na
.nf
.ad
@@ -44,6 +44,30 @@ below would be defined in main.cf as "\fImysqlname\fR_hosts".
Note: with this form, the passwords for the MySQL sources are
written in main.cf, which is normally world-readable. Support
for this form will be removed in a future Postfix version.
Postfix 2.2 has enhanced query interfaces for MySQL and PostreSQL,
these include features previously available only in the Postfix
LDAP client. In the new interface the SQL query is specified via
a single \fBquery\fR parameter (described in more detail below).
When the new \fBquery\fR parameter is not specified in the map
definition, Postfix reverts to the old interface, with the SQL
query constructed from the \fBselect_field\fR, \fBtable\fR,
\fBwhere_field\fR and \fBadditional_conditions\fR parameters.
The old interface will be gradually phased out. To migrate to
the new interface set:
.ti +4
\fBquery\fR = SELECT [\fIselect_field\fR]
.ti +8
FROM [\fItable\fR]
.ti +8
WHERE [\fIwhere_field\fR] = '%s'
.ti +12
[\fIadditional_conditions\fR]
Insert the value, not the name, of each legacy parameter. Note
that the \fBadditional_conditions\fR parameter is optional
and if not empty, will always start with \fBAND\fR.
.SH "LIST MEMBERSHIP"
.na
.nf
@@ -102,33 +126,176 @@ password = some_password
The database name on the servers. Example:
.ti +4
dbname = customer_database
.PP
The following parameters are used to fill in a SELECT
query template of the form:
.IP "\fBquery\fR"
The SQL query template used to search the database, where \fB%s\fR
is a substitute for the address Postfix is trying to resolve,
e.g.
.ti +4
select [\fBselect_field\fR] from [\fBtable\fR] where
.ti +8
[\fBwhere_field\fR] = '$lookup' [\fBadditional_conditions\fR]
query = SELECT replacement FROM aliases WHERE mailbox = '%s'
$lookup contains the search string, and is escaped so if
it contains single quotes or other odd characters, it will
not cause a parse error, or worse, a security problem.
This parameter supports the following '%' expansions:
.RS
.IP "\fB\fB%%\fR\fR"
This is replaced by a literal '%' character.
.IP "\fB\fB%s\fR\fR"
This is replaced by the input key.
SQL quoting is used to make sure that the input key does not
add unexpected metacharacters.
.IP "\fB\fB%u\fR\fR"
When the input key is an address of the form user@domain, \fB%u\fR
is replaced by the SQL quoted local part of the address.
Otherwise, \fB%u\fR is replaced by the entire search string.
If the localpart is empty, the query is suppressed and returns
no results.
.IP "\fB\fB%d\fR\fR"
When the input key is an address of the form user@domain, \fB%d\fR
is replaced by the SQL quoted domain part of the address.
Otherwise, the query is suppressed and returns no results.
.IP "\fB\fB%[SUD]\fR\fR"
The upper-case equivalents of the above expansions behave in the
\fBquery\fR parameter identically to their lower-case counter-parts.
With the \fBresult_format\fR parameter (see below), they expand the
input key rather than the result value.
.IP "\fB\fB%[1-9]\fR\fR"
The patterns %1, %2, ... %9 are replaced by the corresponding
most significant component of the input key's domain. If the
input key is \fIuser@mail.example.com\fR, then %1 is \fBcom\fR,
%2 is \fBexample\fR and %3 is \fBmail\fR. If the input key is
unqualified or does not have enough domain components to satisfy
all the specified patterns, the query is suppressed and returns
no results.
.RE
.IP
The \fBdomain\fR parameter described below limits the input
keys to addresses in matching domains. When the \fBdomain\fR
parameter is non-empty, SQL queries for unqualified addresses
or addresses in non-matching domains are suppressed
and return no results.
This parameter is available with Postfix 2.2. In prior releases
the SQL query was built from the separate parameters:
\fBselect_field\fR, \fBtable\fR, \fBwhere_field\fR and
\fBadditional_conditions\fR. The mapping from the old parameters
to the equivalent query is:
.ti +4
SELECT [\fBselect_field\fR]
.ti +4
FROM [\fBtable\fR]
.ti +4
WHERE [\fBwhere_field\fR] = '%s'
.ti +10
[\fBadditional_conditions\fR]
The '%s' in the \fBWHERE\fR clause expands to the escaped search string.
With Postfix 2.2 these legacy parameters are used if the \fBquery\fR
parameter is not specified.
NOTE: DO NOT put quotes around the query parameter.
.IP "\fBresult_format (default: \fB%s\fR)\fR"
Format template applied to result attributes. Most commonly used
to append (or prepend) text to the result. This parameter supports
the following '%' expansions:
.RS
.IP "\fB\fB%%\fR\fR"
This is replaced by a literal '%' character.
.IP "\fB\fB%s\fR\fR"
This is replaced by the value of the result attribute. When
result is empty it is skipped.
.IP "\fB%u\fR
When the result attribute value is an address of the form
user@domain, \fB%u\fR is replaced by the local part of the
address. When the result has an empty localpart it is skipped.
.IP "\fB\fB%d\fR\fR"
When a result attribute value is an address of the form
user@domain, \fB%d\fR is replaced by the domain part of
the attribute value. When the result is unqualified it
is skipped.
.IP "\fB\fB%[SUD1-9]\fR\fB"
The upper-case and decimal digit expansions interpolate
the parts of the input key rather than the result. Their
behaviour is identical to that described with \fBquery\fR,
and in fact because the input key is known in advance, queries
whose key does not contain all the information specified in
the result template are suppressed and return no results.
.RE
.IP
For example, using "result_format = smtp:[%s]" allows one
to use a mailHost attribute as the basis of a transport(5)
table. After applying the result format, multiple values
are concatenated as comma separated strings. The expansion_limit
and parameter explained below allows one to restrict the number
of values in the result, which is especially useful for maps that
must return at most one value.
The default value \fB%s\fR specifies that each result value should
be used as is.
This parameter is available with Postfix 2.2 and later.
NOTE: DO NOT put quotes around the result format!
.IP "\fBdomain (default: no domain list)\fR"
This is a list of domain names, paths to files, or
dictionaries. When specified, only fully qualified search
keys with a *non-empty* localpart and a matching domain
are eligible for lookup: 'user' lookups, bare domain lookups
and "@domain" lookups are not performed. This can significantly
reduce the query load on the MySQL server.
.ti +4
domain = postfix.org, hash:/etc/postfix/searchdomains
It is best not to use SQL to store the domains eligible
for SQL lookups.
This parameter is available with Postfix 2.2 and later.
NOTE: DO NOT define this parameter for local(8) aliases,
because the input keys are always unqualified.
.IP "\fBexpansion_limit (default: 0)\fR"
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.
.PP
The following parameters can be used to fill in a
SELECT template statement of the form:
.ti +4
SELECT [\fBselect_field\fR]
.ti +4
FROM [\fBtable\fR]
.ti +4
WHERE [\fBwhere_field\fR] = '%s'
.ti +10
[\fBadditional_conditions\fR]
The specifier %s is replaced by the search string, and is
escaped so if it contains single quotes or other odd characters,
it will not cause a parse error, or worse, a security problem.
As of Postfix 2.2 this interface is obsolete, it is replaced
by the more general \fBquery\fR interface described above.
If the \fBquery\fR parameter is defined, the legacy parameters
are ignored. Please migrate to the new interface as the legacy
interface may be removed in a future release.
.IP "\fBselect_field\fR"
The SQL "select" parameter. Example:
.ti +4
select_field = forw_addr
\fBselect_field\fR = forw_addr
.IP "\fBtable\fR"
The SQL "select .. from" table name. Example:
.ti +4
table = mxaliases
\fBtable\fR = mxaliases
.IP "\fBwhere_field\fR
The SQL "select .. where" parameter. Example:
.ti +4
where_field = alias
\fBwhere_field\fR = alias
.IP "\fBadditional_conditions\fR
Additional conditions to the SQL query. Example:
.ti +4
additional_conditions = and status = 'paid'
\fBadditional_conditions\fR = AND status = 'paid'
.SH "SEE ALSO"
.na
.nf

View File

@@ -27,7 +27,7 @@ alias_maps = pgsql:/etc/pgsql-aliases.cf
The file /etc/postfix/pgsql-aliases.cf has the same format as
the Postfix main.cf file, and can specify the parameters
described below.
.SH "ALTERNATIVE CONFIGURATION"
.SH "BACKWARDS COMPATIBILITY"
.na
.nf
.ad
@@ -46,6 +46,37 @@ Note: with this form, the passwords for the PostgreSQL sources
are written in main.cf, which is normally world-readable.
Support for this form will be removed in a future Postfix
version.
Postfix 2.2 has enhanced query interfaces for MySQL and PostgreSQL,
these include features previously available only in the Postfix
LDAP client. In the new interface the SQL query is specified via
a single \fBquery\fR parameter (described in more detail below).
In Postfix 2.1 the parameter precedence was, from highest to lowest,
\fBselect_function\fR, \fBquery\fR and finally \fBselect_field\fR, ...
With Postfix 2.2 the \fBquery\fR parameter has highest precedence,
and is used in preference to the still supported, but slated to be
phased out, \fBselect_function\fR, \fBselect_field\fR, \fBtable\fR,
\fBwhere_field\fR and \fBadditional_conditions\fR parameters. To
migrate to the new interface set:
.ti +4
\fBquery\fR = SELECT \fIselect_function\fR('%s')
or in the absense of \fBselection_function\fR, the lower precedence:
.ti +4
\fBquery\fR = SELECT \fIselect_field\fR
.ti +8
FROM \fItable\fR
.ti +8
WHERE \fIwhere_field\fR = '%s'
.ti +12
\fIadditional_conditions\fR
Use the value, not the name, of each legacy parameter. Note
that the \fBadditional_conditions\fR parameter is optional
and if not empty, will always start with \fBAND\fR.
.SH "LIST MEMBERSHIP"
.na
.nf
@@ -102,61 +133,134 @@ password = some_password
The database name on the servers. Example:
.ti +4
dbname = customer_database
.PP
The following parameters can be used to fill in a SELECT
template statement of the form:
.ti +4
select [\fBselect_field\fR] from [\fBtable\fR] where
.ti +8
[\fBwhere_field\fR] = '$lookup' [\fBadditional_conditions\fR]
$lookup contains the search string, and is escaped so if
it contains single quotes or other odd characters, it will
not cause a parse error, or worse, a security problem.
.IP "\fBselect_field\fR"
The SQL "select" parameter. Example:
.ti +4
select_field = forw_addr
.IP "\fBtable\fR"
The SQL "select .. from" table name. Example:
.ti +4
table = mxaliases
.IP "\fBwhere_field\fR
The SQL "select .. where" parameter. Example:
.ti +4
where_field = alias
.IP "\fBadditional_conditions\fR
Additional conditions to the SQL query. Example:
.ti +4
additional_conditions = and status = 'paid'
.PP
The following parameters provide ways to override the default
SELECT statement. Setting them will instruct Postfix to ignore
the above \fBtable\fR, \fBselect_field\fR, \fBwhere_field\fR and
\fBadditional_conditions\fR parameters:
.IP "\fBquery\fR"
This parameter specifies a complete SQL query. Example:
The SQL query template used to search the database, where \fB%s\fR
is a substitute for the address Postfix is trying to resolve,
e.g.
.ti +4
query = select forw_addr from mxaliases where
.ti +8
alias = '%s' and status = 'paid'
query = SELECT replacement FROM aliases WHERE mailbox = '%s'
This parameter supports the following '%' expansions:
.RS
.IP "\fB\fB%%\fR\fR"
This is replaced by a literal '%' character. (Postfix 2.2 and later)
.IP "\fB\fB%s\fR\fR"
This is replaced by the input key. Quoting is used to make sure
that the input key does not add unexpected metacharacters.
This is replaced by the input key.
SQL quoting is used to make sure that the input key does not
add unexpected metacharacters.
.IP "\fB\fB%u\fR\fR"
When the input key is an address of the form user@domain,
\fB%u\fR is replaced by the quoted local part of the address.
If no domain is specified, \fB%u\fR is replaced by the entire
search string.
When the input key is an address of the form user@domain, \fB%u\fR
is replaced by the SQL quoted local part of the address.
Otherwise, \fB%u\fR is replaced by the entire search string.
If the localpart is empty, the query is suppressed and returns
no results.
.IP "\fB\fB%d\fR\fR"
When the input key is an address of the form user@domain,
\fB%d\fR is replaced by the quoted domain part of the address.
When the input key has no domain qualifier, \fB%d\fR is replaced
by the entire search string.
When the input key is an address of the form user@domain, \fB%d\fR
is replaced by the SQL quoted domain part of the address.
Otherwise, the query is suppressed and returns no results.
.IP "\fB\fB%[SUD]\fR\fR"
The upper-case equivalents of the above expansions behave in the
\fBquery\fR parameter identically to their lower-case counter-parts.
With the \fBresult_format\fR parameter (see below), they expand the
input key rather than the result value.
.IP
The above %S, %U and %D expansions are available with Postfix 2.2
and later
.IP "\fB\fB%[1-9]\fR\fR"
The patterns %1, %2, ... %9 are replaced by the corresponding
most significant component of the input key's domain. If the
input key is \fIuser@mail.example.com\fR, then %1 is \fBcom\fR,
%2 is \fBexample\fR and %3 is \fBmail\fR. If the input key is
unqualified or does not have enough domain components to satisfy
all the specified patterns, the query is suppressed and returns
no results.
.IP
The above %1, ... %9 expansions are available with Postfix 2.2
and later
.RE
.IP
The \fBdomain\fR parameter described below limits the input
keys to addresses in matching domains. When the \fBdomain\fR
parameter is non-empty, SQL queries for unqualified addresses
or addresses in non-matching domains are suppressed
and return no results.
The precedence of this parameter has changed with Postfix 2.2,
in prior releases the precedence was, from highest to lowest,
\fBselect_function\fR, \fBquery\fR, \fBselect_field\fR, ...
With Postfix 2.2 the \fBquery\fR parameter has highest precedence,
see COMPATIBILITY above.
NOTE: DO NOT put quotes around the \fBquery\fR parameter.
.IP "\fBresult_format (default: \fB%s\fR)\fR"
Format template applied to result attributes. Most commonly used
to append (or prepend) text to the result. This parameter supports
the following '%' expansions:
.RS
.IP "\fB\fB%%\fR\fR"
This is replaced by a literal '%' character.
.IP "\fB\fB%s\fR\fR"
This is replaced by the value of the result attribute. When
result is empty it is skipped.
.IP "\fB%u\fR
When the result attribute value is an address of the form
user@domain, \fB%u\fR is replaced by the local part of the
address. When the result has an empty localpart it is skipped.
.IP "\fB\fB%d\fR\fR"
When a result attribute value is an address of the form
user@domain, \fB%d\fR is replaced by the domain part of
the attribute value. When the result is unqualified it
is skipped.
.IP "\fB\fB%[SUD1-9]\fR\fB"
The upper-case and decimal digit expansions interpolate
the parts of the input key rather than the result. Their
behaviour is identical to that described with \fBquery\fR,
and in fact because the input key is known in advance, queries
whose key does not contain all the information specified in
the result template are suppressed and return no results.
.RE
.IP
For example, using "result_format = smtp:[%s]" allows one
to use a mailHost attribute as the basis of a transport(5)
table. After applying the result format, multiple values
are concatenated as comma separated strings. The expansion_limit
and parameter explained below allows one to restrict the number
of values in the result, which is especially useful for maps that
must return at most one value.
The default value \fB%s\fR specifies that each result value should
be used as is.
This parameter is available with Postfix 2.2 and later.
NOTE: DO NOT put quotes around the result format!
.IP "\fBdomain (default: no domain list)\fR"
This is a list of domain names, paths to files, or
dictionaries. When specified, only fully qualified search
keys with a *non-empty* localpart and a matching domain
are eligible for lookup: 'user' lookups, bare domain lookups
and "@domain" lookups are not performed. This can significantly
reduce the query load on the PostgreSQL server.
.ti +4
domain = postfix.org, hash:/etc/postfix/searchdomains
It is best not to use SQL to store the domains eligible
for SQL lookups.
This parameter is available with Postfix 2.2 and later.
NOTE: DO NOT define this parameter for local(8) aliases,
because the input keys are always unqualified.
.IP "\fBexpansion_limit (default: 0)\fR"
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.
.PP
Pre-Postfix 2.2 legacy interfaces:
.IP "\fBselect_function\fR"
This parameter specifies a database function name. Example:
.ti +4
@@ -164,16 +268,54 @@ select_function = my_lookup_user_alias
This is equivalent to:
.ti +4
query = select my_lookup_user_alias('%s')
query = SELECT my_lookup_user_alias('%s')
and overrides both the \fBquery\fR parameter and the table-related
fields above.
This parameter overrides the legacy table-related fields (described
below). With Postfix versions prior to 2.2, it also overrides the
\fBquery\fR parameter. Starting with Postfix 2.2, the \fBquery\fR
parameter has highest precedence, and this parameter is deprecated.
Please migrate to the new \fBquery\fR interface as this interface
is slated to be phased out.
.PP
The following parameters (with lower precedence than the
\fBselect_function\fR interface described above) can be used to
build the SQL select statement as follows:
As of June 2002, if the function returns a single row and
a single column AND that value is NULL, then the result
will be treated as if the key was not in the dictionary.
.ti +4
SELECT [\fBselect_field\fR]
.ti +4
FROM [\fBtable\fR]
.ti +4
WHERE [\fBwhere_field\fR] = '%s'
.ti +10
[\fBadditional_conditions\fR]
Future versions will allow functions to return result sets.
The specifier %s is replaced with each lookup by the lookup key
and is escaped so if it contains single quotes or other odd
characters, it will not cause a parse error, or worse, a security
problem.
Starting with Postfix 2.2, this interface is obsoleted by the more
general \fBquery\fR interface described above. If higher precedence
the \fBquery\fR or \fBselect_function\fR parameters described above
are defined, these parameters are ignored. Please migrate to the new
\fBquery\fR interface as this interface is slated to be phased out.
.IP "\fBselect_field\fR"
The SQL "select" parameter. Example:
.ti +4
\fBselect_field\fR = forw_addr
.IP "\fBtable\fR"
The SQL "select .. from" table name. Example:
.ti +4
\fBtable\fR = mxaliases
.IP "\fBwhere_field\fR
The SQL "select .. where" parameter. Example:
.ti +4
\fBwhere_field\fR = alias
.IP "\fBadditional_conditions\fR
Additional conditions to the SQL query. Example:
.ti +4
\fBadditional_conditions\fR = AND status = 'paid'
.SH "SEE ALSO"
.na
.nf

View File

@@ -306,6 +306,20 @@ difficult to enforce consistently and globally.
Enable the rewriting of the form "user%domain" to "user@domain".
This is enabled by default.
.PP
Note: With Postfix version 2.2, message header address rewriting
happens only when one of the following conditions is true:
.IP \(bu
The message is received with the Postfix \fBsendmail\fR(1) command,
.IP \(bu
The message is received from a network client that matches
$local_header_rewrite_clients,
.IP \(bu
The message is received from the network, and the
remote_header_rewrite_domain parameter specifies a non-empty value.
.PP
To get the behavior before Postfix 2.2, specify
"local_header_rewrite_clients = static:all".
.PP
Example:
.PP
.nf
@@ -371,17 +385,45 @@ With locally submitted mail, append the string "@$myorigin" to mail
addresses without domain information. With remotely submitted mail,
append the string "@$remote_header_rewrite_domain" instead.
.PP
This feature is enabled by default and must not be turned off.
Note 1: This feature is enabled by default and must not be turned off.
Postfix does not support domain-less addresses.
.PP
Note 2: With Postfix version 2.2, message header address rewriting
happens only when one of the following conditions is true:
.IP \(bu
The message is received with the Postfix \fBsendmail\fR(1) command,
.IP \(bu
The message is received from a network client that matches
$local_header_rewrite_clients,
.IP \(bu
The message is received from the network, and the
remote_header_rewrite_domain parameter specifies a non-empty value.
.PP
To get the behavior before Postfix 2.2, specify
"local_header_rewrite_clients = static:all".
.SH append_dot_mydomain (default: yes)
With locally submitted mail, append the string ".$mydomain" to
addresses that have no ".domain" information. With remotely submitted
mail, append the string ".$remote_header_rewrite_domain"
instead.
.PP
This feature is enabled by default. If disabled, users will not be
Note 1: This feature is enabled by default. If disabled, users will not be
able to send mail to "user@partialdomainname" but will have to
specify full domain names instead.
.PP
Note 2: With Postfix version 2.2, message header address rewriting
happens only when one of the following conditions is true:
.IP \(bu
The message is received with the Postfix \fBsendmail\fR(1) command,
.IP \(bu
The message is received from a network client that matches
$local_header_rewrite_clients,
.IP \(bu
The message is received from the network, and the
remote_header_rewrite_domain parameter specifies a non-empty value.
.PP
To get the behavior before Postfix 2.2, specify
"local_header_rewrite_clients = static:all".
.SH application_event_drain_time (default: 100s)
How long the \fBpostkick\fR(1) command waits for a request to enter the
server's input buffer before giving up.
@@ -590,6 +632,20 @@ build the necessary DBM or DB file after every change. The changes
will become visible after a minute or so. Use "\fBpostfix reload\fR"
to eliminate the delay.
.PP
Note: with Postfix version 2.2, message header address mapping
happens only when message header address rewriting is enabled:
.IP \(bu
The message is received with the Postfix \fBsendmail\fR(1) command,
.IP \(bu
The message is received from a network client that matches
$local_header_rewrite_clients,
.IP \(bu
The message is received from the network, and the
remote_header_rewrite_domain parameter specifies a non-empty value.
.PP
To get the behavior before Postfix 2.2, specify
"local_header_rewrite_clients = static:all".
.PP
Examples:
.PP
.nf
@@ -1382,7 +1438,8 @@ inet_interfaces = 192.168.1.2, 127.0.0.1
The Internet protocols Postfix will attempt to use when making
or accepting connections. Specify one or more of "ipv4"
or "ipv6", separated by whitespace or commas. The form
"all" is equivalent to "ipv4, ipv6".
"all" is equivalent to "ipv4, ipv6" or "ipv4", depending
on whether the operating system implements IPv6.
.PP
This feature is available in Postfix version 2.2 and later.
.PP
@@ -2077,6 +2134,20 @@ or its subdomains. Thus,
does not change "user@any.thing.foo.example.com" or "user@foo.example.com",
but strips "user@any.thing.else.example.com" to "user@example.com".
.PP
Note: with Postfix version 2.2, message header address masquerading
happens only when message header address rewriting is enabled:
.IP \(bu
The message is received with the Postfix \fBsendmail\fR(1) command,
.IP \(bu
The message is received from a network client that matches
$local_header_rewrite_clients,
.IP \(bu
The message is received from the network, and the
remote_header_rewrite_domain parameter specifies a non-empty value.
.PP
To get the behavior before Postfix 2.2, specify
"local_header_rewrite_clients = static:all".
.PP
Example:
.PP
.nf
@@ -5205,6 +5276,20 @@ Enable the rewriting of "site!user" into "user@site". This is
necessary if your machine is connected to UUCP networks. It is
enabled by default.
.PP
Note: With Postfix version 2.2, message header address rewriting
happens only when one of the following conditions is true:
.IP \(bu
The message is received with the Postfix \fBsendmail\fR(1) command,
.IP \(bu
The message is received from a network client that matches
$local_header_rewrite_clients,
.IP \(bu
The message is received from the network, and the
remote_header_rewrite_domain parameter specifies a non-empty value.
.PP
To get the behavior before Postfix 2.2, specify
"local_header_rewrite_clients = static:all".
.PP
Example:
.PP
.nf

View File

@@ -317,15 +317,18 @@ turn-off control </th> </tr>
<tr> <td> <a href="#standard"> Rewrite addresses to standard form</a>
</td> <td nowrap> all mail </td> <td> trivial-<br>rewrite(8) </td>
<td> append_at_myorigin, append_dot_mydomain, swap_bangpath,
allow_percent_hack </td> <td> none </td> </tr>
allow_percent_hack </td> <td> local_header_rewrite_clients,
remote_header_rewrite_domain </td> </tr>
<tr> <td> <a href="#canonical"> Canonical address mapping </a>
</td> <td nowrap> all mail </td> <td> cleanup(8) </td> <td>
canonical_maps </td> <td> receive_override_options </td> </tr>
<tr> <td> <a href="#canonical"> Canonical address mapping </a> </td>
<td nowrap> all mail </td> <td> cleanup(8) </td> <td> canonical_maps
</td> <td> receive_override_options, local_header_rewrite_clients,
remote_header_rewrite_domain </td> </tr>
<tr> <td> <a href="#canonical"> Address masquerading </a> </td>
<td nowrap> all mail </td> <td> cleanup(8) </td> <td> masquerade_domains
</td> <td> receive_override_options </td> </tr>
<tr> <td> <a href="#masquerade"> Address masquerading </a> </td> <td
nowrap> all mail </td> <td> cleanup(8) </td> <td> masquerade_domains
</td> <td> receive_override_options, local_header_rewrite_clients,
remote_header_rewrite_domain </td> </tr>
<tr> <td> <a href="#auto_bcc"> Automatic BCC recipients </a> </td>
<td nowrap> new mail </td> <td> cleanup(8) </td> <td> always_bcc,
@@ -411,11 +414,12 @@ trivial-rewrite(8) daemon. The purpose of rewriting to standard
form is to reduce the number of entries needed in lookup tables.
</p>
<p> Postfix versions 2.2 and later do not rewrite message headers
from remote SMTP clients at all, unless a non-empty domain name is
specified with the remote_header_rewrite_domain configuration
parameter. The local_header_rewrite_clients parameter controls
what SMTP clients Postfix considers local. </p>
<p> NOTE: Postfix versions 2.2 and later rewrite message headers
from remote SMTP clients only if the client matches the
local_header_rewrite_clients parameter, or if the
remote_header_rewrite_domain configuration parameter specifies a
non-empty value. To get the behavior before Postfix 2.2, specify
"local_header_rewrite_clients = static:all". </p>
<p> The Postfix trivial-rewrite(8) daemon implements the following
hard-coded address manipulations: </p>
@@ -455,10 +459,13 @@ parameter (default: yes). You should never turn off this feature,
because a lot of Postfix components expect that all addresses have
the form "user@domain". </p>
<p> Postfix versions 2.2 and later either do not rewrite message
headers from remote SMTP clients at all, or they append the domain
name specified with the remote_header_rewrite_domain configuration
parameter. </p>
<p> NOTE: Postfix versions 2.2 and later rewrite message headers
from remote SMTP clients only if the client matches the
local_header_rewrite_clients parameter; otherwise they append the
domain name specified with the remote_header_rewrite_domain
configuration parameter, if one is specified. To get the behavior
before Postfix 2.2, specify "local_header_rewrite_clients =
static:all". </p>
<p> If your machine is not the main machine for $myorigin and you
wish to have some users delivered locally without going via that
@@ -476,10 +483,13 @@ Rewrite "user@host" to "user@host.$mydomain" </dt>
parameter (default: yes). The purpose is to get consistent treatment
of different forms of the same hostname. </p>
<p> Postfix versions 2.2 and later either do not rewrite message
headers from remote clients at all, or they append the domain name
specified with the remote_header_rewrite_domain configuration
parameter. </p>
<p> NOTE: Postfix versions 2.2 and later rewrite message headers
from remote SMTP clients only if the client matches the
local_header_rewrite_clients parameter; otherwise they append the
domain name specified with the remote_header_rewrite_domain
configuration parameter, if one is specified. To get the behavior
before Postfix 2.2, specify "local_header_rewrite_clients =
static:all". </p>
<p> Some will argue that rewriting "host" to "host.domain"
is bad. That is why it can be turned off. Others like the convenience
@@ -502,11 +512,12 @@ addresses in message envelopes and in message headers. By default
all header and envelope addresses are rewritten; this is controlled
with the canonical_classes configuration parameter. </p>
<p> Postfix versions 2.2 and later do not rewrite message headers
from remote clients at all, unless a non-empty domain name is
specified with the remote_header_rewrite_domain configuration
parameter. The local_header_rewrite_clients parameter controls
what SMTP clients Postfix considers local. </p>
<p> NOTE: Postfix versions 2.2 and later rewrite message headers
from remote SMTP clients only if the client matches the
local_header_rewrite_clients parameter, or if the
remote_header_rewrite_domain configuration parameter specifies a
non-empty value. To get the behavior before Postfix 2.2, specify
"local_header_rewrite_clients = static:all". </p>
<p> Address rewriting is
done for local and remote addresses. The mapping is useful to
@@ -585,11 +596,12 @@ behind their mail gateway, and to make it appear as if the mail
comes from the gateway itself, instead of from individual machines.
</p>
<p> Postfix versions 2.2 and later do not rewrite message headers
from remote SMTP clients at all, unless a non-empty domain name is
specified with the remote_header_rewrite_domain configuration
parameter. The local_header_rewrite_clients parameter controls
what SMTP clients Postfix considers local. </p>
<p> NOTE: Postfix versions 2.2 and later rewrite message headers
from remote SMTP clients only if the client matches the
local_header_rewrite_clients parameter, or if the
remote_header_rewrite_domain configuration parameter specifies a
non-empty value. To get the behavior before Postfix 2.2, specify
"local_header_rewrite_clients = static:all". </p>
<p> Address masquerading is disabled by default, and is implemented
by the cleanup(8) server. To enable, edit the masquerade_domains

View File

@@ -257,7 +257,7 @@ maildrop: this, that, theother
make sure the lookup makes sense. In the case of virtual lookups,
maildrops other than mail addresses are pretty useless, because
Postfix can't know how to set the ownership for program or file
delivery. Your query_filter should probably look something like this: </p>
delivery. Your <b>query_filter</b> should probably look something like this: </p>
<blockquote>
<pre>
@@ -276,7 +276,7 @@ query_filter = (&amp;(mailacceptinggeneralid=%s)(!(|(maildrop="*|*")(maildrop="*
require some thought on your part to implement safely, considering the
ramifications of this type of delivery. You may decide it's not worth
the bother to allow any of that nonsense in LDAP lookups, ban it in
the query_filter, and keep things like majordomo lists in local alias
the <b>query_filter</b>, and keep things like majordomo lists in local alias
databases. </p>
<blockquote>
@@ -334,13 +334,20 @@ contents, please include the applicable bits of some directory entries. </p>
<li>Victor Duchovni: ldap_bind() timeout. With fixes from LaMont Jones:
OpenLDAP cache deprecation. Limits on recursion, expansion
and query results size. LDAP connection sharing for maps
and search results size. LDAP connection sharing for maps
differing only in the query parameters.
<li>Liviu Daia: Support for SSL/STARTTLS. Support for storing map definitions in
external files (ldap:/path/ldap.cf) needed to securely store
passwords for plain auth.
<li>Liviu Daia revised the configuration interface and added the main.cf
configuration feature.</li>
<li>Liviu Daia with further refinements from Jose Luis Tallon and
Victor Duchovni developed the common query, result_format, domain and
expansion_limit interface for LDAP, MySQL and PosgreSQL.</li>
</ul>
And of course Wietse.

View File

@@ -89,22 +89,16 @@ password = some_password
# The database name on the servers.
dbname = customer_database
# The table name.
table = mxaliases
# For Postfix 2.2 and later The SQL query template.
# See mysql_table(5) for details.
query = SELECT forw_addr FROM mxaliases WHERE alias='%s' AND status='paid'
# Query components, see below.
# For Postfix releases prior to 2.2. See mysql_table(5) for details.
select_field = forw_addr
table = mxaliases
where_field = alias
# You may specify additional_conditions or leave this empty.
additional_conditions = and status = 'paid'
# The above variables will result in a query of the form:
#
# select forw_addr from mxaliases where alias = '$lookup' and status = 'paid'
#
# ($lookup is escaped so if it contains single quotes or other odd
# characters, it will not cause trouble).
# Don't forget the leading "AND"!
additional_conditions = AND status = 'paid'
</pre>
<h2>Additional notes</h2>
@@ -129,10 +123,14 @@ will be deferred until at least one of those hosts is reachable.
<ul>
<li> The initial version was contributed by Scott Cotton and Joshua
Marcus, IC Group, Inc.
Marcus, IC Group, Inc.</li>
<li>Liviu Daia revised the configuration interface and added the
main.cf configuration feature.
<li> Liviu Daia revised the configuration interface and added the
main.cf configuration feature.</li>
<li> Liviu Daia with further refinements from Jose Luis Tallon and
Victor Duchovni developed the common query, result_format, domain and
expansion_limit interface for LDAP, MySQL and PosgreSQL.</li>
</ul>

View File

@@ -88,25 +88,15 @@ password = some_password
# The database name on the servers.
dbname = customer_database
# The table name.
table = mxaliases
# Postfix 2.2 and later The SQL query template. See pgsql_table(5).
query = SELECT forw_addr FROM mxaliases WHERE alias='%s' AND status='paid'
# Query components, see below.
# For Postfix releases prior to 2.2. See pgsql_table(5) for details.
select_field = forw_addr
table = mxaliases
where_field = alias
# You may specify additional_conditions or leave this empty.
additional_conditions = and status = 'paid'
# The above variables will result in a query of the form:
#
# select forw_addr from mxaliases where alias = '$lookup' and status = 'paid'
#
# ($lookup is escaped so if it contains single quotes or other odd
# characters, it will not cause problems).
#
# You may also override the built-in SELECT template. See pgsql_table(5)
# for details.
# Don't forget the leading "AND"!
additional_conditions = AND status = 'paid'
</pre>
<h2>Using mirrored databases</h2>
@@ -130,17 +120,24 @@ those hosts is reachable. </p>
<ul>
<li> This code is based upon the Postfix mysql map by Scott Cotton
and Joshua Marcus, IC Group, Inc.
and Joshua Marcus, IC Group, Inc.</li>
<li> The PostgreSQL changes were done by Aaron Sethman.
<li> The PostgreSQL changes were done by Aaron Sethman.</li>
<li> Updates for Postfix 1.1.x and PostgreSQL 7.1+ and support for
calling stored procedures were added by Philip Warner.
calling stored procedures were added by Philip Warner.</li>
<li> LaMont Jones was the initial Postfix pgsql maintainer.
<li> LaMont Jones was the initial Postfix pgsql maintainer.</li>
<li> Liviu Daia revised the configuration interface and added the
main.cf configuration feature.
main.cf configuration feature.</li>
<li> Liviu Daia revised the configuration interface and added the main.cf
configuration feature.</li>
<li> Liviu Daia with further refinements from Jose Luis Tallon and
Victor Duchovni developed the common query, result_format, domain and
expansion_limit interface for LDAP, MySQL and PosgreSQL.</li>
</ul>

View File

@@ -45,6 +45,18 @@
# Note: with this form, the passwords for the LDAP sources are
# written in main.cf, which is normally world-readable. Support
# for this form will be removed in a future Postfix version.
#
# Postfix 2.2 has enhanced query interfaces for MySQL and PostgreSQL,
# these now include features previously available only in the
# Postfix LDAP client. This work also created an opportunity for
# improvements in the LDAP interface. The primary compatibility
# issue is that \fBresult_filter\fR (a name that has caused some
# confusion as to its meaning in the past) has been renamed to
# \fBresult_format\fR. For backwards compatibility with the pre
# 2.2 LDAP client, \fBresult_filter\fR can for now be used instead
# of \fBresult_format\fR, when the latter parameter is not also set.
# The new name better reflects the function of the parameter. This
# compatibility interface may be removed in a future release.
# LIST MEMBERSHIP
# .ad
# .fi
@@ -116,14 +128,50 @@
# The port the LDAP server listens on, e.g.
# .ti +4
# server_port = 778
# .IP "\fBsearch_base (No default; you must configure this)\fR"
# The RFC2253 base DN at which to conduct the search, e.g.
# .ti +4
# search_base = dc=your, dc=com
# .IP "\fBtimeout (default: 10 seconds)\fR"
# The number of seconds a search can take before timing out, e.g.
# .ti +4
# timeout = 5
# .IP "\fBsearch_base (No default; you must configure this)\fR"
# The RFC2253 base DN at which to conduct the search, e.g.
# .ti +4
# search_base = dc=your, dc=com
# .IP
# With Postfix 2.2 and later this parameter supports the
# following '%' expansions:
# .RS
# .IP "\fB\fB%%\fR\fR"
# This is replaced by a literal '%' character.
# .IP "\fB\fB%s\fR\fR"
# This is replaced by the input key.
# RFC 2253 quoting is used to make sure that the input key
# does not add unexpected metacharacters.
# .IP "\fB\fB%u\fR\fR"
# When the input key is an address of the form user@domain, \fB%u\fR
# is replaced by the (RFC 2253) quoted local part of the address.
# Otherwise, \fB%u\fR is replaced by the entire search string.
# If the localpart is empty, the search is suppressed and returns
# no results.
# .IP "\fB\fB%d\fR\fR"
# When the input key is an address of the form user@domain, \fB%d\fR
# is replaced by the (RFC 2253) quoted domain part of the address.
# Otherwise, the search is suppressed and returns no results.
# .IP "\fB\fB%[SUD]\fR\fR"
# For the \fBsearch_base\fR parameter, the upper-case equivalents
# of the above expansions behave identically to their lower-case
# counter-parts. With the \fBresult_format\fR parameter (previously
# called \fBresult_filter\fR see the COMPATIBILITY section and below),
# they expand to the corresponding components of input key rather
# than the result value.
# .IP "\fB\fB%[1-9]\fR\fR"
# The patterns %1, %2, ... %9 are replaced by the corresponding
# most significant component of the input key's domain. If the
# input key is \fIuser@mail.example.com\fR, then %1 is \fBcom\fR,
# %2 is \fBexample\fR and %3 is \fBmail\fR. If the input key is
# unqualified or does not have enough domain components to satisfy
# all the specified patterns, the search is suppressed and returns
# no results.
# .RE
# .IP "\fBquery_filter (default: mailacceptinggeneralid=%s)\fR"
# The RFC2254 filter used to search the directory, where \fB%s\fR
# is a substitute for the address Postfix is trying to resolve,
@@ -133,20 +181,43 @@
#
# This parameter supports the following '%' expansions:
# .RS
# .IP "\fB\fB%%\fR\fR"
# This is replaced by a literal '%' character. (Postfix 2.2 and later).
# .IP "\fB\fB%s\fR\fR"
# This is replaced by the input key. RFC 2254 quoting is used
# to make sure that the input key does not add unexpected
# metacharacters.
# This is replaced by the input key.
# RFC 2254 quoting is used to make sure that the input key
# does not add unexpected metacharacters.
# .IP "\fB\fB%u\fR\fR"
# When the input key is an address of the form user@domain,
# \fB%u\fR is replaced by the (RFC 2254) quoted local part of the
# address. Otherwise, \fB%u\fR is replaced by the entire
# search string.
# When the input key is an address of the form user@domain, \fB%u\fR
# is replaced by the (RFC 2254) quoted local part of the address.
# Otherwise, \fB%u\fR is replaced by the entire search string.
# If the localpart is empty, the search is suppressed and returns
# no results.
# .IP "\fB\fB%d\fR\fR"
# When the input key is an address of the form user@domain,
# \fB%d\fR is replaced by the (RFC 2254) quoted domain part of the
# address. Otherwise, \fB%d\fR is replaced by the entire
# search string.
# When the input key is an address of the form user@domain, \fB%d\fR
# is replaced by the (RFC 2254) quoted domain part of the address.
# Otherwise, the search is suppressed and returns no results.
# .IP "\fB\fB%[SUD]\fR\fR"
# The upper-case equivalents of the above expansions behave in the
# \fBquery_filter\fR parameter identically to their lower-case
# counter-parts. With the \fBresult_format\fR parameter (previously
# called \fBresult_filter\fR see the COMPATIBILITY section and below),
# they expand to the corresponding components of input key rather
# than the result value.
# .IP
# The above %S, %U and %D expansions are available with Postfix 2.2
# and later.
# .IP "\fB\fB%[1-9]\fR\fR"
# The patterns %1, %2, ... %9 are replaced by the corresponding
# most significant component of the input key's domain. If the
# input key is \fIuser@mail.example.com\fR, then %1 is \fBcom\fR,
# %2 is \fBexample\fR and %3 is \fBmail\fR. If the input key is
# unqualified or does not have enough domain components to satisfy
# all the specified patterns, the saerch is suppressed and returns
# no results.
# .IP
# The above %1, ..., %9 expansions are available with Postfix 2.2
# and later.
# .RE
# .IP
# The "domain" parameter described below limits the input
@@ -155,30 +226,42 @@
# addresses or addresses in non-matching domains are suppressed
# and return no results.
#
# NOTE: DO NOT put quotes around the query filter.
# .IP "\fBresult_filter (default: \fB%s\fR)\fR"
# Format template applied to result attributes. Supports the
# same expansions as the query_filter, and can be easily used
# to append (or prepend) text. This parameter supports the
# following '%' expansions:
# NOTE: DO NOT put quotes around the \fBquery_filter\fR parameter.
# .IP "\fBresult_format (default: \fB%s\fR)\fR"
# Called \fBresult_filter\fR in Postfix releases prior to 2.2.
# Format template applied to result attributes. Most commonly used
# to append (or prepend) text to the result. This parameter supports
# the following '%' expansions:
# .RS
# .IP "\fB\fB%%\fR\fR"
# This is replaced by a literal '%' character. (Postfix 2.2 and later).
# .IP "\fB\fB%s\fR\fR"
# This is replaced by the value of the result attribute.
# This is replaced by the value of the result attribute. When
# result is empty it is skipped.
# .IP "\fB%u\fR
# When the result attribute value is an address of the form
# user@domain, \fB%u\fR is replaced by the local part of the
# address. Otherwise, \fB%u\fR is replaced by the entire
# attribute value.
# address. When the result has an empty localpart it is skipped.
# .IP "\fB\fB%d\fR\fR"
# When a result attribute value is an address of the form
# user@domain, \fB%d\fR is replaced by the domain part of
# the attribute value. Otherwise, \fB%d\fR is replaced by
# the entire attribute value.
# the attribute value. When the result is unqualified it
# is skipped.
# .IP "\fB\fB%[SUD1-9]\fR\fB"
# The upper-case and decimal digit expansions interpolate
# the parts of the input key rather than the result. Their
# behaviour is identical to that described with \fBquery_filter\fR,
# and in fact because the input key is known in advance, lookups
# whose key does not contain all the information specified in
# the result template are suppressed and return no results.
# .IP
# The above %S, %U, %D and %1, ..., %9 expansions are available with
# Postfix 2.2 and later.
# .RE
# .IP
# For example, using "result_filter = smtp:[%s]" allows one
# For example, using "result_format = smtp:[%s]" allows one
# to use a mailHost attribute as the basis of a transport(5)
# table. After applying the result filter, multiple values
# table. After applying the result format, multiple values
# are concatenated as comma separated strings. The expansion_limit
# and size_limit parameters explained below allow one to
# restrict the number of values in the result, which is
@@ -188,7 +271,13 @@
# The default value \fB%s\fR specifies that each
# attribute value should be used as is.
#
# NOTE: DO NOT put quotes around the result filter!
# This parameter was called \fBresult_filter\fR in Postfix
# releases prior to 2.2. If no "result_format" is specified,
# the value of "result_filter" will be used instead before
# resorting to the default value. This provides compatibility
# with old configuration files.
#
# NOTE: DO NOT put quotes around the result format!
# .IP "\fBdomain (default: no domain list)\fR"
# This is a list of domain names, paths to files, or
# dictionaries. When specified, only fully qualified search
@@ -208,7 +297,7 @@
# entries returned by the lookup, to be resolved to an email
# address.
# .ti +4
# result_attribute = mailbox,maildrop
# result_attribute = mailbox, maildrop
# .IP "\fBspecial_result_attribute (No default)\fR"
# The attribute(s) of directory entries that can contain DNs
# or URLs. If found, a recursive subsequent search is done
@@ -275,14 +364,14 @@
# values.
# .IP "\fBsize_limit (default: $expansion_limit)\fR"
# A limit on the number of LDAP entries returned by any single
# LDAP query performed as part of the lookup. A setting of
# LDAP search 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
# result attributes. This limit caps the per search resource
# utilization on the LDAP server, not the final multiplicity
# of the lookup result. It is analogous to the "-z" option
# of "ldapsearch".

View File

@@ -21,7 +21,7 @@
# The file /etc/postfix/mysql-aliases.cf has the same format as
# the Postfix main.cf file, and can specify the parameters
# described below.
# ALTERNATIVE CONFIGURATION
# BACKWARDS COMPATIBILITY
# .ad
# .fi
# For compatibility with other Postfix lookup tables, MySQL
@@ -36,6 +36,30 @@
# Note: with this form, the passwords for the MySQL sources are
# written in main.cf, which is normally world-readable. Support
# for this form will be removed in a future Postfix version.
#
# Postfix 2.2 has enhanced query interfaces for MySQL and PostreSQL,
# these include features previously available only in the Postfix
# LDAP client. In the new interface the SQL query is specified via
# a single \fBquery\fR parameter (described in more detail below).
# When the new \fBquery\fR parameter is not specified in the map
# definition, Postfix reverts to the old interface, with the SQL
# query constructed from the \fBselect_field\fR, \fBtable\fR,
# \fBwhere_field\fR and \fBadditional_conditions\fR parameters.
# The old interface will be gradually phased out. To migrate to
# the new interface set:
#
# .ti +4
# \fBquery\fR = SELECT [\fIselect_field\fR]
# .ti +8
# FROM [\fItable\fR]
# .ti +8
# WHERE [\fIwhere_field\fR] = '%s'
# .ti +12
# [\fIadditional_conditions\fR]
#
# Insert the value, not the name, of each legacy parameter. Note
# that the \fBadditional_conditions\fR parameter is optional
# and if not empty, will always start with \fBAND\fR.
# LIST MEMBERSHIP
# .ad
# .fi
@@ -90,33 +114,176 @@
# The database name on the servers. Example:
# .ti +4
# dbname = customer_database
# .PP
# The following parameters are used to fill in a SELECT
# query template of the form:
# .IP "\fBquery\fR"
# The SQL query template used to search the database, where \fB%s\fR
# is a substitute for the address Postfix is trying to resolve,
# e.g.
# .ti +4
# select [\fBselect_field\fR] from [\fBtable\fR] where
# .ti +8
# [\fBwhere_field\fR] = '$lookup' [\fBadditional_conditions\fR]
# query = SELECT replacement FROM aliases WHERE mailbox = '%s'
#
# $lookup contains the search string, and is escaped so if
# it contains single quotes or other odd characters, it will
# not cause a parse error, or worse, a security problem.
# This parameter supports the following '%' expansions:
# .RS
# .IP "\fB\fB%%\fR\fR"
# This is replaced by a literal '%' character.
# .IP "\fB\fB%s\fR\fR"
# This is replaced by the input key.
# SQL quoting is used to make sure that the input key does not
# add unexpected metacharacters.
# .IP "\fB\fB%u\fR\fR"
# When the input key is an address of the form user@domain, \fB%u\fR
# is replaced by the SQL quoted local part of the address.
# Otherwise, \fB%u\fR is replaced by the entire search string.
# If the localpart is empty, the query is suppressed and returns
# no results.
# .IP "\fB\fB%d\fR\fR"
# When the input key is an address of the form user@domain, \fB%d\fR
# is replaced by the SQL quoted domain part of the address.
# Otherwise, the query is suppressed and returns no results.
# .IP "\fB\fB%[SUD]\fR\fR"
# The upper-case equivalents of the above expansions behave in the
# \fBquery\fR parameter identically to their lower-case counter-parts.
# With the \fBresult_format\fR parameter (see below), they expand the
# input key rather than the result value.
# .IP "\fB\fB%[1-9]\fR\fR"
# The patterns %1, %2, ... %9 are replaced by the corresponding
# most significant component of the input key's domain. If the
# input key is \fIuser@mail.example.com\fR, then %1 is \fBcom\fR,
# %2 is \fBexample\fR and %3 is \fBmail\fR. If the input key is
# unqualified or does not have enough domain components to satisfy
# all the specified patterns, the query is suppressed and returns
# no results.
# .RE
# .IP
# The \fBdomain\fR parameter described below limits the input
# keys to addresses in matching domains. When the \fBdomain\fR
# parameter is non-empty, SQL queries for unqualified addresses
# or addresses in non-matching domains are suppressed
# and return no results.
#
# This parameter is available with Postfix 2.2. In prior releases
# the SQL query was built from the separate parameters:
# \fBselect_field\fR, \fBtable\fR, \fBwhere_field\fR and
# \fBadditional_conditions\fR. The mapping from the old parameters
# to the equivalent query is:
#
# .ti +4
# SELECT [\fBselect_field\fR]
# .ti +4
# FROM [\fBtable\fR]
# .ti +4
# WHERE [\fBwhere_field\fR] = '%s'
# .ti +10
# [\fBadditional_conditions\fR]
#
# The '%s' in the \fBWHERE\fR clause expands to the escaped search string.
# With Postfix 2.2 these legacy parameters are used if the \fBquery\fR
# parameter is not specified.
#
# NOTE: DO NOT put quotes around the query parameter.
# .IP "\fBresult_format (default: \fB%s\fR)\fR"
# Format template applied to result attributes. Most commonly used
# to append (or prepend) text to the result. This parameter supports
# the following '%' expansions:
# .RS
# .IP "\fB\fB%%\fR\fR"
# This is replaced by a literal '%' character.
# .IP "\fB\fB%s\fR\fR"
# This is replaced by the value of the result attribute. When
# result is empty it is skipped.
# .IP "\fB%u\fR
# When the result attribute value is an address of the form
# user@domain, \fB%u\fR is replaced by the local part of the
# address. When the result has an empty localpart it is skipped.
# .IP "\fB\fB%d\fR\fR"
# When a result attribute value is an address of the form
# user@domain, \fB%d\fR is replaced by the domain part of
# the attribute value. When the result is unqualified it
# is skipped.
# .IP "\fB\fB%[SUD1-9]\fR\fB"
# The upper-case and decimal digit expansions interpolate
# the parts of the input key rather than the result. Their
# behaviour is identical to that described with \fBquery\fR,
# and in fact because the input key is known in advance, queries
# whose key does not contain all the information specified in
# the result template are suppressed and return no results.
# .RE
# .IP
# For example, using "result_format = smtp:[%s]" allows one
# to use a mailHost attribute as the basis of a transport(5)
# table. After applying the result format, multiple values
# are concatenated as comma separated strings. The expansion_limit
# and parameter explained below allows one to restrict the number
# of values in the result, which is especially useful for maps that
# must return at most one value.
#
# The default value \fB%s\fR specifies that each result value should
# be used as is.
#
# This parameter is available with Postfix 2.2 and later.
#
# NOTE: DO NOT put quotes around the result format!
# .IP "\fBdomain (default: no domain list)\fR"
# This is a list of domain names, paths to files, or
# dictionaries. When specified, only fully qualified search
# keys with a *non-empty* localpart and a matching domain
# are eligible for lookup: 'user' lookups, bare domain lookups
# and "@domain" lookups are not performed. This can significantly
# reduce the query load on the MySQL server.
# .ti +4
# domain = postfix.org, hash:/etc/postfix/searchdomains
#
# It is best not to use SQL to store the domains eligible
# for SQL lookups.
#
# This parameter is available with Postfix 2.2 and later.
#
# NOTE: DO NOT define this parameter for local(8) aliases,
# because the input keys are always unqualified.
# .IP "\fBexpansion_limit (default: 0)\fR"
# 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.
# .PP
# The following parameters can be used to fill in a
# SELECT template statement of the form:
#
# .ti +4
# SELECT [\fBselect_field\fR]
# .ti +4
# FROM [\fBtable\fR]
# .ti +4
# WHERE [\fBwhere_field\fR] = '%s'
# .ti +10
# [\fBadditional_conditions\fR]
#
# The specifier %s is replaced by the search string, and is
# escaped so if it contains single quotes or other odd characters,
# it will not cause a parse error, or worse, a security problem.
#
# As of Postfix 2.2 this interface is obsolete, it is replaced
# by the more general \fBquery\fR interface described above.
# If the \fBquery\fR parameter is defined, the legacy parameters
# are ignored. Please migrate to the new interface as the legacy
# interface may be removed in a future release.
# .IP "\fBselect_field\fR"
# The SQL "select" parameter. Example:
# .ti +4
# select_field = forw_addr
# \fBselect_field\fR = forw_addr
# .IP "\fBtable\fR"
# The SQL "select .. from" table name. Example:
# .ti +4
# table = mxaliases
# \fBtable\fR = mxaliases
# .IP "\fBwhere_field\fR
# The SQL "select .. where" parameter. Example:
# .ti +4
# where_field = alias
# \fBwhere_field\fR = alias
# .IP "\fBadditional_conditions\fR
# Additional conditions to the SQL query. Example:
# .ti +4
# additional_conditions = and status = 'paid'
# \fBadditional_conditions\fR = AND status = 'paid'
# SEE ALSO
# postmap(1), Postfix lookup table maintenance
# postconf(5), configuration parameters

View File

@@ -21,7 +21,7 @@
# The file /etc/postfix/pgsql-aliases.cf has the same format as
# the Postfix main.cf file, and can specify the parameters
# described below.
# ALTERNATIVE CONFIGURATION
# BACKWARDS COMPATIBILITY
# .ad
# .fi
# For compatibility with other Postfix lookup tables, PostgreSQL
@@ -38,6 +38,37 @@
# are written in main.cf, which is normally world-readable.
# Support for this form will be removed in a future Postfix
# version.
#
# Postfix 2.2 has enhanced query interfaces for MySQL and PostgreSQL,
# these include features previously available only in the Postfix
# LDAP client. In the new interface the SQL query is specified via
# a single \fBquery\fR parameter (described in more detail below).
# In Postfix 2.1 the parameter precedence was, from highest to lowest,
# \fBselect_function\fR, \fBquery\fR and finally \fBselect_field\fR, ...
#
# With Postfix 2.2 the \fBquery\fR parameter has highest precedence,
# and is used in preference to the still supported, but slated to be
# phased out, \fBselect_function\fR, \fBselect_field\fR, \fBtable\fR,
# \fBwhere_field\fR and \fBadditional_conditions\fR parameters. To
# migrate to the new interface set:
#
# .ti +4
# \fBquery\fR = SELECT \fIselect_function\fR('%s')
#
# or in the absense of \fBselection_function\fR, the lower precedence:
#
# .ti +4
# \fBquery\fR = SELECT \fIselect_field\fR
# .ti +8
# FROM \fItable\fR
# .ti +8
# WHERE \fIwhere_field\fR = '%s'
# .ti +12
# \fIadditional_conditions\fR
#
# Use the value, not the name, of each legacy parameter. Note
# that the \fBadditional_conditions\fR parameter is optional
# and if not empty, will always start with \fBAND\fR.
# LIST MEMBERSHIP
# .ad
# .fi
@@ -90,61 +121,134 @@
# The database name on the servers. Example:
# .ti +4
# dbname = customer_database
# .PP
# The following parameters can be used to fill in a SELECT
# template statement of the form:
# .ti +4
# select [\fBselect_field\fR] from [\fBtable\fR] where
# .ti +8
# [\fBwhere_field\fR] = '$lookup' [\fBadditional_conditions\fR]
#
# $lookup contains the search string, and is escaped so if
# it contains single quotes or other odd characters, it will
# not cause a parse error, or worse, a security problem.
# .IP "\fBselect_field\fR"
# The SQL "select" parameter. Example:
# .ti +4
# select_field = forw_addr
# .IP "\fBtable\fR"
# The SQL "select .. from" table name. Example:
# .ti +4
# table = mxaliases
# .IP "\fBwhere_field\fR
# The SQL "select .. where" parameter. Example:
# .ti +4
# where_field = alias
# .IP "\fBadditional_conditions\fR
# Additional conditions to the SQL query. Example:
# .ti +4
# additional_conditions = and status = 'paid'
# .PP
# The following parameters provide ways to override the default
# SELECT statement. Setting them will instruct Postfix to ignore
# the above \fBtable\fR, \fBselect_field\fR, \fBwhere_field\fR and
# \fBadditional_conditions\fR parameters:
# .IP "\fBquery\fR"
# This parameter specifies a complete SQL query. Example:
# The SQL query template used to search the database, where \fB%s\fR
# is a substitute for the address Postfix is trying to resolve,
# e.g.
# .ti +4
# query = select forw_addr from mxaliases where
# .ti +8
# alias = '%s' and status = 'paid'
# query = SELECT replacement FROM aliases WHERE mailbox = '%s'
#
# This parameter supports the following '%' expansions:
# .RS
# .IP "\fB\fB%%\fR\fR"
# This is replaced by a literal '%' character. (Postfix 2.2 and later)
# .IP "\fB\fB%s\fR\fR"
# This is replaced by the input key. Quoting is used to make sure
# that the input key does not add unexpected metacharacters.
# This is replaced by the input key.
# SQL quoting is used to make sure that the input key does not
# add unexpected metacharacters.
# .IP "\fB\fB%u\fR\fR"
# When the input key is an address of the form user@domain,
# \fB%u\fR is replaced by the quoted local part of the address.
# If no domain is specified, \fB%u\fR is replaced by the entire
# search string.
# When the input key is an address of the form user@domain, \fB%u\fR
# is replaced by the SQL quoted local part of the address.
# Otherwise, \fB%u\fR is replaced by the entire search string.
# If the localpart is empty, the query is suppressed and returns
# no results.
# .IP "\fB\fB%d\fR\fR"
# When the input key is an address of the form user@domain,
# \fB%d\fR is replaced by the quoted domain part of the address.
# When the input key has no domain qualifier, \fB%d\fR is replaced
# by the entire search string.
# When the input key is an address of the form user@domain, \fB%d\fR
# is replaced by the SQL quoted domain part of the address.
# Otherwise, the query is suppressed and returns no results.
# .IP "\fB\fB%[SUD]\fR\fR"
# The upper-case equivalents of the above expansions behave in the
# \fBquery\fR parameter identically to their lower-case counter-parts.
# With the \fBresult_format\fR parameter (see below), they expand the
# input key rather than the result value.
# .IP
# The above %S, %U and %D expansions are available with Postfix 2.2
# and later
# .IP "\fB\fB%[1-9]\fR\fR"
# The patterns %1, %2, ... %9 are replaced by the corresponding
# most significant component of the input key's domain. If the
# input key is \fIuser@mail.example.com\fR, then %1 is \fBcom\fR,
# %2 is \fBexample\fR and %3 is \fBmail\fR. If the input key is
# unqualified or does not have enough domain components to satisfy
# all the specified patterns, the query is suppressed and returns
# no results.
# .IP
# The above %1, ... %9 expansions are available with Postfix 2.2
# and later
# .RE
# .IP
# The \fBdomain\fR parameter described below limits the input
# keys to addresses in matching domains. When the \fBdomain\fR
# parameter is non-empty, SQL queries for unqualified addresses
# or addresses in non-matching domains are suppressed
# and return no results.
#
# The precedence of this parameter has changed with Postfix 2.2,
# in prior releases the precedence was, from highest to lowest,
# \fBselect_function\fR, \fBquery\fR, \fBselect_field\fR, ...
#
# With Postfix 2.2 the \fBquery\fR parameter has highest precedence,
# see COMPATIBILITY above.
#
# NOTE: DO NOT put quotes around the \fBquery\fR parameter.
# .IP "\fBresult_format (default: \fB%s\fR)\fR"
# Format template applied to result attributes. Most commonly used
# to append (or prepend) text to the result. This parameter supports
# the following '%' expansions:
# .RS
# .IP "\fB\fB%%\fR\fR"
# This is replaced by a literal '%' character.
# .IP "\fB\fB%s\fR\fR"
# This is replaced by the value of the result attribute. When
# result is empty it is skipped.
# .IP "\fB%u\fR
# When the result attribute value is an address of the form
# user@domain, \fB%u\fR is replaced by the local part of the
# address. When the result has an empty localpart it is skipped.
# .IP "\fB\fB%d\fR\fR"
# When a result attribute value is an address of the form
# user@domain, \fB%d\fR is replaced by the domain part of
# the attribute value. When the result is unqualified it
# is skipped.
# .IP "\fB\fB%[SUD1-9]\fR\fB"
# The upper-case and decimal digit expansions interpolate
# the parts of the input key rather than the result. Their
# behaviour is identical to that described with \fBquery\fR,
# and in fact because the input key is known in advance, queries
# whose key does not contain all the information specified in
# the result template are suppressed and return no results.
# .RE
# .IP
# For example, using "result_format = smtp:[%s]" allows one
# to use a mailHost attribute as the basis of a transport(5)
# table. After applying the result format, multiple values
# are concatenated as comma separated strings. The expansion_limit
# and parameter explained below allows one to restrict the number
# of values in the result, which is especially useful for maps that
# must return at most one value.
#
# The default value \fB%s\fR specifies that each result value should
# be used as is.
#
# This parameter is available with Postfix 2.2 and later.
#
# NOTE: DO NOT put quotes around the result format!
# .IP "\fBdomain (default: no domain list)\fR"
# This is a list of domain names, paths to files, or
# dictionaries. When specified, only fully qualified search
# keys with a *non-empty* localpart and a matching domain
# are eligible for lookup: 'user' lookups, bare domain lookups
# and "@domain" lookups are not performed. This can significantly
# reduce the query load on the PostgreSQL server.
# .ti +4
# domain = postfix.org, hash:/etc/postfix/searchdomains
#
# It is best not to use SQL to store the domains eligible
# for SQL lookups.
#
# This parameter is available with Postfix 2.2 and later.
#
# NOTE: DO NOT define this parameter for local(8) aliases,
# because the input keys are always unqualified.
# .IP "\fBexpansion_limit (default: 0)\fR"
# 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.
# .PP
# Pre-Postfix 2.2 legacy interfaces:
# .IP "\fBselect_function\fR"
# This parameter specifies a database function name. Example:
# .ti +4
@@ -152,16 +256,54 @@
#
# This is equivalent to:
# .ti +4
# query = select my_lookup_user_alias('%s')
# query = SELECT my_lookup_user_alias('%s')
#
# and overrides both the \fBquery\fR parameter and the table-related
# fields above.
# This parameter overrides the legacy table-related fields (described
# below). With Postfix versions prior to 2.2, it also overrides the
# \fBquery\fR parameter. Starting with Postfix 2.2, the \fBquery\fR
# parameter has highest precedence, and this parameter is deprecated.
# Please migrate to the new \fBquery\fR interface as this interface
# is slated to be phased out.
# .PP
# The following parameters (with lower precedence than the
# \fBselect_function\fR interface described above) can be used to
# build the SQL select statement as follows:
#
# As of June 2002, if the function returns a single row and
# a single column AND that value is NULL, then the result
# will be treated as if the key was not in the dictionary.
# .ti +4
# SELECT [\fBselect_field\fR]
# .ti +4
# FROM [\fBtable\fR]
# .ti +4
# WHERE [\fBwhere_field\fR] = '%s'
# .ti +10
# [\fBadditional_conditions\fR]
#
# Future versions will allow functions to return result sets.
# The specifier %s is replaced with each lookup by the lookup key
# and is escaped so if it contains single quotes or other odd
# characters, it will not cause a parse error, or worse, a security
# problem.
#
# Starting with Postfix 2.2, this interface is obsoleted by the more
# general \fBquery\fR interface described above. If higher precedence
# the \fBquery\fR or \fBselect_function\fR parameters described above
# are defined, these parameters are ignored. Please migrate to the new
# \fBquery\fR interface as this interface is slated to be phased out.
# .IP "\fBselect_field\fR"
# The SQL "select" parameter. Example:
# .ti +4
# \fBselect_field\fR = forw_addr
# .IP "\fBtable\fR"
# The SQL "select .. from" table name. Example:
# .ti +4
# \fBtable\fR = mxaliases
# .IP "\fBwhere_field\fR
# The SQL "select .. where" parameter. Example:
# .ti +4
# \fBwhere_field\fR = alias
# .IP "\fBadditional_conditions\fR
# Additional conditions to the SQL query. Example:
# .ti +4
# \fBadditional_conditions\fR = AND status = 'paid'
# SEE ALSO
# postmap(1), Postfix lookup table manager
# postconf(5), configuration parameters

View File

@@ -511,6 +511,24 @@ Enable the rewriting of the form "user%domain" to "user@domain".
This is enabled by default.
</p>
<p> Note: With Postfix version 2.2, message header address rewriting
happens only when one of the following conditions is true: </p>
<ul>
<li> The message is received with the Postfix sendmail(1) command,
<li> The message is received from a network client that matches
$local_header_rewrite_clients,
<li> The message is received from the network, and the
remote_header_rewrite_domain parameter specifies a non-empty value.
</ul>
<p> To get the behavior before Postfix 2.2, specify
"local_header_rewrite_clients = static:all". </p>
<p>
Example:
</p>
@@ -685,6 +703,24 @@ will become visible after a minute or so. Use "<b>postfix reload</b>"
to eliminate the delay.
</p>
<p> Note: with Postfix version 2.2, message header address mapping
happens only when message header address rewriting is enabled: </p>
<ul>
<li> The message is received with the Postfix sendmail(1) command,
<li> The message is received from a network client that matches
$local_header_rewrite_clients,
<li> The message is received from the network, and the
remote_header_rewrite_domain parameter specifies a non-empty value.
</ul>
<p> To get the behavior before Postfix 2.2, specify
"local_header_rewrite_clients = static:all". </p>
<p>
Examples:
</p>
@@ -1638,7 +1674,8 @@ inet_interfaces = 192.168.1.2, 127.0.0.1
<p> The Internet protocols Postfix will attempt to use when making
or accepting connections. Specify one or more of "ipv4"
or "ipv6", separated by whitespace or commas. The form
"all" is equivalent to "ipv4, ipv6". </p>
"all" is equivalent to "ipv4, ipv6" or "ipv4", depending
on whether the operating system implements IPv6. </p>
<p> This feature is available in Postfix version 2.2 and later. </p>
@@ -2385,6 +2422,25 @@ does not change "user@any.thing.foo.example.com" or "user@foo.example.com",
but strips "user@any.thing.else.example.com" to "user@example.com".
</p>
<p> Note: with Postfix version 2.2, message header address masquerading
happens only when message header address rewriting is enabled: </p>
<ul>
<li> The message is received with the Postfix sendmail(1) command,
<li> The message is received from a network client that matches
$local_header_rewrite_clients,
<li> The message is received from the network, and the
remote_header_rewrite_domain parameter specifies a non-empty value.
</ul>
<p> To get the behavior before Postfix 2.2, specify
"local_header_rewrite_clients = static:all". </p>
<p>
Example:
</p>
@@ -5478,6 +5534,24 @@ necessary if your machine is connected to UUCP networks. It is
enabled by default.
</p>
<p> Note: With Postfix version 2.2, message header address rewriting
happens only when one of the following conditions is true: </p>
<ul>
<li> The message is received with the Postfix sendmail(1) command,
<li> The message is received from a network client that matches
$local_header_rewrite_clients,
<li> The message is received from the network, and the
remote_header_rewrite_domain parameter specifies a non-empty value.
</ul>
<p> To get the behavior before Postfix 2.2, specify
"local_header_rewrite_clients = static:all". </p>
<p>
Example:
</p>
@@ -5834,10 +5908,28 @@ append the string "@$remote_header_rewrite_domain" instead.
</p>
<p>
This feature is enabled by default and must not be turned off.
Note 1: This feature is enabled by default and must not be turned off.
Postfix does not support domain-less addresses.
</p>
<p> Note 2: With Postfix version 2.2, message header address rewriting
happens only when one of the following conditions is true: </p>
<ul>
<li> The message is received with the Postfix sendmail(1) command,
<li> The message is received from a network client that matches
$local_header_rewrite_clients,
<li> The message is received from the network, and the
remote_header_rewrite_domain parameter specifies a non-empty value.
</ul>
<p> To get the behavior before Postfix 2.2, specify
"local_header_rewrite_clients = static:all". </p>
%PARAM append_dot_mydomain yes
<p>
@@ -5848,11 +5940,29 @@ instead.
</p>
<p>
This feature is enabled by default. If disabled, users will not be
Note 1: This feature is enabled by default. If disabled, users will not be
able to send mail to "user@partialdomainname" but will have to
specify full domain names instead.
</p>
<p> Note 2: With Postfix version 2.2, message header address rewriting
happens only when one of the following conditions is true: </p>
<ul>
<li> The message is received with the Postfix sendmail(1) command,
<li> The message is received from a network client that matches
$local_header_rewrite_clients,
<li> The message is received from the network, and the
remote_header_rewrite_domain parameter specifies a non-empty value.
</ul>
<p> To get the behavior before Postfix 2.2, specify
"local_header_rewrite_clients = static:all". </p>
%PARAM application_event_drain_time 100s
<p>

View File

@@ -1,7 +1,7 @@
SHELL = /bin/sh
SRCS = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.c \
canon_addr.c cfg_parser.c cleanup_strerror.c cleanup_strflags.c \
clnt_stream.c debug_peer.c debug_process.c defer.c \
clnt_stream.c debug_peer.c debug_process.c defer.c db_common.c \
deliver_completed.c deliver_flock.c deliver_pass.c deliver_request.c \
dict_ldap.c dict_mysql.c dict_pgsql.c dict_proxy.c domain_list.c \
dot_lockfile.c dot_lockfile_as.c ext_prop.c file_id.c flush_clnt.c \
@@ -28,7 +28,7 @@ SRCS = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.c \
wildcard_inet_addr.c valid_mailhost_addr.c
OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \
clnt_stream.o debug_peer.o debug_process.o defer.o \
clnt_stream.o debug_peer.o debug_process.o defer.o db_common.o \
deliver_completed.o deliver_flock.o deliver_pass.o deliver_request.o \
dict_ldap.o dict_mysql.o dict_pgsql.o dict_proxy.o domain_list.o \
dot_lockfile.o dot_lockfile_as.o ext_prop.o file_id.o flush_clnt.o \
@@ -73,7 +73,7 @@ HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
resolve_local.h rewrite_clnt.h sent.h smtp_stream.h split_addr.h \
string_list.h strip_addr.h sys_exits.h timed_ipc.h tok822.h \
trace.h verify.h verify_clnt.h verp_sender.h virtual8_maps.h \
xtext.h scache.h user_acl.h ehlo_mask.h \
xtext.h scache.h user_acl.h ehlo_mask.h db_common.h \
wildcard_inet_addr.h valid_mailhost_addr.h
TESTSRC = rec2stream.c stream2rec.c recdump.c
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
@@ -520,6 +520,20 @@ clnt_stream.o: mail_proto.h
clnt_stream.o: ../../include/attr.h
clnt_stream.o: mail_params.h
clnt_stream.o: clnt_stream.h
db_common.o: db_common.c
db_common.o: ../../include/sys_defs.h
db_common.o: cfg_parser.h
db_common.o: ../../include/mymalloc.h
db_common.o: ../../include/vstring.h
db_common.o: ../../include/vbuf.h
db_common.o: ../../include/msg.h
db_common.o: ../../include/dict.h
db_common.o: ../../include/vstream.h
db_common.o: ../../include/argv.h
db_common.o: db_common.h
db_common.o: string_list.h
db_common.o: ../../include/match_list.h
db_common.o: ../../include/match_ops.h
debug_peer.o: debug_peer.c
debug_peer.o: ../../include/sys_defs.h
debug_peer.o: ../../include/msg.h
@@ -603,10 +617,59 @@ deliver_request.o: recipient_list.h
deliver_request.o: deliver_request.h
dict_ldap.o: dict_ldap.c
dict_ldap.o: ../../include/sys_defs.h
dict_ldap.o: ../../include/msg.h
dict_ldap.o: ../../include/mymalloc.h
dict_ldap.o: ../../include/vstring.h
dict_ldap.o: ../../include/vbuf.h
dict_ldap.o: ../../include/dict.h
dict_ldap.o: ../../include/vstream.h
dict_ldap.o: ../../include/argv.h
dict_ldap.o: ../../include/stringops.h
dict_ldap.o: ../../include/binhash.h
dict_ldap.o: cfg_parser.h
dict_ldap.o: db_common.h
dict_ldap.o: string_list.h
dict_ldap.o: ../../include/match_list.h
dict_ldap.o: ../../include/match_ops.h
dict_ldap.o: dict_ldap.h
dict_mysql.o: dict_mysql.c
dict_mysql.o: ../../include/sys_defs.h
dict_mysql.o: ../../include/dict.h
dict_mysql.o: ../../include/vstream.h
dict_mysql.o: ../../include/vbuf.h
dict_mysql.o: ../../include/argv.h
dict_mysql.o: ../../include/msg.h
dict_mysql.o: ../../include/mymalloc.h
dict_mysql.o: ../../include/vstring.h
dict_mysql.o: ../../include/split_at.h
dict_mysql.o: ../../include/find_inet.h
dict_mysql.o: ../../include/myrand.h
dict_mysql.o: ../../include/events.h
dict_mysql.o: cfg_parser.h
dict_mysql.o: db_common.h
dict_mysql.o: string_list.h
dict_mysql.o: ../../include/match_list.h
dict_mysql.o: ../../include/match_ops.h
dict_mysql.o: dict_mysql.h
dict_pgsql.o: dict_pgsql.c
dict_pgsql.o: ../../include/sys_defs.h
dict_pgsql.o: ../../include/dict.h
dict_pgsql.o: ../../include/vstream.h
dict_pgsql.o: ../../include/vbuf.h
dict_pgsql.o: ../../include/argv.h
dict_pgsql.o: ../../include/msg.h
dict_pgsql.o: ../../include/mymalloc.h
dict_pgsql.o: ../../include/vstring.h
dict_pgsql.o: ../../include/split_at.h
dict_pgsql.o: ../../include/find_inet.h
dict_pgsql.o: ../../include/myrand.h
dict_pgsql.o: ../../include/events.h
dict_pgsql.o: cfg_parser.h
dict_pgsql.o: db_common.h
dict_pgsql.o: string_list.h
dict_pgsql.o: ../../include/match_list.h
dict_pgsql.o: ../../include/match_ops.h
dict_pgsql.o: dict_pgsql.h
dict_proxy.o: dict_proxy.c
dict_proxy.o: ../../include/sys_defs.h
dict_proxy.o: ../../include/msg.h

View File

@@ -0,0 +1,448 @@
/*++
/* NAME
/* db_common 3
/* SUMMARY
/* utilities common to network based dictionaries
/* SYNOPSIS
/* #include "db_common.h"
/*
/* int db_common_parse(dict, ctx, format, query)
/* DICT *dict;
/* void **ctx;
/* const char *format;
/* int query;
/*
/* void db_common_free_context(ctx)
/* void *ctx;
/*
/* int db_common_expand(ctx, format, value, key, buf, quote_func);
/* void *ctx;
/* const char *format;
/* const char *value;
/* const char *key;
/* VSTRING *buf;
/* void (*quote_func)(DICT *, const char *, VSTRING *);
/*
/* int db_common_check_domain(domain_list, addr);
/* STRING_LIST *domain_list;
/* const char *addr;
/*
/* void db_common_sql_build_query(query,parser);
/* VSTRING *query;
/* CFG_PARSER *parser;
/*
/* DESCRIPTION
/* This module implements utilities common to network based dictionaries.
/*
/* \fIdb_common_parse\fR parses query and result substitution templates.
/* It must be called for each template before any calls to
/* \fIdb_common_expand\fR. The \fIctx\fB argument must be initialized to
/* a reference to a (void *)0 before the first template is parsed, this
/* causes memory for the context to be allocated and the new pointer is
/* stored in *ctx. When the dictionary is closed, this memory must be
/* freed with a final call to \fBdb_common_free_context\fR.
/*
/* Calls for additional templates associated with the same map must use the
/* same ctx argument. The context accumulates run-time lookup key and result
/* validation information (inapplicable keys or results are skipped) and is
/* needed later in each call of \fIdb_common_expand\fR. A non-zero return
/* value indicates that data-depedent '%' expansions were found in the input
/* template.
/*
/* \fIdb_common_expand\fR expands the specifiers in \fIformat\fR.
/* When the input data lacks all fields needed for the expansion, zero
/* is returned and the query or result should be skipped. Otherwise
/* the expansion is appended to the result buffer (after a comma if the
/* the result buffer is not empty).
/*
/* If not NULL, the \fBquote_func\fR callback performs database-specific
/* quoting of each variable before expansion.
/* \fBvalue\fR is the lookup key for query expansion and result for result
/* expansion. \fBkey\fR is NULL for query expansion and the lookup key for
/* result expansion.
/* .PP
/* The following '%' expansions are performed on \fBvalue\fR:
/* .IP %%
/* A literal percent character.
/* .IP %s
/* The entire lookup key \fIaddr\fR.
/* .IP %u
/* If \fBaddr\fR is a fully qualified address, the local part of the
/* address. Otherwise \fIaddr\fR.
/* .IP %d
/* If \fIaddr\fR is a fully qualified address, the domain part of the
/* address. Otherwise the query against the database is suppressed and
/* the lookup returns no results.
/*
/* The following '%' expansions are performed on the lookup \fBkey\fR:
/* .IP %S
/* The entire lookup key \fIkey\fR.
/* .IP %U
/* If \fBkey\fR is a fully qualified address, the local part of the
/* address. Otherwise \fIkey\fR.
/* .IP %D
/* If \fIkey\fR is a fully qualified address, the domain part of the
/* address. Otherwise the query against the database is suppressed and
/* the lookup returns no results.
/*
/* .PP
/* \fIdb_common_check_domain\fR checks domain list so that query optimization
/* can be performed
/*
/* .PP
/* \fIdb_common_sql_build_query\fR builds the "default"(backwards compatible)
/* query from the 'table', 'select_field', 'where_field' and
/* 'additional_conditions' parameters, checking for errors.
/*
/* DIAGNOSTICS
/* Fatal errors: invalid substitution format, invalid string_list pattern,
/* insufficient parameters.
/* SEE ALSO
/* dict(3) dictionary manager
/* string_list(3) string list pattern matching
/* match_ops(3) simple string or host pattern matching
/* 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
/*
/* Liviu Daia
/* Institute of Mathematics of the Romanian Academy
/* P.O. BOX 1-764
/* RO-014700 Bucharest, ROMANIA
/*
/* Jose Luis Tallon
/* G4 J.E. - F.I. - U.P.M.
/* Campus de Montegancedo, S/N
/* E-28660 Madrid, SPAIN
/*
/* Victor Duchovni
/* Morgan Stanley
/*--*/
/*
* System library.
*/
#include "sys_defs.h"
#include <stddef.h>
#include <string.h>
/*
* Global library.
*/
#include "cfg_parser.h"
/*
* Utility library.
*/
#include <mymalloc.h>
#include <vstring.h>
#include <msg.h>
#include <dict.h>
/*
* Application specific
*/
#include "db_common.h"
#define DB_COMMON_KEY_DOMAIN (1 << 0) /* Need lookup key domain */
#define DB_COMMON_KEY_USER (1 << 1) /* Need lookup key localpart */
#define DB_COMMON_VALUE_DOMAIN (1 << 2) /* Need result domain */
#define DB_COMMON_VALUE_USER (1 << 3) /* Need result localpart */
typedef struct {
DICT *dict;
int flags;
int nparts;
} DB_COMMON_CTX;
/* db_common_parse - validate query or result template */
int db_common_parse(DICT *dict, void **ctxPtr, const char *format, int query)
{
DB_COMMON_CTX *ctx = (DB_COMMON_CTX *)*ctxPtr;
const char *cp;
int dynamic = 0;
if (ctx == 0) {
ctx = (DB_COMMON_CTX *)(*ctxPtr = mymalloc(sizeof *ctx));
ctx->dict = dict;
ctx->flags = 0;
ctx->nparts = 0;
}
for (cp = format; *cp; ++cp)
if (*cp == '%')
switch (*++cp) {
case '%':
break;
case 'u':
ctx->flags |=
query ? DB_COMMON_KEY_USER : DB_COMMON_VALUE_USER;
dynamic = 1;
break;
case 'd':
ctx->flags |=
query ? DB_COMMON_KEY_DOMAIN : DB_COMMON_VALUE_DOMAIN;
dynamic = 1;
break;
case 's': case 'S':
dynamic = 1;
break;
case 'U':
ctx->flags |= DB_COMMON_KEY_USER;
dynamic = 1;
break;
case '1': case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9':
if (ctx->nparts < *cp - '0')
ctx->nparts = *cp - '0';
/* FALLTHROUGH */
case 'D':
ctx->flags |= DB_COMMON_KEY_DOMAIN;
dynamic = 1;
break;
default:
msg_fatal("db_common_parse: %s: Invalid %s template: %s",
dict->name, query ? "query" : "result", format);
}
return dynamic;
}
/* db_common_free_ctx - free parse context */
void db_common_free_ctx(void *ctxPtr)
{
myfree((char *)ctxPtr);
}
/* db_common_expand - expand query and result templates */
int db_common_expand(void *ctxArg, const char *format, const char *value,
const char *key, VSTRING *result,
db_quote_callback_t quote_func)
{
char *myname = "db_common_expand";
DB_COMMON_CTX *ctx = (DB_COMMON_CTX *)ctxArg;
const char *vdomain = 0;
const char *kdomain = 0;
char *vuser = 0;
char *kuser = 0;
ARGV *parts = 0;
int i;
const char *cp;
/* Skip NULL or empty values */
if (value == 0 || *value == 0)
return (0);
if (key) {
/* This is a result template and the input value is the result */
if (ctx->flags & (DB_COMMON_VALUE_DOMAIN | DB_COMMON_VALUE_USER))
if ((vdomain = strrchr(value, '@')) != 0)
++vdomain;
if ((!vdomain || !*vdomain) && (ctx->flags&DB_COMMON_VALUE_DOMAIN) != 0
|| vdomain == value + 1 && (ctx->flags&DB_COMMON_VALUE_USER) != 0)
return (0);
/* The result format may use the local or domain part of the key */
if (ctx->flags & (DB_COMMON_KEY_DOMAIN | DB_COMMON_KEY_USER))
if ((kdomain = strrchr(key, '@')) != 0)
++kdomain;
/*
* The key should already be checked before the query. No harm if
* the query did not get optimized out, so we just issue a warning.
*/
if ((!kdomain || !*kdomain) && (ctx->flags&DB_COMMON_KEY_DOMAIN) != 0
|| kdomain == key + 1 && (ctx->flags & DB_COMMON_KEY_USER) != 0) {
msg_warn("%s: %s: lookup key '%s' skipped after query", myname,
ctx->dict->name, value);
return (0);
}
} else {
/* This is a query template and the input value is the lookup key */
if (ctx->flags & (DB_COMMON_KEY_DOMAIN | DB_COMMON_KEY_USER))
if ((vdomain = strrchr(value, '@')) != 0)
++vdomain;
if ((!vdomain || !*vdomain) && (ctx->flags&DB_COMMON_KEY_DOMAIN) != 0
|| vdomain == value + 1 && (ctx->flags & DB_COMMON_KEY_USER) != 0)
return (0);
}
if (ctx->nparts > 0) {
parts = argv_split(key ? kdomain : vdomain, ".");
/*
* Skip domains that lack enough labels to fill-in the template.
*/
if (parts->argc < ctx->nparts) {
argv_free(parts);
return (0);
}
/*
* Skip domains with leading, consecutive or trailing '.'
* separators among the required labels.
*/
for (i = 0; i < ctx->nparts; i++)
if (*parts->argv[parts->argc-i-1] == 0) {
argv_free(parts);
return (0);
}
}
if (VSTRING_LEN(result) > 0)
VSTRING_ADDCH(result, ',');
#define QUOTE_VAL(d, q, v, buf) \
(q ? q(d, v, buf) : vstring_strcat(buf, v))
/*
* Replace all instances of %s with the address to look up. Replace
* %u with the user portion, and %d with the domain portion. "%%"
* expands to "%". lowercase -> addr, uppercase -> key
*/
for (cp = format; *cp; cp++) {
if (*cp == '%') {
switch (*++cp) {
case '%':
VSTRING_ADDCH(result, '%');
break;
case 's':
QUOTE_VAL(ctx->dict, quote_func, value, result);
break;
case 'u':
if (vdomain) {
if (vuser == 0)
vuser = mystrndup(value, vdomain - value - 1);
QUOTE_VAL(ctx->dict, quote_func, vuser, result);
}
else
QUOTE_VAL(ctx->dict, quote_func, value, result);
break;
case 'd':
QUOTE_VAL(ctx->dict, quote_func, vdomain, result);
break;
case 'S':
if (key)
QUOTE_VAL(ctx->dict, quote_func, key, result);
else
QUOTE_VAL(ctx->dict, quote_func, value, result);
break;
case 'U':
if (key) {
if (kdomain) {
if (kuser == 0)
kuser = mystrndup(key, kdomain - key - 1);
QUOTE_VAL(ctx->dict, quote_func, kuser, result);
}
else
QUOTE_VAL(ctx->dict, quote_func, key, result);
} else {
if (vdomain) {
if (vuser == 0)
vuser = mystrndup(value, vdomain - value - 1);
QUOTE_VAL(ctx->dict, quote_func, vuser, result);
}
else
QUOTE_VAL(ctx->dict, quote_func, value, result);
}
break;
case 'D':
if (key)
QUOTE_VAL(ctx->dict, quote_func, kdomain, result);
else
QUOTE_VAL(ctx->dict, quote_func, vdomain, result);
break;
case '1': case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9':
QUOTE_VAL(ctx->dict, quote_func,
parts->argv[parts->argc-(*cp-'0')], result);
break;
default:
msg_fatal("%s: %s: invalid %s template '%s'", myname,
ctx->dict->name, key ? "result" : "query",
format);
}
} else
VSTRING_ADDCH(result, *cp);
}
VSTRING_TERMINATE(result);
if (vuser)
myfree(vuser);
if (kuser)
myfree(kuser);
if (parts)
argv_free(parts);
return (1);
}
/* db_common_check_domain - check domain list */
int db_common_check_domain(STRING_LIST *domain_list, const char *addr)
{
char *domain;
if (domain_list) {
if ((domain = strrchr(addr, '@')) != NULL)
++domain;
if (domain == NULL || domain == addr + 1)
return (0);
if (match_list_match(domain_list, domain) == 0)
return (0);
}
return (1);
}
/* db_common_sql_build_query -- build query for SQL maptypes */
void db_common_sql_build_query(VSTRING *query, CFG_PARSER *parser)
{
char *myname = "db_common_sql_build_query";
char *table;
char *select_field;
char *where_field;
char *additional_conditions;
/*
* Build "old style" query: "select %s from %s where %s"
*/
if ((table = cfg_get_str(parser, "table", NULL, 1, 0)) == 0)
msg_fatal("%s: 'table' parameter not defined", myname);
if ((select_field = cfg_get_str(parser, "select_field", NULL, 1, 0)) == 0)
msg_fatal("%s: 'select_field' parameter not defined", myname);
if ((where_field = cfg_get_str(parser, "where_field", NULL, 1, 0)) == 0)
msg_fatal("%s: 'where_field' parameter not defined", myname);
additional_conditions = cfg_get_str(parser, "additional_conditions",
"", 0, 0);
vstring_sprintf(query, "SELECT %s FROM %s WHERE %s='%%s' %s",
select_field, table, where_field,
additional_conditions);
myfree(table);
myfree(select_field);
myfree(where_field);
myfree(additional_conditions);
}

View File

@@ -0,0 +1,55 @@
#ifndef _DB_COMMON_H_INCLUDED_
#define _DB_COMMON_H_INCLUDED_
/*++
/* NAME
/* db_common 3h
/* SUMMARY
/* utilities common to network based dictionaries
/* SYNOPSIS
/* #include "db_common.h"
/* DESCRIPTION
/* .nf
*/
/*
* External interface.
*/
#include "dict.h"
#include "string_list.h"
typedef void (*db_quote_callback_t)(DICT *, const char *, VSTRING *);
extern int db_common_parse(DICT *, void **, const char *, int);
extern void db_common_free_ctx(void *);
extern int db_common_expand(void *, const char *, const char *,
const char *, VSTRING *, db_quote_callback_t);
extern int db_common_check_domain(STRING_LIST *, const char *);
extern void db_common_sql_build_query(VSTRING *query, CFG_PARSER *parser);
/* 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
/*
/* Liviu Daia
/* Institute of Mathematics of the Romanian Academy
/* P.O. BOX 1-764
/* RO-014700 Bucharest, ROMANIA
/*
/* Jose Luis Tallon
/* G4 J.E. - F.I. - U.P.M.
/* Campus de Montegancedo, S/N
/* E-28660 Madrid, SPAIN
/*
/* Victor Duchovni
/* Morgan Stanley
/*--*/
#endif

View File

@@ -7,7 +7,7 @@
/* #include <dict_ldap.h>
/*
/* DICT *dict_ldap_open(attribute, dummy, dict_flags)
/* const char *attribute;
/* const char *ldapsource;
/* int dummy;
/* int dict_flags;
/* DESCRIPTION
@@ -47,11 +47,13 @@
/* .IP timeout
/* Deadline for LDAP open() and LDAP search() .
/* .IP query_filter
/* The filter used to search for directory entries, for example
/* \fI(mailacceptinggeneralid=%s)\fR.
/* .IP result_filter
/* The filter used to expand results from queries. Default is
/* \fI%s\fR.
/* The search filter template used to search for directory entries,
/* for example \fI(mailacceptinggeneralid=%s)\fR. See ldap_table(5)
/* for details.
/* .IP result_format
/* The result template used to expand results from queries. Default
/* is \fI%s\fR. See ldap_table(5) for details. Also supported under
/* the name \fIresult_filter\fR for compatibility with older releases.
/* .IP result_attribute
/* The attribute(s) returned by the search, in which to find
/* RFC822 addresses, for example \fImaildrop\fR.
@@ -184,8 +186,6 @@
/* Utility library. */
#include "match_list.h"
#include "match_ops.h"
#include "msg.h"
#include "mymalloc.h"
#include "vstring.h"
@@ -196,6 +196,7 @@
/* Global library. */
#include "cfg_parser.h"
#include "db_common.h"
/* Application-specific. */
@@ -212,15 +213,17 @@ typedef struct {
*/
typedef struct {
DICT dict; /* generic member */
CFG_PARSER *parser;
char *ldapsource;
CFG_PARSER *parser; /* common parameter parser */
char *query; /* db_common_expand() query */
char *result_format; /* db_common_expand() result_format */
STRING_LIST *domain; /* restrict queries to these domains */
void *ctx; /* db_common_parse() context */
int dynamic_base; /* Search base has substitutions? */
int expansion_limit;
char *server_host;
int server_port;
int scope;
char *search_base;
MATCH_LIST *domain;
char *query_filter;
char *result_filter;
ARGV *result_attributes;
int num_attributes; /* rest of list is DN's. */
int bind;
@@ -229,7 +232,6 @@ typedef struct {
int timeout;
int dereference;
long recursion_limit;
long expansion_limit;
long size_limit;
int chase_referrals;
int debuglevel;
@@ -251,6 +253,55 @@ typedef struct {
#define DICT_LDAP_CONN(d) ((LDAP_CONN *)((d)->ht->value))
/*
* Quoting rules.
*/
/* rfc2253_quote - Quote input key for safe inclusion in the search base */
static void rfc2253_quote(DICT *unused, const char *name, VSTRING *result)
{
unsigned char *sub = (unsigned char *)name;
size_t len;
/*
* The RFC only requires quoting of a leading or trailing space,
* but it is harmless to quote whitespace everywhere. Similarly,
* we quote all '#' characters, even though only the leading '#'
* character requires quoting per the RFC.
*/
while (*sub)
if ((len = strcspn(sub, " \t\"#+,;<>\\")) > 0) {
vstring_strncat(result, sub, len);
sub += len;
} else
vstring_sprintf_append(result, "\\%02X", *(sub++));
}
/* rfc2254_quote - Quote input key for safe inclusion in the query filter */
static void rfc2254_quote(DICT *unused, const char *name, VSTRING *result)
{
unsigned char *sub = (unsigned char *)name;
size_t len;
/*
* If any characters in the supplied address should be escaped per RFC
* 2254, do so. Thanks to Keith Stevenson and Wietse. And thanks to
* Samuel Tardieu for spotting that wildcard searches were being done in
* the first place, which prompted the ill-conceived lookup_wildcards
* parameter and then this more comprehensive mechanism.
*/
while (*sub)
if ((len = strcspn(sub, "*()\\")) > 0) {
vstring_strncat(result, sub, len);
sub += len;
} else
vstring_sprintf_append(result, "\\%02X", *(sub++));
}
static BINHASH *conn_hash = 0;
#if defined(LDAP_API_FEATURE_X_OPENLDAP) || !defined(LDAP_OPT_NETWORK_TIMEOUT)
@@ -404,7 +455,7 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
#if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN)
if (dict_ldap->debuglevel > 0 &&
ber_set_option(NULL, LBER_OPT_LOG_PRINT_FN,
(LDAP_CONST *) dict_ldap_logprint) != LBER_OPT_SUCCESS)
(LDAP_CONST void *) dict_ldap_logprint) != LBER_OPT_SUCCESS)
msg_warn("%s: Unable to set ber logprint function.", myname);
#if defined(LBER_OPT_DEBUG_LEVEL)
if (ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL,
@@ -497,7 +548,7 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
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);
myname, dict_ldap->parser->name, dict_ldap->size_limit);
}
/*
@@ -582,7 +633,7 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
if (msg_verbose)
msg_info("%s: Cached connection handle for LDAP source %s",
myname, dict_ldap->ldapsource);
myname, dict_ldap->parser->name);
return (0);
}
@@ -643,59 +694,6 @@ static void dict_ldap_conn_find(DICT_LDAP *dict_ldap)
vstring_free(keybuf);
}
/*
* expand a filter (lookup or result)
*/
static void dict_ldap_expand_filter(char *ldapsource, char *filter,
char *value, VSTRING *out)
{
char *myname = "dict_ldap_expand_filter";
char *sub,
*end;
/*
* Yes, replace all instances of %s with the address to look up. Replace
* %u with the user portion, and %d with the domain portion.
*/
sub = filter;
end = sub + strlen(filter);
while (sub < end) {
/*
* Make sure it's %[sud] and not something else. For backward
* compatibilty, treat anything other than %u or %d as %s, with a
* warning.
*/
if (*(sub) == '%') {
char *u = value;
char *p = strrchr(u, '@');
switch (*(sub + 1)) {
case 'd':
if (p)
vstring_strcat(out, p + 1);
break;
case 'u':
if (p)
vstring_strncat(out, u, p - u);
else
vstring_strcat(out, u);
break;
default:
msg_warn("%s: %s: Invalid filter substitution format '%%%c'!",
myname, ldapsource, *(sub + 1));
/* fall through */
case 's':
vstring_strcat(out, u);
break;
}
sub++;
} else
vstring_strncat(out, sub, 1);
sub++;
}
}
/*
* dict_ldap_get_values: for each entry returned by a search, get the values
* of all its attributes. Recurses to resolve any DN or URL values found.
@@ -704,7 +702,7 @@ static void dict_ldap_expand_filter(char *ldapsource, char *filter,
* are thanks to LaMont Jones.
*/
static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
VSTRING *result)
VSTRING *result, const char* name)
{
static int recursion = 0;
static int expansion;
@@ -738,10 +736,12 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
* 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);
if (dict_errno == 0
&& dict_ldap->size_limit
&& ++entries > dict_ldap->size_limit) {
msg_warn("%s[%d]: %s: Query size limit (%ld) exceeded",
myname, recursion, dict_ldap->parser->name,
dict_ldap->size_limit);
dict_errno = DICT_ERR_RETRY;
}
for (attr = ldap_first_attribute(dict_ldap->ld, entry, &ber);
@@ -792,22 +792,16 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
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]);
if (db_common_expand(dict_ldap->ctx,
dict_ldap->result_format, vals[i],
name, result, 0)
&& dict_ldap->expansion_limit > 0
&& ++expansion > dict_ldap->expansion_limit) {
msg_warn("%s[%d]: %s: Expansion limit exceeded for key: '%s'",
myname, recursion, dict_ldap->parser->name, name);
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->ldapsource,
dict_ldap->result_filter,
vals[i], result);
}
if (dict_errno != 0)
continue;
@@ -842,7 +836,7 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
}
switch (rc) {
case LDAP_SUCCESS:
dict_ldap_get_values(dict_ldap, resloop, result);
dict_ldap_get_values(dict_ldap, resloop, result, name);
break;
case LDAP_NO_SUCH_OBJECT:
@@ -875,8 +869,8 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
} 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]);
" for special attribute %s=%s", myname, recursion,
dict_ldap->parser->name, attr, vals[0]);
dict_errno = DICT_ERR_RETRY;
}
ldap_value_free(vals);
@@ -897,14 +891,12 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
char *myname = "dict_ldap_lookup";
DICT_LDAP *dict_ldap = (DICT_LDAP *) dict;
LDAPMessage *res = 0;
static VSTRING *base;
static VSTRING *query;
static VSTRING *result;
struct timeval tv;
VSTRING *escaped_name = 0,
*filter_buf = 0;
int rc = 0;
int sizelimit;
char *sub,
*end;
dict_errno = 0;
@@ -916,24 +908,22 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
* addresses in domains on the list. This can significantly reduce the
* load on the LDAP server.
*/
if (dict_ldap->domain) {
const char *p = strrchr(name, '@');
if (p == 0 || p == name ||
match_list_match(dict_ldap->domain, ++p) == 0) {
if (msg_verbose)
msg_info("%s: domain of %s not found in domain list", myname,
name);
return (0);
}
if (db_common_check_domain(dict_ldap->domain, name) == 0) {
if (msg_verbose)
msg_info("%s: Skipping lookup of '%s'", myname, name);
return (0);
}
/*
* Initialize the result holder.
*/
if (result == 0)
result = vstring_alloc(2);
vstring_strcpy(result, "");
#define INIT_VSTR(buf, len) do { \
if (buf == 0) \
buf = vstring_alloc(len); \
VSTRING_RESET(buf); \
VSTRING_TERMINATE(buf); \
} while (0)
INIT_VSTR(base, 10);
INIT_VSTR(query, 10);
INIT_VSTR(result, 10);
/*
* Because the connection may be shared and invalidated via queries for
@@ -949,7 +939,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
if (msg_verbose)
msg_info
("%s: No existing connection for LDAP source %s, reopening",
myname, dict_ldap->ldapsource);
myname, dict_ldap->parser->name);
dict_ldap_connect(dict_ldap);
@@ -960,7 +950,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
return (0);
} else if (msg_verbose)
msg_info("%s: Using existing connection for LDAP source %s",
myname, dict_ldap->ldapsource);
myname, dict_ldap->parser->name);
/*
* Connection caching, means that the connection handle may have the
@@ -973,89 +963,55 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
if (ldap_set_option(dict_ldap->ld, LDAP_OPT_SIZELIMIT, &sizelimit)
!= LDAP_OPT_SUCCESS)
msg_warn("%s: %s: Unable to set query result size limit to %ld.",
myname, dict_ldap->ldapsource, dict_ldap->size_limit);
myname, dict_ldap->parser->name, dict_ldap->size_limit);
/*
* Expand the search base and query. Skip lookup when the
* input key lacks sufficient domain components to satisfy
* all the requested %-substitutions.
*
* When the search base is not static, LDAP_NO_SUCH_OBJECT is
* expected and is therefore treated as a non-error: the lookup
* returns no results rather than a soft error.
*/
if (!db_common_expand(dict_ldap->ctx, dict_ldap->search_base,
name, 0, base, rfc2253_quote)) {
if (msg_verbose > 1)
msg_info("%s: %s: Empty expansion for %s", myname,
dict_ldap->parser->name, dict_ldap->search_base);
return (0);
}
if (!db_common_expand(dict_ldap->ctx, dict_ldap->query,
name, 0, query, rfc2254_quote)) {
if (msg_verbose > 1)
msg_info("%s: %s: Empty expansion for %s", myname,
dict_ldap->parser->name, dict_ldap->query);
return (0);
}
/*
* Prepare the query.
*/
tv.tv_sec = dict_ldap->timeout;
tv.tv_usec = 0;
escaped_name = vstring_alloc(20);
filter_buf = vstring_alloc(30);
/*
* If any characters in the supplied address should be escaped per RFC
* 2254, do so. Thanks to Keith Stevenson and Wietse. And thanks to
* Samuel Tardieu for spotting that wildcard searches were being done in
* the first place, which prompted the ill-conceived lookup_wildcards
* parameter and then this more comprehensive mechanism.
*/
end = (char *) name + strlen((char *) name);
sub = (char *) strpbrk((char *) name, "*()\\\0");
if (sub && sub != end) {
if (msg_verbose)
msg_info("%s: Found character(s) in %s that must be escaped",
myname, name);
for (sub = (char *) name; sub != end; sub++) {
switch (*sub) {
case '*':
vstring_strcat(escaped_name, "\\2a");
break;
case '(':
vstring_strcat(escaped_name, "\\28");
break;
case ')':
vstring_strcat(escaped_name, "\\29");
break;
case '\\':
vstring_strcat(escaped_name, "\\5c");
break;
case '\0':
vstring_strcat(escaped_name, "\\00");
break;
default:
vstring_strncat(escaped_name, sub, 1);
}
}
if (msg_verbose)
msg_info("%s: After escaping, it's %s", myname,
vstring_str(escaped_name));
} else
vstring_strcpy(escaped_name, (char *) name);
/*
* Does the supplied query_filter even include a substitution?
*/
if ((char *) strchr(dict_ldap->query_filter, '%') == NULL) {
/*
* No, log the fact and continue.
*/
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->ldapsource, dict_ldap->query_filter,
vstring_str(escaped_name), filter_buf);
}
/*
* On to the search.
*/
if (msg_verbose)
msg_info("%s: Searching with filter %s", myname,
vstring_str(filter_buf));
msg_info("%s: %s: Searching with filter %s", myname,
dict_ldap->parser->name, vstring_str(query));
rc = ldap_search_st(dict_ldap->ld, dict_ldap->search_base,
dict_ldap->scope,
vstring_str(filter_buf),
rc = ldap_search_st(dict_ldap->ld, vstring_str(base),
dict_ldap->scope, vstring_str(query),
dict_ldap->result_attributes->argv,
0, &tv, &res);
if (rc == LDAP_SERVER_DOWN) {
if (msg_verbose)
msg_info("%s: Lost connection for LDAP source %s, reopening",
myname, dict_ldap->ldapsource);
myname, dict_ldap->parser->name);
ldap_unbind(dict_ldap->ld);
dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld = 0;
@@ -1067,20 +1023,21 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
if (dict_errno)
return (0);
rc = ldap_search_st(dict_ldap->ld, dict_ldap->search_base,
dict_ldap->scope,
vstring_str(filter_buf),
rc = ldap_search_st(dict_ldap->ld, vstring_str(base),
dict_ldap->scope, vstring_str(query),
dict_ldap->result_attributes->argv,
0, &tv, &res);
}
if (rc == LDAP_SUCCESS) {
switch (rc) {
case LDAP_SUCCESS:
/*
* Search worked; extract the requested result_attribute.
*/
dict_ldap_get_values(dict_ldap, res, result);
dict_ldap_get_values(dict_ldap, res, result, name);
/*
* OpenLDAP's ldap_next_attribute returns a bogus
@@ -1097,8 +1054,24 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
msg_info("%s: Search returned %s", myname,
VSTRING_LEN(result) >
0 ? vstring_str(result) : "nothing");
} else {
break;
case LDAP_NO_SUCH_OBJECT:
/*
* If the search base is input key dependent, then not finding it,
* is equivalent to not finding the input key. Sadly, we cannot
* detect misconfiguration in this case.
*/
if (dict_ldap->dynamic_base)
break;
msg_warn("%s: %s: Search base '%s' not found: %d: %s",
myname, dict_ldap->parser->name,
vstring_str(base), rc, ldap_err2string(rc));
dict_errno = DICT_ERR_RETRY;
break;
default:
/*
* Rats. The search didn't work.
*/
@@ -1116,6 +1089,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
* And tell the caller to try again later.
*/
dict_errno = DICT_ERR_RETRY;
break;
}
/*
@@ -1123,10 +1097,6 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
*/
if (res != 0)
ldap_msgfree(res);
if (filter_buf != 0)
vstring_free(filter_buf);
if (escaped_name != 0)
vstring_free(escaped_name);
/*
* If we had an error, return nothing, Otherwise, return the result, if
@@ -1148,23 +1118,24 @@ static void dict_ldap_close(DICT *dict)
if (conn->conn_ld) {
if (msg_verbose)
msg_info("%s: Closed connection handle for LDAP source %s",
myname, dict_ldap->ldapsource);
myname, dict_ldap->parser->name);
ldap_unbind(conn->conn_ld);
}
binhash_delete(conn_hash, ht->key, ht->key_len, myfree);
}
cfg_parser_free(dict_ldap->parser);
myfree(dict_ldap->ldapsource);
myfree(dict_ldap->server_host);
myfree(dict_ldap->search_base);
if (dict_ldap->domain)
match_list_free(dict_ldap->domain);
myfree(dict_ldap->query_filter);
if (dict_ldap->result_filter)
myfree(dict_ldap->result_filter);
string_list_free(dict_ldap->domain);
myfree(dict_ldap->query);
if (dict_ldap->result_format)
myfree(dict_ldap->result_format);
argv_free(dict_ldap->result_attributes);
myfree(dict_ldap->bind_dn);
myfree(dict_ldap->bind_pw);
if (dict_ldap->ctx)
db_common_free_ctx(dict_ldap->ctx);
#ifdef LDAP_API_FEATURE_X_OPENLDAP
myfree(dict_ldap->tls_ca_cert_file);
myfree(dict_ldap->tls_ca_cert_dir);
@@ -1202,7 +1173,6 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
dict_ldap->ld = NULL;
dict_ldap->parser = cfg_parser_alloc(ldapsource);
dict_ldap->ldapsource = mystrdup(ldapsource);
server_host = cfg_get_str(dict_ldap->parser, "server_host",
"localhost", 1, 0);
@@ -1260,20 +1230,26 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
if (strcasecmp(url_desc->lud_scheme, "ldaps") == 0)
dict_ldap->ldap_ssl = 1;
ldap_free_urldesc(url_desc);
vstring_sprintf_append(url_list, " %s", h);
if (VSTRING_LEN(url_list) > 0)
VSTRING_ADDCH(url_list, ' ');
vstring_strcat(url_list, h);
} else {
if (VSTRING_LEN(url_list) > 0)
VSTRING_ADDCH(url_list, ' ');
if (strrchr(h, ':'))
vstring_sprintf_append(url_list, " ldap://%s", h);
vstring_sprintf_append(url_list, "ldap://%s", h);
else
vstring_sprintf_append(url_list, " ldap://%s:%d", h,
vstring_sprintf_append(url_list, "ldap://%s:%d", h,
dict_ldap->server_port);
}
#else
vstring_sprintf_append(url_list, " %s", h);
if (VSTRING_LEN(url_list) > 0)
VSTRING_ADDCH(url_list, ' ');
vstring_strcat(url_list, h);
#endif
}
dict_ldap->server_host =
mystrdup(VSTRING_LEN(url_list) > 0 ? vstring_str(url_list) + 1 : "");
VSTRING_TERMINATE(url_list);
dict_ldap->server_host = vstring_export(url_list);
#if defined(LDAP_API_FEATURE_X_OPENLDAP)
@@ -1286,7 +1262,6 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
dict_ldap->server_host);
#endif
myfree(server_host);
vstring_free(url_list);
/*
* Scope handling thanks to Carsten Hoeger of SuSE.
@@ -1312,18 +1287,15 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
domainlist = cfg_get_str(dict_ldap->parser, "domain", "", 0, 0);
if (*domainlist) {
#ifdef MATCH_FLAG_NONE
dict_ldap->domain = match_list_init(MATCH_FLAG_NONE,
domainlist, 1, match_string);
#else
dict_ldap->domain = match_list_init(domainlist, 1, match_string);
#endif
dict_ldap->domain = string_list_init(MATCH_FLAG_NONE, domainlist);
if (dict_ldap->domain == NULL)
msg_warn("%s: domain match list creation using \"%s\" failed, will continue without it",
myname, domainlist);
if (msg_verbose)
msg_info("%s: domain list created using \"%s\"", myname,
domainlist);
/*
* The "domain" optimization skips input keys that may in fact
* have unwanted matches in the database, so failure to create
* the match list is fatal.
*/
msg_fatal("%s: %s: domain match list creation using '%s' failed",
myname, ldapsource, domainlist);
} else {
dict_ldap->domain = NULL;
}
@@ -1335,20 +1307,37 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
* Thanks to Manuel Guesdon for spotting that this wasn't really getting
* set.
*/
dict_ldap->timeout = cfg_get_int(dict_ldap->parser, "timeout",
10, 0, 0);
dict_ldap->timeout = cfg_get_int(dict_ldap->parser, "timeout", 10, 0, 0);
dict_ldap->query_filter =
cfg_get_str(dict_ldap->parser, "query_filter",
"(mailacceptinggeneralid=%s)", 0, 0);
#if 0 /* No benefit from changing this to match the MySQL/PGSQL syntax */
if ((dict_ldap->query =
cfg_get_str(dict_ldap->parser, "query", 0, 0, 0)) == 0)
#endif
dict_ldap->query =
cfg_get_str(dict_ldap->parser, "query_filter",
"(mailacceptinggeneralid=%s)", 0, 0);
dict_ldap->result_filter =
cfg_get_str(dict_ldap->parser, "result_filter", "%s", 0, 0);
if ((dict_ldap->result_format =
cfg_get_str(dict_ldap->parser, "result_format", 0, 0, 0)) == 0)
dict_ldap->result_format =
cfg_get_str(dict_ldap->parser, "result_filter", "%s", 1, 0);
if (strcmp(dict_ldap->result_filter, "%s") == 0) {
myfree(dict_ldap->result_filter);
dict_ldap->result_filter = NULL;
/*
* Must parse all templates before we can use db_common_expand()
* If data dependent substitutions are found in the search base,
* treat NO_SUCH_OBJECT search errors as a non-matching key, rather
* than a fatal run-time error.
*/
dict_ldap->ctx = 0;
dict_ldap->dynamic_base =
db_common_parse(&dict_ldap->dict, &dict_ldap->ctx,
dict_ldap->search_base, 1);
if (!db_common_parse(0, &dict_ldap->ctx, dict_ldap->query, 1)) {
msg_warn("%s: %s: Fixed query_filter %s is probably useless",
myname, ldapsource, dict_ldap->query);
}
(void) db_common_parse(0, &dict_ldap->ctx, dict_ldap->result_format, 0);
attr = cfg_get_str(dict_ldap->parser, "result_attribute",
"maildrop", 0, 0);
dict_ldap->result_attributes = argv_split(attr, " ,\t\r\n");
@@ -1357,9 +1346,8 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
attr = cfg_get_str(dict_ldap->parser, "special_result_attribute",
"", 0, 0);
if (*attr) {
if (*attr)
argv_split_append(dict_ldap->result_attributes, attr, " ,\t\r\n");
}
myfree(attr);
/*
@@ -1378,44 +1366,32 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
dict_ldap->bind_pw = cfg_get_str(dict_ldap->parser, "bind_pw", "", 0, 0);
/*
* get configured value of "cache"; default to false
* LDAP message caching never worked and is no longer supported.
*/
tmp = cfg_get_bool(dict_ldap->parser, "cache", 0);
if (tmp)
msg_warn("%s: %s ignoring cache", myname, ldapsource);
/*
* get configured value of "cache_expiry"; default to 30 seconds
*/
tmp = cfg_get_int(dict_ldap->parser, "cache_expiry", -1, 0, 0);
if (tmp >= 0)
msg_warn("%s: %s ignoring cache_expiry", myname, ldapsource);
/*
* get configured value of "cache_size"; default to 32k
*/
tmp = cfg_get_int(dict_ldap->parser, "cache_size", -1, 0, 0);
if (tmp >= 0)
msg_warn("%s: %s ignoring cache_size", myname, ldapsource);
/*
* get configured value of "recursion_limit"; default to 1000
*/
dict_ldap->recursion_limit = cfg_get_int(dict_ldap->parser,
"recursion_limit", 1000, 1, 0);
/*
* get configured value of "expansion_limit"; default to 0
* XXX: The default should be non-zero for safety, but that is not
* backwards compatible.
*/
dict_ldap->expansion_limit = cfg_get_int(dict_ldap->parser,
"expansion_limit", 0, 0, 0);
/*
* get configured value of "size_limit"; default to expansion_limit
*/
dict_ldap->size_limit = cfg_get_int(dict_ldap->parser, "size_limit",
dict_ldap->expansion_limit,
0, 0);
dict_ldap->expansion_limit, 0, 0);
/*
* Alias dereferencing suggested by Mike Mattice.

View File

@@ -57,14 +57,69 @@
/* Password for the above.
/* .IP \fIdbname\fR
/* Name of the database.
/* .IP \fIdomain\fR
/* List of domains the queries should be restricted to. If
/* specified, only FQDN addresses whose domain parts matching this
/* list will be queried against the SQL database. Lookups for
/* partial addresses are also supressed. This can significantly
/* reduce the query load on the server.
/* .IP \fIquery\fR
/* Query template, before the query is actually issued, variable
/* substitutions are performed. See mysql_table(5) for details. If
/* No query is specified, the legacy variables \fItable\fR,
/* \fIselect_field\fR, \fIwhere_field\fR and \fIadditional_conditions\fR
/* are used to construct the query template.
/* .IP \fIresult_format\fR
/* The format used to expand results from queries. Substitutions
/* are performed as described in mysql_table(5). Defaults to returning
/* the lookup result unchanged.
/* .IP expansion_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
/* non-empty (and non-NULL) column of a multi-column result row counts as
/* one result.
/* .IP \fItable\fR
/* Name of the table.
/* When \fIquery\fR is not set, name of the table used to construct the
/* query string. This provides compatibility with older releases.
/* .IP \fIselect_field\fR
/* Name of the result field.
/* When \fIquery\fR is not set, name of the result field used to
/* construct the query string. This provides compatibility with older
/* releases.
/* .IP \fIwhere_field\fR
/* Field used in the WHERE clause.
/* When \fIquery\fR is not set, name of the where clause field used to
/* construct the query string. This provides compatibility with older
/* releases.
/* .IP \fIadditional_conditions\fR
/* Additional conditions to the WHERE clause.
/* When \fIquery\fR is not set, additional where clause conditions used
/* to construct the query string. This provides compatibility with older
/* releases.
/* .IP \fIhosts\fR
/* List of hosts to connect to.
/* .PP
/* For example, if you want the map to reference databases of
/* the name "your_db" and execute a query like this: select
/* forw_addr from aliases where alias like '<some username>'
/* against any database called "vmailer_info" located on hosts
/* host1.some.domain and host2.some.domain, logging in as user
/* "vmailer" and password "passwd" then the configuration file
/* should read:
/* .PP
/* \fIuser\fR = \fBvmailer\fR
/* .br
/* \fIpassword\fR = \fBpasswd\fR
/* .br
/* \fIdbname\fR = \fBvmailer_info\fR
/* .br
/* \fItable\fR = \fBaliases\fR
/* .br
/* \fIselect_field\fR = \fBforw_addr\fR
/* .br
/* \fIwhere_field\fR = \fBalias\fR
/* .br
/* \fIhosts\fR = \fBhost1.some.domain\fR \fBhost2.some.domain\fR
/* .IP \fIadditional_conditions\fR
/* Backward compatibility when \fIquery\fR is not set, additional
/* conditions to the WHERE clause.
/* .IP \fIhosts\fR
/* List of hosts to connect to.
/* .PP
@@ -132,6 +187,7 @@
/* Global library. */
#include "cfg_parser.h"
#include "db_common.h"
/* Application-specific. */
@@ -156,22 +212,18 @@ typedef struct {
} PLMYSQL;
typedef struct {
DICT dict;
CFG_PARSER *parser;
char *query;
char *result_format;
STRING_LIST *domain;
void *ctx;
int expansion_limit;
char *username;
char *password;
char *dbname;
char *table;
char *select_field;
char *where_field;
char *additional_conditions;
char **hostnames;
int len_hosts;
} MYSQL_NAME;
typedef struct {
DICT dict;
ARGV *hosts;
PLMYSQL *pldb;
MYSQL_NAME *name;
} DICT_MYSQL;
#define STATACTIVE (1<<0)
@@ -186,7 +238,7 @@ typedef struct {
#define IDLE_CONN_INTV 60 /* 1 minute */
/* internal function declarations */
static PLMYSQL *plmysql_init(char *hostnames[], int);
static PLMYSQL *plmysql_init(ARGV *);
static MYSQL_RES *plmysql_query(PLMYSQL *, const char *, char *, char *, char *);
static void plmysql_dealloc(PLMYSQL *);
static void plmysql_close_host(HOST *);
@@ -195,89 +247,120 @@ static void plmysql_connect_single(HOST *, char *, char *, char *);
static const char *dict_mysql_lookup(DICT *, const char *);
DICT *dict_mysql_open(const char *, int, int);
static void dict_mysql_close(DICT *);
static MYSQL_NAME *mysqlname_parse(const char *);
static void mysql_parse_config(DICT_MYSQL *, const char *);
static HOST *host_init(const char *);
/* dict_mysql_quote - escape SQL metacharacters in input string */
static void dict_mysql_quote(DICT *dict, const char *name, VSTRING *result)
{
DICT_MYSQL *dict_mysql = (DICT_MYSQL *) dict;
int len = strlen(name);
int buflen = 2*len + 1;
/*
* We won't get integer overflows in 2*len + 1, because Postfix
* input keys have reasonable size limits, better safe than sorry.
*/
if (buflen < len)
msg_panic("dict_mysql_quote: integer overflow in 2*%d+1", len);
VSTRING_SPACE(result, buflen);
/*
* XXX Too expensive to find out which connection is still open at
* this point. Grrr!
*/
#if 0 && defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
mysql_real_escape_string(dict_mysql->pldb->db_hosts[i].db,
vstring_end(result), name, len);
#else
mysql_escape_string(vstring_end(result), name, len);
#endif
VSTRING_SKIP(result);
}
/* dict_mysql_lookup - find database entry */
/**********************************************************************
* public interface dict_mysql_lookup
* find database entry return 0 if no alias found, set dict_errno
* on errors to DICT_ERRBO_RETRY and set dict_errno to 0 on success
*********************************************************************/
static const char *dict_mysql_lookup(DICT *dict, const char *name)
{
char *myname = "dict_mysql_lookup";
DICT_MYSQL *dict_mysql = (DICT_MYSQL *)dict;
PLMYSQL *pldb = dict_mysql->pldb;
MYSQL_RES *query_res;
MYSQL_ROW row;
DICT_MYSQL *dict_mysql;
PLMYSQL *pldb;
static VSTRING *result;
static VSTRING *query = 0;
int i,
j,
numrows;
char *name_escaped = 0;
static VSTRING *query;
int i;
int j;
int numrows;
int expansion;
const char *r;
dict_mysql = (DICT_MYSQL *) dict;
pldb = dict_mysql->pldb;
/* initialization for query */
query = vstring_alloc(24);
vstring_strcpy(query, "");
if ((name_escaped = (char *) mymalloc((sizeof(char) * (strlen(name) * 2) +1))) == NULL) {
msg_fatal("dict_mysql_lookup: out of memory.");
}
/* prepare the query */
mysql_escape_string(name_escaped, name, (unsigned int) strlen(name));
vstring_sprintf(query, "select %s from %s where %s = '%s' %s", dict_mysql->name->select_field,
dict_mysql->name->table, dict_mysql->name->where_field, name_escaped,
dict_mysql->name->additional_conditions);
if (msg_verbose)
msg_info("dict_mysql_lookup using sql query: %s", vstring_str(query));
/* free mem associated with preparing the query */
myfree(name_escaped);
/* do the query - set dict_errno & cleanup if there's an error */
if ((query_res = plmysql_query(pldb,
vstring_str(query),
dict_mysql->name->dbname,
dict_mysql->name->username,
dict_mysql->name->password)) == 0) {
dict_errno = DICT_ERR_RETRY;
vstring_free(query);
return 0;
}
dict_errno = 0;
/* free the vstring query */
vstring_free(query);
/*
* If there is a domain list for this map, then only search for
* addresses in domains on the list. This can significantly reduce
* the load on the server. Do not try "@domain" keys.
*/
if (db_common_check_domain(dict_mysql->domain, name) == 0) {
if (msg_verbose)
msg_info("%s: Skipping lookup of '%s'", myname, name);
return (0);
}
#define INIT_VSTR(buf, len) do { \
if (buf == 0) \
buf = vstring_alloc(len); \
VSTRING_RESET(buf); \
VSTRING_TERMINATE(buf); \
} while (0)
INIT_VSTR(query, 10);
/*
* Suppress the lookup if the query expansion is empty
*/
if (!db_common_expand(dict_mysql->ctx, dict_mysql->query,
name, 0, query, dict_mysql_quote))
return (0);
/* do the query - set dict_errno & cleanup if there's an error */
if ((query_res = plmysql_query(pldb, vstring_str(query),
dict_mysql->dbname,
dict_mysql->username,
dict_mysql->password)) == 0) {
dict_errno = DICT_ERR_RETRY;
return (0);
}
numrows = mysql_num_rows(query_res);
if (msg_verbose)
msg_info("dict_mysql_lookup: retrieved %d rows", numrows);
msg_info("%s: retrieved %d rows", myname, numrows);
if (numrows == 0) {
mysql_free_result(query_res);
return 0;
}
if (result == 0)
result = vstring_alloc(10);
vstring_strcpy(result, "");
for (i = 0; i < numrows; i++) {
INIT_VSTR(result, 10);
for (expansion = i = 0; i < numrows && dict_errno == 0; i++) {
row = mysql_fetch_row(query_res);
if (i > 0)
vstring_strcat(result, ",");
for (j = 0; j < mysql_num_fields(query_res); j++) {
if (row[j] == 0) {
if (msg_verbose > 1)
msg_info("dict_mysql_lookup: null field #%d row #%d", j, i);
mysql_free_result(query_res);
return (0);
if (db_common_expand(dict_mysql->ctx, dict_mysql->result_format,
row[j], name, result, 0)
&& dict_mysql->expansion_limit > 0
&& ++expansion > dict_mysql->expansion_limit) {
msg_warn("%s: %s: Expansion limit exceeded for key: '%s'",
myname, dict_mysql->parser->name, name);
dict_errno = DICT_ERR_RETRY;
break;
}
if (j > 0)
vstring_strcat(result, ",");
vstring_strcat(result, row[j]);
if (msg_verbose > 1)
msg_info("dict_mysql_lookup: retrieved field: %d: %s", j, row[j]);
}
}
mysql_free_result(query_res);
return vstring_str(result);
r = vstring_str(result);
return ((dict_errno == 0 && *r) ? r : 0);
}
/* dict_mysql_check_stat - check the status of a host */
@@ -461,12 +544,77 @@ static void plmysql_down_host(HOST *host)
event_cancel_timer(dict_mysql_event, (char *) host);
}
/**********************************************************************
* public interface dict_mysql_open
* create association with database with appropriate values
* parse the map's config file
* allocate memory
**********************************************************************/
/* mysql_parse_config - parse mysql configuration file */
static void mysql_parse_config(DICT_MYSQL *dict_mysql, const char *mysqlcf)
{
const char *myname = "mysqlname_parse";
CFG_PARSER *p;
VSTRING *buf;
int i;
char *hosts;
char *domain;
p = dict_mysql->parser = cfg_parser_alloc(mysqlcf);
dict_mysql->username = cfg_get_str(p, "user", "", 0, 0);
dict_mysql->password = cfg_get_str(p, "password", "", 0, 0);
dict_mysql->dbname = cfg_get_str(p, "dbname", "", 1, 0);
dict_mysql->result_format = cfg_get_str(p, "result_format", "%s", 1, 0);
/*
* XXX: The default should be non-zero for safety, but that is not
* backwards compatible.
*/
dict_mysql->expansion_limit = cfg_get_int(dict_mysql->parser,
"expansion_limit", 0, 0, 0);
if ((dict_mysql->query = cfg_get_str(p, "query", NULL, 0, 0)) == 0) {
/*
* No query specified -- fallback to building it from components
* (old style "select %s from %s where %s")
*/
buf = vstring_alloc(64);
db_common_sql_build_query(buf, p);
dict_mysql->query = vstring_export(buf);
}
/*
* Must parse all templates before we can use db_common_expand()
*/
dict_mysql->ctx = 0;
(void) db_common_parse(&dict_mysql->dict, &dict_mysql->ctx,
dict_mysql->query, 1);
(void) db_common_parse(0, &dict_mysql->ctx, dict_mysql->result_format, 0);
domain = cfg_get_str(p, "domain", "", 0, 0);
if (*domain) {
if (!(dict_mysql->domain = string_list_init(MATCH_FLAG_NONE, domain)))
/*
* The "domain" optimization skips input keys that may in fact
* have unwanted matches in the database, so failure to create
* the match list is fatal.
*/
msg_fatal("%s: %s: domain match list creation using '%s' failed",
myname, mysqlcf, domain);
}
else
dict_mysql->domain = 0;
myfree(domain);
hosts = cfg_get_str(p, "hosts", "", 0, 0);
dict_mysql->hosts = argv_split(hosts, " ,\t\r\n");
if (dict_mysql->hosts->argc == 0) {
argv_add(dict_mysql->hosts, "localhost", ARGV_END);
argv_terminate(dict_mysql->hosts);
if (msg_verbose)
msg_info("%s: %s: no hostnames specified, defaulting to '%s'",
myname, mysqlcf, dict_mysql->hosts->argv[0]);
}
myfree(hosts);
}
/* dict_mysql_open - open MYSQL data base */
DICT *dict_mysql_open(const char *name, int open_flags, int dict_flags)
{
DICT_MYSQL *dict_mysql;
@@ -483,95 +631,31 @@ DICT *dict_mysql_open(const char *name, int open_flags, int dict_flags)
dict_mysql->dict.lookup = dict_mysql_lookup;
dict_mysql->dict.close = dict_mysql_close;
dict_mysql->dict.flags = dict_flags | DICT_FLAG_FIXED;
dict_mysql->name = mysqlname_parse(name);
dict_mysql->pldb = plmysql_init(dict_mysql->name->hostnames,
dict_mysql->name->len_hosts);
mysql_parse_config(dict_mysql, name);
dict_mysql->pldb = plmysql_init(dict_mysql->hosts);
if (dict_mysql->pldb == NULL)
msg_fatal("couldn't intialize pldb!\n");
return (DICT_DEBUG (&dict_mysql->dict));
}
/* mysqlname_parse - parse mysql configuration file */
static MYSQL_NAME *mysqlname_parse(const char *mysqlcf)
{
const char *myname = "mysqlname_parse";
int i;
char *hosts;
MYSQL_NAME *name = (MYSQL_NAME *) mymalloc(sizeof(MYSQL_NAME));
ARGV *hosts_argv;
/* parser */
name->parser = cfg_parser_alloc(mysqlcf);
/* username */
name->username = cfg_get_str(name->parser, "user", "", 0, 0);
/* password */
name->password = cfg_get_str(name->parser, "password", "", 0, 0);
/* database name */
name->dbname = cfg_get_str(name->parser, "dbname", "", 1, 0);
/* table name */
name->table = cfg_get_str(name->parser, "table", "", 1, 0);
/* select field */
name->select_field = cfg_get_str(name->parser, "select_field", "", 1, 0);
/* where field */
name->where_field = cfg_get_str(name->parser, "where_field", "", 1, 0);
/* additional conditions */
name->additional_conditions = cfg_get_str(name->parser,
"additional_conditions",
"", 0, 0);
/* mysql server hosts */
hosts = cfg_get_str(name->parser, "hosts", "", 0, 0);
/* coo argv interface */
hosts_argv = argv_split(hosts, " ,\t\r\n");
if (hosts_argv->argc == 0) { /* no hosts specified,
* default to 'localhost' */
if (msg_verbose)
msg_info("%s: %s: no hostnames specified, defaulting to 'localhost'",
myname, mysqlcf);
argv_add(hosts_argv, "localhost", ARGV_END);
argv_terminate(hosts_argv);
}
name->len_hosts = hosts_argv->argc;
name->hostnames = (char **) mymalloc((sizeof(char *)) * name->len_hosts);
i = 0;
for (i = 0; hosts_argv->argv[i] != NULL; i++) {
name->hostnames[i] = mystrdup(hosts_argv->argv[i]);
if (msg_verbose)
msg_info("%s: %s: adding host '%s' to list of mysql server hosts",
myname, mysqlcf, name->hostnames[i]);
}
myfree(hosts);
argv_free(hosts_argv);
return name;
}
/*
* plmysql_init - initalize a MYSQL database.
* Return NULL on failure, or a PLMYSQL * on success.
*/
static PLMYSQL *plmysql_init(char *hostnames[], int len_hosts)
static PLMYSQL *plmysql_init(ARGV *hosts)
{
PLMYSQL *PLDB;
int i;
if ((PLDB = (PLMYSQL *) mymalloc(sizeof(PLMYSQL))) == NULL) {
if ((PLDB = (PLMYSQL *) mymalloc(sizeof(PLMYSQL))) == 0)
msg_fatal("mymalloc of pldb failed");
}
PLDB->len_hosts = len_hosts;
if ((PLDB->db_hosts = (HOST **) mymalloc(sizeof(HOST *) * len_hosts)) == NULL)
return NULL;
for (i = 0; i < len_hosts; i++) {
PLDB->db_hosts[i] = host_init(hostnames[i]);
}
PLDB->len_hosts = hosts->argc;
if ((PLDB->db_hosts = (HOST **) mymalloc(sizeof(HOST *) * hosts->argc)) == 0)
return (0);
for (i = 0; i < hosts->argc; i++)
PLDB->db_hosts[i] = host_init(hosts->argv[i]);
return PLDB;
}
@@ -619,29 +703,26 @@ static HOST *host_init(const char *hostname)
return host;
}
/**********************************************************************
* public interface dict_mysql_close
* unregister, disassociate from database, freeing appropriate memory
**********************************************************************/
/* dict_mysql_close - close MYSQL database */
static void dict_mysql_close(DICT *dict)
{
int i;
DICT_MYSQL *dict_mysql = (DICT_MYSQL *) dict;
plmysql_dealloc(dict_mysql->pldb);
cfg_parser_free(dict_mysql->name->parser);
myfree(dict_mysql->name->username);
myfree(dict_mysql->name->password);
myfree(dict_mysql->name->dbname);
myfree(dict_mysql->name->table);
myfree(dict_mysql->name->select_field);
myfree(dict_mysql->name->where_field);
myfree(dict_mysql->name->additional_conditions);
for (i = 0; i < dict_mysql->name->len_hosts; i++) {
myfree(dict_mysql->name->hostnames[i]);
}
myfree((char *) dict_mysql->name->hostnames);
myfree((char *) dict_mysql->name);
cfg_parser_free(dict_mysql->parser);
myfree(dict_mysql->username);
myfree(dict_mysql->password);
myfree(dict_mysql->dbname);
myfree(dict_mysql->query);
myfree(dict_mysql->result_format);
if (dict_mysql->domain)
string_list_free(dict_mysql->domain);
if (dict_mysql->hosts)
argv_free(dict_mysql->hosts);
if (dict_mysql->ctx)
db_common_free_ctx(dict_mysql->ctx);
dict_free(dict);
}

View File

@@ -58,24 +58,44 @@
/* Password for the above.
/* .IP \fIdbname\fR
/* Name of the database.
/* .IP \fItable\fR
/* Name of the table.
/* .IP \fIselect_field\fR
/* Name of the result field.
/* .IP \fIwhere_field\fR
/* Field used in the WHERE clause.
/* .IP \fIadditional_conditions\fR
/* Additional conditions to the WHERE clause.
/* .IP \fIquery\fR
/* Query overriding \fItable\fR, \fIselect_field\fR,
/* \fIwhere_field\fR, and \fIadditional_conditions\fR. Before the
/* query is actually issued, all occurrences of %s are replaced
/* with the address to look up, %u are replaced with the user
/* portion, and %d with the domain portion.
/* Query template. If not defined a default query template is constructed
/* from the legacy \fIselect_function\fR or failing that the \fItable\fR,
/* \fIselect_field\fR, \fIwhere_field\fR, and \fIadditional_conditions\fR
/* parameters. Before the query is issues, variable substitutions are
/* performed. See pgsql_table(5).
/* .IP \fIdomain\fR
/* List of domains the queries should be restricted to. If
/* specified, only FQDN addresses whose domain parts matching this
/* list will be queried against the SQL database. Lookups for
/* partial addresses are also supressed. This can significantly
/* reduce the query load on the server.
/* .IP \fIresult_format\fR
/* The format used to expand results from queries. Substitutions
/* are performed as described in pgsql_table(5). Defaults to returning
/* the lookup result unchanged.
/* .IP expansion_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
/* non-empty (and non-NULL) column of a multi-column result row counts as
/* one result.
/* .IP \fIselect_function\fR
/* Function to be used instead of the SELECT statement. Overrides
/* both \fIquery\fR and \fItable\fR, \fIselect_field\fR,
/* \fIwhere_field\fR, and \fIadditional_conditions\fR settings.
/* When \fIquery\fR is not defined, the function to be used instead of
/* the default query based on the legacy \fItable\fR, \fIselect_field\fR,
/* \fIwhere_field\fR, and \fIadditional_conditions\fR parameters.
/* .IP \fItable\fR
/* When \fIquery\fR and \fIselect_function\fR are not defined, the name of the
/* FROM table used to construct the default query template, see pgsql_table(5).
/* .IP \fIselect_field\fR
/* When \fIquery\fR and \fIselect_function\fR are not defined, the name of the
/* SELECT field used to construct the default query template, see pgsql_table(5).
/* .IP \fIwhere_field\fR
/* When \fIquery\fR and \fIselect_function\fR are not defined, the name of the
/* WHERE field used to construct the default query template, see pgsql_table(5).
/* .IP \fIadditional_conditions\fR
/* When \fIquery\fR and \fIselect_function\fR are not defined, the name of the
/* additional text to add to the WHERE field in the default query template (this
/* usually begins with "and") see pgsql_table(5).
/* .IP \fIhosts\fR
/* List of hosts to connect to.
/* .PP
@@ -151,6 +171,7 @@
/* Global library. */
#include "cfg_parser.h"
#include "db_common.h"
/* Application-specific. */
@@ -183,24 +204,19 @@ typedef struct {
} PLPGSQL;
typedef struct {
DICT dict;
CFG_PARSER *parser;
char *query;
char *result_format;
STRING_LIST *domain;
void *ctx;
int expansion_limit;
char *username;
char *password;
char *dbname;
char *table;
char *query; /* if set, overrides fields, etc */
char *select_function;
char *select_field;
char *where_field;
char *additional_conditions;
char **hostnames;
int len_hosts;
} PGSQL_NAME;
typedef struct {
DICT dict;
ARGV *hosts;
PLPGSQL *pldb;
PGSQL_NAME *name;
} DICT_PGSQL;
@@ -208,7 +224,7 @@ typedef struct {
#define PGSQL_RES PGresult
/* internal function declarations */
static PLPGSQL *plpgsql_init(char *hostnames[], int);
static PLPGSQL *plpgsql_init(ARGV *);
static PGSQL_RES *plpgsql_query(PLPGSQL *, const char *, char *, char *, char *);
static void plpgsql_dealloc(PLPGSQL *);
static void plpgsql_close_host(HOST *);
@@ -217,213 +233,129 @@ static void plpgsql_connect_single(HOST *, char *, char *, char *);
static const char *dict_pgsql_lookup(DICT *, const char *);
DICT *dict_pgsql_open(const char *, int, int);
static void dict_pgsql_close(DICT *);
static PGSQL_NAME *pgsqlname_parse(const char *);
static HOST *host_init(const char *);
/* dict_pgsql_quote - escape SQL metacharacters in input string */
/**********************************************************************
* public interface dict_pgsql_lookup
* find database entry return 0 if no alias found, set dict_errno
* on errors to DICT_ERROR_RETRY and set dict_errno to 0 on success
*********************************************************************/
static void pgsql_escape_string(char *new, const char *old, unsigned int len)
static void dict_pgsql_quote(DICT *unused, const char *name, VSTRING *result)
{
unsigned int x,
y;
const char *sub;
/*
* XXX We really should be using an escaper that is provided by the PGSQL
* library. The code below seems to be over-kill (see RUS-CERT Advisory
* 2001-08:01), but it's better to be safe than to be sorry -- Wietse
*/
for (x = 0, y = 0; x < len; x++, y++) {
switch (old[x]) {
for (sub = name; *sub; sub++) {
switch(*sub) {
case '\n':
new[y++] = '\\';
new[y] = 'n';
vstring_strcat(result, "\\n");
break;
case '\r':
new[y++] = '\\';
new[y] = 'r';
vstring_strcat(result, "\\r");
break;
case '\'':
new[y++] = '\\';
new[y] = '\'';
vstring_strcat(result, "\\'");
break;
case '"':
new[y++] = '\\';
new[y] = '"';
vstring_strcat(result, "\\\"");
break;
case 0:
new[y++] = '\\';
new[y] = '0';
vstring_strcat(result, "\\0");
break;
default:
new[y] = old[x];
VSTRING_ADDCH(result, *sub);
break;
}
}
new[y] = 0;
VSTRING_TERMINATE(result);
}
/*
* expand a filter (lookup or result)
*/
static void dict_pgsql_expand_filter(char *filter, char *value, VSTRING *out)
{
const char *myname = "dict_pgsql_expand_filter";
char *sub,
*end;
/*
* Yes, replace all instances of %s with the address to look up. Replace
* %u with the user portion, and %d with the domain portion.
*/
sub = filter;
end = sub + strlen(filter);
while (sub < end) {
/*
* Make sure it's %[sud] and not something else. For backward
* compatibilty, treat anything other than %u or %d as %s, with a
* warning.
*/
if (*(sub) == '%') {
char *u = value;
char *p = strrchr(u, '@');
switch (*(sub + 1)) {
case 'd':
if (p)
vstring_strcat(out, p + 1);
break;
case 'u':
if (p)
vstring_strncat(out, u, p - u);
else
vstring_strcat(out, u);
break;
default:
msg_warn
("%s: Invalid filter substitution format '%%%c'!",
myname, *(sub + 1));
break;
case 's':
vstring_strcat(out, u);
break;
}
sub++;
} else
vstring_strncat(out, sub, 1);
sub++;
}
}
/* dict_pgsql_lookup - find database entry */
static const char *dict_pgsql_lookup(DICT *dict, const char *name)
{
char *myname = "dict_pgsql_lookup";
PGSQL_RES *query_res;
DICT_PGSQL *dict_pgsql;
PLPGSQL *pldb;
static VSTRING *query;
static VSTRING *result;
static VSTRING *query = 0;
int i,
j,
numrows;
char *name_escaped = 0;
int isFunctionCall;
int i;
int j;
int numrows;
int numcols;
int expansion;
const char *r;
dict_pgsql = (DICT_PGSQL *) dict;
pldb = dict_pgsql->pldb;
/* initialization for query */
query = vstring_alloc(24);
vstring_strcpy(query, "");
if ((name_escaped = (char *) mymalloc((sizeof(char) * (strlen(name) * 2) +1))) == NULL) {
msg_fatal("dict_pgsql_lookup: out of memory.");
}
/* prepare the query */
pgsql_escape_string(name_escaped, name, (unsigned int) strlen(name));
/* Build SQL - either a select from table or select a function */
#define INIT_VSTR(buf, len) do { \
if (buf == 0) \
buf = vstring_alloc(len); \
VSTRING_RESET(buf); \
VSTRING_TERMINATE(buf); \
} while (0)
isFunctionCall = (dict_pgsql->name->select_function != NULL);
if (isFunctionCall) {
vstring_sprintf(query, "select %s('%s')",
dict_pgsql->name->select_function,
name_escaped);
} else if (dict_pgsql->name->query) {
dict_pgsql_expand_filter(dict_pgsql->name->query, name_escaped, query);
} else {
vstring_sprintf(query, "select %s from %s where %s = '%s' %s",
dict_pgsql->name->select_field,
dict_pgsql->name->table,
dict_pgsql->name->where_field,
name_escaped,
dict_pgsql->name->additional_conditions);
INIT_VSTR(query, 10);
INIT_VSTR(result, 10);
dict_errno = 0;
/*
* If there is a domain list for this map, then only search for
* addresses in domains on the list. This can significantly reduce
* the load on the server. Do not try "@domain" keys.
*/
if (db_common_check_domain(dict_pgsql->domain, name) == 0) {
if (msg_verbose)
msg_info("%s: Skipping lookup of '%s'", myname, name);
return (0);
}
if (msg_verbose)
msg_info("dict_pgsql_lookup using sql query: %s", vstring_str(query));
/* free mem associated with preparing the query */
myfree(name_escaped);
/*
* Suppress the actual lookup if the expansion is empty
*/
if (!db_common_expand(dict_pgsql->ctx, dict_pgsql->query,
name, 0, query, dict_pgsql_quote))
return (0);
/* do the query - set dict_errno & cleanup if there's an error */
if ((query_res = plpgsql_query(pldb,
vstring_str(query),
dict_pgsql->name->dbname,
dict_pgsql->name->username,
dict_pgsql->name->password)) == 0) {
if ((query_res = plpgsql_query(pldb, vstring_str(query),
dict_pgsql->dbname,
dict_pgsql->username,
dict_pgsql->password)) == 0) {
dict_errno = DICT_ERR_RETRY;
vstring_free(query);
return 0;
}
dict_errno = 0;
/* free the vstring query */
vstring_free(query);
numrows = PQntuples(query_res);
if (msg_verbose)
msg_info("dict_pgsql_lookup: retrieved %d rows", numrows);
msg_info("%s: retrieved %d rows", myname, numrows);
if (numrows == 0) {
PQclear(query_res);
return 0;
}
numcols = PQnfields(query_res);
if (numcols == 1 && numrows == 1 && isFunctionCall) {
/*
* We do the above check because PostgreSQL 7.3 will allow functions
* to return result sets
*/
if (PQgetisnull(query_res, 0, 0) == 1) {
/*
* Functions returning a single row & column that is null are
* deemed to have not found the key.
*/
PQclear(query_res);
return 0;
}
}
if (result == 0)
result = vstring_alloc(10);
vstring_strcpy(result, "");
for (i = 0; i < numrows; i++) {
if (i > 0)
vstring_strcat(result, ",");
for (expansion = i = 0; i < numrows && dict_errno == 0; i++) {
for (j = 0; j < numcols; j++) {
if (j > 0)
vstring_strcat(result, ",");
vstring_strcat(result, PQgetvalue(query_res, i, j));
if (msg_verbose > 1)
msg_info("dict_pgsql_lookup: retrieved field: %d: %s", j, PQgetvalue(query_res, i, j));
r = PQgetvalue(query_res, i, j);
if (db_common_expand(dict_pgsql->ctx, dict_pgsql->result_format,
r, name, result, 0)
&& dict_pgsql->expansion_limit > 0
&& ++expansion > dict_pgsql->expansion_limit) {
msg_warn("%s: %s: Expansion limit exceeded for key: '%s'",
myname, dict_pgsql->parser->name, name);
dict_errno = DICT_ERR_RETRY;
break;
}
}
}
PQclear(query_res);
return vstring_str(result);
r = vstring_str(result);
return ((dict_errno == 0 && *r) ? r : 0);
}
/* dict_pgsql_check_stat - check the status of a host */
@@ -603,19 +535,87 @@ static void plpgsql_down_host(HOST *host)
event_cancel_timer(dict_pgsql_event, (char *) host);
}
/**********************************************************************
* public interface dict_pgsql_open
* create association with database with appropriate values
* parse the map's config file
* allocate memory
**********************************************************************/
/* pgsql_parse_config - parse pgsql configuration file */
static void pgsql_parse_config(DICT_PGSQL *dict_pgsql, const char *pgsqlcf)
{
const char *myname = "pgsql_parse_config";
CFG_PARSER *p;
int i;
char *hosts;
VSTRING *query;
char *select_function;
char *domain;
p = dict_pgsql->parser = cfg_parser_alloc(pgsqlcf);
dict_pgsql->username = cfg_get_str(p, "user", "", 0, 0);
dict_pgsql->password = cfg_get_str(p, "password", "", 0, 0);
dict_pgsql->dbname = cfg_get_str(p, "dbname", "", 1, 0);
dict_pgsql->result_format = cfg_get_str(p, "result_format", "%s", 1, 0);
/*
* XXX: The default should be non-zero for safety, but that is not
* backwards compatible.
*/
dict_pgsql->expansion_limit = cfg_get_int(dict_pgsql->parser,
"expansion_limit", 0, 0, 0);
if ((dict_pgsql->query = cfg_get_str(p, "query", 0, 0, 0)) == 0) {
/*
* No query specified -- fallback to building it from components
* ( old style "select %s from %s where %s" )
*/
query = vstring_alloc(64);
select_function = cfg_get_str(p, "select_function", 0, 0, 0);
if (select_function != 0) {
vstring_sprintf(query, "SELECT %s('%%s')", select_function);
myfree(select_function);
} else
db_common_sql_build_query(query, p);
dict_pgsql->query = vstring_export(query);
}
/*
* Must parse all templates before we can use db_common_expand()
*/
dict_pgsql->ctx = 0;
(void) db_common_parse(&dict_pgsql->dict, &dict_pgsql->ctx,
dict_pgsql->query, 1);
(void) db_common_parse(0, &dict_pgsql->ctx, dict_pgsql->result_format, 0);
domain = cfg_get_str(p, "domain", "", 0, 0);
if (*domain) {
if (!(dict_pgsql->domain = string_list_init(MATCH_FLAG_NONE, domain)))
/*
* The "domain" optimization skips input keys that may in fact
* have unwanted matches in the database, so failure to create
* the match list is fatal.
*/
msg_fatal("%s: %s: domain match list creation using '%s' failed",
myname, pgsqlcf, domain);
}
else
dict_pgsql->domain = 0;
myfree(domain);
hosts = cfg_get_str(p, "hosts", "", 0, 0);
dict_pgsql->hosts = argv_split(hosts, " ,\t\r\n");
if (dict_pgsql->hosts == 0) {
argv_add(dict_pgsql->hosts, "localhost", ARGV_END);
argv_terminate(dict_pgsql->hosts);
if (msg_verbose)
msg_info("%s: %s: no hostnames specified, defaulting to '%s'",
myname, pgsqlcf, dict_pgsql->hosts->argv[0]);
}
myfree(hosts);
}
/* dict_pgsql_open - open PGSQL data base */
DICT *dict_pgsql_open(const char *name, int open_flags, int dict_flags)
{
DICT_PGSQL *dict_pgsql;
/*
* Sanity checks.
*/
if (open_flags != O_RDONLY)
msg_fatal("%s:%s map requires O_RDONLY access mode",
DICT_TYPE_PGSQL, name);
@@ -624,122 +624,33 @@ DICT *dict_pgsql_open(const char *name, int open_flags, int dict_flags)
sizeof(DICT_PGSQL));
dict_pgsql->dict.lookup = dict_pgsql_lookup;
dict_pgsql->dict.close = dict_pgsql_close;
dict_pgsql->name = pgsqlname_parse(name);
dict_pgsql->pldb = plpgsql_init(dict_pgsql->name->hostnames,
dict_pgsql->name->len_hosts);
pgsql_parse_config(dict_pgsql, name);
dict_pgsql->pldb = plpgsql_init(dict_pgsql->hosts);
dict_pgsql->dict.flags = dict_flags | DICT_FLAG_FIXED;
if (dict_pgsql->pldb == NULL)
msg_fatal("couldn't intialize pldb!\n");
return &dict_pgsql->dict;
}
/* pgsqlname_parse - parse pgsql configuration file */
static PGSQL_NAME *pgsqlname_parse(const char *pgsqlcf)
{
const char *myname = "pgsqlname_parse";
int i;
char *hosts;
PGSQL_NAME *name = (PGSQL_NAME *) mymalloc(sizeof(PGSQL_NAME));
ARGV *hosts_argv;
/* plpgsql_init - initalize a PGSQL database */
name->parser = cfg_parser_alloc(pgsqlcf);
/* username */
name->username = cfg_get_str(name->parser, "user", "", 0, 0);
/* password */
name->password = cfg_get_str(name->parser, "password", "", 0, 0);
/* database name lookup */
name->dbname = cfg_get_str(name->parser, "dbname", "", 1, 0);
/*
* See what kind of lookup we have - a traditional 'select' or a function
* call
*/
name->select_function = cfg_get_str(name->parser, "select_function",
NULL, 0, 0);
name->query = cfg_get_str(name->parser, "query", NULL, 0, 0);
if (name->select_function == 0 && name->query == 0) {
/*
* We have an old style 'select %s from %s...' call
*/
/* table name */
name->table = cfg_get_str(name->parser, "table", "", 1, 0);
/* select field */
name->select_field = cfg_get_str(name->parser, "select_field",
"", 1, 0);
/* where field */
name->where_field = cfg_get_str(name->parser, "where_field",
"", 1, 0);
/* additional conditions */
name->additional_conditions = cfg_get_str(name->parser,
"additional_conditions",
"", 0, 0);
} else {
name->table = 0;
name->select_field = 0;
name->where_field = 0;
name->additional_conditions = 0;
}
/* server hosts */
hosts = cfg_get_str(name->parser, "hosts", "", 0, 0);
/* coo argv interface */
hosts_argv = argv_split(hosts, " ,\t\r\n");
if (hosts_argv->argc == 0) { /* no hosts specified,
* default to 'localhost' */
if (msg_verbose)
msg_info("%s: %s: no hostnames specified, defaulting to 'localhost'",
myname, pgsqlcf);
argv_add(hosts_argv, "localhost", ARGV_END);
argv_terminate(hosts_argv);
}
name->len_hosts = hosts_argv->argc;
name->hostnames = (char **) mymalloc((sizeof(char *)) * name->len_hosts);
i = 0;
for (i = 0; hosts_argv->argv[i] != NULL; i++) {
name->hostnames[i] = mystrdup(hosts_argv->argv[i]);
if (msg_verbose)
msg_info("%s: %s: adding host '%s' to list of pgsql server hosts",
myname, pgsqlcf, name->hostnames[i]);
}
myfree(hosts);
argv_free(hosts_argv);
return name;
}
/*
* plpgsql_init - initalize a PGSQL database.
* Return NULL on failure, or a PLPGSQL * on success.
*/
static PLPGSQL *plpgsql_init(char *hostnames[], int len_hosts)
static PLPGSQL *plpgsql_init(ARGV *hosts)
{
PLPGSQL *PLDB;
int i;
if ((PLDB = (PLPGSQL *) mymalloc(sizeof(PLPGSQL))) == NULL) {
msg_fatal("mymalloc of pldb failed");
}
PLDB->len_hosts = len_hosts;
if ((PLDB->db_hosts = (HOST **) mymalloc(sizeof(HOST *) * len_hosts)) == NULL)
return NULL;
for (i = 0; i < len_hosts; i++) {
PLDB->db_hosts[i] = host_init(hostnames[i]);
}
PLDB = (PLPGSQL *) mymalloc(sizeof(PLPGSQL));
PLDB->len_hosts = hosts->argc;
PLDB->db_hosts = (HOST **) mymalloc(sizeof(HOST *) * hosts->argc);
for (i = 0; i < hosts->argc; i++)
PLDB->db_hosts[i] = host_init(hosts->argv[i]);
return PLDB;
}
/* host_init - initialize HOST structure */
static HOST *host_init(const char *hostname)
{
const char *myname = "pgsql host_init";
@@ -773,41 +684,31 @@ static HOST *host_init(const char *hostname)
return host;
}
/**********************************************************************
* public interface dict_pgsql_close
* unregister, disassociate from database, freeing appropriate memory
**********************************************************************/
/* dict_pgsql_close - close PGSQL data base */
static void dict_pgsql_close(DICT *dict)
{
int i;
DICT_PGSQL *dict_pgsql = (DICT_PGSQL *) dict;
plpgsql_dealloc(dict_pgsql->pldb);
cfg_parser_free(dict_pgsql->name->parser);
myfree(dict_pgsql->name->username);
myfree(dict_pgsql->name->password);
myfree(dict_pgsql->name->dbname);
if (dict_pgsql->name->table)
myfree(dict_pgsql->name->table);
if (dict_pgsql->name->query)
myfree(dict_pgsql->name->query);
if (dict_pgsql->name->select_function)
myfree(dict_pgsql->name->select_function);
if (dict_pgsql->name->select_field)
myfree(dict_pgsql->name->select_field);
if (dict_pgsql->name->where_field)
myfree(dict_pgsql->name->where_field);
if (dict_pgsql->name->additional_conditions)
myfree(dict_pgsql->name->additional_conditions);
for (i = 0; i < dict_pgsql->name->len_hosts; i++) {
myfree(dict_pgsql->name->hostnames[i]);
}
myfree((char *) dict_pgsql->name->hostnames);
myfree((char *) dict_pgsql->name);
cfg_parser_free(dict_pgsql->parser);
myfree(dict_pgsql->username);
myfree(dict_pgsql->password);
myfree(dict_pgsql->dbname);
myfree(dict_pgsql->query);
myfree(dict_pgsql->result_format);
if (dict_pgsql->domain)
string_list_free(dict_pgsql->domain);
if (dict_pgsql->hosts)
argv_free(dict_pgsql->hosts);
if (dict_pgsql->ctx)
db_common_free_ctx(dict_pgsql->ctx);
dict_free(dict);
}
/* plpgsql_dealloc - free memory associated with PLPGSQL close databases */
static void plpgsql_dealloc(PLPGSQL *PLDB)
{
int i;

View File

@@ -20,8 +20,8 @@
* Patches change the patchlevel and the release date. Snapshots change the
* release date only.
*/
#define MAIL_RELEASE_DATE "20050304"
#define MAIL_VERSION_NUMBER "2.2"
#define MAIL_RELEASE_DATE "20050308"
#define MAIL_VERSION_NUMBER "2.3"
#define VAR_MAIL_VERSION "mail_version"
#ifdef SNAPSHOT

View File

@@ -416,7 +416,7 @@ DNS_RR *smtp_domain_addr(char *name, int misc_flags, VSTRING *why,
if (addr_list == 0) {
if (var_smtp_defer_mxaddr)
smtp_errno = SMTP_ERR_RETRY;
msg_warn("no MX host for %s has a valid A record", name);
msg_warn("no MX host for %s has a valid address record", name);
break;
}
best_found = (addr_list ? addr_list->pref : IMPOSSIBLE_PREFERENCE);

View File

@@ -2260,6 +2260,33 @@ static int check_server_access(SMTPD_STATE *state, const char *table,
else
domain = name;
/*
* Treat an address literal as its own MX server, just like we treat a
* name without MX record as its own MX server. There is, however, no
* applicable NS server equivalent.
*/
if (*domain == '[') {
char *saved_addr;
const char *bare_addr;
int len;
if (type != T_MX)
return (SMTPD_CHECK_DUNNO);
len = strlen(domain);
if (domain[len - 1] != ']')
return (SMTPD_CHECK_DUNNO);
/* Memory leak alert: no early returns after this point. */
saved_addr = mystrndup(domain + 1, len - 2);
if ((bare_addr = valid_mailhost_addr(saved_addr, DONT_GRIPE)) == 0)
status = SMTPD_CHECK_DUNNO;
else
status = check_addr_access(state, table, bare_addr, FULL,
&found, reply_name, reply_class,
def_acl);
myfree(saved_addr);
return (status);
}
/*
* If the domain name does not exist then we apply no restriction.
*