mirror of
https://github.com/vdukhovni/postfix
synced 2025-08-31 06:05:37 +00:00
postfix-2.0.8-20030414
This commit is contained in:
committed by
Viktor Dukhovni
parent
1c1296a56f
commit
5a69a3ff2c
4
postfix/.indent.pro
vendored
4
postfix/.indent.pro
vendored
@@ -1,3 +1,7 @@
|
||||
-THOST
|
||||
-TPLPGSQL
|
||||
-TPGSQL_NAME
|
||||
-TDICT_PGSQL
|
||||
-TABOUNCE
|
||||
-TALIAS_TOKEN
|
||||
-TARGV
|
||||
|
42
postfix/CA-2003-12
Normal file
42
postfix/CA-2003-12
Normal file
@@ -0,0 +1,42 @@
|
||||
Postfix CA-2003-12 Preliminary REJECT pattern
|
||||
=============================================
|
||||
|
||||
CERT advisory CA-2003-12 concerns a Sendmail buffer overflow exploit
|
||||
that can happen with message headers containing the 0xff byte value.
|
||||
|
||||
At this time, 8-bit text in message headers violates Internet email
|
||||
standards. A properly implemented mail client encodes 8-bit message
|
||||
header text as 7-bit text.
|
||||
|
||||
According to documentation from Sendmail, some exploits can be
|
||||
stopped by configuring a gateway MTA to remove 0xff bytes from
|
||||
message headers. This provides partial protection, because downstream
|
||||
Sendmail systems may still use untrusted information from the DNS
|
||||
while (re)writing message headers.
|
||||
|
||||
For the same reason, configuring a gateway MTA to limit the length
|
||||
of message headers would be a partial solution for downstream
|
||||
Sendmail systems.
|
||||
|
||||
Using Postfix to block 0xff in message headers
|
||||
==============================================
|
||||
|
||||
One quick way to stop 0xff characters in message headers is to
|
||||
specify a header_checks REGEXP pattern and action. Specifying
|
||||
numerical character codes in REGEXP patterns turns out to be painful.
|
||||
Here is a somewhat clumsy method to specify a 0xff matching REGEXP:
|
||||
|
||||
perl -e 'print "/\xff/ REJECT Possible CA-2003-12 exploit\n"' > /etc/postfix/block255
|
||||
|
||||
/etc/postfix/main.cf:
|
||||
header_checks = regexp:/etc/postfix/block255 ...other_files...
|
||||
|
||||
The pattern was tested with FreeBSD 4, Redhat 8, Solaris 9, all on Intel.
|
||||
|
||||
Raw binary data such as 0xff may cause trouble with text editors.
|
||||
This is why the above example uses a separate file for blocking
|
||||
the 0xff character instead of appending the pattern to an existing
|
||||
header_checks file.
|
||||
|
||||
The equivalent PCRE pattern may be easier to specify, but PCRE
|
||||
support is not universally available with Postfix.
|
@@ -7926,8 +7926,51 @@ Apologies for any names omitted.
|
||||
Feature: the installation procedure records build information
|
||||
(by default: in /etc/postfix/makedefs.out).
|
||||
|
||||
20030324
|
||||
|
||||
Bugfix: smtp-source flushed too often, causing suboptimal
|
||||
performance with smtp-source sending directly into smtp-sink.
|
||||
Files: smtpstone/smtp-source.c.
|
||||
|
||||
20030410
|
||||
|
||||
Safety: log a fatal error when a net/mask pattern has a
|
||||
non-zero host part, so that mail delivery is deferred.
|
||||
File: util/match_ops.c.
|
||||
|
||||
20030411
|
||||
|
||||
Bugfix: extraneous warning about out-of-order original
|
||||
recipient records by Patrik Rak. Files: *qmgr/qmgr_message.c.
|
||||
|
||||
20030412
|
||||
|
||||
Workaround: log a warning and reset the queue file time
|
||||
stamps when the file system clock is ahead of the local
|
||||
clock. File: global/mail_stream.c.
|
||||
|
||||
20030414
|
||||
|
||||
Feature: PostgreSQL client module, adopted by LaMont Jones.
|
||||
Files: README_FILES/PGSQL_README, util/dict_pgsql.c,
|
||||
util/dict_pgsql.h, conf/sample-pgsql-aliases.cf.
|
||||
|
||||
Cleanup: the generic smtp client/server code in smtp_stream.c
|
||||
now has an explicit flush operation, and the smtp-source/sink
|
||||
programs are updated to take advantage of this.
|
||||
|
||||
Cleanup: the file system clock drift detection code now runs
|
||||
only once per process instance, to minimize the performance
|
||||
impact. File: global/mail_stream.c.
|
||||
|
||||
Robustness: avoid TIME_WAIT state with smtp/qmqp-source
|
||||
client sockets. This puts less strain on local system
|
||||
resources.
|
||||
|
||||
Open problems:
|
||||
|
||||
Low: smtp-source may block when sending large test messages.
|
||||
|
||||
Med: make qmgr recipient bounce/defer activity asynchronous
|
||||
or add a multi-recipient operation that reduces overhead.
|
||||
|
||||
|
@@ -144,15 +144,19 @@ configuration directory other than /etc/postfix, use:
|
||||
|
||||
Be sure to get the quotes right. These details matter a lot.
|
||||
|
||||
Other parameters whose defaults can be specified in this way are:
|
||||
Parameters whose defaults can be specified in this way are:
|
||||
|
||||
Macro name default value for
|
||||
-------------------------------------
|
||||
DEF_COMMAND_DIR command_directory
|
||||
DEF_DAEMON_DIR daemon_directory
|
||||
DEF_SENDMAIL_PATH sendmail_path
|
||||
DEF_MAILQ_PATH mailq_path
|
||||
DEF_NEWALIAS_PATH newaliases_path
|
||||
Macro name default value for typical default
|
||||
-----------------------------------------------------------
|
||||
DEF_COMMAND_DIR command_directory /usr/sbin
|
||||
DEF_CONFIG_DIR config_directory /etc/postfix
|
||||
DEF_DAEMON_DIR daemon_directory /usr/libexec/postfix
|
||||
DEF_MAILQ_PATH mailq_path /usr/bin/mailq
|
||||
DEF_MANPAGE_DIR manpage_directory /usr/local/man
|
||||
DEF_NEWALIAS_PATH newaliases_path /usr/bin/newaliases
|
||||
DEF_README_DIR readme_directory no (do not install)
|
||||
DEF_SAMPLE_DIR sample_directory /etc/postfix
|
||||
DEF_SENDMAIL_PATH sendmail_path /usr/sbin/sendmail
|
||||
|
||||
In order to build Postfix for very large applications, where you
|
||||
expect to run more than 1000 delivery processes, you may need to
|
||||
|
119
postfix/README_FILES/PGSQL_README
Normal file
119
postfix/README_FILES/PGSQL_README
Normal file
@@ -0,0 +1,119 @@
|
||||
PostgreSQL map type for Postfix. Currently this code is maintained
|
||||
by LaMont Jones, <lamont@hp.com>.
|
||||
|
||||
This implementation allows for multiple pgsql databases: you can
|
||||
use one for a virtual table, one for an access table, and one for
|
||||
an aliases table if you want.
|
||||
|
||||
You can specify multiple servers for the same database, so that
|
||||
Postfix can switch to a good database server if one goes bad.
|
||||
|
||||
Performance of postfix with pgsql has not been thoroughly tested,
|
||||
however, we have found it to be stable. Busy mail servers using
|
||||
pgsql maps will generate lots of concurrent pgsql clients, so the
|
||||
pgsql server(s) should be run with this fact in mind. Any further
|
||||
performance information, in addition to any feedback is most welcome.
|
||||
|
||||
This is based upon code written by Scott Cotton and Joshua Marcus,
|
||||
IC Group, Inc. The PostgreSQL changes were done by Aaron Sethman
|
||||
<androsyn@ratbox.org>. Updates for Postfix 1.1.x and PostgreSQL
|
||||
7.1+, and support for calling stored procedures were added by Philip
|
||||
Warner (pjw@rhyme.com.au).
|
||||
|
||||
Building Postfix with PostgreSQL support
|
||||
========================================
|
||||
|
||||
To use pgsql with Postfix on Debian GNU/Linux, you must install
|
||||
the postfix-pgsql package.
|
||||
|
||||
In order to build Postfix with pgsql map support, you will need to
|
||||
add -DHAS_PGSQL and -I for the directory containing the PostgreSQL
|
||||
header files and the libpq library to AUXLIBS, for example:
|
||||
|
||||
make tidy
|
||||
make -f Makefile.init makefiles \
|
||||
'CCARGS=-DHAS_PGSQL -I/usr/local/include/pgsql' \
|
||||
'AUXLIBS=-L/usr/local/lib -lpq'
|
||||
|
||||
Then just run 'make'.
|
||||
|
||||
Configuring PostgreSQL lookup tables
|
||||
====================================
|
||||
|
||||
Once postfix is built with pgsql support, you can specify a map type
|
||||
in main.cf like this:
|
||||
|
||||
alias_maps = pgsql:/etc/postfix/pgsql-aliases.cf
|
||||
|
||||
The file /etc/postfix/pgsql-aliases.cf specifies lots of information
|
||||
telling postfix how to reference the pgsql database. An example
|
||||
pgsql map config file follows:
|
||||
|
||||
#
|
||||
# pgsql config file for alias lookups on postfix
|
||||
# comments are ok.
|
||||
#
|
||||
|
||||
# the user name and password to log into the pgsql server
|
||||
user = someone
|
||||
password = some_password
|
||||
|
||||
# the database name on the servers
|
||||
dbname = customer_database
|
||||
|
||||
# the table name
|
||||
table = mxaliases
|
||||
|
||||
# these should be obvious :-)
|
||||
select_field = forw_addr
|
||||
where_field = alias
|
||||
|
||||
# you may specify additional_conditions here
|
||||
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 a parse error in the sql).
|
||||
|
||||
# If you just want to use a PostgreSQL function, you can ignore the
|
||||
# table name, select_field, where_field and additional_conditions,
|
||||
# and just specify a database function to call:
|
||||
|
||||
#select_function = my_lookup_user_alias
|
||||
|
||||
# this will result in "select my_lookup_user_alias('name')" being
|
||||
# used as the SQL statement to execute. If select_function is specified
|
||||
# the table-related fields above will be ignored.
|
||||
#
|
||||
# As of 25-Jun-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.
|
||||
#
|
||||
# Future versions of PG will allow functions to return result sets.
|
||||
#
|
||||
|
||||
#
|
||||
# the hosts that postfix will try to connect to
|
||||
# and query from (in the order listed)
|
||||
# specify unix: for unix-domain sockets, inet: for TCP connections (default)
|
||||
hosts = host1.some.domain host2.some.domain unix:/file/name
|
||||
|
||||
# end pgsql config file
|
||||
|
||||
Eliminating single points of failure
|
||||
====================================
|
||||
|
||||
Since sites that have a need for multiple mail exchangers may enjoy
|
||||
the convenience of using a networked mailer database, but do not
|
||||
want to introduce a single point of failure to their system, we've
|
||||
included the ability to have postfix reference multiple hosts for
|
||||
access to a single pgsql map. This will work if sites set up
|
||||
mirrored pgsql databases on two or more hosts. Whenever queries
|
||||
fail with an error at one host, the rest of the hosts will be tried
|
||||
in order. Each host that is in an error state will undergo a
|
||||
reconnection attempt every so often, and if no pgsql server hosts
|
||||
are reachable, then mail will be deferred until at least one of
|
||||
those hosts is reachable.
|
@@ -22,6 +22,27 @@ snapshot release). Patches change the patchlevel and the release
|
||||
date. Snapshots change only the release date, unless they include
|
||||
the same bugfixes as a patch release.
|
||||
|
||||
Incompatible changes with Postfix snapshot 2.0.8-20040414
|
||||
=========================================================
|
||||
|
||||
Too many people mess up their net/mask patterns, causing open
|
||||
mail relay problems. Postfix processes now abort when given a
|
||||
net/mask pattern with a non-zero host portion (for example,
|
||||
168.100.189.2/28), and suggest to specify the proper net/mask
|
||||
pattern instead (for example, 168.100.189.0/28).
|
||||
|
||||
Major changes with Postfix snapshot 2.0.8-20040414
|
||||
==================================================
|
||||
|
||||
PostgreSQL table lookups. Specify "pgsql:/file/name" where "/file/name"
|
||||
defines the database. See the sample-pgsql-aliases.cf file for
|
||||
examples, and the PGSQL_README file for general information.
|
||||
|
||||
Workarounds for file systems whose clock runs ahead of the local
|
||||
clock (this can happen with remote file systems). Postfix now logs
|
||||
a warning and proceeds with reduced performance, instead of ignoring
|
||||
new mail completely.
|
||||
|
||||
Incompatible changes with Postfix snapshot 2.0.6-20030305
|
||||
=========================================================
|
||||
|
||||
|
124
postfix/US_PATENT_6321267
Normal file
124
postfix/US_PATENT_6321267
Normal file
@@ -0,0 +1,124 @@
|
||||
1. Disclaimer: This text is not an authoritative statement. If
|
||||
you are concerned about the implications of US patent 6,321,267,
|
||||
then you should give this text to your own lawyer and get their
|
||||
advice.
|
||||
|
||||
1.1 Postfix is an MTA that aims to be an alternative to the widely
|
||||
used Sendmail MTA. Postfix is available as open source code
|
||||
from http://www.postfix.org/. One of the features implemented
|
||||
by Postfix is called "sender address verification".
|
||||
|
||||
1.2 US patent 6,321,267 (reference 4.1) describes a number of means
|
||||
to stop junk email. One of the elements described in this
|
||||
patent is called "active user testing".
|
||||
|
||||
1.3 Postfix "sender address verification" and US patent 6,321,267
|
||||
"active user testing" are implemented by connecting to an MTA
|
||||
that is responsible for the sender address. Specifically, both
|
||||
use the SMTP RCPT command, and both infer the validity of the
|
||||
address from the MTA's response. Reference 4.3 defines SMTP.
|
||||
|
||||
=====================================================================
|
||||
|
||||
2. It is my understanding that the Postfix MTA's "sender address
|
||||
verification" does not infringe on US patent 6,321,267 for the
|
||||
following reasons:
|
||||
|
||||
2.1 There is prior art for US patent 6,321,267 "active user testing"
|
||||
within the context of the Sendmail MTA. See item (3.1) below.
|
||||
|
||||
2.2 US patent 6,321,267 covers "active user testing" only in
|
||||
combination with functions that the Postfix MTA does not
|
||||
implement. See items (3.2) through (3.5) below.
|
||||
|
||||
=====================================================================
|
||||
|
||||
3. Discussion of specific details of US patent 6,321,267, and their
|
||||
relevance with respect to the Postfix MTA.
|
||||
|
||||
3.1 Prior art. The "active user testing" method is described in
|
||||
the paper "Selectively Rejecting SPAM Using Sendmail" by Robert
|
||||
Harker (reference 4.2). The paper is cited as the first
|
||||
reference in US patent 6,321,267, and was presented in October
|
||||
1997. The patent was filed more than two years later, in November
|
||||
1999. The paper says:
|
||||
|
||||
Bogus User Address
|
||||
|
||||
A desirable criterion for rejecting mail is to filter on
|
||||
bogus user address. However, testing for a bad user address
|
||||
is much harder because, short of sending a message to that
|
||||
user address, there is no reliable way to check the validity
|
||||
of the address. A simplistic test for a bad user address
|
||||
might be to connect to the sender's SMTP server and use
|
||||
either the SMTP VRFY or RCPT command to check the address.
|
||||
If the server does local delivery of the message then this
|
||||
would work well.
|
||||
|
||||
The prior art is about stopping junk mail with the Sendmail
|
||||
MTA. It is my understanding that this prior art is equally
|
||||
applicable to other MTAs, including the Postfix MTA (see items
|
||||
1.1 and 2.2 above).
|
||||
|
||||
3.2 Combination of elements not implemented by the Postfix MTA.
|
||||
Claim 1 of US patent 6,321,267 involves a combination of A)
|
||||
determining whether the sending system is a dialup host, B)
|
||||
determining whether the sending system is an open mail relay,
|
||||
and C) active user testing.
|
||||
|
||||
Postfix does not implement elements A) and B) of claim 1.
|
||||
Therefore, it is my understanding that the Postfix MTA does
|
||||
not infringe on US patent 6,321,267 claim 1.
|
||||
|
||||
3.3 Combination of elements not implemented by the Postfix MTA.
|
||||
Claim 52 of US patent 6,321,267 involves the combination of A)
|
||||
a proxy filter and B) active user testing.
|
||||
|
||||
Postfix is an MTA, not a proxy, and does not implement element
|
||||
A) of claim 52. Therefore, it is my understanding that the
|
||||
Postfix MTA does not infringe on US patent 6,321,267 claim 52.
|
||||
|
||||
US patent 6,321,267 makes a clear distinction between proxies
|
||||
and MTAs.
|
||||
|
||||
Figure 13 in US patent 6,321,267 shows how a proxy interacts
|
||||
with a sending system and a local MTA. In the case of (sending
|
||||
system, proxy, local MTA), the proxy assumes no responsibility
|
||||
for delivery of the email message. The responsibility remains
|
||||
with the sending system or passes directly to the local MTA.
|
||||
|
||||
Figure 4 in US patent 6,321,267 shows how a sending system
|
||||
interacts with an intermediate MTA. In the case of (sending
|
||||
system, intermediate MTA, local MTA), the intermediate MTA
|
||||
assumes full responsibility for delivery of the email message.
|
||||
|
||||
Figure 2 in US patent 6,321,267 shows how a sending system
|
||||
interacts with a local MTA. In the case of (sending system,
|
||||
local MTA), the local MTA assumes full responsibility for
|
||||
delivery of the email message.
|
||||
|
||||
3.4 The other independent claims in US patent 6,321,267 involve
|
||||
elements that the Postfix MTA does not implement, and do not
|
||||
involve sender address verification. Therefore, it is my
|
||||
understanding that the Postfix MTA does not infringe on these
|
||||
claims in US patent 6,321,267.
|
||||
|
||||
3.5 All dependent claims in US patent 6,321,267 depend on claims
|
||||
that involve elements that the Postfix MTA does not implement.
|
||||
Therefore, it is my understanding that the Postfix MTA does
|
||||
not infringe on these claims in US patent 6,321,267.
|
||||
|
||||
4.References:
|
||||
|
||||
4.1 Albert L. Donaldson, "Method and apparatus for filtering junk
|
||||
email", US patent 6,321,267. Filing date: November 23, 1999.
|
||||
http://www.uspto.gov/
|
||||
|
||||
4.2 Robert Harker, "Selectively Rejecting SPAM Using Sendmail",
|
||||
Proceedings of the Eleventh Systems Administration Conference
|
||||
(LISA '97), San Diego, California, Oct. 1997, pp. 205-220.
|
||||
http://www.usenix.org/publications/library/proceedings/lisa97/
|
||||
full_papers/22.harker/22.pdf
|
||||
|
||||
4.3 Jonathan B. Postel, "Simple Mail Transfer Protocol", August
|
||||
1982. http://www.ietf.org/rfc.html
|
@@ -163,6 +163,7 @@ $sample_directory/sample-misc.cf:f:root:-:644
|
||||
$sample_directory/sample-pcre-access.cf:f:root:-:644
|
||||
$sample_directory/sample-pcre-body.cf:f:root:-:644
|
||||
$sample_directory/sample-pcre-header.cf:f:root:-:644
|
||||
$sample_directory/sample-pgsql-aliases.cf:f:root:-:644
|
||||
$sample_directory/sample-qmqpd.cf:f:root:-:644
|
||||
$sample_directory/sample-rate.cf:f:root:-:644
|
||||
$sample_directory/sample-regexp-access.cf:f:root:-:644
|
||||
|
@@ -4,6 +4,13 @@
|
||||
# This file contains example settings for miscellaneous Postfix
|
||||
# configuration parameters.
|
||||
|
||||
# The allow_min_user parameter specifies whether a recipient address
|
||||
# can have a '-' as the first character. By default, this is not
|
||||
# allowed, to avoid accidents with software that passes email addresses
|
||||
# via the command line.
|
||||
#
|
||||
allow_min_user = no
|
||||
|
||||
# The always_bcc parameter specifies an optional address that
|
||||
# receives a copy of each message that enters the Postfix system,
|
||||
# not including bounces that are generated locally.
|
||||
|
51
postfix/conf/sample-pgsql-aliases.cf
Normal file
51
postfix/conf/sample-pgsql-aliases.cf
Normal file
@@ -0,0 +1,51 @@
|
||||
#
|
||||
# pgsql config file for alias lookups on postfix
|
||||
# comments are ok.
|
||||
#
|
||||
|
||||
# the user name and password to log into the pgsql server
|
||||
user = someone
|
||||
password = some_password
|
||||
|
||||
# the database name on the servers
|
||||
dbname = customer_database
|
||||
|
||||
# the table name
|
||||
table = mxaliases
|
||||
|
||||
# query components, see below
|
||||
select_field = forw_addr
|
||||
where_field = alias
|
||||
|
||||
# you may specify additional_conditions here
|
||||
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 a parse error in the sql).
|
||||
|
||||
# If you just want to use a PostgreSQL function, you can ignore the
|
||||
# table name, select_field, where_field and additional_conditions,
|
||||
# and just specify a database function to call:
|
||||
|
||||
#select_function = my_lookup_user_alias
|
||||
|
||||
# this will result in "select my_lookup_user_alias('name')" being
|
||||
# used as the SQL statement to execute. If select_function is specified
|
||||
# the table-related fields above will be ignored.
|
||||
#
|
||||
# As of 25-Jun-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.
|
||||
#
|
||||
# Future versions of PG will allow functions to return result sets.
|
||||
#
|
||||
|
||||
#
|
||||
# the hosts that postfix will try to connect to
|
||||
# and query from (in the order listed)
|
||||
# specify unix: for unix-domain sockets, inet: for TCP connections (default)
|
||||
hosts = host1.some.domain host2.some.domain unix:/file/name
|
@@ -355,9 +355,10 @@ top-level domain).
|
||||
<a name="mynetworks"> <h2> My own networks </h2> </a>
|
||||
|
||||
The <b>mynetworks</b> parameter lists all networks that this machine
|
||||
somehow trusts. This information can be used by the <a href="uce.html">
|
||||
anti-UCE</a> features to recognize trusted SMTP clients that are
|
||||
allowed to relay mail through Postfix.
|
||||
somehow trusts. This information can be used by the <a
|
||||
href="uce.html#smtpd_recipient_restrictions"> anti-UCE</a> features
|
||||
to recognize trusted SMTP clients that are allowed to relay mail
|
||||
through Postfix.
|
||||
|
||||
<p>
|
||||
|
||||
|
@@ -138,9 +138,9 @@ VIRTUAL(5) VIRTUAL(5)
|
||||
<i>user2@virtual-alias.domain</i> <i>address2,</i> <i>address3</i>
|
||||
|
||||
The <i>virtual-alias.domain</i> <i>anything</i> entry is required for a
|
||||
virtual alias domain. Without this entry, mail is rejected
|
||||
with "relay access denied", or bounces with "mail loops
|
||||
back to myself".
|
||||
virtual alias domain. <b>Without</b> <b>this</b> <b>entry,</b> <b>mail</b> <b>is</b> <b>rejected</b>
|
||||
<b>with</b> <b>"relay</b> <b>access</b> <b>denied",</b> <b>or</b> <b>bounces</b> <b>with</b> <b>"mail</b> <b>loops</b>
|
||||
<b>back</b> <b>to</b> <b>myself".</b>
|
||||
|
||||
Do not specify virtual alias domain names in the <b>main.cf</b>
|
||||
<b>mydestination</b> or <b>relay</b><i>_</i><b>domains</b> configuration parameters.
|
||||
|
@@ -41,7 +41,7 @@ Terminate after \fIcount\fR sessions. This is for testing purposes.
|
||||
Disable ESMTP command pipelining.
|
||||
.IP \fB-P\fR
|
||||
Change the server greeting so that it appears to come through
|
||||
a CISCO PIX system.
|
||||
a CISCO PIX system. Implies \fB-e\fR.
|
||||
.IP "\fB-s \fIcommand,command,...\fR"
|
||||
Log the named commands to syslogd.
|
||||
Examples of commands that can be logged are HELO, EHLO, LHLO, MAIL,
|
||||
|
@@ -141,9 +141,9 @@ See the output from \fBpostconf -m\fR for available database types.
|
||||
.fi
|
||||
.sp
|
||||
The \fIvirtual-alias.domain anything\fR entry is required for a
|
||||
virtual alias domain. Without this entry, mail is rejected
|
||||
virtual alias domain. \fBWithout this entry, mail is rejected
|
||||
with "relay access denied", or bounces with
|
||||
"mail loops back to myself".
|
||||
"mail loops back to myself".\fR
|
||||
|
||||
Do not specify virtual alias domain names in the \fBmain.cf
|
||||
mydestination\fR or \fBrelay_domains\fR configuration parameters.
|
||||
|
@@ -81,36 +81,37 @@
|
||||
# The built-in default directory name is the current directory.
|
||||
# This parameter setting is not recorded in the installed main.cf file.
|
||||
# .IP config_directory
|
||||
# The destination directory for Postfix configuration files.
|
||||
# The final destination directory for Postfix configuration files.
|
||||
# The built-in default directory name is /etc/postfix.
|
||||
# This parameter setting is not recorded in the installed main.cf file.
|
||||
# This parameter setting is not recorded in the installed main.cf file
|
||||
# and can be changed only by recompiling Postfix.
|
||||
# .IP daemon_directory
|
||||
# The destination directory for Postfix daemon programs. This directory
|
||||
# should not be in the command search path of any users.
|
||||
# The final destination directory for Postfix daemon programs. This
|
||||
# directory should not be in the command search path of any users.
|
||||
# The built-in default directory name is /usr/libexec/postfix.
|
||||
# This parameter setting is recorded in the installed main.cf file.
|
||||
# .IP command_directory
|
||||
# The destination directory for Postfix administrative commands. This
|
||||
# directory should be in the command search path of adminstrative users.
|
||||
# The built-in default directory name is system dependent.
|
||||
# The final destination directory for Postfix administrative commands.
|
||||
# This directory should be in the command search path of adminstrative
|
||||
# users. The built-in default directory name is system dependent.
|
||||
# This parameter setting is recorded in the installed main.cf file.
|
||||
# .IP queue_directory
|
||||
# The destination directory for Postfix queues.
|
||||
# The final destination directory for Postfix queues.
|
||||
# The built-in default directory name is /var/spool/postfix.
|
||||
# This parameter setting is recorded in the installed main.cf file.
|
||||
# .IP sendmail_path
|
||||
# The full destination pathname for the Postfix sendmail command.
|
||||
# The final destination pathname for the Postfix sendmail command.
|
||||
# This is the Sendmail-compatible mail posting interface.
|
||||
# The built-in default pathname is system dependent.
|
||||
# This parameter setting is recorded in the installed main.cf file.
|
||||
# .IP newaliases_path
|
||||
# The full destination pathname for the Postfix newaliases command.
|
||||
# The final destination pathname for the Postfix newaliases command.
|
||||
# This is the Sendmail-compatible command to build alias databases
|
||||
# for the Postfix local delivery agent.
|
||||
# The built-in default pathname is system dependent.
|
||||
# This parameter setting is recorded in the installed main.cf file.
|
||||
# .IP mailq_path
|
||||
# The full destination pathname for the Postfix mailq command.
|
||||
# The final destination pathname for the Postfix mailq command.
|
||||
# This is the Sendmail-compatible command to list the mail queue.
|
||||
# The built-in default pathname is system dependent.
|
||||
# This parameter setting is recorded in the installed main.cf file.
|
||||
@@ -286,30 +287,30 @@ distribution to other machines."
|
||||
tempdir_prompt="a directory for scratch files while installing
|
||||
Postfix. You must have write permission in this directory."
|
||||
|
||||
config_directory_prompt="the destination directory for installed
|
||||
Postfix configuration files."
|
||||
config_directory_prompt="the final destination directory for
|
||||
installed Postfix configuration files."
|
||||
|
||||
daemon_directory_prompt="the destination directory for installed
|
||||
Postfix daemon programs. This directory should not be in the
|
||||
command search path of any users."
|
||||
daemon_directory_prompt="the final destination directory for
|
||||
installed Postfix daemon programs. This directory should not be
|
||||
in the command search path of any users."
|
||||
|
||||
command_directory_prompt="the destination directory for installed
|
||||
Postfix administrative commands. This directory should be in the
|
||||
command search path of adminstrative users."
|
||||
command_directory_prompt="the final destination directory for
|
||||
installed Postfix administrative commands. This directory should
|
||||
be in the command search path of adminstrative users."
|
||||
|
||||
queue_directory_prompt="the destination directory for Postfix
|
||||
queue_directory_prompt="the final destination directory for Postfix
|
||||
queues."
|
||||
|
||||
sendmail_path_prompt="the full destination pathname for the installed
|
||||
Postfix sendmail command. This is the Sendmail-compatible mail
|
||||
posting interface."
|
||||
sendmail_path_prompt="the final destination pathname for the
|
||||
installed Postfix sendmail command. This is the Sendmail-compatible
|
||||
mail posting interface."
|
||||
|
||||
newaliases_path_prompt="the full destination pathname for the
|
||||
newaliases_path_prompt="the final destination pathname for the
|
||||
installed Postfix newaliases command. This is the Sendmail-compatible
|
||||
command to build alias databases for the Postfix local delivery
|
||||
agent."
|
||||
|
||||
mailq_path_prompt="the full destination pathname for the installed
|
||||
mailq_path_prompt="the final destination pathname for the installed
|
||||
Postfix mailq command. This is the Sendmail-compatible mail queue
|
||||
listing command."
|
||||
|
||||
|
@@ -129,9 +129,9 @@
|
||||
# .fi
|
||||
# .sp
|
||||
# The \fIvirtual-alias.domain anything\fR entry is required for a
|
||||
# virtual alias domain. Without this entry, mail is rejected
|
||||
# virtual alias domain. \fBWithout this entry, mail is rejected
|
||||
# with "relay access denied", or bounces with
|
||||
# "mail loops back to myself".
|
||||
# "mail loops back to myself".\fR
|
||||
#
|
||||
# Do not specify virtual alias domain names in the \fBmain.cf
|
||||
# mydestination\fR or \fBrelay_domains\fR configuration parameters.
|
||||
|
@@ -83,6 +83,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <utime.h>
|
||||
|
||||
/* Utility library. */
|
||||
|
||||
@@ -125,6 +126,12 @@ static int mail_stream_finish_file(MAIL_STREAM * info, VSTRING *unused_why)
|
||||
{
|
||||
int status = 0;
|
||||
static char wakeup[] = {TRIGGER_REQ_WAKEUP};
|
||||
struct stat st;
|
||||
time_t now;
|
||||
struct utimbuf tbuf;
|
||||
char *queue_file_path = 0;
|
||||
static int fs_clock_ok = 0;
|
||||
static int fs_clock_warned = 0;
|
||||
|
||||
/*
|
||||
* Make sure the message makes it to file. Set the execute bit when no
|
||||
@@ -137,15 +144,46 @@ static int mail_stream_finish_file(MAIL_STREAM * info, VSTRING *unused_why)
|
||||
* as are files with unknown record type codes. Every Postfix queue file
|
||||
* must end with an explicit END record. Postfix queue files without END
|
||||
* record are discarded.
|
||||
*
|
||||
* Attempt to detect file system clocks that are ahead of local time. the
|
||||
* effect can be difficult to understand (mail is enqueued but Postfix
|
||||
* ignores it). This clock drift detection may not work with file systems
|
||||
* that work on a local copy of the file and that update the server only
|
||||
* after the file is closed.
|
||||
*/
|
||||
if (vstream_fflush(info->stream)
|
||||
|| fchmod(vstream_fileno(info->stream), 0700 | info->mode)
|
||||
#ifdef HAS_FSYNC
|
||||
|| fsync(vstream_fileno(info->stream))
|
||||
#endif
|
||||
|| (fs_clock_ok == 0 && fstat(vstream_fileno(info->stream), &st) < 0)
|
||||
)
|
||||
status = (errno == EFBIG ? CLEANUP_STAT_SIZE : CLEANUP_STAT_WRITE);
|
||||
|
||||
#ifdef TEST
|
||||
st.st_mtime += 10;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Don't check the file system clock all the time.
|
||||
*/
|
||||
if (fs_clock_ok == 0 && st.st_mtime <= time(&now))
|
||||
fs_clock_ok = 1;
|
||||
|
||||
/*
|
||||
* Work around file system clocks that are ahead of local time.
|
||||
*/
|
||||
if (status == CLEANUP_STAT_OK && fs_clock_ok == 0) {
|
||||
if (fs_clock_warned == 0) {
|
||||
msg_warn("%s: file system clock is %d seconds ahead of local clock",
|
||||
info->id, (int) (st.st_mtime - now));
|
||||
msg_warn("%s: resetting file time stamps - this hurts performance",
|
||||
info->id);
|
||||
fs_clock_warned = 1;
|
||||
}
|
||||
queue_file_path = mystrdup(VSTREAM_PATH(info->stream));
|
||||
}
|
||||
|
||||
/*
|
||||
* Close the queue file and mark it as closed. Be prepared for
|
||||
* vstream_fclose() to fail even after vstream_fflush() and fsync()
|
||||
@@ -158,6 +196,16 @@ static int mail_stream_finish_file(MAIL_STREAM * info, VSTRING *unused_why)
|
||||
status = (errno == EFBIG ? CLEANUP_STAT_SIZE : CLEANUP_STAT_WRITE);
|
||||
info->stream = 0;
|
||||
|
||||
/*
|
||||
* Work around file system clocks that are ahead of local time.
|
||||
*/
|
||||
if (queue_file_path != 0) {
|
||||
tbuf.actime = tbuf.modtime = now;
|
||||
if (utime(queue_file_path, &tbuf) < 0 && errno != ENOENT)
|
||||
msg_fatal("%s: update file time stamps: %m", info->id);
|
||||
myfree(queue_file_path);
|
||||
}
|
||||
|
||||
/*
|
||||
* When all is well, notify the next service that a new message has been
|
||||
* queued.
|
||||
|
@@ -20,10 +20,10 @@
|
||||
* Patches change the patchlevel and the release date. Snapshots change the
|
||||
* release date only, unless they include the same bugfix as a patch release.
|
||||
*/
|
||||
#define MAIL_RELEASE_DATE "20030319"
|
||||
#define MAIL_RELEASE_DATE "20030414"
|
||||
|
||||
#define VAR_MAIL_VERSION "mail_version"
|
||||
#define DEF_MAIL_VERSION "2.0.7-" MAIL_RELEASE_DATE
|
||||
#define DEF_MAIL_VERSION "2.0.8-" MAIL_RELEASE_DATE
|
||||
extern char *var_mail_version;
|
||||
|
||||
/*
|
||||
|
@@ -79,7 +79,6 @@ static VSTRING *last_result;
|
||||
|
||||
VSTRING *rewrite_clnt(const char *rule, const char *addr, VSTRING *result)
|
||||
{
|
||||
char *myname = "rewrite_clnt";
|
||||
VSTREAM *stream;
|
||||
|
||||
/*
|
||||
|
@@ -14,6 +14,9 @@
|
||||
/* VSTREAM *stream;
|
||||
/* const char *format;
|
||||
/*
|
||||
/* void smtp_flush(stream)
|
||||
/* VSTREAM *stream;
|
||||
/*
|
||||
/* int smtp_get(vp, stream, maxlen)
|
||||
/* VSTRING *vp;
|
||||
/* VSTREAM *stream;
|
||||
@@ -53,9 +56,11 @@
|
||||
/* The stream is configured to enable exception handling.
|
||||
/* .PP
|
||||
/* smtp_printf() formats its arguments and writes the result to
|
||||
/* the named stream, followed by a CR LF pair. The stream is flushed.
|
||||
/* the named stream, followed by a CR LF pair. The stream is NOT flushed.
|
||||
/* Long lines of text are not broken.
|
||||
/*
|
||||
/* smtp_flush() flushes the named stream.
|
||||
/*
|
||||
/* smtp_get() reads the named stream up to and including
|
||||
/* the next LF character and strips the trailing CR LF. The
|
||||
/* \fImaxlen\fR argument limits the length of a line of text,
|
||||
@@ -152,6 +157,29 @@ void smtp_timeout_setup(VSTREAM *stream, int maxtime)
|
||||
VSTREAM_CTL_END);
|
||||
}
|
||||
|
||||
/* smtp_flush - flush stream */
|
||||
|
||||
void smtp_flush(VSTREAM *stream)
|
||||
{
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Do the I/O, protected against timeout.
|
||||
*/
|
||||
smtp_timeout_reset(stream);
|
||||
err = vstream_fflush(stream);
|
||||
smtp_timeout_detect(stream);
|
||||
|
||||
/*
|
||||
* See if there was a problem.
|
||||
*/
|
||||
if (err != 0) {
|
||||
if (msg_verbose)
|
||||
msg_info("smtp_flush: EOF");
|
||||
vstream_longjmp(stream, SMTP_ERR_EOF);
|
||||
}
|
||||
}
|
||||
|
||||
/* smtp_vprintf - write one line to SMTP peer */
|
||||
|
||||
void smtp_vprintf(VSTREAM *stream, const char *fmt, va_list ap)
|
||||
@@ -164,7 +192,7 @@ void smtp_vprintf(VSTREAM *stream, const char *fmt, va_list ap)
|
||||
smtp_timeout_reset(stream);
|
||||
vstream_vfprintf(stream, fmt, ap);
|
||||
vstream_fputs("\r\n", stream);
|
||||
err = vstream_fflush(stream);
|
||||
err = vstream_ferror(stream);
|
||||
smtp_timeout_detect(stream);
|
||||
|
||||
/*
|
||||
|
@@ -31,6 +31,7 @@
|
||||
|
||||
extern void smtp_timeout_setup(VSTREAM *, int);
|
||||
extern void PRINTFLIKE(2, 3) smtp_printf(VSTREAM *, const char *,...);
|
||||
extern void smtp_flush(VSTREAM *);
|
||||
extern int smtp_get(VSTRING *, VSTREAM *, int);
|
||||
extern void smtp_fputs(const char *, int len, VSTREAM *);
|
||||
extern void smtp_fwrite(const char *, int len, VSTREAM *);
|
||||
|
@@ -404,6 +404,7 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
|
||||
message->rcpt_unread--;
|
||||
}
|
||||
} else if (rec_type == REC_TYPE_RCPT) {
|
||||
/* See also below for code setting orig_rcpt. */
|
||||
if (message->rcpt_list.len < recipient_limit) {
|
||||
message->rcpt_unread--;
|
||||
qmgr_rcpt_list_add(&message->rcpt_list, curr_offset,
|
||||
@@ -478,7 +479,9 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
|
||||
orig_rcpt = 0;
|
||||
}
|
||||
if (rec_type == REC_TYPE_ORCP)
|
||||
orig_rcpt = mystrdup(start);
|
||||
/* See also above for code clearing orig_rcpt. */
|
||||
if (message->rcpt_offset == 0)
|
||||
orig_rcpt = mystrdup(start);
|
||||
} while (rec_type > 0 && rec_type != REC_TYPE_END);
|
||||
|
||||
/*
|
||||
|
@@ -280,6 +280,7 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
|
||||
"queue %s", message->queue_name);
|
||||
}
|
||||
} else if (rec_type == REC_TYPE_RCPT) {
|
||||
/* See also below for code setting orig_rcpt. */
|
||||
#define FUDGE(x) ((x) * (var_qmgr_fudge / 100.0))
|
||||
if (message->rcpt_list.len < FUDGE(var_qmgr_rcpt_limit)) {
|
||||
qmgr_rcpt_list_add(&message->rcpt_list, curr_offset,
|
||||
@@ -358,7 +359,9 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
|
||||
orig_rcpt = 0;
|
||||
}
|
||||
if (rec_type == REC_TYPE_ORCP)
|
||||
orig_rcpt = mystrdup(start);
|
||||
/* See also above for code clearing orig_rcpt. */
|
||||
if (message->rcpt_offset == 0)
|
||||
orig_rcpt = mystrdup(start);
|
||||
} while (rec_type > 0 && rec_type != REC_TYPE_END);
|
||||
|
||||
/*
|
||||
|
@@ -293,6 +293,7 @@ static void fail_connect(SESSION *session)
|
||||
static void start_connect(SESSION *session)
|
||||
{
|
||||
int fd;
|
||||
struct linger linger;
|
||||
|
||||
/*
|
||||
* Some systems don't set the socket error when connect() fails early
|
||||
@@ -303,6 +304,11 @@ static void start_connect(SESSION *session)
|
||||
if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0)
|
||||
msg_fatal("socket: %m");
|
||||
(void) non_blocking(fd, NON_BLOCKING);
|
||||
linger.l_onoff = 1;
|
||||
linger.l_linger = 0;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &linger,
|
||||
sizeof(linger)) < 0)
|
||||
msg_warn("setsockopt SO_LINGER %d: %m", linger.l_linger);
|
||||
session->stream = vstream_fdopen(fd, O_RDWR);
|
||||
event_enable_write(fd, connect_done, (char *) session);
|
||||
netstring_setup(session->stream, var_timeout);
|
||||
|
@@ -35,7 +35,7 @@
|
||||
/* Disable ESMTP command pipelining.
|
||||
/* .IP \fB-P\fR
|
||||
/* Change the server greeting so that it appears to come through
|
||||
/* a CISCO PIX system.
|
||||
/* a CISCO PIX system. Implies \fB-e\fR.
|
||||
/* .IP "\fB-s \fIcommand,command,...\fR"
|
||||
/* Log the named commands to syslogd.
|
||||
/* Examples of commands that can be logged are HELO, EHLO, LHLO, MAIL,
|
||||
@@ -146,6 +146,7 @@ static void ehlo_response(SINK_STATE *state)
|
||||
if (!disable_8bitmime)
|
||||
smtp_printf(state->stream, "250-8BITMIME");
|
||||
smtp_printf(state->stream, "250 ");
|
||||
smtp_flush(state->stream);
|
||||
}
|
||||
|
||||
/* helo_response - respond to HELO command */
|
||||
@@ -153,6 +154,7 @@ static void ehlo_response(SINK_STATE *state)
|
||||
static void helo_response(SINK_STATE *state)
|
||||
{
|
||||
smtp_printf(state->stream, "250 %s", var_myhostname);
|
||||
smtp_flush(state->stream);
|
||||
}
|
||||
|
||||
/* ok_response - send 250 OK */
|
||||
@@ -160,6 +162,7 @@ static void helo_response(SINK_STATE *state)
|
||||
static void ok_response(SINK_STATE *state)
|
||||
{
|
||||
smtp_printf(state->stream, "250 Ok");
|
||||
smtp_flush(state->stream);
|
||||
}
|
||||
|
||||
/* mail_response - reset recipient count, send 250 OK */
|
||||
@@ -184,6 +187,7 @@ static void data_response(SINK_STATE *state)
|
||||
{
|
||||
state->data_state = ST_CR_LF;
|
||||
smtp_printf(state->stream, "354 End data with <CR><LF>.<CR><LF>");
|
||||
smtp_flush(state->stream);
|
||||
state->read = data_read;
|
||||
}
|
||||
|
||||
@@ -213,6 +217,7 @@ static void dot_response(SINK_STATE *state)
|
||||
static void quit_response(SINK_STATE *state)
|
||||
{
|
||||
smtp_printf(state->stream, "221 Bye");
|
||||
smtp_flush(state->stream);
|
||||
if (count) {
|
||||
counter++;
|
||||
vstream_printf("%d\r", counter);
|
||||
@@ -418,6 +423,7 @@ static int command_read(SINK_STATE *state)
|
||||
ptr = vstring_str(state->buffer);
|
||||
if ((command = mystrtok(&ptr, " \t")) == 0) {
|
||||
smtp_printf(state->stream, "500 Error: unknown command");
|
||||
smtp_flush(state->stream);
|
||||
return (0);
|
||||
}
|
||||
if (msg_verbose)
|
||||
@@ -427,6 +433,7 @@ static int command_read(SINK_STATE *state)
|
||||
break;
|
||||
if (cmdp->name == 0 || (cmdp->flags & FLAG_ENABLE) == 0) {
|
||||
smtp_printf(state->stream, "500 Error: unknown command");
|
||||
smtp_flush(state->stream);
|
||||
return (0);
|
||||
}
|
||||
/* We use raw syslog. Sanitize data content and length. */
|
||||
@@ -522,6 +529,7 @@ static void connect_event(int unused_event, char *context)
|
||||
smtp_printf(state->stream, "220 %s", var_myhostname);
|
||||
else
|
||||
smtp_printf(state->stream, "220 %s ESMTP", var_myhostname);
|
||||
smtp_flush(state->stream);
|
||||
event_enable_read(fd, read_event, (char *) state);
|
||||
}
|
||||
}
|
||||
@@ -569,6 +577,7 @@ int main(int argc, char **argv)
|
||||
break;
|
||||
case 'P':
|
||||
pretend_pix = 1;
|
||||
disable_esmtp = 1;
|
||||
break;
|
||||
case 's':
|
||||
openlog(basename(argv[0]), LOG_PID, LOG_MAIL);
|
||||
|
@@ -214,6 +214,7 @@ static void command(VSTREAM *stream, char *fmt,...)
|
||||
va_start(ap, fmt);
|
||||
smtp_vprintf(stream, fmt, ap);
|
||||
va_end(ap);
|
||||
smtp_flush(stream);
|
||||
}
|
||||
|
||||
/* socket_error - look up and reset the last socket error */
|
||||
@@ -398,6 +399,7 @@ static void fail_connect(SESSION *session)
|
||||
static void start_connect(SESSION *session)
|
||||
{
|
||||
int fd;
|
||||
struct linger linger;
|
||||
|
||||
/*
|
||||
* Some systems don't set the socket error when connect() fails early
|
||||
@@ -408,6 +410,11 @@ static void start_connect(SESSION *session)
|
||||
if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0)
|
||||
msg_fatal("socket: %m");
|
||||
(void) non_blocking(fd, NON_BLOCKING);
|
||||
linger.l_onoff = 1;
|
||||
linger.l_linger = 0;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &linger,
|
||||
sizeof(linger)) < 0)
|
||||
msg_warn("setsockopt SO_LINGER %d: %m", linger.l_linger);
|
||||
session->stream = vstream_fdopen(fd, O_RDWR);
|
||||
event_enable_write(fd, connect_done, (char *) session);
|
||||
smtp_timeout_setup(session->stream, var_timeout);
|
||||
@@ -672,6 +679,11 @@ static void data_done(int unused_event, char *context)
|
||||
smtp_fputs("La de da de da 3.", 17, session->stream);
|
||||
smtp_fputs("La de da de da 4.", 17, session->stream);
|
||||
} else {
|
||||
|
||||
/*
|
||||
* XXX This may cause the process to block with message content
|
||||
* larger than VSTREAM_BUFIZ bytes.
|
||||
*/
|
||||
smtp_fputs(message_data, message_length, session->stream);
|
||||
}
|
||||
|
||||
|
@@ -4,75 +4,75 @@ SRCS = alldig.c argv.c argv_split.c attr_print0.c attr_print64.c \
|
||||
chroot_uid.c clean_env.c close_on_exec.c concatenate.c ctable.c \
|
||||
dict.c dict_alloc.c dict_db.c dict_dbm.c dict_debug.c dict_env.c \
|
||||
dict_ht.c dict_ldap.c dict_mysql.c dict_ni.c dict_nis.c \
|
||||
dict_nisplus.c dict_open.c dict_pcre.c dict_regexp.c dict_static.c \
|
||||
dict_tcp.c dict_unix.c dir_forest.c doze.c duplex_pipe.c \
|
||||
environ.c events.c exec_command.c fifo_listen.c fifo_trigger.c \
|
||||
file_limit.c find_inet.c fsspace.c fullname.c get_domainname.c \
|
||||
get_hostname.c hex_quote.c htable.c inet_addr_host.c \
|
||||
inet_addr_list.c inet_addr_local.c inet_connect.c inet_listen.c \
|
||||
inet_trigger.c inet_util.c intv.c line_wrap.c lowercase.c \
|
||||
lstat_as.c mac_expand.c mac_parse.c make_dirs.c match_list.c \
|
||||
match_ops.c msg.c msg_output.c msg_syslog.c msg_vstream.c \
|
||||
mvect.c myflock.c mymalloc.c myrand.c mystrtok.c name_mask.c \
|
||||
netstring.c non_blocking.c open_as.c open_limit.c open_lock.c \
|
||||
peekfd.c percentm.c posix_signals.c printable.c rand_sleep.c \
|
||||
read_wait.c readable.c readlline.c ring.c safe_getenv.c \
|
||||
safe_open.c sane_accept.c sane_link.c sane_rename.c \
|
||||
sane_socketpair.c sane_time.c scan_dir.c set_eugid.c set_ugid.c \
|
||||
sigdelay.c skipblanks.c spawn_command.c split_at.c \
|
||||
split_nameval.c stat_as.c stream_connect.c stream_listen.c \
|
||||
stream_trigger.c sys_compat.c timed_connect.c timed_read.c \
|
||||
timed_wait.c timed_write.c translit.c trimblanks.c unescape.c \
|
||||
unix_connect.c unix_listen.c unix_trigger.c unsafe.c username.c \
|
||||
valid_hostname.c vbuf.c vbuf_print.c vstream.c vstream_popen.c \
|
||||
vstring.c vstring_vstream.c watchdog.c writable.c write_buf.c \
|
||||
write_wait.c strcasecmp.c nvtable.c host_port.c
|
||||
dict_nisplus.c dict_open.c dict_pcre.c dict_pgsql.c dict_regexp.c \
|
||||
dict_static.c dict_tcp.c dict_unix.c dir_forest.c doze.c \
|
||||
duplex_pipe.c environ.c events.c exec_command.c fifo_listen.c \
|
||||
fifo_trigger.c file_limit.c find_inet.c fsspace.c fullname.c \
|
||||
get_domainname.c get_hostname.c hex_quote.c host_port.c htable.c \
|
||||
inet_addr_host.c inet_addr_list.c inet_addr_local.c inet_connect.c \
|
||||
inet_listen.c inet_trigger.c inet_util.c intv.c line_wrap.c \
|
||||
lowercase.c lstat_as.c mac_expand.c mac_parse.c make_dirs.c \
|
||||
match_list.c match_ops.c msg.c msg_output.c msg_syslog.c \
|
||||
msg_vstream.c mvect.c myflock.c mymalloc.c myrand.c mystrtok.c \
|
||||
name_mask.c netstring.c non_blocking.c nvtable.c open_as.c \
|
||||
open_limit.c open_lock.c peekfd.c percentm.c posix_signals.c \
|
||||
printable.c rand_sleep.c read_wait.c readable.c readlline.c \
|
||||
ring.c safe_getenv.c safe_open.c sane_accept.c sane_link.c \
|
||||
sane_rename.c sane_socketpair.c sane_time.c scan_dir.c \
|
||||
set_eugid.c set_ugid.c sigdelay.c skipblanks.c spawn_command.c \
|
||||
split_at.c split_nameval.c stat_as.c strcasecmp.c stream_connect.c \
|
||||
stream_listen.c stream_trigger.c sys_compat.c timed_connect.c \
|
||||
timed_read.c timed_wait.c timed_write.c translit.c trimblanks.c \
|
||||
unescape.c unix_connect.c unix_listen.c unix_trigger.c unsafe.c \
|
||||
username.c valid_hostname.c vbuf.c vbuf_print.c vstream.c \
|
||||
vstream_popen.c vstring.c vstring_vstream.c watchdog.c writable.c \
|
||||
write_buf.c write_wait.c
|
||||
OBJS = alldig.o argv.o argv_split.o attr_print0.o attr_print64.o \
|
||||
attr_scan0.o attr_scan64.o base64_code.o basename.o binhash.o \
|
||||
chroot_uid.o clean_env.o close_on_exec.o concatenate.o ctable.o \
|
||||
dict.o dict_alloc.o dict_db.o dict_dbm.o dict_debug.o dict_env.o \
|
||||
dict_ht.o dict_ldap.o dict_mysql.o dict_ni.o dict_nis.o \
|
||||
dict_nisplus.o dict_open.o dict_pcre.o dict_regexp.o dict_static.o \
|
||||
dict_tcp.o dict_unix.o dir_forest.o doze.o duplex_pipe.o \
|
||||
environ.o events.o exec_command.o fifo_listen.o fifo_trigger.o \
|
||||
file_limit.o find_inet.o fsspace.o fullname.o get_domainname.o \
|
||||
get_hostname.o hex_quote.o htable.o inet_addr_host.o \
|
||||
inet_addr_list.o inet_addr_local.o inet_connect.o inet_listen.o \
|
||||
inet_trigger.o inet_util.o intv.o line_wrap.o lowercase.o \
|
||||
lstat_as.o mac_expand.o mac_parse.o make_dirs.o match_list.o \
|
||||
match_ops.o msg.o msg_output.o msg_syslog.o msg_vstream.o \
|
||||
mvect.o myflock.o mymalloc.o myrand.o mystrtok.o name_mask.o \
|
||||
netstring.o non_blocking.o open_as.o open_limit.o open_lock.o \
|
||||
peekfd.o percentm.o posix_signals.o printable.o rand_sleep.o \
|
||||
read_wait.o readable.o readlline.o ring.o safe_getenv.o \
|
||||
safe_open.o sane_accept.o sane_link.o sane_rename.o \
|
||||
sane_socketpair.o sane_time.o scan_dir.o set_eugid.o set_ugid.o \
|
||||
sigdelay.o skipblanks.o spawn_command.o split_at.o \
|
||||
split_nameval.o stat_as.o stream_connect.o stream_listen.o \
|
||||
stream_trigger.o sys_compat.o timed_connect.o timed_read.o \
|
||||
timed_wait.o timed_write.o translit.o trimblanks.o unescape.o \
|
||||
unix_connect.o unix_listen.o unix_trigger.o unsafe.o username.o \
|
||||
valid_hostname.o vbuf.o vbuf_print.o vstream.o vstream_popen.o \
|
||||
vstring.o vstring_vstream.o watchdog.o writable.o write_buf.o \
|
||||
write_wait.o nvtable.o $(STRCASE) host_port.o
|
||||
dict_nisplus.o dict_open.o dict_pcre.o dict_pgsql.o dict_regexp.o \
|
||||
dict_static.o dict_tcp.o dict_unix.o dir_forest.o doze.o \
|
||||
duplex_pipe.o environ.o events.o exec_command.o fifo_listen.o \
|
||||
fifo_trigger.o file_limit.o find_inet.o fsspace.o fullname.o \
|
||||
get_domainname.o get_hostname.o hex_quote.o host_port.o htable.o \
|
||||
inet_addr_host.o inet_addr_list.o inet_addr_local.o inet_connect.o \
|
||||
inet_listen.o inet_trigger.o inet_util.o intv.o line_wrap.o \
|
||||
lowercase.o lstat_as.o mac_expand.o mac_parse.o make_dirs.o \
|
||||
match_list.o match_ops.o msg.o msg_output.o msg_syslog.o \
|
||||
msg_vstream.o mvect.o myflock.o mymalloc.o myrand.o mystrtok.o \
|
||||
name_mask.o netstring.o non_blocking.o nvtable.o open_as.o \
|
||||
open_limit.o open_lock.o peekfd.o percentm.o posix_signals.o \
|
||||
printable.o rand_sleep.o read_wait.o readable.o readlline.o \
|
||||
ring.o safe_getenv.o safe_open.o sane_accept.o sane_link.o \
|
||||
sane_rename.o sane_socketpair.o sane_time.o scan_dir.o \
|
||||
set_eugid.o set_ugid.o sigdelay.o skipblanks.o spawn_command.o \
|
||||
split_at.o split_nameval.o stat_as.o stream_connect.o \
|
||||
stream_listen.o stream_trigger.o sys_compat.o timed_connect.o \
|
||||
timed_read.o timed_wait.o timed_write.o translit.o trimblanks.o \
|
||||
unescape.o unix_connect.o unix_listen.o unix_trigger.o unsafe.o \
|
||||
username.o valid_hostname.o vbuf.o vbuf_print.o vstream.o \
|
||||
vstream_popen.o vstring.o vstring_vstream.o watchdog.o writable.o \
|
||||
write_buf.o write_wait.o $(STRCASE)
|
||||
HDRS = argv.h attr.h base64_code.h binhash.h chroot_uid.h clean_env.h \
|
||||
connect.h ctable.h dict.h dict_db.h dict_dbm.h dict_env.h \
|
||||
dict_ht.h dict_ldap.h dict_mysql.h dict_ni.h dict_nis.h \
|
||||
dict_nisplus.h dict_pcre.h dict_regexp.h dict_static.h dict_tcp.h \
|
||||
dict_unix.h dir_forest.h events.h exec_command.h find_inet.h \
|
||||
fsspace.h fullname.h get_domainname.h get_hostname.h hex_quote.h \
|
||||
htable.h inet_addr_host.h inet_addr_list.h inet_addr_local.h \
|
||||
inet_util.h intv.h iostuff.h line_wrap.h listen.h lstat_as.h \
|
||||
mac_expand.h mac_parse.h make_dirs.h match_list.h match_ops.h \
|
||||
msg.h msg_output.h msg_syslog.h msg_vstream.h mvect.h myflock.h \
|
||||
mymalloc.h myrand.h name_mask.h netstring.h open_as.h open_lock.h \
|
||||
dict_nisplus.h dict_pcre.h dict_pgsql.h dict_regexp.h \
|
||||
dict_static.h dict_tcp.h dict_unix.h dir_forest.h events.h \
|
||||
exec_command.h find_inet.h fsspace.h fullname.h get_domainname.h \
|
||||
get_hostname.h hex_quote.h host_port.h htable.h inet_addr_host.h \
|
||||
inet_addr_list.h inet_addr_local.h inet_util.h intv.h iostuff.h \
|
||||
line_wrap.h listen.h lstat_as.h mac_expand.h mac_parse.h \
|
||||
make_dirs.h match_list.h match_ops.h msg.h msg_output.h \
|
||||
msg_syslog.h msg_vstream.h mvect.h myflock.h mymalloc.h myrand.h \
|
||||
name_mask.h netstring.h nvtable.h open_as.h open_lock.h \
|
||||
percentm.h posix_signals.h readlline.h ring.h safe.h safe_open.h \
|
||||
sane_accept.h sane_fsops.h sane_socketpair.h sane_time.h \
|
||||
scan_dir.h set_eugid.h set_ugid.h sigdelay.h spawn_command.h \
|
||||
split_at.h stat_as.h stringops.h sys_defs.h timed_connect.h \
|
||||
timed_wait.h trigger.h username.h valid_hostname.h vbuf.h \
|
||||
vbuf_print.h vstream.h vstring.h vstring_vstream.h watchdog.h \
|
||||
nvtable.h host_port.h
|
||||
vbuf_print.h vstream.h vstring.h vstring_vstream.h watchdog.h
|
||||
TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
|
||||
stream_test.c dup2_pass_on_exec.c
|
||||
WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
|
||||
@@ -615,6 +615,7 @@ dict_open.o: dict_nisplus.h
|
||||
dict_open.o: dict_ni.h
|
||||
dict_open.o: dict_ldap.h
|
||||
dict_open.o: dict_mysql.h
|
||||
dict_open.o: dict_pgsql.h
|
||||
dict_open.o: dict_pcre.h
|
||||
dict_open.o: dict_regexp.h
|
||||
dict_open.o: dict_static.h
|
||||
@@ -636,6 +637,8 @@ dict_pcre.o: dict.h
|
||||
dict_pcre.o: argv.h
|
||||
dict_pcre.o: dict_pcre.h
|
||||
dict_pcre.o: mac_parse.h
|
||||
dict_pgsql.o: dict_pgsql.c
|
||||
dict_pgsql.o: sys_defs.h
|
||||
dict_regexp.o: dict_regexp.c
|
||||
dict_regexp.o: sys_defs.h
|
||||
dict_regexp.o: mymalloc.h
|
||||
|
@@ -174,6 +174,7 @@
|
||||
#include <dict_ni.h>
|
||||
#include <dict_ldap.h>
|
||||
#include <dict_mysql.h>
|
||||
#include <dict_pgsql.h>
|
||||
#include <dict_pcre.h>
|
||||
#include <dict_regexp.h>
|
||||
#include <dict_static.h>
|
||||
@@ -217,6 +218,9 @@ static DICT_OPEN_INFO dict_open_info[] = {
|
||||
#ifdef HAS_MYSQL
|
||||
DICT_TYPE_MYSQL, dict_mysql_open,
|
||||
#endif
|
||||
#ifdef HAS_PGSQL
|
||||
DICT_TYPE_PGSQL, dict_pgsql_open,
|
||||
#endif
|
||||
#ifdef HAS_PCRE
|
||||
DICT_TYPE_PCRE, dict_pcre_open,
|
||||
#endif
|
||||
|
712
postfix/src/util/dict_pgsql.c
Normal file
712
postfix/src/util/dict_pgsql.c
Normal file
@@ -0,0 +1,712 @@
|
||||
/*++
|
||||
/* NAME
|
||||
/* dict_pgsql 3
|
||||
/* SUMMARY
|
||||
/* dictionary manager interface to Postgresql files
|
||||
/* SYNOPSIS
|
||||
/* #include <dict_pgsql.h>
|
||||
/*
|
||||
/* DICT *dict_pgsql_open(name, dummy, unused_dict_flags)
|
||||
/* const char *name;
|
||||
/* int dummy;
|
||||
/* int unused_dict_flags;
|
||||
/* DESCRIPTION
|
||||
/* dict_pgsql_open() creates a dictionary of type 'pgsql'. This
|
||||
/* dictionary is an interface for the postfix key->value mappings
|
||||
/* to pgsql. The result is a pointer to the installed dictionary,
|
||||
/* or a null pointer in case of problems.
|
||||
/*
|
||||
/* The pgsql dictionary can manage multiple connections to
|
||||
/* different sql servers on different hosts. It assumes that
|
||||
/* the underlying data on each host is identical (mirrored) and
|
||||
/* maintains one connection at any given time. If any connection
|
||||
/* fails, any other available ones will be opened and used.
|
||||
/* The intent of this feature is to eliminate a single point of
|
||||
/* failure for mail systems that would otherwise rely on a single
|
||||
/* pgsql server.
|
||||
/*
|
||||
/* Arguments:
|
||||
/* .IP name
|
||||
/* The path of the PostgreSQL configuration file. The file
|
||||
/* encodes number of pieces of information: username, password,
|
||||
/* databasename, table, select_field, where_field, and hosts.
|
||||
/* 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 "postfix_info" located on hosts
|
||||
/* host1.some.domain and host2.some.domain, logging in as user
|
||||
/* "postfix" and password "passwd" then the configuration file
|
||||
/* should read:
|
||||
/*
|
||||
/* user = postfix
|
||||
/* password = passwd
|
||||
/* DBname = postfix_info
|
||||
/* table = aliases
|
||||
/* select_field = forw_addr
|
||||
/* where_field = alias
|
||||
/* hosts = host1.some.domain host2.some.domain
|
||||
/*
|
||||
/* .IP other_name
|
||||
/* reference for outside use.
|
||||
/* .IP unusued_flags
|
||||
/* unused flags
|
||||
/* SEE ALSO
|
||||
/* dict(3) generic dictionary manager
|
||||
/* AUTHOR(S)
|
||||
/* Aaron Sethman
|
||||
/* androsyn@ratbox.org
|
||||
/*
|
||||
/* Based upon dict_mysql.c by
|
||||
/*
|
||||
/* Scott Cotton
|
||||
/* IC Group, Inc.
|
||||
/* scott@icgroup.com
|
||||
/*
|
||||
/* Joshua Marcus
|
||||
/* IC Group, Inc.
|
||||
/* josh@icgroup.com
|
||||
/*--*/
|
||||
|
||||
/* System library. */
|
||||
#include "sys_defs.h"
|
||||
|
||||
#ifdef HAS_PGSQL
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <syslog.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <postgres_ext.h>
|
||||
#include <libpq-fe.h>
|
||||
|
||||
/* Utility library. */
|
||||
#include "dict.h"
|
||||
#include "msg.h"
|
||||
#include "mymalloc.h"
|
||||
#include "dict_pgsql.h"
|
||||
#include "argv.h"
|
||||
#include "vstring.h"
|
||||
#include "split_at.h"
|
||||
#include "find_inet.h"
|
||||
|
||||
#define STATACTIVE 0
|
||||
#define STATFAIL 1
|
||||
#define STATUNTRIED 2
|
||||
#define RETRY_CONN_INTV 60 /* 1 minute */
|
||||
|
||||
typedef struct {
|
||||
PGconn *db;
|
||||
char *hostname;
|
||||
int stat; /* STATUNTRIED | STATFAIL | STATCUR */
|
||||
time_t ts; /* used for attempting reconnection */
|
||||
} HOST;
|
||||
|
||||
typedef struct {
|
||||
int len_hosts; /* number of hosts */
|
||||
HOST *db_hosts; /* hosts on which databases reside */
|
||||
} PLPGSQL;
|
||||
|
||||
typedef struct {
|
||||
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;
|
||||
PLPGSQL *pldb;
|
||||
PGSQL_NAME *name;
|
||||
} DICT_PGSQL;
|
||||
|
||||
|
||||
/* Just makes things a little easier for me.. */
|
||||
#define PGSQL_RES PGresult
|
||||
|
||||
/* internal function declarations */
|
||||
static PLPGSQL *plpgsql_init(char *hostnames[], int);
|
||||
static PGSQL_RES *plpgsql_query(PLPGSQL *, const char *, char *, char *, char *);
|
||||
static void plpgsql_dealloc(PLPGSQL *);
|
||||
static void plpgsql_down_host(HOST *);
|
||||
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(char *);
|
||||
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* 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)
|
||||
{
|
||||
unsigned int x,
|
||||
y;
|
||||
|
||||
/*
|
||||
* 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]) {
|
||||
case '\n':
|
||||
new[y++] = '\\';
|
||||
new[y] = 'n';
|
||||
break;
|
||||
case '\r':
|
||||
new[y++] = '\\';
|
||||
new[y] = 'r';
|
||||
break;
|
||||
case '\'':
|
||||
new[y++] = '\\';
|
||||
new[y] = '\'';
|
||||
break;
|
||||
case '"':
|
||||
new[y++] = '\\';
|
||||
new[y] = '"';
|
||||
break;
|
||||
case 0:
|
||||
new[y++] = '\\';
|
||||
new[y] = '0';
|
||||
break;
|
||||
default:
|
||||
new[y] = old[x];
|
||||
break;
|
||||
}
|
||||
}
|
||||
new[y] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* expand a filter (lookup or result)
|
||||
*/
|
||||
static void dict_pgsql_expand_filter(char *filter, char *value, VSTRING *out)
|
||||
{
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *dict_pgsql_lookup(DICT *dict, const char *name)
|
||||
{
|
||||
PGSQL_RES *query_res;
|
||||
DICT_PGSQL *dict_pgsql;
|
||||
PLPGSQL *pldb;
|
||||
static VSTRING *result;
|
||||
static VSTRING *query = 0;
|
||||
int i,
|
||||
j,
|
||||
numrows;
|
||||
char *name_escaped = 0;
|
||||
int isFunctionCall;
|
||||
int numcols;
|
||||
|
||||
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 */
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* 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) {
|
||||
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);
|
||||
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 (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));
|
||||
}
|
||||
}
|
||||
PQclear(query_res);
|
||||
return vstring_str(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* plpgsql_query - process a PostgreSQL query. Return PGSQL_RES* on success.
|
||||
* On failure, log failure and try other db instances.
|
||||
* on failure of all db instances, return 0;
|
||||
* close unnecessary active connections
|
||||
*/
|
||||
|
||||
static PGSQL_RES *plpgsql_query(PLPGSQL *PLDB,
|
||||
const char *query,
|
||||
char *dbname,
|
||||
char *username,
|
||||
char *password)
|
||||
{
|
||||
int i;
|
||||
HOST *host;
|
||||
PGSQL_RES *res = 0;
|
||||
|
||||
for (i = 0; i < PLDB->len_hosts; i++) {
|
||||
/* can't deal with typing or reading PLDB->db_hosts[i] over & over */
|
||||
host = &(PLDB->db_hosts[i]);
|
||||
if (msg_verbose > 1)
|
||||
msg_info("dict_pgsql: trying host %s stat %d, last res %p", host->hostname, host->stat, res);
|
||||
|
||||
/* answer already found */
|
||||
if (res != 0 && host->stat == STATACTIVE) {
|
||||
if (msg_verbose)
|
||||
msg_info("dict_pgsql: closing unnessary connection to %s",
|
||||
host->hostname);
|
||||
plpgsql_down_host(host);
|
||||
}
|
||||
/* try to connect for the first time if we don't have a result yet */
|
||||
if (res == 0 && host->stat == STATUNTRIED) {
|
||||
if (msg_verbose)
|
||||
msg_info("dict_pgsql: attempting to connect to host %s",
|
||||
host->hostname);
|
||||
plpgsql_connect_single(host, dbname, username, password);
|
||||
}
|
||||
|
||||
/*
|
||||
* try to reconnect if we don't have an answer and the host had a
|
||||
* prob in the past and it's time for it to reconnect
|
||||
*/
|
||||
if (res == 0 && host->stat == STATFAIL && host->ts < time((time_t *) 0)) {
|
||||
if (msg_verbose)
|
||||
msg_info("dict_pgsql: attempting to reconnect to host %s",
|
||||
host->hostname);
|
||||
plpgsql_connect_single(host, dbname, username, password);
|
||||
}
|
||||
|
||||
/*
|
||||
* if we don't have a result and the current host is marked active,
|
||||
* try the query. If the query fails, mark the host STATFAIL
|
||||
*/
|
||||
if (res == 0 && host->stat == STATACTIVE) {
|
||||
if ((res = PQexec(host->db, query))) {
|
||||
if (msg_verbose)
|
||||
msg_info("dict_pgsql: successful query from host %s", host->hostname);
|
||||
} else {
|
||||
msg_warn("%s", PQerrorMessage(host->db));
|
||||
plpgsql_down_host(host);
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* plpgsql_connect_single -
|
||||
* used to reconnect to a single database when one is down or none is
|
||||
* connected yet. Log all errors and set the stat field of host accordingly
|
||||
*/
|
||||
static void plpgsql_connect_single(HOST *host, char *dbname, char *username, char *password)
|
||||
{
|
||||
char *destination = host->hostname;
|
||||
char *unix_socket = 0;
|
||||
char *hostname = 0;
|
||||
char *service;
|
||||
char *port = 0;
|
||||
|
||||
/*
|
||||
* Ad-hoc parsing code. Expect "unix:pathname" or "inet:host:port", where
|
||||
* both "inet:" and ":port" are optional.
|
||||
*/
|
||||
if (strncmp(destination, "unix:", 5) == 0) {
|
||||
unix_socket = destination + 5;
|
||||
} else {
|
||||
if (strncmp(destination, "inet:", 5) == 0)
|
||||
destination += 5;
|
||||
hostname = mystrdup(destination);
|
||||
if ((service = split_at(hostname, ':')) != 0)
|
||||
port = service;
|
||||
}
|
||||
|
||||
if ((host->db = PQsetdbLogin(hostname, port, NULL, NULL, dbname, username, password))) {
|
||||
if (PQstatus(host->db) == CONNECTION_OK) {
|
||||
if (msg_verbose)
|
||||
msg_info("dict_pgsql: successful connection to host %s",
|
||||
host->hostname);
|
||||
host->stat = STATACTIVE;
|
||||
} else
|
||||
msg_warn("%s", PQerrorMessage(host->db));
|
||||
} else {
|
||||
msg_warn("Unable to connect to database");
|
||||
plpgsql_down_host(host);
|
||||
}
|
||||
if (hostname)
|
||||
myfree(hostname);
|
||||
}
|
||||
|
||||
/*
|
||||
* plpgsql_down_host - mark a HOST down update ts if marked down
|
||||
* for the first time so that we'll know when to retry the connection
|
||||
*/
|
||||
static void plpgsql_down_host(HOST *host)
|
||||
{
|
||||
if (host->stat != STATFAIL) {
|
||||
host->ts = time((time_t *) 0) + RETRY_CONN_INTV;
|
||||
host->stat = STATFAIL;
|
||||
}
|
||||
PQfinish(host->db);
|
||||
host->db = 0;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* public interface dict_pgsql_open
|
||||
* create association with database with appropriate values
|
||||
* parse the map's config file
|
||||
* allocate memory
|
||||
**********************************************************************/
|
||||
DICT *dict_pgsql_open(const char *name, int unused_flags, int unused_dict_flags)
|
||||
{
|
||||
DICT_PGSQL *dict_pgsql;
|
||||
|
||||
dict_pgsql = (DICT_PGSQL *) mymalloc(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);
|
||||
if (dict_pgsql->pldb == NULL)
|
||||
msg_fatal("couldn't intialize pldb!\n");
|
||||
dict_register(name, (DICT *) dict_pgsql);
|
||||
return &dict_pgsql->dict;
|
||||
}
|
||||
|
||||
/* pgsqlname_parse - parse pgsql configuration file */
|
||||
static PGSQL_NAME *pgsqlname_parse(const char *pgsqlcf_path)
|
||||
{
|
||||
int i;
|
||||
char *nameval;
|
||||
char *hosts;
|
||||
PGSQL_NAME *name = (PGSQL_NAME *) mymalloc(sizeof(PGSQL_NAME));
|
||||
ARGV *hosts_argv;
|
||||
VSTRING *opt_dict_name;
|
||||
|
||||
/*
|
||||
* setup a dict containing info in the pgsql cf file. the dict has a
|
||||
* name, and a path. The name must be distinct from the path, or the
|
||||
* dict interface gets confused. The name must be distinct for two
|
||||
* different paths, or the configuration info will cache across different
|
||||
* pgsql maps, which can be confusing.
|
||||
*/
|
||||
opt_dict_name = vstring_alloc(64);
|
||||
vstring_sprintf(opt_dict_name, "pgsql opt dict %s", pgsqlcf_path);
|
||||
dict_load_file(vstring_str(opt_dict_name), pgsqlcf_path);
|
||||
/* pgsql username lookup */
|
||||
if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "user")) == NULL)
|
||||
name->username = mystrdup("");
|
||||
else
|
||||
name->username = mystrdup(nameval);
|
||||
if (msg_verbose)
|
||||
msg_info("pgsqlname_parse(): set username to '%s'", name->username);
|
||||
/* password lookup */
|
||||
if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "password")) == NULL)
|
||||
name->password = mystrdup("");
|
||||
else
|
||||
name->password = mystrdup(nameval);
|
||||
if (msg_verbose)
|
||||
msg_info("pgsqlname_parse(): set password to '%s'", name->password);
|
||||
|
||||
/* database name lookup */
|
||||
if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "dbname")) == NULL)
|
||||
msg_fatal("%s: pgsql options file does not include database name", pgsqlcf_path);
|
||||
else
|
||||
name->dbname = mystrdup(nameval);
|
||||
if (msg_verbose)
|
||||
msg_info("pgsqlname_parse(): set database name to '%s'", name->dbname);
|
||||
|
||||
/* table lookup */
|
||||
if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "table")) == NULL)
|
||||
msg_fatal("%s: pgsql options file does not include table name", pgsqlcf_path);
|
||||
else
|
||||
name->table = mystrdup(nameval);
|
||||
if (msg_verbose)
|
||||
msg_info("pgsqlname_parse(): set table name to '%s'", name->table);
|
||||
|
||||
name->select_function = NULL;
|
||||
name->query = NULL;
|
||||
|
||||
/*
|
||||
* See what kind of lookup we have - a traditional 'select' or a function
|
||||
* call
|
||||
*/
|
||||
if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "select_function")) != NULL) {
|
||||
|
||||
/* We have a 'select %s(%s)' function call. */
|
||||
name->select_function = mystrdup(nameval);
|
||||
if (msg_verbose)
|
||||
msg_info("pgsqlname_parse(): set function name to '%s'", name->table);
|
||||
/* query string */
|
||||
} else if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "query")) != NULL) {
|
||||
name->query = mystrdup(nameval);
|
||||
if (msg_verbose)
|
||||
msg_info("pgsqlname_parse(): set query to '%s'", name->query);
|
||||
} else {
|
||||
|
||||
/*
|
||||
* We have an old style 'select %s from %s...' call, so get the
|
||||
* fields
|
||||
*/
|
||||
|
||||
/* table lookup */
|
||||
if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "table")) == NULL)
|
||||
msg_fatal("%s: pgsql options file does not include table name", pgsqlcf_path);
|
||||
else
|
||||
name->table = mystrdup(nameval);
|
||||
if (msg_verbose)
|
||||
msg_info("pgsqlname_parse(): set table name to '%s'", name->table);
|
||||
|
||||
/* select field lookup */
|
||||
if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "select_field")) == NULL)
|
||||
msg_fatal("%s: pgsql options file does not include select field", pgsqlcf_path);
|
||||
else
|
||||
name->select_field = mystrdup(nameval);
|
||||
if (msg_verbose)
|
||||
msg_info("pgsqlname_parse(): set select_field to '%s'", name->select_field);
|
||||
|
||||
/* where field lookup */
|
||||
if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "where_field")) == NULL)
|
||||
msg_fatal("%s: pgsql options file does not include where field", pgsqlcf_path);
|
||||
else
|
||||
name->where_field = mystrdup(nameval);
|
||||
if (msg_verbose)
|
||||
msg_info("pgsqlname_parse(): set where_field to '%s'", name->where_field);
|
||||
|
||||
/* additional conditions */
|
||||
if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "additional_conditions")) == NULL)
|
||||
name->additional_conditions = mystrdup("");
|
||||
else
|
||||
name->additional_conditions = mystrdup(nameval);
|
||||
if (msg_verbose)
|
||||
msg_info("pgsqlname_parse(): set additional_conditions to '%s'", name->additional_conditions);
|
||||
}
|
||||
|
||||
/* pgsql server hosts */
|
||||
if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "hosts")) == NULL)
|
||||
hosts = mystrdup("");
|
||||
else
|
||||
hosts = mystrdup(nameval);
|
||||
/* 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("pgsqlname_parse(): no hostnames specified, defaulting to 'localhost'");
|
||||
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("pgsqlname_parse(): adding host '%s' to list of pgsql server hosts",
|
||||
name->hostnames[i]);
|
||||
}
|
||||
myfree(hosts);
|
||||
vstring_free(opt_dict_name);
|
||||
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)
|
||||
{
|
||||
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]);
|
||||
}
|
||||
return PLDB;
|
||||
}
|
||||
|
||||
|
||||
/* host_init - initialize HOST structure */
|
||||
static HOST host_init(char *hostname)
|
||||
{
|
||||
HOST host;
|
||||
|
||||
host.stat = STATUNTRIED;
|
||||
host.hostname = mystrdup(hostname);
|
||||
host.db = 0;
|
||||
host.ts = 0;
|
||||
return host;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* public interface dict_pgsql_close
|
||||
* unregister, disassociate from database, freeing appropriate memory
|
||||
**********************************************************************/
|
||||
static void dict_pgsql_close(DICT *dict)
|
||||
{
|
||||
int i;
|
||||
DICT_PGSQL *dict_pgsql = (DICT_PGSQL *) dict;
|
||||
|
||||
plpgsql_dealloc(dict_pgsql->pldb);
|
||||
myfree(dict_pgsql->name->username);
|
||||
myfree(dict_pgsql->name->password);
|
||||
myfree(dict_pgsql->name->dbname);
|
||||
myfree(dict_pgsql->name->table);
|
||||
myfree(dict_pgsql->name->select_field);
|
||||
myfree(dict_pgsql->name->where_field);
|
||||
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);
|
||||
}
|
||||
|
||||
/* plpgsql_dealloc - free memory associated with PLPGSQL close databases */
|
||||
static void plpgsql_dealloc(PLPGSQL *PLDB)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PLDB->len_hosts; i++) {
|
||||
if (PLDB->db_hosts[i].db)
|
||||
PQfinish(PLDB->db_hosts[i].db);
|
||||
myfree(PLDB->db_hosts[i].hostname);
|
||||
}
|
||||
myfree((char *) PLDB->db_hosts);
|
||||
myfree((char *) (PLDB));
|
||||
}
|
||||
|
||||
#endif
|
41
postfix/src/util/dict_pgsql.h
Normal file
41
postfix/src/util/dict_pgsql.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifndef _DICT_PGSQL_INCLUDED_
|
||||
#define _DICT_PGSQL_INCLUDED_
|
||||
|
||||
/*++
|
||||
/* NAME
|
||||
/* dict_pgsql 3h
|
||||
/* SUMMARY
|
||||
/* dictionary manager interface to Postgresql files
|
||||
/* SYNOPSIS
|
||||
/* #include <dict_pgsql.h>
|
||||
/* DESCRIPTION
|
||||
/* .nf
|
||||
|
||||
/*
|
||||
* Utility library.
|
||||
*/
|
||||
#include <dict.h>
|
||||
|
||||
/*
|
||||
* External interface.
|
||||
*/
|
||||
#define DICT_TYPE_PGSQL "pgsql"
|
||||
|
||||
extern DICT *dict_pgsql_open(const char *name, int unused_flags, int dict_flags);
|
||||
|
||||
/* AUTHOR(S)
|
||||
/* Aaron Sethman
|
||||
/* androsyn@ratbox.org
|
||||
/*
|
||||
/* Based upon dict_mysql.c by
|
||||
/*
|
||||
/* Scott Cotton
|
||||
/* IC Group, Inc.
|
||||
/* scott@icgroup.com
|
||||
/*
|
||||
/* Joshua Marcus
|
||||
/* IC Group, Inc.
|
||||
/* josh@icgroup.com
|
||||
/*--*/
|
||||
|
||||
#endif
|
@@ -207,6 +207,7 @@ int match_hostaddr(int unused_flags, const char *addr, const char *pattern)
|
||||
unsigned long mask_bits;
|
||||
unsigned long net_bits;
|
||||
unsigned long addr_bits;
|
||||
struct in_addr net_addr;
|
||||
|
||||
if (msg_verbose)
|
||||
msg_info("%s: %s ~? %s", myname, addr, pattern);
|
||||
@@ -242,7 +243,14 @@ int match_hostaddr(int unused_flags, const char *addr, const char *pattern)
|
||||
if (addr_bits == INADDR_NONE)
|
||||
msg_fatal("%s: bad address argument: %s", myname, addr);
|
||||
mask_bits = htonl((0xffffffff) << (BITS_PER_ADDR - mask_shift));
|
||||
return ((addr_bits & mask_bits) == (net_bits & mask_bits));
|
||||
if ((addr_bits & mask_bits) == net_bits)
|
||||
return (1);
|
||||
if (net_bits & ~mask_bits) {
|
||||
net_addr.s_addr = (net_bits & mask_bits);
|
||||
msg_fatal("net/mask pattern %s has a non-null host portion; "
|
||||
"specify %s/%d if this is really what you want",
|
||||
pattern, inet_ntoa(net_addr), mask_shift);
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
Reference in New Issue
Block a user