]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.0.14-20030712
authorWietse Venema <wietse@porcupine.org>
Sat, 12 Jul 2003 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:29:02 +0000 (06:29 +0000)
68 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/README_FILES/ADDRESS_VERIFICATION_README
postfix/README_FILES/FILTER_README
postfix/README_FILES/SMTPD_PROXY_README
postfix/conf/main.cf
postfix/conf/sample-smtp.cf
postfix/conf/tcp_table
postfix/examples/smtpd-policy/smtpd-policy.pl [new file with mode: 0755]
postfix/html/nqmgr.8.html
postfix/html/qmgr.8.html
postfix/html/showq.8.html
postfix/html/smtp.8.html
postfix/html/smtpd.8.html
postfix/html/spawn.8.html
postfix/html/tcp_table.5.html
postfix/man/man5/tcp_table.5
postfix/man/man8/nqmgr.8
postfix/man/man8/qmgr.8
postfix/man/man8/smtp.8
postfix/man/man8/smtpd.8
postfix/proto/tcp_table
postfix/src/dns/dns_lookup.c
postfix/src/global/mail_copy.c
postfix/src/global/mail_params.h
postfix/src/global/mail_version.h
postfix/src/lmtp/lmtp_proto.c
postfix/src/lmtp/lmtp_sasl.h
postfix/src/lmtp/lmtp_sasl_glue.c
postfix/src/lmtp/lmtp_sasl_proto.c
postfix/src/nqmgr/qmgr.c
postfix/src/qmgr/qmgr.c
postfix/src/showq/showq.c
postfix/src/smtp/smtp.c
postfix/src/smtp/smtp_sasl.h
postfix/src/smtp/smtp_sasl_glue.c
postfix/src/smtp/smtp_sasl_proto.c
postfix/src/smtpd/smtpd.c
postfix/src/smtpd/smtpd.h
postfix/src/smtpd/smtpd_check.c
postfix/src/smtpd/smtpd_check.h
postfix/src/smtpd/smtpd_exp.in
postfix/src/smtpd/smtpd_exp.ref
postfix/src/smtpd/smtpd_proxy.c
postfix/src/smtpd/smtpd_sasl_glue.c
postfix/src/smtpd/smtpd_sasl_glue.h
postfix/src/smtpd/smtpd_state.c
postfix/src/spawn/spawn.c
postfix/src/util/Makefile.in
postfix/src/util/argv.c
postfix/src/util/argv.h
postfix/src/util/attr.h
postfix/src/util/attr_clnt.c [new file with mode: 0644]
postfix/src/util/attr_clnt.h [new file with mode: 0644]
postfix/src/util/attr_print0.c
postfix/src/util/attr_print64.c
postfix/src/util/attr_print_plain [new file with mode: 0755]
postfix/src/util/attr_print_plain.c [new file with mode: 0644]
postfix/src/util/attr_scan0.c
postfix/src/util/attr_scan64.c
postfix/src/util/attr_scan_plain [new file with mode: 0755]
postfix/src/util/attr_scan_plain.c [new file with mode: 0644]
postfix/src/util/attr_scan_plain.ref [new file with mode: 0644]
postfix/src/util/auto_clnt.c [new file with mode: 0644]
postfix/src/util/auto_clnt.h [new file with mode: 0644]
postfix/src/util/vstream.c
postfix/src/util/vstream.h
postfix/src/util/vstring.c

index 2388a60ed6a84b87fefba423f2ad4b3687f6c9b1..f146bceba129005034f58686ad5658824a6115ce 100644 (file)
@@ -1,7 +1,9 @@
 -TABOUNCE
 -TALIAS_TOKEN
 -TARGV
+-TATTR_CLNT
 -TATTR_TABLE
+-TAUTO_CLNT
 -TBH_TABLE
 -TBINATTR
 -TBINATTR_INFO
index 9d3745c0818b545975846b899edfc44dff6686d2..075de2f22ce9f175fb69c685ec4faee26eb7c062 100644 (file)
@@ -8339,6 +8339,55 @@ Apologies for any names omitted.
        and header/body checks. This eliminates the need to configure
        multiple cleanup services in the master.cf file.
 
+20030707
+
+       Feature: context dependent SASL security options (i.e.
+       different options when TLS is enabled/disabled). Lutz
+       Jaenicke.  Files:  */*sasl_glue.[hc].
+
+20030708
+
+       Hardened the attr_scan routines for exposure to an untrusted
+       environment, in preparation of the tuple0 and tuple64
+       protocols that will be used for SMTP policy delegation.
+
+       Feature: address filter for RBL lookups, for use with
+       multi-valued RBL services. File: smtpd/smtpd_check.c.
+
+       Feature: accept_socket option to pass the spawn(8) listen
+       socket to a persistent non-Postfix process. File: spawn/spawn.c.
+
+20030709
+
+       Cleanup: use off_t instead of int for VSTREAM file offsets.
+       This was needed for mailboxes > 2GB on 32-bit systems.
+       Files: util/vstream.c, global/mail_copy.c.
+
+20030710
+
+       Support for multiple A and TXT results in RBL lookups.
+       Victor Duchovni, Morgan Stanley.  File: smtpd/smtpd_check.c.
+
+       Support for attribute-based query-reply protocols.
+       Files: util/attr_clnt.[hc], util/auto_clnt.[hc].
+
+20030711
+
+       Support for plain "name=value\n" attribute protocol.
+       Files: util/attr_{scan,print}_plain.c.
+
+       Bugfix: the LMTP session caching code did not reset the
+       EHLO server feature list when it needed to reconnect.
+       Problem found by Tobias Erbsland.
+
+20030712
+
+       Feature: delegated SMTP policy server. As an example, see
+       the greylisting server in examples/smtpd-policy.  Specify
+       "check_smtpd_policy_service" in smtpd_mumble_restrictions.
+       See SMTPD_POLICY_SERVICE_README for details.
+
+
 Open problems:
 
        Low: smtp-source may block when sending large test messages.
index 01262944f321d3d665b2bbc46ff7c043f171c0cc..d2cc1129c4d8448b1145b60005f1f80bcd1a7665 100644 (file)
@@ -61,7 +61,7 @@ Turning on recipient address verification
 Recipient address verification may be useful to block mail for
 undeliverable recipients on mail relay hosts that do not have a
 copy of all the relayed recipient addresses. This prevents the mail
-queue from filling up with undeliverable and bounced SPAM.
+queue from filling up with undeliverable and bounced mail.
 
 Recipient address verification is relatively straightforward and
 there are no surprises. If a recipient probe fails, then Postfix
index fb7469ff20ee519984b315bda19dad6f108e10a2..9c8e592d833f43c122d8040e005bae889deb7db5 100644 (file)
@@ -17,7 +17,7 @@ and does one of the following:
     content.
 
 2 - Reject the mail (by sending a suitable status code back to
-    Postfix) so that it is returned to sender.
+    Postfix). Postfix will return the mail to the sender.
 
 3 - Send the mail somewhere else.
 
index 0b3a24455f5c5422a06fa3d1bf175c0a29ae229d..613ad27e92bf07b5d41a427a3e33511fc8c00edd 100644 (file)
@@ -111,6 +111,11 @@ process.
     smtp      inet  n       -       n       -       -       smtpd 
         -o smtpd_proxy_filter=26
     :26       inet  n       -       n       -       -       smtpd
+       -o smtpd_client_restrictions=
+       -o smtpd_helo_restrictions=
+       -o smtpd_sender_restrictions=
+       -o smtpd_recipient_restrictions=permit_mynetworks,reject
+       -o mynetworks=127.0.0.0/8
         -o receive_override_options=no_unknown_recipient_checks
 
 Note: do not specify spaces around the "=" or "," characters.
index 3d3bc6706d3176b7594eef96ee6fa4a330a0c10c..c139cee41fdee33fa0b775d0927272c7a0b1afb9 100644 (file)
@@ -559,7 +559,7 @@ unknown_local_recipient_reject_code = 450
 # most delivery transports. For the local delivery agent the default is 2.
 
 #local_destination_concurrency_limit = 2
-#default_destination_concurrency_limit = 10
+#default_destination_concurrency_limit = 20
 
 # DEBUGGING CONTROL
 #
index 19b76893b51cd8ecf3f4c933832a9e77b213269b..b9c521b2fe097fdc80834d2ed15005e04e54c578 100644 (file)
@@ -134,6 +134,18 @@ smtp_pix_workaround_delay_time = 10s
 # 
 smtp_pix_workaround_threshold_time = 500s
 
+# The smtp_quote_rfc821_envelope parameter controls whether the
+# Postfix SMTP client quotes addresses in MAIL FROM and RCPT TO
+# commands as required by RFC 821. This includes putting quotes around
+# an address localpart that ends in ".".
+# 
+# The default is to comply with RFC 821. If you have to send mail to
+# a broken SMTP server, configure a special SMTP client in master.cf
+# with "-o smtp_quote_rfc821_envelope=no" on the SMTP client command
+# line.
+#
+smtp_quote_rfc821_envelope = yes
+
 #
 # RATE CONTROLS
 #
index a9d69a86c8c62b480f51ccdeefdfa5296d080045..bb58ce363c5470362fcdc7050c9a1b9753abb052 100644 (file)
@@ -27,6 +27,8 @@
 #        terminated  by  the  ASCII  newline character. Request and
 #        reply parameters (see below) are separated by  whitespace.
 # 
+#        Send  and receive operations must complete in 100 seconds.
+# 
 # REQUEST FORMAT
 #        Each request specifies a command, a lookup key, and possi-
 #        bly a lookup result.
 #               This request is currently not implemented.
 # 
 # REPLY FORMAT
-#        Each reply specifies a status code and text. Replies  must
-#        be  no  longer  than 4096 characters including the newline
+#        Each  reply specifies a status code and text. Replies must
+#        be no longer than 4096 characters  including  the  newline
 #        terminator.
 # 
 #        500 SPACE text NEWLINE
-#               In case of a lookup  request,  the  requested  data
-#               does  not exist.  In case of an update request, the
+#               In  case  of  a  lookup request, the requested data
+#               does not exist.  In case of an update request,  the
 #               request  was  rejected.   The  text  describes  the
 #               nature of the problem.
 # 
 #        400 SPACE text NEWLINE
-#               This   indicates   an  error  condition.  The  text
-#               describes the nature of  the  problem.  The  client
+#               This  indicates  an  error  condition.   The   text
+#               describes  the  nature  of  the problem. The client
 #               should retry the request later.
 # 
 #        200 SPACE text NEWLINE
 #               The request was successful. In the case of a lookup
-#               request, the text contains an  encoded  version  of
+#               request,  the  text  contains an encoded version of
 #               the requested data.
 # 
 # ENCODING
-#        In  request  and  reply  parameters, the character %, each
+#        In request and reply parameters,  the  character  %,  each
 #        non-printing character, and each whitespace character must
-#        be  replaced  by  %XX, where XX is the corresponding ASCII
-#        hexadecimal character value. The hexadecimal codes can  be
+#        be replaced by %XX, where XX is  the  corresponding  ASCII
+#        hexadecimal  character value. The hexadecimal codes can be
 #        specified in any case (upper, lower, mixed).
 # 
-#        The  Postfix  client always encodes a request.  The server
-#        may omit the encoding as long as the reply  is  guaranteed
+#        The Postfix client always encodes a request.   The  server
+#        may  omit  the encoding as long as the reply is guaranteed
 #        to not contain the % or NEWLINE character.
 # 
 # SECURITY
-#        Do  not  use  TCP lookup tables for security critical pur-
-#        poses.  The client-server connection is not protected  and
+#        Do not use TCP lookup tables for  security  critical  pur-
+#        poses.   The client-server connection is not protected and
 #        the server is not authenticated.
 # 
 # SEE ALSO
 # BUGS
 #        Only the lookup method is currently implemented.
 # 
-#        The  client  does  not hang up when the connection is idle
+#        The client does not hang up when the  connection  is  idle
 #        for a long time.
 # 
 # LICENSE
-#        The Secure Mailer license must be  distributed  with  this
+#        The  Secure  Mailer  license must be distributed with this
 #        software.
 # 
 # AUTHOR(S)
diff --git a/postfix/examples/smtpd-policy/smtpd-policy.pl b/postfix/examples/smtpd-policy/smtpd-policy.pl
new file mode 100755 (executable)
index 0000000..aed1e72
--- /dev/null
@@ -0,0 +1,183 @@
+#!/usr/bin/perl
+
+use DB_File;
+use Fcntl;
+use Sys::Syslog qw(:DEFAULT setlogsock);
+
+#
+# Usage: smtpd-policy.pl [-v]
+#
+# Demo delegated Postfix SMTPD policy server. This server implements
+# greylisting. State is kept in a Berkeley DB database.  Logging is
+# sent to syslogd.
+#
+# To run this from /etc/postfix/master.cf:
+#
+#    policy  unix  -       n       n       -       -       spawn
+#      user=nobody argv=/usr/bin/perl /usr/libexec/postfix/smtpd-policy.pl
+#
+# To use this from Postfix SMTPD, use in /etc/postfix/main.cf:
+#
+#    smtpd_policy_service_endpoint = plain:unix:private/policy
+#    smtpd_recipient_restrictions = ... check_policy_service ...
+#
+# This script runs as one PERL process per SMTP server process.
+# By default, a Postfix SMTP server process terminates after 100
+# seconds of idle time, or after serving 100 clients.
+#
+
+#
+# greylist status database and greylist time interval. DO NOT create the
+# greylist status database in a world-writable directory such as /tmp
+# or /var/tmp. DO NOT create the greylist database in a file system
+# that can run out of space.
+#
+$database_name="/var/mta/smtpd-policy.db";
+$greylist_delay=3600;
+
+#
+# Syslogging options for verbose mode and for fatal errors. Comment
+# out the $syslog_socktype line if syslogging does not work.
+#
+$syslog_socktype = 'unix'; # inet, unix, stream, console
+$syslog_facility="mail";
+$syslog_options="pid";
+$syslog_priority="info";
+
+#
+# Demo policy routine. The result is an action just like it would
+# be specified on the right-hand side of a Postfix access table.
+# Request attributes are passed in via the %attr hash.
+#
+sub policy {
+    local(*attr) = @_;
+    my($key, $time_stamp, $now);
+
+    # Open the database on the fly.
+    open_database() unless $database_obj;
+
+    # Lookup the time stamp for this client/sender/recipient.
+    $key = $attr{"client_address"}."/".$attr{"sender"}."/".$attr{"recipient"};
+    $time_stamp = read_database($key);
+    $now = time();
+
+    # If new request, add this client/sender/recipient to the database.
+    if ($time_stamp == 0) {
+       $time_stamp = $now;
+       update_database($key, $time_stamp);
+    }
+
+    syslog $syslog_priority, "request age %d", $now - $time_stamp if $verbose;
+    if ($time_stamp + $greylist_delay < $now) {
+       return "ok";
+    } else {
+       return "450 request is greylisted";
+    }
+}
+
+#
+# You should not have to make changes below this point.
+#
+sub LOCK_SH { 1 };     # Shared lock (used for reading).
+sub LOCK_EX { 2 };     # Exclusive lock (used for writing).
+sub LOCK_NB { 4 };     # Don't block (for testing).
+sub LOCK_UN { 8 };     # Release lock.
+
+#
+# Log an error and abort.
+#
+sub fatal_exit {
+    syslog "err", @_;
+    exit 1;
+}
+
+#
+# Open hash database.
+#
+sub open_database {
+    my($database_fd);
+
+    $database_obj = tie(%db_hash, 'DB_File', $database_name, 
+                           O_CREAT|O_RDWR, 0644) ||
+       fatal_exit "Cannot open database %s: %m", $database_name;
+    $database_fd = $database_obj->fd;
+    open DATABASE_HANDLE, "+<&=$database_fd" ||
+       fatal_exit "Cannot fdopen database %s: %m", $database_name;
+    syslog $syslog_priority, "open %s", $database_name if $verbose;
+}
+
+#
+# Read database.
+#
+sub read_database {
+    my($key) = @_;
+    my($value);
+
+    flock DATABASE_HANDLE, LOCK_SH ||
+       fatal_exit "Can't get shared lock on %s: %m", $database_name;
+    $value = $db_hash{$key};
+    syslog $syslog_priority, "lookup %s: %s", $key, $value if $verbose;
+    flock DATABASE_HANDLE, LOCK_UN ||
+       fatal_exit "Can't unlock %s: %m", $database_name;
+    return $value;
+}
+
+#
+# Update database.
+#
+sub update_database {
+    my($key, $value) = @_;
+
+    syslog $syslog_priority, "store %s: %s", $key, $value if $verbose;
+    flock DATABASE_HANDLE, LOCK_EX ||
+       fatal_exit "Can't exclusively lock %s: %m", $database_name;
+    $db_hash{$key} = $value;
+    $database_obj->sync() &&
+       fatal_exit "Can't update %s: %m", $database_name;
+    flock DATABASE_HANDLE, LOCK_UN ||
+       fatal_exit "Can't unlock %s: %m", $database_name;
+}
+
+#
+# This process runs as a daemon, so it can't log to a terminal. Use
+# syslog so that people can actually see our messages.
+#
+setlogsock $syslog_socktype;
+openlog $0, $syslog_options, $syslog_facility;
+
+#
+# We don't need getopt() for now.
+#
+while ($option = shift(@ARGV)) {
+    if ($option eq "-v") {
+       $verbose = 1;
+    } else {
+       syslog $syslog_priority, "Invalid option: %s. Usage: %s [-v]", 
+               $option, $0;
+       exit 1;
+    }
+}
+
+#
+# Unbuffer standard output.
+#
+select((select(STDOUT), $| = 1)[0]);
+
+#
+# Receive a bunch of attributes, evaluate the policy, send the result.
+#
+while (<STDIN>) {
+    if (/([^=]+)=(.*)\n/) {
+       $attr{$1} = $2;
+    } else {
+       if ($verbose) {
+           for (keys %attr) {
+               syslog $syslog_priority, "Attribute: %s=%s", $_, $attr{$_};
+           }
+       }
+       $action = &policy(*attr);
+       syslog $syslog_priority, "Action: %s", $action if $verbose;
+       print STDOUT "action=$action\n\n";
+       %attr = ();
+    }
+}
index aef34e5638881d0a81e9dd7102cf2a472512f04c..90cf9d859ed26308a0f6f9d68938ee9c29f26d0d 100644 (file)
@@ -15,10 +15,10 @@ NQMGR(8)                                                 NQMGR(8)
        from the <a href="master.8.html"><b>master</b>(8)</a> process manager.
 
        Mail  addressed  to  the  local  <b>double-bounce</b>  address is
-       silently discarded.  This stops potential loops caused  by
-       undeliverable bounce notifications.
+       logged and discarded.  This stops potential  loops  caused
+       by undeliverable bounce notifications.
 
-<b>MAIL</b> <b>QUEUES</b>
+<b>MAIL QUEUES</b>
        The <b>nqmgr</b> daemon maintains the following queues:
 
        <b>incoming</b>
@@ -43,7 +43,7 @@ NQMGR(8)                                                 NQMGR(8)
        <b>hold</b>   Messages that are kept  "on  hold"  are  kept  here
               until someone sets them free.
 
-<b>DELIVERY</b> <b>STATUS</b> <b>REPORTS</b>
+<b>DELIVERY STATUS REPORTS</b>
        The <b>nqmgr</b> daemon keeps an eye on per-message delivery sta-
        tus reports in  the  following  directories.  Each  status
        report file has the same name as the corresponding message
@@ -65,7 +65,7 @@ NQMGR(8)                                                 NQMGR(8)
        either opening queue files (input) or for message delivery
        (output).
 
-       <b>leaky</b> <b>bucket</b>
+       <b>leaky bucket</b>
               This  strategy limits the number of messages in the
               <b>active</b> queue and prevents the  queue  manager  from
               running out of memory under heavy load.
@@ -76,28 +76,28 @@ NQMGR(8)                                                 NQMGR(8)
               from the <b>deferred</b> queue. This prevents a large mail
               backlog from blocking the delivery of new mail.
 
-       <b>slow</b> <b>start</b>
+       <b>slow start</b>
               This strategy eliminates "thundering herd" problems
               by slowly adjusting the number of parallel deliver-
               ies to the same destination.
 
-       <b>round</b> <b>robin</b>
+       <b>round robin</b>
               The queue manager sorts delivery requests by desti-
               nation.   Round-robin selection prevents one desti-
               nation from dominating deliveries to other destina-
               tions.
 
-       <b>exponential</b> <b>backoff</b>
+       <b>exponential backoff</b>
               Mail  that  cannot  be  delivered  upon  the  first
               attempt is deferred.   The  time  interval  between
               delivery attempts is doubled after each attempt.
 
-       <b>destination</b> <b>status</b> <b>cache</b>
+       <b>destination status cache</b>
               The   queue  manager  avoids  unnecessary  delivery
               attempts by  maintaining  a  short-term,  in-memory
               list of unreachable destinations.
 
-       <b>preemptive</b> <b>message</b> <b>scheduling</b>
+       <b>preemptive message scheduling</b>
               The  queue manager attempts to minimize the average
               per-recipient delay while still preserving the cor-
               rect per-message delays, using a sophisticated pre-
@@ -111,25 +111,25 @@ NQMGR(8)                                                 NQMGR(8)
        actions (the message is followed by the symbolic  constant
        used internally by the software):
 
-       <b>D</b> <b>(QMGR</b><i>_</i><b>REQ</b><i>_</i><b>SCAN</b><i>_</i><b>DEFERRED)</b>
+       <b>D (QMGR</b><i>_</i><b>REQ</b><i>_</i><b>SCAN</b><i>_</i><b>DEFERRED)</b>
               Start  a  deferred queue scan.  If a deferred queue
               scan is already in  progress,  that  scan  will  be
               restarted as soon as it finishes.
 
-       <b>I</b> <b>(QMGR</b><i>_</i><b>REQ</b><i>_</i><b>SCAN</b><i>_</i><b>INCOMING)</b>
+       <b>I (QMGR</b><i>_</i><b>REQ</b><i>_</i><b>SCAN</b><i>_</i><b>INCOMING)</b>
               Start  an incoming queue scan. If an incoming queue
               scan is already in  progress,  that  scan  will  be
               restarted as soon as it finishes.
 
-       <b>A</b> <b>(QMGR</b><i>_</i><b>REQ</b><i>_</i><b>SCAN</b><i>_</i><b>ALL)</b>
+       <b>A (QMGR</b><i>_</i><b>REQ</b><i>_</i><b>SCAN</b><i>_</i><b>ALL)</b>
               Ignore deferred queue file time stamps. The request
               affects the next deferred queue scan.
 
-       <b>F</b> <b>(QMGR</b><i>_</i><b>REQ</b><i>_</i><b>FLUSH</b><i>_</i><b>DEAD)</b>
+       <b>F (QMGR</b><i>_</i><b>REQ</b><i>_</i><b>FLUSH</b><i>_</i><b>DEAD)</b>
               Purge all information  about  dead  transports  and
               destinations.
 
-       <b>W</b> <b>(TRIGGER</b><i>_</i><b>REQ</b><i>_</i><b>WAKEUP)</b>
+       <b>W (TRIGGER</b><i>_</i><b>REQ</b><i>_</i><b>WAKEUP)</b>
               Wakeup  call,  This is used by the master server to
               instantiate servers that should not  go  away  for-
               ever.  The  action  is  to  start an incoming queue
@@ -139,7 +139,7 @@ NQMGR(8)                                                 NQMGR(8)
        Multiple  identical  trigger  requests  are collapsed into
        one, and trigger requests are sorted so that <b>A</b> and <b>F</b>  pre-
        cede  <b>D</b>  and  <b>I</b>.  Thus, in order to force a deferred queue
-       run, one would request <b>A</b> <b>F</b> <b>D</b>; in order to notify the queue
+       run, one would request <b>A F D</b>; in order to notify the queue
        manager of the arrival of new mail one would request <b>I</b>.
 
 <b>STANDARDS</b>
@@ -169,10 +169,10 @@ NQMGR(8)                                                 NQMGR(8)
        sudden  burst  of  inbound mail can negatively impact out-
        bound delivery rates.
 
-<b>CONFIGURATION</b> <b>PARAMETERS</b>
+<b>CONFIGURATION PARAMETERS</b>
        The following <b>main.cf</b> parameters are  especially  relevant
        to  this  program. See the Postfix <b>main.cf</b> file for syntax
-       details and for default values.  Use  the  <b>postfix</b>  <b>reload</b>
+       details and for default values.  Use  the  <b>postfix  reload</b>
        command after a configuration change.
 
 <b>Miscellaneous</b>
@@ -183,7 +183,7 @@ NQMGR(8)                                                 NQMGR(8)
        <b>queue</b><i>_</i><b>directory</b>
               Top-level directory of the Postfix queue.
 
-<b>Active</b> <b>queue</b> <b>controls</b>
+<b>Active queue controls</b>
        In the text below, <i>transport</i> is the first field in a  <b>mas-</b>
        <b>ter.cf</b> entry.
 
@@ -222,7 +222,7 @@ NQMGR(8)                                                 NQMGR(8)
               all  preempting messages delivered by the transport
               <i>transport</i> can have.
 
-<b>Timing</b> <b>controls</b>
+<b>Timing controls</b>
        <b>minimal</b><i>_</i><b>backoff</b><i>_</i><b>time</b>
               Minimal time in seconds between  delivery  attempts
               of a deferred message.
@@ -247,7 +247,7 @@ NQMGR(8)                                                 NQMGR(8)
               Time in seconds between attempts to contact a  bro-
               ken delivery transport.
 
-<b>Concurrency</b> <b>controls</b>
+<b>Concurrency controls</b>
        <b>initial</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b>
               Initial  per-destination concurrency level for par-
               allel delivery to the same destination.
@@ -261,7 +261,7 @@ NQMGR(8)                                                 NQMGR(8)
               same destination, for delivery via the  named  mes-
               sage <i>transport</i>.
 
-<b>Recipient</b> <b>controls</b>
+<b>Recipient controls</b>
        <b>default</b><i>_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
               Default  limit on the number of recipients per mes-
               sage transfer.
@@ -270,7 +270,7 @@ NQMGR(8)                                                 NQMGR(8)
               Limit on  the  number  of  recipients  per  message
               transfer, for the named message <i>transport</i>.
 
-<b>Message</b> <b>scheduling</b>
+<b>Message scheduling</b>
        <i>transport_</i><b>delivery</b><i>_</i><b>slot</b><i>_</i><b>cost</b> (valid range: 0,2,3...)
               This  parameter basically controls how often a mes-
               sage delivered by <i>transport</i>  can  be  preempted  by
@@ -316,7 +316,7 @@ NQMGR(8)                                                 NQMGR(8)
               Default  values  for the transport specific parame-
               ters described above.
 
-<b>SEE</b> <b>ALSO</b>
+<b>SEE ALSO</b>
        <a href="master.8.html">master(8)</a>, process manager
        syslogd(8) system logging
        <a href="trivial-rewrite.8.html">trivial-rewrite(8)</a>, address routing
index 48c8b188fa43e2d25ac954ae34e2781df4b65556..8e741d266c0edbbc3dbc3efffc7c85eb68259fed 100644 (file)
@@ -15,10 +15,10 @@ QMGR(8)                                                   QMGR(8)
        from the <a href="master.8.html"><b>master</b>(8)</a> process manager.
 
        Mail  addressed  to  the  local  <b>double-bounce</b>  address is
-       silently discarded.  This stops potential loops caused  by
-       undeliverable bounce notifications.
+       logged and discarded.  This stops potential  loops  caused
+       by undeliverable bounce notifications.
 
-<b>MAIL</b> <b>QUEUES</b>
+<b>MAIL QUEUES</b>
        The <b>qmgr</b> daemon maintains the following queues:
 
        <b>incoming</b>
@@ -43,7 +43,7 @@ QMGR(8)                                                   QMGR(8)
        <b>hold</b>   Messages that are kept  "on  hold"  are  kept  here
               until someone sets them free.
 
-<b>DELIVERY</b> <b>STATUS</b> <b>REPORTS</b>
+<b>DELIVERY STATUS REPORTS</b>
        The  <b>qmgr</b> daemon keeps an eye on per-message delivery sta-
        tus reports in  the  following  directories.  Each  status
        report file has the same name as the corresponding message
@@ -65,7 +65,7 @@ QMGR(8)                                                   QMGR(8)
        either opening queue files (input) or for message delivery
        (output).
 
-       <b>leaky</b> <b>bucket</b>
+       <b>leaky bucket</b>
               This  strategy limits the number of messages in the
               <b>active</b> queue and prevents the  queue  manager  from
               running out of memory under heavy load.
@@ -76,23 +76,23 @@ QMGR(8)                                                   QMGR(8)
               from the <b>deferred</b> queue. This prevents a large mail
               backlog from blocking the delivery of new mail.
 
-       <b>slow</b> <b>start</b>
+       <b>slow start</b>
               This strategy eliminates "thundering herd" problems
               by slowly adjusting the number of parallel deliver-
               ies to the same destination.
 
-       <b>round</b> <b>robin</b>
+       <b>round robin</b>
               The queue manager sorts delivery requests by desti-
               nation.   Round-robin selection prevents one desti-
               nation from dominating deliveries to other destina-
               tions.
 
-       <b>exponential</b> <b>backoff</b>
+       <b>exponential backoff</b>
               Mail  that  cannot  be  delivered  upon  the  first
               attempt is deferred.   The  time  interval  between
               delivery attempts is doubled after each attempt.
 
-       <b>destination</b> <b>status</b> <b>cache</b>
+       <b>destination status cache</b>
               The   queue  manager  avoids  unnecessary  delivery
               attempts by  maintaining  a  short-term,  in-memory
               list of unreachable destinations.
@@ -105,25 +105,25 @@ QMGR(8)                                                   QMGR(8)
        actions  (the message is followed by the symbolic constant
        used internally by the software):
 
-       <b>D</b> <b>(QMGR</b><i>_</i><b>REQ</b><i>_</i><b>SCAN</b><i>_</i><b>DEFERRED)</b>
+       <b>D (QMGR</b><i>_</i><b>REQ</b><i>_</i><b>SCAN</b><i>_</i><b>DEFERRED)</b>
               Start a deferred queue scan.  If a  deferred  queue
               scan  is  already  in  progress,  that scan will be
               restarted as soon as it finishes.
 
-       <b>I</b> <b>(QMGR</b><i>_</i><b>REQ</b><i>_</i><b>SCAN</b><i>_</i><b>INCOMING)</b>
+       <b>I (QMGR</b><i>_</i><b>REQ</b><i>_</i><b>SCAN</b><i>_</i><b>INCOMING)</b>
               Start an incoming queue scan. If an incoming  queue
               scan  is  already  in  progress,  that scan will be
               restarted as soon as it finishes.
 
-       <b>A</b> <b>(QMGR</b><i>_</i><b>REQ</b><i>_</i><b>SCAN</b><i>_</i><b>ALL)</b>
+       <b>A (QMGR</b><i>_</i><b>REQ</b><i>_</i><b>SCAN</b><i>_</i><b>ALL)</b>
               Ignore deferred queue file time stamps. The request
               affects the next deferred queue scan.
 
-       <b>F</b> <b>(QMGR</b><i>_</i><b>REQ</b><i>_</i><b>FLUSH</b><i>_</i><b>DEAD)</b>
+       <b>F (QMGR</b><i>_</i><b>REQ</b><i>_</i><b>FLUSH</b><i>_</i><b>DEAD)</b>
               Purge  all  information  about  dead transports and
               destinations.
 
-       <b>W</b> <b>(TRIGGER</b><i>_</i><b>REQ</b><i>_</i><b>WAKEUP)</b>
+       <b>W (TRIGGER</b><i>_</i><b>REQ</b><i>_</i><b>WAKEUP)</b>
               Wakeup call, This is used by the master  server  to
               instantiate  servers  that  should not go away for-
               ever. The action is  to  start  an  incoming  queue
@@ -133,7 +133,7 @@ QMGR(8)                                                   QMGR(8)
        Multiple identical trigger  requests  are  collapsed  into
        one,  and trigger requests are sorted so that <b>A</b> and <b>F</b> pre-
        cede <b>D</b> and <b>I</b>. Thus, in order to  force  a  deferred  queue
-       run, one would request <b>A</b> <b>F</b> <b>D</b>; in order to notify the queue
+       run, one would request <b>A F D</b>; in order to notify the queue
        manager of the arrival of new mail one would request <b>I</b>.
 
 <b>STANDARDS</b>
@@ -162,10 +162,10 @@ QMGR(8)                                                   QMGR(8)
        sudden  burst  of  inbound mail can negatively impact out-
        bound delivery rates.
 
-<b>CONFIGURATION</b> <b>PARAMETERS</b>
+<b>CONFIGURATION PARAMETERS</b>
        The following <b>main.cf</b> parameters are  especially  relevant
        to  this  program. See the Postfix <b>main.cf</b> file for syntax
-       details and for default values.  Use  the  <b>postfix</b>  <b>reload</b>
+       details and for default values.  Use  the  <b>postfix  reload</b>
        command after a configuration change.
 
 <b>Miscellaneous</b>
@@ -176,7 +176,7 @@ QMGR(8)                                                   QMGR(8)
        <b>queue</b><i>_</i><b>directory</b>
               Top-level directory of the Postfix queue.
 
-<b>Active</b> <b>queue</b> <b>controls</b>
+<b>Active queue controls</b>
        <b>qmgr</b><i>_</i><b>clog</b><i>_</i><b>warn</b><i>_</i><b>time</b>
               Minimal delay between warnings that a specific des-
               tination is clogging up the active queue. Specify 0
@@ -191,7 +191,7 @@ QMGR(8)                                                   QMGR(8)
               This parameter also limits the size of  the  short-
               term, in-memory destination cache.
 
-<b>Timing</b> <b>controls</b>
+<b>Timing controls</b>
        <b>minimal</b><i>_</i><b>backoff</b><i>_</i><b>time</b>
               Minimal  time  in seconds between delivery attempts
               of a deferred message.
@@ -216,7 +216,7 @@ QMGR(8)                                                   QMGR(8)
               Time  in seconds between attempts to contact a bro-
               ken delivery transport.
 
-<b>Concurrency</b> <b>controls</b>
+<b>Concurrency controls</b>
        In the text below, <i>transport</i> is the first field in a  <b>mas-</b>
        <b>ter.cf</b> entry.
 
@@ -249,7 +249,7 @@ QMGR(8)                                                   QMGR(8)
               same  destination,  for delivery via the named mes-
               sage <i>transport</i>.
 
-<b>Recipient</b> <b>controls</b>
+<b>Recipient controls</b>
        <b>default</b><i>_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
               Default limit on the number of recipients per  mes-
               sage transfer.
@@ -258,7 +258,7 @@ QMGR(8)                                                   QMGR(8)
               Limit  on  the  number  of  recipients  per message
               transfer, for the named message <i>transport</i>.
 
-<b>SEE</b> <b>ALSO</b>
+<b>SEE ALSO</b>
        <a href="master.8.html">master(8)</a>, process manager
        syslogd(8) system logging
        <a href="trivial-rewrite.8.html">trivial-rewrite(8)</a>, address routing
index 77b7c1528a3b93347c8a1d5fe0420e493cece850..d694e57ceb57bfa8d7d75ab8f263f2bffc4c799c 100644 (file)
@@ -34,7 +34,7 @@ SHOWQ(8)                                                 SHOWQ(8)
        quently, it cannot extract information from queue files in
        the <b>maildrop</b> directory.
 
-<b>SEE</b> <b>ALSO</b>
+<b>SEE ALSO</b>
        <a href="cleanup.8.html">cleanup(8)</a> canonicalize and enqueue mail
        <a href="pickup.8.html">pickup(8)</a> local mail pickup service
        <a href="qmgr.8.html">qmgr(8)</a> mail being delivered, delayed mail
index 167e4b3436384ea80afbd0933d2dbc34d680621a..a6ebcb31a10d6e441defad5c2375442775db818c 100644 (file)
@@ -57,10 +57,10 @@ SMTP(8)                                                   SMTP(8)
        and of other trouble.
 
 <b>BUGS</b>
-<b>CONFIGURATION</b> <b>PARAMETERS</b>
+<b>CONFIGURATION PARAMETERS</b>
        The following <b>main.cf</b> parameters are  especially  relevant
        to  this  program. See the Postfix <b>main.cf</b> file for syntax
-       details and for default values.  Use  the  <b>postfix</b>  <b>reload</b>
+       details and for default values.  Use  the  <b>postfix  reload</b>
        command after a configuration change.
 
 <b>Miscellaneous</b>
@@ -101,7 +101,7 @@ SMTP(8)                                                   SMTP(8)
 
        <b>ignore</b><i>_</i><b>mx</b><i>_</i><b>lookup</b><i>_</i><b>error</b>
               When a name server fails to respond to an MX query,
-              search for  an  A  record  instead  deferring  mail
+              search for an A record, instead of  deferring  mail
               delivery.
 
        <b>inet</b><i>_</i><b>interfaces</b>
@@ -167,7 +167,7 @@ SMTP(8)                                                   SMTP(8)
               PIX firewall &lt;CR&gt;&lt;LF&gt;.&lt;CR&gt;&lt;LF&gt;  bug  workaround  is
               turned on.
 
-<b>MIME</b> <b>Conversion</b>
+<b>MIME Conversion</b>
        <b>disable</b><i>_</i><b>mime</b><i>_</i><b>output</b><i>_</i><b>conversion</b>
               Disable  the  conversion of 8BITMIME format to 7BIT
               format when the remote system  does  not  advertise
@@ -186,7 +186,7 @@ SMTP(8)                                                   SMTP(8)
               nested deeper, when converting from 8BITMIME format
               to 7BIT format.
 
-<b>Authentication</b> <b>controls</b>
+<b>Authentication controls</b>
        <b>smtp</b><i>_</i><b>sasl</b><i>_</i><b>auth</b><i>_</i><b>enable</b>
               Enable per-session authentication as per  <a href="http://www.faqs.org/rfcs/rfc2554.html">RFC  2554</a>
               (SASL).   By default, Postfix is built without SASL
@@ -215,7 +215,7 @@ SMTP(8)                                                   SMTP(8)
               <b>noanonymous</b>
                      Disallow anonymous logins.
 
-<b>Resource</b> <b>controls</b>
+<b>Resource controls</b>
        <b>smtp</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b>
               Limit the number of parallel deliveries to the same
               destination.   The  default limit is taken from the
@@ -226,7 +226,7 @@ SMTP(8)                                                   SMTP(8)
               ery.    The   default   limit  is  taken  from  the
               <b>default</b><i>_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b> parameter.
 
-<b>Timeout</b> <b>controls</b>
+<b>Timeout controls</b>
        The default time unit is seconds; an  explicit  time  unit
        can  be  specified by appending a one-letter suffix to the
        value: s (seconds), m (minutes), h (hours), d (days) or  w
@@ -250,11 +250,11 @@ SMTP(8)                                                   SMTP(8)
               receiving the server response.
 
        <b>smtp</b><i>_</i><b>mail</b><i>_</i><b>timeout</b>
-              Timeout  for sending the <b>MAIL</b> <b>FROM</b> command, and for
+              Timeout  for sending the <b>MAIL FROM</b> command, and for
               receiving the server response.
 
        <b>smtp</b><i>_</i><b>rcpt</b><i>_</i><b>timeout</b>
-              Timeout for sending the <b>RCPT</b> <b>TO</b>  command,  and  for
+              Timeout for sending the <b>RCPT TO</b>  command,  and  for
               receiving the server response.
 
        <b>smtp</b><i>_</i><b>data</b><i>_</i><b>init</b><i>_</i><b>timeout</b>
@@ -277,7 +277,7 @@ SMTP(8)                                                   SMTP(8)
               Timeout  for  sending  the  <b>QUIT</b>  command,  and for
               receiving the server response.
 
-<b>SEE</b> <b>ALSO</b>
+<b>SEE ALSO</b>
        <a href="bounce.8.html">bounce(8)</a> non-delivery status reports
        <a href="master.8.html">master(8)</a> process manager
        <a href="qmgr.8.html">qmgr(8)</a> queue manager
index 8eba72b2a4d31c0de5c025353587e750af543bfe..fd9e642190b4c4607c2ac7e80aac7d9f4cc73b89 100644 (file)
@@ -73,6 +73,9 @@ SMTPD(8)                                                 SMTPD(8)
               overrides built-in command definitions.
 
 <b>Content inspection controls</b>
+       Optionally,  Postfix can be configured to send new mail to
+       external content filter software AFTER the mail is queued.
+
        <b>content</b><i>_</i><b>filter</b>
               The  name of a mail delivery transport that filters
               mail and that either bounces mail or re-injects the
@@ -103,10 +106,27 @@ SMTPD(8)                                                 SMTPD(8)
                      cally  specified  with the SMTP server <b>after</b>
                      an external content filter.
 
+<b>Pass-through proxy</b>
+       Optionally, the Postfix SMTP server can be  configured  to
+       forward  all  mail  to a proxy server, for example a real-
+       time content filter, BEFORE mail is queued.
+
+       <b>smtpd</b><i>_</i><b>proxy</b><i>_</i><b>filter</b>
+              The <i>host:port</i> of the SMTP proxy server. The <i>host</i> or
+              <i>host:</i> portion is optional.
+
+       <b>smtpd</b><i>_</i><b>proxy</b><i>_</i><b>timeout</b>
+              Timeout for connecting to, sending to and receiving
+              from the SMTP proxy server.
+
+       <b>smtpd</b><i>_</i><b>proxy</b><i>_</i><b>ehlo</b>
+              The hostname to use when sending an EHLO command to
+              the SMTP proxy server.
+
 <b>Authentication controls</b>
        <b>enable</b><i>_</i><b>sasl</b><i>_</i><b>authentication</b>
-              Enable per-session authentication as per  <a href="http://www.faqs.org/rfcs/rfc2554.html">RFC  2554</a>
-              (SASL).   This functionality is available only when
+              Enable  per-session  authentication as per <a href="http://www.faqs.org/rfcs/rfc2554.html">RFC 2554</a>
+              (SASL).  This functionality is available only  when
               explicitly  selected  at  program  build  time  and
               explicitly enabled at runtime.
 
@@ -132,30 +152,11 @@ SMTPD(8)                                                 SMTPD(8)
                      Disallow anonymous logins.
 
        <b>smtpd</b><i>_</i><b>sender</b><i>_</i><b>login</b><i>_</i><b>maps</b>
-              Maps  that  specify the SASL login name that owns a
-              MAIL   FROM   sender   address.   Used    by    the
+              Maps that specify the SASL login name that  owns  a
+              MAIL    FROM    sender   address.   Used   by   the
               <b>reject</b><i>_</i><b>sender</b><i>_</i><b>login</b><i>_</i><b>mismatch</b>  sender  anti-spoofing
               restriction.
 
-<b>Pass-through proxy</b>
-       Optionally, the Postfix SMTP server can be  configured  to
-       forward  all  mail  to a proxy server, for example a real-
-       time content filter. This proxy server should support  the
-       same  MAIL FROM and RCPT TO command syntax as Postfix, but
-       does not need to support ESMTP command pipelining.
-
-       <b>smtpd</b><i>_</i><b>proxy</b><i>_</i><b>filter</b>
-              The <i>host:port</i> of the SMTP proxy server. The <i>host</i> or
-              <i>host:</i> portion is optional.
-
-       <b>smtpd</b><i>_</i><b>proxy</b><i>_</i><b>timeout</b>
-              Timeout for connecting to, sending to and receiving
-              from the SMTP proxy server.
-
-       <b>smtpd</b><i>_</i><b>proxy</b><i>_</i><b>ehlo</b>
-              The hostname to use when sending an EHLO command to
-              the SMTP proxy server.
-
 <b>Miscellaneous</b>
        <b>authorized</b><i>_</i><b>verp</b><i>_</i><b>clients</b>
               Hostnames, domain names and/or addresses of clients
@@ -293,6 +294,25 @@ SMTPD(8)                                                 SMTPD(8)
               SMTP  session  before  it  is penalized with tarpit
               delays.
 
+<b>Delegated policy</b>
+       <b>smtpd</b><i>_</i><b>policy</b><i>_</i><b>service</b><i>_</i><b>endpoint</b>
+              The <i>transport</i>:<i>endpoint</i> of a server that speaks  the
+              delegated SMTP policy protocol. <i>transport</i> is either
+              <b>inet</b> or <b>unix</b>. <i>endpoint</i> is  either  <i>host:port</i>,  <b>pri-</b>
+              <b>vate/</b><i>servicename</i> or <b>public/</b><i>servicename</i>.
+
+       <b>smtpd</b><i>_</i><b>policy</b><i>_</i><b>service</b><i>_</i><b>timeout</b>
+              Time  limit  for  connecting  to,  writing  to  and
+              receiving from a delegated SMTP policy server.
+
+       <b>smtpd</b><i>_</i><b>policy</b><i>_</i><b>service</b><i>_</i><b>max</b><i>_</i><b>idle</b>
+              Time after which an unused policy  service  connec-
+              tion is closed.
+
+       <b>smtpd</b><i>_</i><b>policy</b><i>_</i><b>service</b><i>_</i><b>timeout</b>
+              Time  after  which an active policy service connec-
+              tion is closed.
+
 <b>UCE control restrictions</b>
        <b>parent</b><i>_</i><b>domain</b><i>_</i><b>matches</b><i>_</i><b>subdomains</b>
               List of Postfix features that use  <i>domain.tld</i>  pat-
index cf7d27797fd5c01bb928e7c2f8ed25ff086a21d0..498058fb1ff104cb26956919181dc1bca28ff51a 100644 (file)
@@ -20,7 +20,7 @@ SPAWN(8)                                                 SPAWN(8)
        This daemon expects to be run from the  <a href="master.8.html"><b>master</b>(8)</a>  process
        manager.
 
-<b>COMMAND</b> <b>ATTRIBUTE</b> <b>SYNTAX</b>
+<b>COMMAND ATTRIBUTE SYNTAX</b>
        The external command attributes are given in the <b>master.cf</b>
        file at the end of a service definition.  The syntax is as
        follows:
@@ -60,10 +60,10 @@ SPAWN(8)                                                 SPAWN(8)
        talk to the external command and thus is not vulnerable to
        data-driven attacks.
 
-<b>CONFIGURATION</b> <b>PARAMETERS</b>
+<b>CONFIGURATION PARAMETERS</b>
        The  following  <b>main.cf</b> parameters are especially relevant
        to this program. See the Postfix <b>main.cf</b> file  for  syntax
-       details  and  for  default  values. Use the <b>postfix</b> <b>reload</b>
+       details  and  for  default  values. Use the <b>postfix reload</b>
        command after a configuration change.
 
 <b>Miscellaneous</b>
@@ -75,7 +75,7 @@ SPAWN(8)                                                 SPAWN(8)
               The  process  privileges  used while not running an
               external command.
 
-<b>Resource</b> <b>control</b>
+<b>Resource control</b>
        <i>service_</i><b>command</b><i>_</i><b>time</b><i>_</i><b>limit</b>
               The amount of time the command is  allowed  to  run
               before it is killed with force. The <i>service</i> name is
@@ -83,7 +83,7 @@ SPAWN(8)                                                 SPAWN(8)
               default  time  limit  is  given  by the global <b>com-</b>
               <b>mand</b><i>_</i><b>time</b><i>_</i><b>limit</b> configuration parameter.
 
-<b>SEE</b> <b>ALSO</b>
+<b>SEE ALSO</b>
        <a href="master.8.html">master(8)</a> process manager
        syslogd(8) system logging
 
index cd248906cb992f4be6ddb9005d6b286636fff541..177f9ccf454a41d73fc0a4b62c9e49e98ad7abb7 100644 (file)
@@ -28,6 +28,8 @@ TCP_TABLE(5)                                         TCP_TABLE(5)
        terminated  by  the  ASCII  newline character. Request and
        reply parameters (see below) are separated by  whitespace.
 
+       Send  and receive operations must complete in 100 seconds.
+
 <b>REQUEST FORMAT</b>
        Each request specifies a command, a lookup key, and possi-
        bly a lookup result.
@@ -39,40 +41,40 @@ TCP_TABLE(5)                                         TCP_TABLE(5)
               This request is currently not implemented.
 
 <b>REPLY FORMAT</b>
-       Each reply specifies a status code and text. Replies  must
-       be  no  longer  than 4096 characters including the newline
+       Each  reply specifies a status code and text. Replies must
+       be no longer than 4096 characters  including  the  newline
        terminator.
 
        <b>500</b> SPACE <i>text</i> NEWLINE
-              In case of a lookup  request,  the  requested  data
-              does  not exist.  In case of an update request, the
+              In  case  of  a  lookup request, the requested data
+              does not exist.  In case of an update request,  the
               request  was  rejected.   The  text  describes  the
               nature of the problem.
 
        <b>400</b> SPACE <i>text</i> NEWLINE
-              This   indicates   an  error  condition.  The  text
-              describes the nature of  the  problem.  The  client
+              This  indicates  an  error  condition.   The   text
+              describes  the  nature  of  the problem. The client
               should retry the request later.
 
        <b>200</b> SPACE <i>text</i> NEWLINE
               The request was successful. In the case of a lookup
-              request, the text contains an  encoded  version  of
+              request,  the  text  contains an encoded version of
               the requested data.
 
 <b>ENCODING</b>
-       In  request  and  reply  parameters, the character %, each
+       In request and reply parameters,  the  character  %,  each
        non-printing character, and each whitespace character must
-       be  replaced  by  %XX, where XX is the corresponding ASCII
-       hexadecimal character value. The hexadecimal codes can  be
+       be replaced by %XX, where XX is  the  corresponding  ASCII
+       hexadecimal  character value. The hexadecimal codes can be
        specified in any case (upper, lower, mixed).
 
-       The  Postfix  client always encodes a request.  The server
-       may omit the encoding as long as the reply  is  guaranteed
+       The Postfix client always encodes a request.   The  server
+       may  omit  the encoding as long as the reply is guaranteed
        to not contain the % or NEWLINE character.
 
 <b>SECURITY</b>
-       Do  not  use  TCP lookup tables for security critical pur-
-       poses.  The client-server connection is not protected  and
+       Do not use TCP lookup tables for  security  critical  pur-
+       poses.   The client-server connection is not protected and
        the server is not authenticated.
 
 <b>SEE ALSO</b>
@@ -83,11 +85,11 @@ TCP_TABLE(5)                                         TCP_TABLE(5)
 <b>BUGS</b>
        Only the lookup method is currently implemented.
 
-       The  client  does  not hang up when the connection is idle
+       The client does not hang up when the  connection  is  idle
        for a long time.
 
 <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>
index 2ae2ea6af2d04e61d2243383fc014433505160b4..9630cb80ef993159ed9acb34bf842439c7edf9bc 100644 (file)
@@ -34,6 +34,8 @@ sends a request, and the server sends one reply. Requests and
 replies are sent as one line of ASCII text, terminated by the
 ASCII newline character. Request and reply parameters (see below)
 are separated by whitespace.
+
+Send and receive operations must complete in 100 seconds.
 .SH REQUEST FORMAT
 .na
 .nf
index c57d03413c8e95f166acac3e29bb8a0d7ef9b368..1f3ffe1987ed2b74045c5df970d27859eb3b7b2d 100644 (file)
@@ -19,9 +19,9 @@ The actual mail routing strategy is delegated to the
 This program expects to be run from the \fBmaster\fR(8) process
 manager.
 
-Mail addressed to the local \fBdouble-bounce\fR address is silently
-discarded.  This stops potential loops caused by undeliverable
-bounce notifications.
+Mail addressed to the local \fBdouble-bounce\fR address is
+logged and discarded.  This stops potential loops caused by
+undeliverable bounce notifications.
 .SH MAIL QUEUES
 .na
 .nf
index 039112b61ccfc54a8f838d08a0dde14decfb4694..529fb39892be50ccfcedd8d9c29be4f940533900 100644 (file)
@@ -19,9 +19,9 @@ The actual mail routing strategy is delegated to the
 This program expects to be run from the \fBmaster\fR(8) process
 manager.
 
-Mail addressed to the local \fBdouble-bounce\fR address is silently
-discarded.  This stops potential loops caused by undeliverable
-bounce notifications.
+Mail addressed to the local \fBdouble-bounce\fR address is
+logged and discarded.  This stops potential loops caused by
+undeliverable bounce notifications.
 .SH MAIL QUEUES
 .na
 .nf
index 10484ffac00c49248640eeb03d3b996a67b4733e..4f751864b110a38a2cb22704129344582fdefec2 100644 (file)
@@ -103,7 +103,7 @@ Hosts to hand off mail to if a message destination is not found
 or if a destination is unreachable.
 .IP \fBignore_mx_lookup_error\fR
 When a name server fails to respond to an MX query, search for an
-A record instead deferring mail delivery.
+A record, instead of deferring mail delivery.
 .IP \fBinet_interfaces\fR
 The network interface addresses that this mail system receives
 mail on. When any of those addresses appears in the list of mail
index b40558130c2cee96f3103bd270703f67c335e65c..f5a93985176ef50f85e2d3f5e36023a34afc78e3 100644 (file)
@@ -81,6 +81,8 @@ List of commands that are treated as NOOP (no operation) commands,
 without any parameter syntax checking and without any state change.
 This list overrides built-in command definitions.
 .SH "Content inspection controls"
+Optionally, Postfix can be configured to send new mail to
+external content filter software AFTER the mail is queued.
 .IP \fBcontent_filter\fR
 The name of a mail delivery transport that filters mail and that
 either bounces mail or re-injects the result back into Postfix.
@@ -103,6 +105,22 @@ content filter.
 Disable header/body_checks. This is typically specified with the
 SMTP server \fBafter\fR an external content filter.
 .RE
+.SH "Pass-through proxy"
+.ad
+.fi
+.ad
+Optionally, the Postfix SMTP server can be configured to
+forward all mail to a proxy server, for example a real-time
+content filter, BEFORE mail is queued.
+.IP \fBsmtpd_proxy_filter\fR
+The \fIhost:port\fR of the SMTP proxy server. The \fIhost\fR
+or \fIhost:\fR portion is optional.
+.IP \fBsmtpd_proxy_timeout\fR
+Timeout for connecting to, sending to and receiving from
+the SMTP proxy server.
+.IP \fBsmtpd_proxy_ehlo\fR
+The hostname to use when sending an EHLO command to the
+SMTP proxy server.
 .SH "Authentication controls"
 .IP \fBenable_sasl_authentication\fR
 Enable per-session authentication as per RFC 2554 (SASL).
@@ -128,24 +146,6 @@ Disallow anonymous logins.
 Maps that specify the SASL login name that owns a MAIL FROM sender
 address. Used by the \fBreject_sender_login_mismatch\fR sender
 anti-spoofing restriction.
-.SH "Pass-through proxy"
-.ad
-.fi
-.ad
-Optionally, the Postfix SMTP server can be configured to
-forward all mail to a proxy server, for example a real-time
-content filter. This proxy server should support the same
-MAIL FROM and RCPT TO command syntax as Postfix, but does not
-need to support ESMTP command pipelining.
-.IP \fBsmtpd_proxy_filter\fR
-The \fIhost:port\fR of the SMTP proxy server. The \fIhost\fR
-or \fIhost:\fR portion is optional.
-.IP \fBsmtpd_proxy_timeout\fR
-Timeout for connecting to, sending to and receiving from
-the SMTP proxy server.
-.IP \fBsmtpd_proxy_ehlo\fR
-The hostname to use when sending an EHLO command to the
-SMTP proxy server.
 .SH Miscellaneous
 .ad
 .fi
@@ -247,6 +247,22 @@ Disconnect after a client has made this number of errors.
 Limit the number of times a client can issue a junk command
 such as NOOP, VRFY, ETRN or RSET in one SMTP session before
 it is penalized with tarpit delays.
+.SH "Delegated policy"
+.ad
+.fi
+.IP \fBsmtpd_policy_service_endpoint\fR
+The \fItransport\fR:\fIendpoint\fR of a server that speaks
+the delegated SMTP policy protocol. \fItransport\fR is
+either \fBinet\fR or \fBunix\fR. \fIendpoint\fR is either
+\fIhost:port\fR, \fBprivate/\fIservicename\fR or
+\fBpublic/\fIservicename\fR.
+.IP \fBsmtpd_policy_service_timeout\fR
+Time limit for connecting to, writing to and receiving from
+a delegated SMTP policy server.
+.IP \fBsmtpd_policy_service_max_idle\fR
+Time after which an unused policy service connection is closed.
+.IP \fBsmtpd_policy_service_timeout\fR
+Time after which an active policy service connection is closed.
 .SH "UCE control restrictions"
 .ad
 .fi
index 54406626ae7429e7ca1da2dd1f90317cfef7e806..f009c54b48eaa9a989941c4f2e766992fce40fcd 100644 (file)
@@ -26,6 +26,8 @@
 #      replies are sent as one line of ASCII text, terminated by the
 #      ASCII newline character. Request and reply parameters (see below)
 #      are separated by whitespace.
+#
+#      Send and receive operations must complete in 100 seconds.
 # REQUEST FORMAT
 # .ad
 # .fi
index 4ff4fe2bd50f422492c65fdbe17c940e58dcf86f..5281296d95da39dfe404e51acae34f4fbaaa8273 100644 (file)
@@ -546,8 +546,12 @@ int     dns_lookup(const char *name, unsigned type, unsigned flags,
                vstring_sprintf(why, "Name service error for name=%s type=%s: "
                                "Malformed name server reply",
                                name, dns_strtype(type));
-       case DNS_NOTFOUND:
        case DNS_OK:
+           /* Don't return DNS_OK when all results are censored away. */
+           if (rrlist && *rrlist == 0)
+               msg_panic("dns_lookup: name=%s type=%s: success but no data",
+                         name, dns_strtype(type));
+       case DNS_NOTFOUND:
            return (status);
        case DNS_RECURSE:
            if (msg_verbose)
index 496625764b0ce03726965948dd7edf5fb48a2f81..463b5a3632f8e9b820218535aa702322d90a0603 100644 (file)
@@ -126,7 +126,7 @@ int     mail_copy(const char *sender,
     char   *myname = "mail_copy";
     VSTRING *buf;
     char   *bp;
-    long    orig_length;
+    off_t   orig_length;
     int     read_error;
     int     write_error;
     int     corrupt_error = 0;
@@ -139,7 +139,7 @@ int     mail_copy(const char *sender,
      */
 #ifndef NO_TRUNCATE
     if ((flags & MAIL_COPY_TOFILE) != 0)
-       if ((orig_length = vstream_fseek(dst, 0L, SEEK_END)) < 0)
+       if ((orig_length = vstream_fseek(dst, (off_t) 0, SEEK_END)) < 0)
            msg_fatal("seek file %s: %m", VSTREAM_PATH(dst));
 #endif
     buf = vstring_alloc(100);
@@ -249,7 +249,7 @@ int     mail_copy(const char *sender,
 #ifndef NO_TRUNCATE
     if ((flags & MAIL_COPY_TOFILE) != 0)
        if (corrupt_error || read_error || write_error)
-           ftruncate(vstream_fileno(dst), (off_t) orig_length);
+           ftruncate(vstream_fileno(dst), orig_length);
 #endif
     write_error |= vstream_fclose(dst);
     if (why && read_error)
index c36fe78a818fda073ea5ee39f30c01ecb802935b..bd96bcd966d44a64721aafa4204b61825470afa6 100644 (file)
@@ -1850,6 +1850,27 @@ extern int var_smtpd_proxy_tmout;
 #define DEF_INPUT_TRANSP               ""
 extern char *var_smtpd_input_transp;
 
+ /*
+  * SMTP server policy delegation.
+  */
+#define VAR_SMTPD_POLICY_SRV           "smtpd_policy_service_endpoint"
+#define DEF_SMTPD_POLICY_SRV           ""
+extern char *var_smtpd_policy_srv;
+
+#define VAR_SMTPD_POLICY_TMOUT         "smtpd_policy_service_timeout"
+#define DEF_SMTPD_POLICY_TMOUT         "100s"
+extern int var_smtpd_policy_tmout;
+
+#define VAR_SMTPD_POLICY_IDLE          "smtpd_policy_service_max_idle"
+#define DEF_SMTPD_POLICY_IDLE          "300s"
+extern int var_smtpd_policy_idle;
+
+#define VAR_SMTPD_POLICY_TTL           "smtpd_policy_service_max_ttl"
+#define DEF_SMTPD_POLICY_TTL           "1000s"
+extern int var_smtpd_policy_ttl;
+
+#define CHECK_POLICY_SERVICE           "check_smtpd_policy_service"
+
 /* LICENSE
 /* .ad
 /* .fi
index 5c0b972a8a5b28021ed4c9bef39d5a2e1dd78fad..f9b390b0b1379d3cab4759056ca610b2e23242aa 100644 (file)
   * 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      "20030706"
+#define MAIL_RELEASE_DATE      "20030712"
 
 #define VAR_MAIL_VERSION       "mail_version"
-#define DEF_MAIL_VERSION       "2.0.13-" MAIL_RELEASE_DATE
+#define DEF_MAIL_VERSION       "2.0.14-" MAIL_RELEASE_DATE
 extern char *var_mail_version;
 
  /*
index e17d7b0aa4e35504533e4f6b60f4754f941631c2..808811baec25dc088dbc91a22f329f7b183432ad 100644 (file)
@@ -226,6 +226,7 @@ int     lmtp_lhlo(LMTP_STATE *state)
      * LMTP server. Otherwise, we might do the wrong thing when the server
      * advertises a really huge message size limit.
      */
+    state->features = 0;
     lines = resp->str;
     (void) mystrtok(&lines, "\n");
     while ((words = mystrtok(&lines, "\n")) != 0) {
index 1054d688c58b1f8613a58febf9f02d37026b2243..c1e7a5bd8f8a39e831536a59f90678a60ddd4e92 100644 (file)
@@ -14,7 +14,7 @@
 extern void lmtp_sasl_initialize(void);
 extern void lmtp_sasl_connect(LMTP_STATE *);
 extern int lmtp_sasl_passwd_lookup(LMTP_STATE *);
-extern void lmtp_sasl_start(LMTP_STATE *);
+extern void lmtp_sasl_start(LMTP_STATE *, const char *, const char *);
 extern int lmtp_sasl_authenticate(LMTP_STATE *, VSTRING *);
 extern void lmtp_sasl_cleanup(LMTP_STATE *);
 
index 60e622fcabddc69557f23cd1116cc2a51cad49c7..ca11019ca9c1d1b40a5d12a04385fdbda9ec05bb 100644 (file)
@@ -11,7 +11,7 @@
 /*     void    lmtp_sasl_connect(state)
 /*     LMTP_STATE *state;
 /*
-/*     void    lmtp_sasl_start(state)
+/*     void    lmtp_sasl_start(state, sasl_opts_name, sasl_opts_var)
 /*     LMTP_STATE *state;
 /*
 /*     int     lmtp_sasl_passwd_lookup(state)
@@ -33,7 +33,9 @@
 /*
 /*     lmtp_sasl_start() performs per-session initialization. This
 /*     routine must be called once per session before doing any SASL
-/*     authentication.
+/*     authentication. The sasl_opts_name and sasl_opts_var parameters are
+/*     the postfix configuration parameters setting the security
+/*     policy of the SASL authentication.
 /*
 /*     lmtp_sasl_passwd_lookup() looks up the username/password
 /*     for the current SMTP server. The result is zero in case
@@ -122,8 +124,6 @@ static NAME_MASK lmtp_sasl_sec_mask[] = {
     0,
 };
 
-static int lmtp_sasl_sec_opts;
-
  /*
   * Silly little macros.
   */
@@ -319,11 +319,6 @@ void    lmtp_sasl_initialize(void)
     if (sasl_client_init(callbacks) != SASL_OK)
        msg_fatal("SASL library initialization");
 
-    /*
-     * Configuration parameters.
-     */
-    lmtp_sasl_sec_opts = name_mask(VAR_LMTP_SASL_OPTS, lmtp_sasl_sec_mask,
-                                  var_lmtp_sasl_opts);
 }
 
 /* lmtp_sasl_connect - per-session client initialization */
@@ -341,7 +336,8 @@ void    lmtp_sasl_connect(LMTP_STATE *state)
 
 /* lmtp_sasl_start - per-session SASL initialization */
 
-void    lmtp_sasl_start(LMTP_STATE *state)
+void    lmtp_sasl_start(LMTP_STATE *state, const char *sasl_opts_name,
+                               const char *sasl_opts_var)
 {
     static sasl_callback_t callbacks[] = {
        {SASL_CB_USER, &lmtp_sasl_get_user, 0},
@@ -383,7 +379,8 @@ void    lmtp_sasl_start(LMTP_STATE *state)
     sec_props.min_ssf = 0;
     sec_props.max_ssf = 1;                     /* don't allow real SASL
                                                 * security layer */
-    sec_props.security_flags = lmtp_sasl_sec_opts;
+    sec_props.security_flags = name_mask(sasl_opts_name, lmtp_sasl_sec_mask,
+                                        sasl_opts_var);
     sec_props.maxbufsize = 0;
     sec_props.property_names = 0;
     sec_props.property_values = 0;
index b8c280c019c92b385f3c08f5917e174774d3e193..e9fc09a2cdb6b77f3e464150d4ccc58961200d7b 100644 (file)
@@ -114,7 +114,7 @@ int     lmtp_sasl_helo_login(LMTP_STATE *state)
      * required, and assume that an authentication error is recoverable.
      */
     if (lmtp_sasl_passwd_lookup(state) != 0) {
-       lmtp_sasl_start(state);
+       lmtp_sasl_start(state, VAR_LMTP_SASL_OPTS, var_lmtp_sasl_opts);
        if (lmtp_sasl_authenticate(state, why) <= 0)
            ret = lmtp_site_fail(state, 450, "Authentication failed: %s",
                                 vstring_str(why));
index 1c37cdef4d6a5ccc74a52eb1335fa2a39629c756..36ebef0172f48c6779ce9e088910f90d08bf940c 100644 (file)
@@ -13,9 +13,9 @@
 /*     This program expects to be run from the \fBmaster\fR(8) process
 /*     manager.
 /*
-/*     Mail addressed to the local \fBdouble-bounce\fR address is silently
-/*     discarded.  This stops potential loops caused by undeliverable
-/*     bounce notifications.
+/*     Mail addressed to the local \fBdouble-bounce\fR address is
+/*     logged and discarded.  This stops potential loops caused by
+/*     undeliverable bounce notifications.
 /* MAIL QUEUES
 /* .ad
 /* .fi
index 4ff798eb71df821fef2a14ae228d98c9386e99b6..7e899d4c9b611812801f80d5a360dce6ca409d44 100644 (file)
@@ -13,9 +13,9 @@
 /*     This program expects to be run from the \fBmaster\fR(8) process
 /*     manager.
 /*
-/*     Mail addressed to the local \fBdouble-bounce\fR address is silently
-/*     discarded.  This stops potential loops caused by undeliverable
-/*     bounce notifications.
+/*     Mail addressed to the local \fBdouble-bounce\fR address is
+/*     logged and discarded.  This stops potential loops caused by
+/*     undeliverable bounce notifications.
 /* MAIL QUEUES
 /* .ad
 /* .fi
index 4c561a1afa107591317334ed0019e03afb59aa59..e514f396fcda878435c3a7e34ce1b27ea4bb1c1d 100644 (file)
@@ -254,8 +254,8 @@ static void showq_service(VSTREAM *client, char *unused_service, char **argv)
 
     static struct queue_info queue_info[] = {
        MAIL_QUEUE_MAILDROP, scan_dir_next,
-       MAIL_QUEUE_INCOMING, mail_scan_dir_next,
        MAIL_QUEUE_ACTIVE, mail_scan_dir_next,
+       MAIL_QUEUE_INCOMING, mail_scan_dir_next,
        MAIL_QUEUE_DEFERRED, mail_scan_dir_next,
        MAIL_QUEUE_HOLD, mail_scan_dir_next,
        0,
index 531878753fc61db6ce7273c0691ae14eb55090f4..cd48b8bf778f056cb56876104aa4faf58d49c304 100644 (file)
@@ -87,7 +87,7 @@
 /*     or if a destination is unreachable.
 /* .IP \fBignore_mx_lookup_error\fR
 /*     When a name server fails to respond to an MX query, search for an
-/*     A record instead deferring mail delivery.
+/*     A record, instead of deferring mail delivery.
 /* .IP \fBinet_interfaces\fR
 /*     The network interface addresses that this mail system receives
 /*     mail on. When any of those addresses appears in the list of mail
index 43c721006e7ad81b395e9854af07a971bfb64310..35f9e1c4d2b152c1120ff5bcd85ebd2b2c21af23 100644 (file)
@@ -14,7 +14,7 @@
 extern void smtp_sasl_initialize(void);
 extern void smtp_sasl_connect(SMTP_STATE *);
 extern int smtp_sasl_passwd_lookup(SMTP_STATE *);
-extern void smtp_sasl_start(SMTP_STATE *);
+extern void smtp_sasl_start(SMTP_STATE *, const char *, const char *);
 extern int smtp_sasl_authenticate(SMTP_STATE *, VSTRING *);
 extern void smtp_sasl_cleanup(SMTP_STATE *);
 
index a1a7c9952d3c15d3ffebc72a37bc4f5db34a5222..d357d85b06606bc94389c8216c9aa5ea65ba3c99 100644 (file)
@@ -11,7 +11,7 @@
 /*     void    smtp_sasl_connect(state)
 /*     SMTP_STATE *state;
 /*
-/*     void    smtp_sasl_start(state)
+/*     void    smtp_sasl_start(state, sasl_opts_name, sasl_opts_var)
 /*     SMTP_STATE *state;
 /*
 /*     int     smtp_sasl_passwd_lookup(state)
@@ -33,7 +33,9 @@
 /*
 /*     smtp_sasl_start() performs per-session initialization. This
 /*     routine must be called once per session before doing any SASL
-/*     authentication.
+/*     authentication. The sasl_opts_name and sasl_opts_var parameters are
+/*     the postfix configuration parameters setting the security
+/*     policy of the SASL authentication.
 /*
 /*     smtp_sasl_passwd_lookup() looks up the username/password
 /*     for the current SMTP server. The result is zero in case
@@ -122,8 +124,6 @@ static NAME_MASK smtp_sasl_sec_mask[] = {
     0,
 };
 
-static int smtp_sasl_sec_opts;
-
  /*
   * Silly little macros.
   */
@@ -319,11 +319,6 @@ void    smtp_sasl_initialize(void)
     if (sasl_client_init(callbacks) != SASL_OK)
        msg_fatal("SASL library initialization");
 
-    /*
-     * Configuration parameters.
-     */
-    smtp_sasl_sec_opts = name_mask(VAR_SMTP_SASL_OPTS, smtp_sasl_sec_mask,
-                                  var_smtp_sasl_opts);
 }
 
 /* smtp_sasl_connect - per-session client initialization */
@@ -341,7 +336,8 @@ void    smtp_sasl_connect(SMTP_STATE *state)
 
 /* smtp_sasl_start - per-session SASL initialization */
 
-void    smtp_sasl_start(SMTP_STATE *state)
+void    smtp_sasl_start(SMTP_STATE *state, const char *sasl_opts_name,
+                               const char *sasl_opts_var)
 {
     static sasl_callback_t callbacks[] = {
        {SASL_CB_USER, &smtp_sasl_get_user, 0},
@@ -383,7 +379,8 @@ void    smtp_sasl_start(SMTP_STATE *state)
     sec_props.min_ssf = 0;
     sec_props.max_ssf = 1;                     /* don't allow real SASL
                                                 * security layer */
-    sec_props.security_flags = smtp_sasl_sec_opts;
+    sec_props.security_flags = name_mask(sasl_opts_name, smtp_sasl_sec_mask,
+                                        sasl_opts_var);
     sec_props.maxbufsize = 0;
     sec_props.property_names = 0;
     sec_props.property_values = 0;
index 97c2592c02eb3cec0542eb290f2fae3819c72ad1..30cf0ab58e52de674919b611762b3616c913bd39 100644 (file)
@@ -114,7 +114,7 @@ int     smtp_sasl_helo_login(SMTP_STATE *state)
      * required, and assume that an authentication error is recoverable.
      */
     if (smtp_sasl_passwd_lookup(state) != 0) {
-       smtp_sasl_start(state);
+       smtp_sasl_start(state, VAR_SMTP_SASL_OPTS, var_smtp_sasl_opts);
        if (smtp_sasl_authenticate(state, why) <= 0)
            ret = smtp_site_fail(state, 450, "Authentication failed: %s",
                                 vstring_str(why));
index ec72a986e8c6d7b7513f42b9720888baf87369b5..e6b6a31b85cb56afd57d610428b96b560d1c3432 100644 (file)
@@ -67,6 +67,8 @@
 /*     without any parameter syntax checking and without any state change.
 /*     This list overrides built-in command definitions.
 /* .SH "Content inspection controls"
+/*     Optionally, Postfix can be configured to send new mail to 
+/*     external content filter software AFTER the mail is queued.
 /* .IP \fBcontent_filter\fR
 /*     The name of a mail delivery transport that filters mail and that
 /*     either bounces mail or re-injects the result back into Postfix.
 /*     Disable header/body_checks. This is typically specified with the
 /*     SMTP server \fBafter\fR an external content filter.
 /* .RE
+/* .SH "Pass-through proxy"
+/* .ad
+/* .fi
+/* .ad
+/*     Optionally, the Postfix SMTP server can be configured to
+/*     forward all mail to a proxy server, for example a real-time
+/*     content filter, BEFORE mail is queued.
+/* .IP \fBsmtpd_proxy_filter\fR
+/*     The \fIhost:port\fR of the SMTP proxy server. The \fIhost\fR
+/*     or \fIhost:\fR portion is optional.
+/* .IP \fBsmtpd_proxy_timeout\fR
+/*     Timeout for connecting to, sending to and receiving from
+/*     the SMTP proxy server.
+/* .IP \fBsmtpd_proxy_ehlo\fR
+/*     The hostname to use when sending an EHLO command to the
+/*     SMTP proxy server.
 /* .SH "Authentication controls"
 /* .IP \fBenable_sasl_authentication\fR
 /*     Enable per-session authentication as per RFC 2554 (SASL).
 /*     Maps that specify the SASL login name that owns a MAIL FROM sender
 /*     address. Used by the \fBreject_sender_login_mismatch\fR sender
 /*     anti-spoofing restriction.
-/* .SH "Pass-through proxy"
-/* .ad
-/* .fi
-/* .ad
-/*     Optionally, the Postfix SMTP server can be configured to
-/*     forward all mail to a proxy server, for example a real-time
-/*     content filter. This proxy server should support the same
-/*     MAIL FROM and RCPT TO command syntax as Postfix, but does not
-/*     need to support ESMTP command pipelining.
-/* .IP \fBsmtpd_proxy_filter\fR
-/*     The \fIhost:port\fR of the SMTP proxy server. The \fIhost\fR
-/*     or \fIhost:\fR portion is optional.
-/* .IP \fBsmtpd_proxy_timeout\fR
-/*     Timeout for connecting to, sending to and receiving from
-/*     the SMTP proxy server.
-/* .IP \fBsmtpd_proxy_ehlo\fR
-/*     The hostname to use when sending an EHLO command to the
-/*     SMTP proxy server.
 /* .SH Miscellaneous
 /* .ad
 /* .fi
 /*     Limit the number of times a client can issue a junk command
 /*     such as NOOP, VRFY, ETRN or RSET in one SMTP session before
 /*     it is penalized with tarpit delays.
+/* .SH "Delegated policy"
+/* .ad
+/* .fi
+/* .IP \fBsmtpd_policy_service_endpoint\fR
+/*     The \fItransport\fR:\fIendpoint\fR of a server that speaks
+/*     the delegated SMTP policy protocol. \fItransport\fR is
+/*     either \fBinet\fR or \fBunix\fR. \fIendpoint\fR is either
+/*     \fIhost:port\fR, \fBprivate/\fIservicename\fR or
+/*     \fBpublic/\fIservicename\fR.
+/* .IP \fBsmtpd_policy_service_timeout\fR
+/*     Time limit for connecting to, writing to and receiving from
+/*     a delegated SMTP policy server.
+/* .IP \fBsmtpd_policy_service_max_idle\fR
+/*     Time after which an unused policy service connection is closed.
+/* .IP \fBsmtpd_policy_service_timeout\fR
+/*     Time after which an active policy service connection is closed.
 /* .SH "UCE control restrictions"
 /* .ad
 /* .fi
@@ -506,6 +522,10 @@ char   *var_smtpd_proxy_filt;
 int     var_smtpd_proxy_tmout;
 char   *var_smtpd_proxy_ehlo;
 char   *var_input_transp;
+char   *var_smtpd_policy_srv;
+int     var_smtpd_policy_tmout;
+int     var_smtpd_policy_idle;
+int     var_smtpd_policy_ttl;
 
  /*
   * Silly little macros.
@@ -1860,6 +1880,9 @@ int     main(int argc, char **argv)
        VAR_SMTPD_ERR_SLEEP, DEF_SMTPD_ERR_SLEEP, &var_smtpd_err_sleep, 0, 0,
        VAR_SMTPD_PROXY_TMOUT, DEF_SMTPD_PROXY_TMOUT, &var_smtpd_proxy_tmout, 1, 0,
        VAR_VERIFY_POLL_DELAY, DEF_VERIFY_POLL_DELAY, &var_verify_poll_delay, 1, 0,
+       VAR_SMTPD_POLICY_TMOUT, DEF_SMTPD_POLICY_TMOUT, &var_smtpd_policy_tmout, 1, 0,
+       VAR_SMTPD_POLICY_IDLE, DEF_SMTPD_POLICY_IDLE, &var_smtpd_policy_idle, 1, 0,
+       VAR_SMTPD_POLICY_TTL, DEF_SMTPD_POLICY_TTL, &var_smtpd_policy_ttl, 1, 0,
        0,
     };
     static CONFIG_BOOL_TABLE bool_table[] = {
@@ -1905,6 +1928,7 @@ int     main(int argc, char **argv)
        VAR_SMTPD_PROXY_FILT, DEF_SMTPD_PROXY_FILT, &var_smtpd_proxy_filt, 0, 0,
        VAR_SMTPD_PROXY_EHLO, DEF_SMTPD_PROXY_EHLO, &var_smtpd_proxy_ehlo, 0, 0,
        VAR_INPUT_TRANSP, DEF_INPUT_TRANSP, &var_input_transp, 0, 0,
+       VAR_SMTPD_POLICY_SRV, DEF_SMTPD_POLICY_SRV, &var_smtpd_policy_srv, 0, 0,
        0,
     };
     static CONFIG_RAW_TABLE raw_table[] = {
index 888055c9fa4e4ead7998e4fbe16ba96241884153..8cc4e5ab51bdea72b9940938782d45e79739b845 100644 (file)
@@ -119,14 +119,14 @@ extern void smtpd_state_reset(SMTPD_STATE *);
  /*
   * SMPTD peer information lookup.
   */
-void    smtpd_peer_init(SMTPD_STATE *state);
-void    smtpd_peer_reset(SMTPD_STATE *state);
+extern void smtpd_peer_init(SMTPD_STATE *state);
+extern void smtpd_peer_reset(SMTPD_STATE *state);
 
  /*
   * Transparency: before mail is queued, do we check for unknown recipients,
   * do we allow address mapping, automatic bcc, header/body checks?
   */
-int     smtpd_input_transp_mask;
+extern int smtpd_input_transp_mask;
 
 /* LICENSE
 /* .ad
index 0eaa39c0c2f6203cce3b667644bf6fec74c2d0c5..7d87b06e8a0307629edb6f7e87376ae396440df8 100644 (file)
@@ -57,6 +57,9 @@
 /*     Reject, defer or permit the request unconditionally. This is to be used
 /*     at the end of a restriction list in order to make the default
 /*     action explicit.
+/* .IP check_policy_service
+/*     query an external policy service with client, helo, sender, recipient
+/*     and queue ID attributes.
 /* .IP reject_unknown_client
 /*     Reject the request when the client hostname could not be found.
 /*     The \fIunknown_client_reject_code\fR configuration parameter
 /*     The message would use up more than half the available queue file
 /*     system space. This is a temporary error.
 /* .PP
+/*     smtpd_check_data() enforces generic restrictions after the
+/*     client has sent the DATA command.
+/*
 /*     Arguments:
 /* .IP name
 /*     The client hostname, or \fIunknown\fR.
 #include <htable.h>
 #include <ctable.h>
 #include <mac_expand.h>
+#include <myrand.h>
+#include <attr_clnt.h>
 
 /* DNS library. */
 
@@ -415,6 +423,11 @@ static int generic_checks(SMTPD_STATE *, ARGV *, const char *, const char *, con
   */
 static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient);
 
+ /*
+  * Delegated policy.
+  */
+static ATTR_CLNT *policy_clnt;
+
  /*
   * Reject context.
   */
@@ -478,7 +491,8 @@ static void PRINTFLIKE(3, 4) defer_if(SMTPD_DEFER *, int, const char *,...);
   * Cached RBL lookup state.
   */
 typedef struct {
-    char   *txt;                       /* TXT record or "" */
+    ARGV   *txt;                       /* TXT records or NULL */
+    ARGV   *a;                         /* A records */
 } SMTPD_RBL_STATE;
 
 static void *rbl_pagein(const char *, void *);
@@ -489,10 +503,10 @@ static void rbl_pageout(void *, void *);
   */
 typedef struct {
     SMTPD_STATE *state;                        /* general state */
-    SMTPD_RBL_STATE *rbl_state;                /* cached RBL state */
     const char *domain;                        /* query domain */
     const char *what;                  /* rejected value */
     const char *class;                 /* name of rejected value */
+    const char *txt;                   /* randomly selected trimmed TXT rr */
 } SMTPD_RBL_EXPAND_CONTEXT;
 
 /* resolve_pagein - page in an address resolver result */
@@ -750,6 +764,15 @@ void    smtpd_check_init(void)
      */
     expand_filter = vstring_alloc(10);
     unescape(expand_filter, var_smtpd_exp_filter);
+
+    /*
+     * Delegated policy.
+     */
+    if (*var_smtpd_policy_srv)
+       policy_clnt = attr_clnt_create(var_smtpd_policy_srv,
+                                      var_smtpd_policy_tmout,
+                                      var_smtpd_policy_idle,
+                                      var_smtpd_policy_ttl);
 }
 
 /* log_whatsup - log as much context as we have */
@@ -2357,6 +2380,9 @@ static void *rbl_pagein(const char *query, void *unused_context)
     VSTRING *why;
     int     dns_status;
     SMTPD_RBL_STATE *rbl;
+    DNS_RR *addr_list;
+    struct in_addr addr;
+    DNS_RR *rr;
 
     /*
      * Do the query. If the DNS lookup produces no definitive reply, give the
@@ -2364,8 +2390,7 @@ static void *rbl_pagein(const char *query, void *unused_context)
      * because an RBL server is unavailable.
      */
     why = vstring_alloc(10);
-    dns_status = dns_lookup(query, T_A, 0, (DNS_RR **) 0,
-                           (VSTRING *) 0, why);
+    dns_status = dns_lookup(query, T_A, 0, &addr_list, (VSTRING *) 0, why);
     if (dns_status != DNS_OK && dns_status != DNS_NOTFOUND)
        msg_warn("%s: RBL lookup error: %s", query, STR(why));
     vstring_free(why);
@@ -2376,13 +2401,24 @@ static void *rbl_pagein(const char *query, void *unused_context)
      * Save the result. Yes, we cache negative results as well as positive
      * results.
      */
+#define RBL_TXT_LIMIT  256
+
     rbl = (SMTPD_RBL_STATE *) mymalloc(sizeof(*rbl));
     if (dns_lookup(query, T_TXT, 0, &txt_list,
                   (VSTRING *) 0, (VSTRING *) 0) == DNS_OK) {
-       rbl->txt = mystrndup(txt_list->data, 512);
+       rbl->txt = argv_alloc(1);
+       for (rr = txt_list; rr != 0; rr = rr->next)
+           argv_addn(rbl->txt, rr->data, rr->data_len > RBL_TXT_LIMIT ?
+                     RBL_TXT_LIMIT : rr->data_len, ARGV_END);
        dns_rr_free(txt_list);
     } else
-       rbl->txt = mystrdup("");
+       rbl->txt = 0;
+    rbl->a = argv_alloc(1);
+    for (rr = addr_list; rr != 0; rr = rr->next) {
+       memcpy((char *) &addr.s_addr, addr_list->data, sizeof(addr.s_addr));
+       argv_add(rbl->a, inet_ntoa(addr), ARGV_END);
+    }
+    dns_rr_free(addr_list);
     return ((void *) rbl);
 }
 
@@ -2394,7 +2430,9 @@ static void rbl_pageout(void *data, void *unused_context)
 
     if (rbl != 0) {
        if (rbl->txt)
-           myfree(rbl->txt);
+           argv_free(rbl->txt);
+       if (rbl->a)
+           argv_free(rbl->a);
        myfree((char *) rbl);
     }
 }
@@ -2405,7 +2443,6 @@ static const char *rbl_expand_lookup(const char *name, int mode,
                                             char *context)
 {
     SMTPD_RBL_EXPAND_CONTEXT *rbl_exp = (SMTPD_RBL_EXPAND_CONTEXT *) context;
-    SMTPD_RBL_STATE *rbl = rbl_exp->rbl_state;
     SMTPD_STATE *state = rbl_exp->state;
 
     if (state->expand_buf == 0)
@@ -2423,9 +2460,9 @@ static const char *rbl_expand_lookup(const char *name, int mode,
     } else if (STREQ(name, MAIL_ATTR_RBL_DOMAIN)) {
        return (rbl_exp->domain);
     } else if (STREQ(name, MAIL_ATTR_RBL_REASON)) {
-       return (rbl->txt);
+       return (rbl_exp->txt);
     } else if (STREQ(name, MAIL_ATTR_RBL_TXT)) {/* LaMont compat */
-       return (rbl->txt);
+       return (rbl_exp->txt);
     } else if (STREQ(name, MAIL_ATTR_RBL_WHAT)) {
        return (rbl_exp->what);
     } else if (STREQ(name, MAIL_ATTR_RBL_CLASS)) {
@@ -2459,10 +2496,20 @@ static int rbl_reject_reply(SMTPD_STATE *state, SMTPD_RBL_STATE *rbl,
     }
     why = vstring_alloc(100);
     rbl_exp.state = state;
-    rbl_exp.rbl_state = rbl;
     rbl_exp.domain = rbl_domain;
     rbl_exp.what = what;
     rbl_exp.class = reply_class;
+
+    /*
+     * XXX When presented with multiple txt records pick a random one! There
+     * is no way to pair up the A records with associated TXT records. If a
+     * client connects multiple times, it will learn the right reject reason
+     * at least some of the time.
+     */
+    rbl_exp.txt = mystrdup(rbl->txt == 0 ? "" :
+                          rbl->txt->argc == 1 ? rbl->txt->argv[0] :
+                          rbl->txt->argv[myrand() % rbl->txt->argc]);
+
     for (;;) {
        if (template == 0)
            template = var_def_rbl_reply;
@@ -2483,10 +2530,23 @@ static int rbl_reject_reply(SMTPD_STATE *state, SMTPD_RBL_STATE *rbl,
      * Clean up.
      */
     vstring_free(why);
+    myfree((char *) rbl_exp.txt);
 
     return (result);
 }
 
+/* rbl_match_addr - match address list */
+
+static int rbl_match_addr(SMTPD_RBL_STATE *rbl, const char *addr)
+{
+    char  **cpp;
+
+    for (cpp = rbl->a->argv; *cpp; cpp++)
+       if (strcmp(*cpp, addr) == 0)
+           return (1);
+    return (0);
+}
+
 /* reject_rbl_addr - reject if address in real-time blackhole list */
 
 static int reject_rbl_addr(SMTPD_STATE *state, const char *rbl_domain,
@@ -2497,6 +2557,7 @@ static int reject_rbl_addr(SMTPD_STATE *state, const char *rbl_domain,
     VSTRING *query;
     int     i;
     SMTPD_RBL_STATE *rbl;
+    const char *reply_addr;
 
     if (msg_verbose)
        msg_info("%s: %s %s", myname, reply_class, addr);
@@ -2521,15 +2582,17 @@ static int reject_rbl_addr(SMTPD_STATE *state, const char *rbl_domain,
     }
     argv_free(octets);
     vstring_strcat(query, rbl_domain);
+    reply_addr = split_at(STR(query), '=');
     rbl = (SMTPD_RBL_STATE *) ctable_locate(smtpd_rbl_cache, STR(query));
-    vstring_free(query);
 
     /*
      * If the record exists, the address is blacklisted.
      */
-    if (rbl == 0) {
+    if (rbl == 0 || (reply_addr != 0 && !rbl_match_addr(rbl, reply_addr))) {
+       vstring_free(query);
        return (SMTPD_CHECK_DUNNO);
     } else {
+       vstring_free(query);
        return (rbl_reject_reply(state, rbl, rbl_domain, addr, reply_class));
     }
 }
@@ -2543,6 +2606,7 @@ static int reject_rbl_domain(SMTPD_STATE *state, const char *rbl_domain,
     VSTRING *query;
     SMTPD_RBL_STATE *rbl;
     const char *domain;
+    const char *reply_addr;
 
     if (msg_verbose)
        msg_info("%s: %s %s", myname, reply_class, what);
@@ -2562,15 +2626,17 @@ static int reject_rbl_domain(SMTPD_STATE *state, const char *rbl_domain,
 
     query = vstring_alloc(100);
     vstring_sprintf(query, "%s.%s", domain, rbl_domain);
+    reply_addr = split_at(STR(query), '=');
     rbl = (SMTPD_RBL_STATE *) ctable_locate(smtpd_rbl_cache, STR(query));
-    vstring_free(query);
 
     /*
      * If the record exists, the domain is blacklisted.
      */
-    if (rbl == 0) {
+    if (rbl == 0 || (reply_addr != 0 && !rbl_match_addr(rbl, reply_addr))) {
+       vstring_free(query);
        return (SMTPD_CHECK_DUNNO);
     } else {
+       vstring_free(query);
        return (rbl_reject_reply(state, rbl, rbl_domain, what, reply_class));
     }
 }
@@ -2646,6 +2712,59 @@ static int reject_sender_login_mismatch(SMTPD_STATE *state, const char *sender)
     return (SMTPD_CHECK_DUNNO);
 }
 
+/* check_policy_service - check delegated policy service */
+
+static int check_policy_service(SMTPD_STATE *state, const char *reply_name,
+                              const char *reply_class, const char *def_acl)
+{
+    static VSTRING *action = 0;
+
+    /*
+     * Sanity check.
+     */
+    if (policy_clnt == 0) {
+       msg_warn("%s is used in %s restrictions, but no service is defined",
+                CHECK_POLICY_SERVICE, reply_class);
+       return (SMTPD_CHECK_DUNNO);
+    }
+
+    /*
+     * Initialize.
+     */
+    if (action == 0)
+       action = vstring_alloc(10);
+
+    if (attr_clnt_request(policy_clnt,
+                         ATTR_FLAG_NONE,       /* Query attributes. */
+                         ATTR_TYPE_STR, MAIL_ATTR_CLIENT, state->namaddr,
+                         ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, state->addr,
+                         ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, state->name,
+                         ATTR_TYPE_STR, MAIL_ATTR_HELO_NAME,
+                         state->helo_name ? state->helo_name : "",
+                         ATTR_TYPE_STR, MAIL_ATTR_SENDER,
+                         state->sender ? state->sender : "",
+                         ATTR_TYPE_STR, MAIL_ATTR_RECIP,
+                         state->recipient ? state->recipient : "",
+                         ATTR_TYPE_STR, MAIL_ATTR_QUEUEID,
+                         state->queue_id ? state->queue_id : "",
+                         ATTR_TYPE_END,
+                         ATTR_FLAG_MISSING,    /* Reply attributes. */
+                         ATTR_TYPE_STR, MAIL_ATTR_ACTION, action,
+                         ATTR_TYPE_END) != 1) {
+       return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
+                                  "450 Server configuration problem"));
+    } else {
+
+       /*
+        * XXX This produces bogus error messages when the reply is
+        * malformed.
+        */
+       return (check_table_result(state, var_smtpd_policy_srv, STR(action),
+                                  "policy query", reply_name,
+                                  reply_class, def_acl));
+    }
+}
+
 /* is_map_command - restriction has form: check_xxx_access type:name */
 
 static int is_map_command(SMTPD_STATE *state, const char *name,
@@ -2747,6 +2866,9 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
                         cpp[1], REJECT_ALL);
        } else if (strcasecmp(name, REJECT_UNAUTH_PIPE) == 0) {
            status = reject_unauth_pipelining(state, reply_name, reply_class);
+       } else if (strcasecmp(name, CHECK_POLICY_SERVICE) == 0) {
+           status = check_policy_service(state, reply_name,
+                                         reply_class, def_acl);
        } else if (strcasecmp(name, DEFER_IF_PERMIT) == 0) {
            DEFER_IF_PERMIT2(state, MAIL_ERROR_POLICY,
                         "450 <%s>: %s rejected: defer_if_permit requested",
@@ -3524,6 +3646,8 @@ char   *smtpd_check_data(SMTPD_STATE *state)
 
 #include <smtpd_chat.h>
 
+int     smtpd_input_transp_mask;
+
  /*
   * Dummies. These are never set.
   */
@@ -3564,6 +3688,8 @@ char   *var_smtpd_exp_filter;
 char   *var_def_rbl_reply;
 char   *var_relay_rcpt_maps;
 char   *var_verify_sender;
+char   *var_smtpd_sasl_opts;
+char   *var_smtpd_policy_srv;
 
 typedef struct {
     char   *name;
@@ -3603,6 +3729,8 @@ static STRING_TABLE string_table[] = {
     VAR_RELAY_RCPT_MAPS, DEF_RELAY_RCPT_MAPS, &var_relay_rcpt_maps,
     VAR_VERIFY_SENDER, DEF_VERIFY_SENDER, &var_verify_sender,
     VAR_MAIL_NAME, DEF_MAIL_NAME, &var_mail_name,
+    VAR_SMTPD_SASL_OPTS, DEF_SMTPD_SASL_OPTS, &var_smtpd_sasl_opts,
+    VAR_SMTPD_POLICY_SRV, DEF_SMTPD_POLICY_SRV, &var_smtpd_policy_srv, 0, 0,
     0,
 };
 
@@ -3664,6 +3792,9 @@ int     var_virt_alias_code;
 int     var_show_unk_rcpt_table;
 int     var_verify_poll_count;
 int     var_verify_poll_delay;
+int     var_smtpd_policy_tmout;
+int     var_smtpd_policy_idle;
+int     var_smtpd_policy_ttl;
 
 static INT_TABLE int_table[] = {
     "msg_verbose", 0, &msg_verbose,
@@ -3688,7 +3819,6 @@ static INT_TABLE int_table[] = {
     VAR_VIRT_MAILBOX_CODE, DEF_VIRT_MAILBOX_CODE, &var_virt_mailbox_code,
     VAR_SHOW_UNK_RCPT_TABLE, DEF_SHOW_UNK_RCPT_TABLE, &var_show_unk_rcpt_table,
     VAR_VERIFY_POLL_COUNT, DEF_VERIFY_POLL_COUNT, &var_verify_poll_count,
-    VAR_VERIFY_POLL_DELAY, DEF_VERIFY_POLL_DELAY, &var_verify_poll_delay,
     0,
 };
 
@@ -3795,7 +3925,8 @@ bool    var_smtpd_sasl_enable = 0;
 
 /* smtpd_sasl_connect - stub */
 
-void    smtpd_sasl_connect(SMTPD_STATE *state)
+void    smtpd_sasl_connect(SMTPD_STATE *state, const char *opts_name,
+                                  const char *opts_var)
 {
     msg_panic("smtpd_sasl_connect was called");
 }
index 302e2690354b3fad5d65b6f1ef672a8b692590f9..9f2927c92047414179014b1508037a49bae4173d 100644 (file)
@@ -20,6 +20,7 @@ extern char *smtpd_check_size(SMTPD_STATE *, off_t);
 extern char *smtpd_check_rcpt(SMTPD_STATE *, char *);
 extern char *smtpd_check_etrn(SMTPD_STATE *, char *);
 extern char *smtpd_check_data(SMTPD_STATE *);
+extern char *smtpd_check_policy(SMTPD_STATE *, char *);
 
 /* LICENSE
 /* .ad
index ce2a514d9f31119648d695d57b84d939fb677752..50aa0e18f0f67fab4d3a3d773584e9e26bc50141 100644 (file)
@@ -24,6 +24,11 @@ client spike.porcupine.org 168.100.189.2
 rcpt rname@rdomain
 client foo 127.0.0.2
 rcpt rname@rdomain
+recipient_restrictions reject_rbl_client,relays.mail-abuse.org=127.0.0.2
+client foo 127.0.0.2
+rcpt rname@rdomain
+client foo 127.0.0.1
+rcpt rname@rdomain
 #
 # RHSBL sender domain name
 #
index 36fc6b161f1b2b78377aae55cd5b0ca1012ad37f..36e2cf519764dae6834869bd958d8384d323f67d 100644 (file)
@@ -44,6 +44,17 @@ OK
 >>> rcpt rname@rdomain
 ./smtpd_check: <queue id>: reject: RCPT from foo[127.0.0.2]: 554 Service unavailable; Client host [127.0.0.2] blocked using relays.mail-abuse.org; from=<sname@sdomain> to=<rname@rdomain> proto=SMTP helo=<foobar>
 554 Service unavailable; Client host [127.0.0.2] blocked using relays.mail-abuse.org
+>>> recipient_restrictions reject_rbl_client,relays.mail-abuse.org=127.0.0.2
+OK
+>>> client foo 127.0.0.2
+OK
+>>> rcpt rname@rdomain
+./smtpd_check: <queue id>: reject: RCPT from foo[127.0.0.2]: 554 Service unavailable; Client host [127.0.0.2] blocked using relays.mail-abuse.org=127.0.0.2; from=<sname@sdomain> to=<rname@rdomain> proto=SMTP helo=<foobar>
+554 Service unavailable; Client host [127.0.0.2] blocked using relays.mail-abuse.org=127.0.0.2
+>>> client foo 127.0.0.1
+OK
+>>> rcpt rname@rdomain
+OK
 >>> #
 >>> # RHSBL sender domain name
 >>> #
index b34a7ea09675f9879a13435be9c4e41b046e2cbf..6ba94a55c890a717927e83e3e268d6dd6490efc8 100644 (file)
@@ -15,7 +15,7 @@
 /*             /* other fields... */
 /* .in -4
 /*     } SMTPD_STATE;
-/* SMTP-LEVEL ROUTINES
+/* 
 /*     int     smtpd_proxy_open(state, service, timeout, ehlo_name, mail_from)
 /*     SMTPD_STATE *state;
 /*     const char *service;
index 936f167366571fe0513180758c1cb7bf04d47731..490a2633c80773ac8342adc4f3880b245559e620 100644 (file)
@@ -8,7 +8,7 @@
 /*
 /*     void    smtpd_sasl_initialize()
 /*
-/*     void    smtpd_sasl_connect(state)
+/*     void    smtpd_sasl_connect(state, sasl_opts_name, sasl_opts_var)
 /*     SMTPD_STATE *state;
 /*
 /*     char    *smtpd_sasl_authenticate(state, sasl_method, init_response)
@@ -32,7 +32,9 @@
 /*
 /*     smtpd_sasl_connect() performs per-connection initialization.
 /*     This routine should be called once at the start of every
-/*     connection.
+/*     connection. The sasl_opts_name and sasl_opts_var parameters
+/*     are the postfix configuration parameters setting the security
+/*     policy of the SASL authentication.
 /*
 /*     smtpd_sasl_authenticate() implements the authentication dialog.
 /*     The result is a null pointer in case of success, an SMTP reply
@@ -201,8 +203,6 @@ static NAME_MASK smtpd_sasl_mask[] = {
     0,
 };
 
-static int smtpd_sasl_opts;
-
 /* smtpd_sasl_initialize - per-process initialization */
 
 void    smtpd_sasl_initialize(void)
@@ -214,16 +214,12 @@ void    smtpd_sasl_initialize(void)
     if (sasl_server_init(callbacks, "smtpd") != SASL_OK)
        msg_fatal("SASL per-process initialization failed");
 
-    /*
-     * Configuration parameters.
-     */
-    smtpd_sasl_opts = name_mask(VAR_SMTPD_SASL_OPTS, smtpd_sasl_mask,
-                               var_smtpd_sasl_opts);
 }
 
 /* smtpd_sasl_connect - per-connection initialization */
 
-void    smtpd_sasl_connect(SMTPD_STATE *state)
+void    smtpd_sasl_connect(SMTPD_STATE *state, const char *sasl_opts_name,
+                                  const char *sasl_opts_var)
 {
 #if SASL_VERSION_MAJOR < 2
     unsigned sasl_mechanism_count;
@@ -291,7 +287,8 @@ void    smtpd_sasl_connect(SMTPD_STATE *state)
     sec_props.min_ssf = 0;
     sec_props.max_ssf = 1;                     /* don't allow real SASL
                                                 * security layer */
-    sec_props.security_flags = smtpd_sasl_opts;
+    sec_props.security_flags = name_mask(sasl_opts_name, smtpd_sasl_mask,
+                                        sasl_opts_var);
     sec_props.maxbufsize = 0;
     sec_props.property_names = 0;
     sec_props.property_values = 0;
index 9bd793690bf5177cc9f226f5bc07b5d7514fd818..2466f9a57e0df2e5a9aafab6270d3dd5bb4dcfd1 100644 (file)
@@ -12,7 +12,7 @@
   * SASL protocol interface
   */
 extern void smtpd_sasl_initialize(void);
-extern void smtpd_sasl_connect(SMTPD_STATE *);
+extern void smtpd_sasl_connect(SMTPD_STATE *, const char *, const char *);
 extern void smtpd_sasl_disconnect(SMTPD_STATE *);
 extern char *smtpd_sasl_authenticate(SMTPD_STATE *, const char *, const char *);
 extern void smtpd_sasl_logout(SMTPD_STATE *);
index f0b148d5515d62aee60711d8abbcfe1252ef7f6a..53e805794096daa86ecc7acb7be666e0d15f4086 100644 (file)
@@ -106,7 +106,7 @@ void    smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream)
     if (SMTPD_STAND_ALONE(state))
        var_smtpd_sasl_enable = 0;
     if (var_smtpd_sasl_enable)
-       smtpd_sasl_connect(state);
+       smtpd_sasl_connect(state, VAR_SMTPD_SASL_OPTS, var_smtpd_sasl_opts);
 #endif
 
     /*
index d33824a8d44b03c5c1147b534359f8ab98ba7ddb..3e0ab78de748c0b06b5ddb28193e2317ae2094f6 100644 (file)
@@ -174,11 +174,11 @@ static void get_service_attr(SPAWN_ATTR *attr, char *service, char **argv)
                if (*group == 0)
                    group = 0;
            if ((pwd = getpwnam(user)) == 0)
-               msg_fatal("%s: unknown username: %s", myname, user);
+               msg_fatal("unknown user name: %s", user);
            attr->uid = pwd->pw_uid;
            if (group != 0) {
                if ((grp = getgrnam(group)) == 0)
-                   msg_fatal("%s: unknown group: %s", myname, group);
+                   msg_fatal("unknown group name: %s", group);
                attr->gid = grp->gr_gid;
            } else {
                attr->gid = pwd->pw_gid;
index 74b5387831560b476e980b21a7bac123e999b25d..e1012ea061bdb0a419cf730c280e54fe9bc3171b 100644 (file)
@@ -26,7 +26,8 @@ SRCS  = alldig.c argv.c argv_split.c attr_print0.c attr_print64.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
+       write_buf.c write_wait.c auto_clnt.c attr_clnt.c attr_scan_plain.c \
+       attr_print_plain.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 \
@@ -54,7 +55,8 @@ OBJS  = alldig.o argv.o argv_split.o attr_print0.o attr_print64.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)
+       write_buf.o write_wait.o auto_clnt.o attr_clnt.o attr_scan_plain.o \
+       attr_print_plain.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_cidr.h dict_ht.h dict_ldap.h dict_mysql.h dict_ni.h dict_nis.h \
@@ -72,7 +74,8 @@ HDRS  = argv.h attr.h base64_code.h binhash.h chroot_uid.h clean_env.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
+       vbuf_print.h vstream.h vstring.h vstring_vstream.h watchdog.h \
+       auto_clnt.h attr_clnt.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 \
@@ -324,6 +327,16 @@ host_port: $(LIB)
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
        mv junk $@.o
 
+attr_scan_plain: $(LIB)
+       mv $@.o junk
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
+       mv junk $@.o
+
+attr_print_plain: $(LIB)
+       mv $@.o junk
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
+       mv junk $@.o
+
 depend: $(MAKES)
        (sed '1,/^# do not edit/!d' Makefile.in; \
        set -e; for i in [a-z][a-z0-9]*.c; do \
@@ -338,7 +351,7 @@ stream_test: stream_test.c $(LIB)
 tests: valid_hostname_test mac_expand_test dict_test unescape_test \
        hex_quote_test ctable_test inet_addr_list_test base64_code_test \
        attr_scan64_test attr_scan0_test dict_pcre_test host_port_test \
-       dict_cidr_test
+       dict_cidr_test attr_scan_plain_test
 
 valid_hostname_test: valid_hostname valid_hostname.in valid_hostname.ref
        ./valid_hostname <valid_hostname.in 2>valid_hostname.tmp
@@ -414,6 +427,11 @@ host_port_test: host_port host_port.in host_port.ref
        diff host_port.ref host_port.tmp
        rm -f host_port.tmp
 
+attr_scan_plain_test: attr_print_plain attr_scan_plain attr_scan_plain.ref
+       (./attr_print_plain 2>&3 | (sleep 1; ./attr_scan_plain)) >attr_scan_plain.tmp 2>&1 3>&1
+       diff attr_scan_plain.ref attr_scan_plain.tmp
+       rm -f attr_scan_plain.tmp
+
 # do not edit below this line - it is generated by 'make depend'
 alldig.o: alldig.c
 alldig.o: sys_defs.h
@@ -423,6 +441,7 @@ alldig.o: vbuf.h
 argv.o: argv.c
 argv.o: sys_defs.h
 argv.o: mymalloc.h
+argv.o: msg.h
 argv.o: argv.h
 argv_split.o: argv_split.c
 argv_split.o: sys_defs.h
@@ -431,6 +450,18 @@ argv_split.o: stringops.h
 argv_split.o: vstring.h
 argv_split.o: vbuf.h
 argv_split.o: argv.h
+attr_clnt.o: attr_clnt.c
+attr_clnt.o: sys_defs.h
+attr_clnt.o: msg.h
+attr_clnt.o: mymalloc.h
+attr_clnt.o: split_at.h
+attr_clnt.o: vstream.h
+attr_clnt.o: vbuf.h
+attr_clnt.o: connect.h
+attr_clnt.o: iostuff.h
+attr_clnt.o: attr.h
+attr_clnt.o: auto_clnt.h
+attr_clnt.o: attr_clnt.h
 attr_print0.o: attr_print0.c
 attr_print0.o: sys_defs.h
 attr_print0.o: msg.h
@@ -449,6 +480,16 @@ attr_print64.o: htable.h
 attr_print64.o: base64_code.h
 attr_print64.o: vstring.h
 attr_print64.o: attr.h
+attr_print_plain.o: attr_print_plain.c
+attr_print_plain.o: sys_defs.h
+attr_print_plain.o: msg.h
+attr_print_plain.o: mymalloc.h
+attr_print_plain.o: vstream.h
+attr_print_plain.o: vbuf.h
+attr_print_plain.o: htable.h
+attr_print_plain.o: base64_code.h
+attr_print_plain.o: vstring.h
+attr_print_plain.o: attr.h
 attr_scan0.o: attr_scan0.c
 attr_scan0.o: sys_defs.h
 attr_scan0.o: msg.h
@@ -469,6 +510,24 @@ attr_scan64.o: vstring.h
 attr_scan64.o: htable.h
 attr_scan64.o: base64_code.h
 attr_scan64.o: attr.h
+attr_scan_plain.o: attr_scan_plain.c
+attr_scan_plain.o: sys_defs.h
+attr_scan_plain.o: msg.h
+attr_scan_plain.o: mymalloc.h
+attr_scan_plain.o: vstream.h
+attr_scan_plain.o: vbuf.h
+attr_scan_plain.o: vstring.h
+attr_scan_plain.o: htable.h
+attr_scan_plain.o: attr.h
+auto_clnt.o: auto_clnt.c
+auto_clnt.o: sys_defs.h
+auto_clnt.o: msg.h
+auto_clnt.o: mymalloc.h
+auto_clnt.o: vstream.h
+auto_clnt.o: vbuf.h
+auto_clnt.o: events.h
+auto_clnt.o: iostuff.h
+auto_clnt.o: auto_clnt.h
 base64_code.o: base64_code.c
 base64_code.o: sys_defs.h
 base64_code.o: msg.h
index 88721096e79b4c5d03d69b3486e6344d7abb1161..ff94f0328595a7441cc7336ac47cff5ab2e0c1f4 100644 (file)
 /*     ARGV    *argvp;
 /*     char    *arg;
 /*
+/*     void    argv_addn(argvp, arg, arg_len, ..., ARGV_END)
+/*     ARGV    *argvp;
+/*     char    *arg;
+/*     int     arg_len;
+/*
 /*     void    argv_terminate(argvp);
 /*     ARGV    *argvp;
 /* DESCRIPTION
@@ -37,6 +42,9 @@
 /*     Terminate the argument list with a null pointer. The manifest
 /*     constant ARGV_END provides a convenient notation for this.
 /*
+/*     argv_addn() is like argv_add(), but each string is followed
+/*     by a string length argument.
+/*
 /*     argv_free() releases storage for a string array, and conveniently
 /*     returns a null pointer.
 /*
@@ -66,6 +74,7 @@
 /* Application-specific. */
 
 #include "mymalloc.h"
+#include "msg.h"
 #include "argv.h"
 
 /* argv_free - destroy string array */
@@ -98,6 +107,16 @@ ARGV   *argv_alloc(int len)
     return (argvp);
 }
 
+/* argv_extend - extend array */
+
+static void argv_extend(ARGV *argvp)
+{
+    argvp->len *= 2;
+    argvp->argv = (char **)
+       myrealloc((char *) argvp->argv,
+                 (argvp->len + 1) * sizeof(char *));
+}
+
 /* argv_add - add string to vector */
 
 void    argv_add(ARGV *argvp,...)
@@ -108,20 +127,41 @@ void    argv_add(ARGV *argvp,...)
     /*
      * Make sure that always argvp->argc < argvp->len.
      */
+#define ARGV_SPACE_LEFT(a) ((a)->len - (a)->argc - 1)
+
     va_start(ap, argvp);
     while ((arg = va_arg(ap, char *)) != 0) {
-       if (argvp->argc + 1 >= argvp->len) {
-           argvp->len *= 2;
-           argvp->argv = (char **)
-               myrealloc((char *) argvp->argv,
-                         (argvp->len + 1) * sizeof(char *));
-       }
+       if (ARGV_SPACE_LEFT(argvp) <= 0)
+           argv_extend(argvp);
        argvp->argv[argvp->argc++] = mystrdup(arg);
     }
     va_end(ap);
     argvp->argv[argvp->argc] = 0;
 }
 
+/* argv_addn - add string to vector */
+
+void    argv_addn(ARGV *argvp,...)
+{
+    char   *arg;
+    int     len;
+    va_list ap;
+
+    /*
+     * Make sure that always argvp->argc < argvp->len.
+     */
+    va_start(ap, argvp);
+    while ((arg = va_arg(ap, char *)) != 0) {
+       if ((len = va_arg(ap, int)) < 0)
+           msg_panic("argv_addn: bad string length %d", len);
+       if (ARGV_SPACE_LEFT(argvp) <= 0)
+           argv_extend(argvp);
+       argvp->argv[argvp->argc++] = mystrndup(arg, len);
+    }
+    va_end(ap);
+    argvp->argv[argvp->argc] = 0;
+}
+
 /* argv_terminate - terminate string array */
 
 void    argv_terminate(ARGV *argvp)
index 0d5562310ad71d1988068f7daa308d8c53cfe16c..d603dac1c8dcb8efd682d2d8c987f9909b84ca4c 100644 (file)
@@ -22,6 +22,7 @@ typedef struct ARGV {
 
 extern ARGV *argv_alloc(int);
 extern void argv_add(ARGV *,...);
+extern void argv_addn(ARGV *,...);
 extern void argv_terminate(ARGV *);
 extern ARGV *argv_free(ARGV *);
 
index c656dadf8e1ece6281a12875806b12980a6c2003..f0626f304c86c91d4a291da49209ef44c95fb9f5 100644 (file)
 #define ATTR_TYPE_NUM          1       /* Unsigned integer */
 #define ATTR_TYPE_STR          2       /* Character string */
 #define ATTR_TYPE_HASH         3       /* Hash table */
+#define ATTR_TYPE_NV           3       /* Name-value table */
 #define ATTR_TYPE_LONG         4       /* Unsigned long */
 
+#define ATTR_HASH_LIMIT                1024    /* Size of hash table */
+
  /*
   * Flags that control processing. See attr_scan(3) for documentation.
   */
@@ -70,6 +73,19 @@ extern int attr_vprint0(VSTREAM *, int, va_list);
 extern int attr_scan0(VSTREAM *, int,...);
 extern int attr_vscan0(VSTREAM *, int, va_list);
 
+ /*
+  * attr_scan_plain.c.
+  */
+extern int attr_print_plain(VSTREAM *, int,...);
+extern int attr_vprint_plain(VSTREAM *, int, va_list);
+
+ /*
+  * attr_print_plain.c.
+  */
+extern int attr_scan_plain(VSTREAM *, int,...);
+extern int attr_vscan_plain(VSTREAM *, int, va_list);
+
+
  /*
   * Attribute names for testing the compatibility of the read and write
   * routines.
diff --git a/postfix/src/util/attr_clnt.c b/postfix/src/util/attr_clnt.c
new file mode 100644 (file)
index 0000000..a0edae7
--- /dev/null
@@ -0,0 +1,230 @@
+/*++
+/* NAME
+/*     attr_clnt 3
+/* SUMMARY
+/*     attribute query-reply client
+/* SYNOPSIS
+/*     #include <attr_clnt.h>
+/*
+/*     ATTR_CLNT *attr_clnt_create(server, timeout, max_idle, max_ttl)
+/*     const char *server;
+/*     int     timeout;
+/*     int     max_idle;
+/*     int     max_ttl;
+/*
+/*     int     attr_clnt_request(client,
+/*                     send_flags, send_type, send_name, ..., ATTR_TYPE_END,
+/*                     recv_flags, recv_type, recv_name, ..., ATTR_TYPE_END)
+/*     ATTR_CLNT *client;
+/*     int     send_flags;
+/*     int     send_type;
+/*     const char *send_name;
+/*     int     recv_flags;
+/*     int     recv_type;
+/*     const char *recv_name;
+/*
+/*     void    attr_clnt_free(client)
+/*     ATTR_CLNT *client;
+/* DESCRIPTION
+/*     This module implements a client for a simple attribute-based
+/*     protocol as described in attr_scan0(3) and attr_scan64(3).
+/*
+/*     attr_clnt_create() creates a client handle. The server
+/*     argument specifies "transport:servername" where transport is
+/*     currently limited to "inet" or "unix", and servername has the
+/*     form "host:port", "private/servicename" or "public/servicename".
+/*     The timeout parameter limits the time for sending or receiving
+/*     a reply, and the ttl parameter controls how long an unused
+/*     connection is kept open.
+/*
+/*     attr_clnt_request() sends the specified request attributes and
+/*     receives a reply. The reply argument specifies a name-value table.
+/*     The other arguments are as described in attr_print0(3) and in
+/*     attr_print64(3). The result is the number of attributes received
+/*     or -1 in case of trouble.
+/*
+/*     attr_clnt_free() destroys a client handle and closes its connection.
+/* DIAGNOSTICS
+/*     Warnings: communication failure.
+/* SEE ALSO
+/*     auto_clnt(3), client endpoint management
+/*     attr_scan_plain(3), attribute protocol
+/*     attr_print_plain(3), attribute protocol
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <split_at.h>
+#include <vstream.h>
+#include <connect.h>
+#include <htable.h>
+#include <attr.h>
+#include <auto_clnt.h>
+#include <attr_clnt.h>
+
+/* Application-specific. */
+
+struct ATTR_CLNT {
+    AUTO_CLNT *auto_clnt;
+    int     (*connect) (const char *, int, int);
+    char   *endpoint;
+    int     timeout;
+    int     (*print) (VSTREAM *, int, va_list);
+    int     (*scan) (VSTREAM *, int, va_list);
+};
+
+/* attr_clnt_connect - connect to server */
+
+static VSTREAM *attr_clnt_connect(void *context)
+{
+    const char *myname = "attr_clnt_connect";
+    ATTR_CLNT *client = (ATTR_CLNT *) context;
+    VSTREAM *fp;
+    int     fd;
+
+    fd = client->connect(client->endpoint, NON_BLOCKING, client->timeout);
+    if (fd < 0) {
+       msg_warn("connect to %s: %m", client->endpoint);
+       return (0);
+    } else {
+       if (msg_verbose)
+           msg_info("%s: connected to %s", myname, client->endpoint);
+       fp = vstream_fdopen(fd, O_RDWR);
+       vstream_control(fp, VSTREAM_CTL_PATH, client->endpoint,
+                       VSTREAM_CTL_TIMEOUT, client->timeout,
+                       VSTREAM_CTL_END);
+       return (fp);
+    }
+}
+
+/* attr_clnt_free - destroy attribute client */
+
+void    attr_clnt_free(ATTR_CLNT *client)
+{
+    myfree(client->endpoint);
+    auto_clnt_free(client->auto_clnt);
+    myfree((char *) client);
+}
+
+/* attr_clnt_create - create attribute client */
+
+ATTR_CLNT *attr_clnt_create(const char *service, int timeout,
+                                   int max_idle, int max_ttl)
+{
+    const char *myname = "attr_clnt_create";
+    char   *transport = mystrdup(service);
+    char   *endpoint;
+    ATTR_CLNT *client;
+
+    if ((endpoint = split_at(transport, ':')) == 0 || *endpoint == 0)
+       msg_fatal("missing attribute server endpoint: %s", service);
+    if (msg_verbose)
+       msg_info("%s: transport=%s endpoint=%s", myname, transport, endpoint);
+
+    client = (ATTR_CLNT *) mymalloc(sizeof(*client));
+    client->scan = attr_vscan_plain;
+    client->print = attr_vprint_plain;
+    client->endpoint = mystrdup(endpoint);
+    client->timeout = timeout;
+    if (strcmp(transport, "inet") == 0) {
+       client->connect = inet_connect;
+    } else if (strcmp(transport, "unix") == 0) {
+       client->connect = unix_connect;
+    } else {
+       msg_fatal("invalid attribute transport name: %s", service);
+    }
+    client->auto_clnt = auto_clnt_create(max_idle, max_ttl,
+                                        attr_clnt_connect, (void *) client);
+    myfree(transport);
+    return (client);
+}
+
+/* attr_clnt_request - send query, receive reply */
+
+int     attr_clnt_request(ATTR_CLNT *client, int send_flags,...)
+{
+    const char *myname = "attr_clnt_request";
+    VSTREAM *stream;
+    int     count = 0;
+    va_list ap;
+    int     type;
+    int     recv_flags;
+    int     err;
+    int     ret;
+
+    /*
+     * XXX If the stream is readable before we send anything, then assume the
+     * remote end disconnected.
+     * 
+     * XXX For some reason we can't simply call the scan routine after the print
+     * routine, that messes up the argument list.
+     */
+#define SKIP_ARG(ap, type) { \
+       (void) va_arg(ap, char *); \
+       (void) va_arg(ap, type); \
+    }
+
+    for (;;) {
+       if ((stream = auto_clnt_access(client->auto_clnt)) != 0
+           && readable(vstream_fileno(stream)) == 0) {
+           errno = 0;
+           va_start(ap, send_flags);
+           err = (client->print(stream, send_flags, ap) != 0
+                  || vstream_fflush(stream) != 0);
+           va_end(ap);
+           if (err == 0) {
+               va_start(ap, send_flags);
+               while ((type = va_arg(ap, int)) != ATTR_TYPE_END) {
+                   switch (type) {
+                   case ATTR_TYPE_STR:
+                       SKIP_ARG(ap, char *);
+                       break;
+                   case ATTR_TYPE_NUM:
+                       SKIP_ARG(ap, int);
+                       break;
+                   case ATTR_TYPE_LONG:
+                       SKIP_ARG(ap, long);
+                       break;
+                   case ATTR_TYPE_HASH:
+                       SKIP_ARG(ap, HTABLE *);
+                       break;
+                   default:
+                       msg_panic("%s: unexpected attribute type %d",
+                                 myname, type);
+                   }
+               }
+               recv_flags = va_arg(ap, int);
+               ret = client->scan(stream, recv_flags, ap);
+               va_end(ap);
+               if (ret > 0)
+                   return (ret);
+           }
+       }
+       if (++count >= 2
+           || msg_verbose
+           || (errno != EPIPE && errno != ENOENT && errno != ECONNRESET))
+           msg_warn("problem talking to server %s: %m", client->endpoint);
+       if (count >= 3)
+           return (-1);
+       sleep(1);                               /* XXX make configurable */
+       auto_clnt_recover(client->auto_clnt);
+    }
+}
diff --git a/postfix/src/util/attr_clnt.h b/postfix/src/util/attr_clnt.h
new file mode 100644 (file)
index 0000000..9edb796
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _ATTR_CLNT_H_INCLUDED_
+#define _ATTR_CLNT_H_INCLUDED_
+
+/*++
+/* NAME
+/*     attr_clnt 3h
+/* SUMMARY
+/*     attribute query-reply client
+/* SYNOPSIS
+/*     #include <attr_clnt.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <attr.h>
+
+ /*
+  * External interface.
+  */
+typedef struct ATTR_CLNT ATTR_CLNT;
+
+extern ATTR_CLNT *attr_clnt_create(const char *, int, int, int);
+extern int attr_clnt_request(ATTR_CLNT *, int,...);
+extern void attr_clnt_free(ATTR_CLNT *);
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
index c39b72104dad0fc89f41f20327654b75cbe92309..5bfa31df143d83ff342b5715d18c1e614c9d49bf 100644 (file)
@@ -6,7 +6,7 @@
 /* SYNOPSIS
 /*     #include <attr.h>
 /*
-/*     int     attr_print0(fp, flags, type, name, ...)
+/*     int     attr_print0(fp, flags, type, name, ..., ATTR_TYPE_END)
 /*     VSTREAM fp;
 /*     int     flags;
 /*     int     type;
@@ -18,9 +18,8 @@
 /*     va_list ap;
 /* DESCRIPTION
 /*     attr_print0() takes zero or more (name, value) simple attributes
-/*     or (name, count, value) list attributes, and converts its input
-/*     to a byte stream that can be recovered with attr_scan0(). The stream
-/*     is not flushed.
+/*     and converts its input to a byte stream that can be recovered with
+/*     attr_scan0(). The stream is not flushed.
 /*
 /*     attr_vprint0() provides an alternate interface that is convenient
 /*     for calling from within variadoc functions.
@@ -53,8 +52,9 @@
 /*     This argument is followed by an attribute name and a null-terminated
 /*     string.
 /* .IP "ATTR_TYPE_HASH (HTABLE *)"
-/*     The content of the hash table is sent as a sequence of string-valued
-/*     attributes with names equal to the hash table lookup key.
+/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
+/*     The content of the table is sent as a sequence of string-valued
+/*     attributes with names equal to the table lookup keys.
 /* .IP ATTR_TYPE_END
 /*     This terminates the attribute list.
 /* .RE
index 1fc37d7b34608a709ca63c1abf8d39cb0e05647d..d43d5db78561531ed4b78caa0b9f8603e08f4cfc 100644 (file)
@@ -6,7 +6,7 @@
 /* SYNOPSIS
 /*     #include <attr.h>
 /*
-/*     int     attr_print64(fp, flags, type, name, ...)
+/*     int     attr_print64(fp, flags, type, name, ..., ATTR_TYPE_END)
 /*     VSTREAM fp;
 /*     int     flags;
 /*     int     type;
@@ -18,9 +18,8 @@
 /*     va_list ap;
 /* DESCRIPTION
 /*     attr_print64() takes zero or more (name, value) simple attributes
-/*     or (name, count, value) list attributes, and converts its input
-/*     to a byte stream that can be recovered with attr_scan64(). The stream
-/*     is not flushed.
+/*     and converts its input to a byte stream that can be recovered with
+/*     attr_scan64(). The stream is not flushed.
 /*
 /*     attr_vprint64() provides an alternate interface that is convenient
 /*     for calling from within variadoc functions.
@@ -53,8 +52,9 @@
 /*     This argument is followed by an attribute name and a null-terminated
 /*     string.
 /* .IP "ATTR_TYPE_HASH (HTABLE *)"
-/*     The content of the hash table is sent as a sequence of string-valued
-/*     attributes with names equal to the hash table lookup key.
+/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
+/*     The content of the table is sent as a sequence of string-valued
+/*     attributes with names equal to the table lookup keys.
 /* .IP ATTR_TYPE_END
 /*     This terminates the attribute list.
 /* .RE
@@ -160,6 +160,7 @@ int     attr_vprint64(VSTREAM *fp, int flags, va_list ap)
            int_val = va_arg(ap, int);
            VSTREAM_PUTC(':', fp);
            attr_print64_num(fp, (unsigned) int_val);
+           VSTREAM_PUTC('\n', fp);
            if (msg_verbose)
                msg_info("send attr %s = %u", attr_name, int_val);
            break;
@@ -169,6 +170,7 @@ int     attr_vprint64(VSTREAM *fp, int flags, va_list ap)
            long_val = va_arg(ap, long);
            VSTREAM_PUTC(':', fp);
            attr_print64_long_num(fp, (unsigned long) long_val);
+           VSTREAM_PUTC('\n', fp);
            if (msg_verbose)
                msg_info("send attr %s = %lu", attr_name, long_val);
            break;
@@ -178,6 +180,7 @@ int     attr_vprint64(VSTREAM *fp, int flags, va_list ap)
            str_val = va_arg(ap, char *);
            VSTREAM_PUTC(':', fp);
            attr_print64_str(fp, str_val, strlen(str_val));
+           VSTREAM_PUTC('\n', fp);
            if (msg_verbose)
                msg_info("send attr %s = %s", attr_name, str_val);
            break;
@@ -187,18 +190,16 @@ int     attr_vprint64(VSTREAM *fp, int flags, va_list ap)
                attr_print64_str(fp, ht[0]->key, strlen(ht[0]->key));
                VSTREAM_PUTC(':', fp);
                attr_print64_str(fp, ht[0]->value, strlen(ht[0]->value));
+               VSTREAM_PUTC('\n', fp);
                if (msg_verbose)
                    msg_info("send attr name %s value %s",
                             ht[0]->key, ht[0]->value);
-               if (ht[1] != 0)
-                   VSTREAM_PUTC('\n', fp);
            }
            myfree((char *) ht_info_list);
            break;
        default:
            msg_panic("%s: unknown type code: %d", myname, attr_type);
        }
-       VSTREAM_PUTC('\n', fp);
     }
     if ((flags & ATTR_FLAG_MORE) == 0)
        VSTREAM_PUTC('\n', fp);
diff --git a/postfix/src/util/attr_print_plain b/postfix/src/util/attr_print_plain
new file mode 100755 (executable)
index 0000000..b94f004
Binary files /dev/null and b/postfix/src/util/attr_print_plain differ
diff --git a/postfix/src/util/attr_print_plain.c b/postfix/src/util/attr_print_plain.c
new file mode 100644 (file)
index 0000000..89054b2
--- /dev/null
@@ -0,0 +1,207 @@
+/*++
+/* NAME
+/*     attr_print_plain 3
+/* SUMMARY
+/*     send attributes over byte stream
+/* SYNOPSIS
+/*     #include <attr.h>
+/*
+/*     int     attr_print_plain(fp, flags, type, name, ..., ATTR_TYPE_END)
+/*     VSTREAM fp;
+/*     int     flags;
+/*     int     type;
+/*     char    *name;
+/*
+/*     int     attr_vprint_plain(fp, flags, ap)
+/*     VSTREAM fp;
+/*     int     flags;
+/*     va_list ap;
+/* DESCRIPTION
+/*     attr_print_plain() takes zero or more (name, value) simple attributes
+/*     and converts its input to a byte stream that can be recovered with
+/*     attr_scan_plain(). The stream is not flushed.
+/*
+/*     attr_vprint_plain() provides an alternate interface that is convenient
+/*     for calling from within variadoc functions.
+/*
+/*     Attributes are sent in the requested order as specified with the
+/*     attr_print_plain() argument list. This routine satisfies the formatting
+/*     rules as outlined in attr_scan_plain(3).
+/*
+/*     Arguments:
+/* .IP fp
+/*     Stream to write the result to.
+/* .IP flags
+/*     The bit-wise OR of zero or more of the following.
+/* .RS
+/* .IP ATTR_FLAG_MORE
+/*     After sending the requested attributes, leave the output stream in
+/*     a state that is usable for more attribute sending operations on
+/*     the same output attribute list.
+/*     By default, attr_print_plain() automatically appends an attribute list
+/*     terminator when it has sent the last requested attribute.
+/* .RE
+/* .IP type
+/*     The type determines the arguments that follow.
+/* .RS
+/* .IP "ATTR_TYPE_NUM (char *, int)"
+/*     This argument is followed by an attribute name and an integer.
+/* .IP "ATTR_TYPE_NUM (char *, long)"
+/*     This argument is followed by an attribute name and a long integer.
+/* .IP "ATTR_TYPE_STR (char *, char *)"
+/*     This argument is followed by an attribute name and a null-terminated
+/*     string.
+/* .IP "ATTR_TYPE_HASH (HTABLE *)"
+/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
+/*     The content of the table is sent as a sequence of string-valued
+/*     attributes with names equal to the table lookup keys.
+/* .IP ATTR_TYPE_END
+/*     This terminates the attribute list.
+/* .RE
+/* DIAGNOSTICS
+/*     The result value is 0 in case of success, VSTREAM_EOF in case
+/*     of trouble.
+/*
+/*     Panic: interface violation. All system call errors are fatal.
+/* SEE ALSO
+/*     attr_scan_plain(3) recover attributes from byte stream
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdarg.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <htable.h>
+#include <base64_code.h>
+#include <attr.h>
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* attr_vprint_plain - send attribute list to stream */
+
+int     attr_vprint_plain(VSTREAM *fp, int flags, va_list ap)
+{
+    const char *myname = "attr_print_plain";
+    int     attr_type;
+    char   *attr_name;
+    unsigned int_val;
+    unsigned long long_val;
+    char   *str_val;
+    HTABLE_INFO **ht_info_list;
+    HTABLE_INFO **ht;
+
+    /*
+     * Sanity check.
+     */
+    if (flags & ~ATTR_FLAG_ALL)
+       msg_panic("%s: bad flags: 0x%x", myname, flags);
+
+    /*
+     * Iterate over all (type, name, value) triples, and produce output on
+     * the fly.
+     */
+    while ((attr_type = va_arg(ap, int)) != ATTR_TYPE_END) {
+       switch (attr_type) {
+       case ATTR_TYPE_NUM:
+           attr_name = va_arg(ap, char *);
+           int_val = va_arg(ap, int);
+           vstream_fprintf(fp, "%s=%u\n", attr_name, (unsigned) int_val);
+           if (msg_verbose)
+               msg_info("send attr %s = %u", attr_name, (unsigned) int_val);
+           break;
+       case ATTR_TYPE_LONG:
+           attr_name = va_arg(ap, char *);
+           long_val = va_arg(ap, long);
+           vstream_fprintf(fp, "%s=%lu\n", attr_name, long_val);
+           if (msg_verbose)
+               msg_info("send attr %s = %lu", attr_name, long_val);
+           break;
+       case ATTR_TYPE_STR:
+           attr_name = va_arg(ap, char *);
+           str_val = va_arg(ap, char *);
+           vstream_fprintf(fp, "%s=%s\n", attr_name, str_val);
+           if (msg_verbose)
+               msg_info("send attr %s = %s", attr_name, str_val);
+           break;
+       case ATTR_TYPE_HASH:
+           ht_info_list = htable_list(va_arg(ap, HTABLE *));
+           for (ht = ht_info_list; *ht; ht++) {
+               vstream_fprintf(fp, "%s=%s\n", ht[0]->key, ht[0]->value);
+               if (msg_verbose)
+                   msg_info("send attr name %s value %s",
+                            ht[0]->key, ht[0]->value);
+           }
+           myfree((char *) ht_info_list);
+           break;
+       default:
+           msg_panic("%s: unknown type code: %d", myname, attr_type);
+       }
+    }
+    if ((flags & ATTR_FLAG_MORE) == 0)
+       VSTREAM_PUTC('\n', fp);
+    return (vstream_ferror(fp));
+}
+
+int     attr_print_plain(VSTREAM *fp, int flags,...)
+{
+    va_list ap;
+    int     ret;
+
+    va_start(ap, flags);
+    ret = attr_vprint_plain(fp, flags, ap);
+    va_end(ap);
+    return (ret);
+}
+
+#ifdef TEST
+
+ /*
+  * Proof of concept test program.  Mirror image of the attr_scan_plain test
+  * program.
+  */
+#include <msg_vstream.h>
+
+int     main(int unused_argc, char **argv)
+{
+    HTABLE *table = htable_create(1);
+
+    msg_vstream_init(argv[0], VSTREAM_ERR);
+    msg_verbose = 1;
+    htable_enter(table, "foo-name", mystrdup("foo-value"));
+    htable_enter(table, "bar-name", mystrdup("bar-value"));
+    attr_print_plain(VSTREAM_OUT, ATTR_FLAG_NONE,
+                    ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711,
+                    ATTR_TYPE_LONG, ATTR_NAME_LONG, 1234,
+                    ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee",
+                    ATTR_TYPE_HASH, table,
+                    ATTR_TYPE_END);
+    attr_print_plain(VSTREAM_OUT, ATTR_FLAG_NONE,
+                    ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711,
+                    ATTR_TYPE_LONG, ATTR_NAME_LONG, 1234,
+                    ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee",
+                    ATTR_TYPE_END);
+    if (vstream_fflush(VSTREAM_OUT) != 0)
+       msg_fatal("write error: %m");
+
+    htable_free(table, myfree);
+    return (0);
+}
+
+#endif
index 528c6147a94084408e7dbf035b63b2f20a1c6f36..c95342601f14c837e73b2435f3d2155c38e5202c 100644 (file)
@@ -6,7 +6,7 @@
 /* SYNOPSIS
 /*     #include <attr.h>
 /*
-/*     int     attr_scan0(fp, flags, type, name, ...)
+/*     int     attr_scan0(fp, flags, type, name, ..., ATTR_TYPE_END)
 /*     VSTREAM fp;
 /*     int     flags;
 /*     int     type;
@@ -41,8 +41,9 @@
 /* .in
 /*
 /*     All attribute names and attribute values are sent as null terminated
-/*     strings. Each string must be no longer than 2*var_line_limit
-/*     characters. The formatting rules favor implementations in C.
+/*     strings. Each string must be no longer than 4*var_line_limit
+/*     characters including the terminator.
+/*     These formatting rules favor implementations in C.
 /*
 /*      Normally, attributes must be received in the sequence as specified with
 /*     the attr_scan0() argument list.  The input stream may contain additional
 /* .IP "ATTR_TYPE_STR (char *, VSTRING *)"
 /*     This argument is followed by an attribute name and a VSTRING pointer.
 /* .IP "ATTR_TYPE_HASH (HTABLE *)"
+/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
 /*     All further input attributes are processed as string attributes.
 /*     No specific attribute sequence is enforced.
 /*     All attributes up to the attribute list terminator are read,
 /*     but only the first instance of each attribute is stored.
+/*     There can be no more than 1024 attributes in a hash table.
 /* .sp
 /*     The attribute string values are stored in the hash table under
 /*     keys equal to the attribute name (obtained from the input stream).
 /*     This argument terminates the requested attribute list.
 /* .RE
 /* BUGS
-/*     ATTR_TYPE_HASH accepts attributes with arbitrary names from possibly
-/*     untrusted sources. This is unsafe, unless the resulting table is
-/*     queried only with known to be good attribute names.
+/*     ATTR_TYPE_HASH (ATTR_TYPE_NAMEVAL) accepts attributes with arbitrary
+/*     names from possibly untrusted sources.
+/*     This is unsafe, unless the resulting table is queried only with
+/*     known to be good attribute names.
 /* DIAGNOSTICS
 /*     attr_scan0() and attr_vscan0() return -1 when malformed input is
 /*     detected (string too long, incomplete line, missing end marker).
 static int attr_scan0_string(VSTREAM *fp, VSTRING *plain_buf, const char *context)
 {
     extern int var_line_limit;         /* XXX */
-    int     limit = var_line_limit * 2;
+    int     limit = var_line_limit * 4;
     int     ch;
 
-    if ((ch = vstring_get_null(plain_buf, fp)) == VSTREAM_EOF) {
+    if ((ch = vstring_get_null_bound(plain_buf, fp, limit)) == VSTREAM_EOF) {
        msg_warn("%s on %s while reading %s",
                 vstream_ftimeout(fp) ? "timeout" : "premature end-of-input",
                 VSTREAM_PATH(fp), context);
@@ -351,6 +355,10 @@ int     attr_vscan0(VSTREAM *fp, int flags, va_list ap)
                             STR(name_buf), VSTREAM_PATH(fp));
                    return (conversions);
                }
+           } else if (hash_table->used >= ATTR_HASH_LIMIT) {
+               msg_warn("attribute count exceeds limit %d in input from %s",
+                        ATTR_HASH_LIMIT, VSTREAM_PATH(fp));
+               return (conversions);
            } else {
                htable_enter(hash_table, STR(name_buf),
                             mystrdup(STR(str_buf)));
index 26e7d04d2ee8365744e42036d2aac88d4cce5225..4da29aedd5d45baf4da0f56b68e29e3f6e354d67 100644 (file)
@@ -6,7 +6,7 @@
 /* SYNOPSIS
 /*     #include <attr.h>
 /*
-/*     int     attr_scan64(fp, flags, type, name, ...)
+/*     int     attr_scan64(fp, flags, type, name, ..., ATTR_TYPE_END)
 /*     VSTREAM fp;
 /*     int     flags;
 /*     int     type;
@@ -43,7 +43,7 @@
 /* .in
 /*
 /*     All attribute names and attribute values are sent as base64-encoded
-/*     strings. Each base64 encoding must be no longer than 2*var_line_limit
+/*     strings. Each base64 encoding must be no longer than 4*var_line_limit
 /*     characters. The formatting rules aim to make implementations in PERL
 /*     and other languages easy.
 /*
 /* .IP "ATTR_TYPE_STR (char *, VSTRING *)"
 /*     This argument is followed by an attribute name and a VSTRING pointer.
 /* .IP "ATTR_TYPE_HASH (HTABLE *)"
+/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
 /*     All further input attributes are processed as string attributes.
 /*     No specific attribute sequence is enforced.
 /*     All attributes up to the attribute list terminator are read,
 /*     but only the first instance of each attribute is stored.
+/*     There can be no more than 1024 attributes in a hash table.
 /* .sp
 /*     The attribute string values are stored in the hash table under
 /*     keys equal to the attribute name (obtained from the input stream).
 /*     This argument terminates the requested attribute list.
 /* .RE
 /* BUGS
-/*     ATTR_TYPE_HASH accepts attributes with arbitrary names from possibly
-/*     untrusted sources. This is unsafe, unless the resulting table is
-/*     queried only with known to be good attribute names.
+/*     ATTR_TYPE_HASH (ATTR_TYPE_NAMEVAL) accepts attributes with arbitrary
+/*     names from possibly untrusted sources.
+/*     This is unsafe, unless the resulting table is queried only with
+/*     known to be good attribute names.
 /* DIAGNOSTICS
 /*     attr_scan64() and attr_vscan64() return -1 when malformed input is
 /*     detected (string too long, incomplete line, missing end marker).
@@ -161,7 +164,7 @@ static int attr_scan64_string(VSTREAM *fp, VSTRING *plain_buf, const char *conte
 {
     static VSTRING *base64_buf = 0;
     extern int var_line_limit;         /* XXX */
-    int     limit = var_line_limit * 2;
+    int     limit = var_line_limit * 4;
     int     ch;
 
     if (base64_buf == 0)
@@ -176,13 +179,11 @@ static int attr_scan64_string(VSTREAM *fp, VSTRING *plain_buf, const char *conte
            return (-1);
        }
        VSTRING_ADDCH(base64_buf, ch);
-#if 0
        if (LEN(base64_buf) > limit) {
            msg_warn("string length > %d characters from %s while reading %s",
                     limit, VSTREAM_PATH(fp), context);
            return (-1);
        }
-#endif
     }
     VSTRING_TERMINATE(base64_buf);
     if (base64_decode(plain_buf, STR(base64_buf), LEN(base64_buf)) == 0) {
@@ -335,7 +336,7 @@ int     attr_vscan64(VSTREAM *fp, int flags, va_list ap)
            /*
             * Skip over this attribute. The caller does not ask for it.
             */
-           while ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF && ch != '\n')
+           while (ch != '\n' && (ch = VSTREAM_GETC(fp)) != VSTREAM_EOF)
                 /* void */ ;
        }
 
@@ -416,6 +417,10 @@ int     attr_vscan64(VSTREAM *fp, int flags, va_list ap)
                             STR(name_buf), VSTREAM_PATH(fp));
                    return (conversions);
                }
+           } else if (hash_table->used >= ATTR_HASH_LIMIT) {
+               msg_warn("attribute count exceeds limit %d in input from %s",
+                        ATTR_HASH_LIMIT, VSTREAM_PATH(fp));
+               return (conversions);
            } else {
                htable_enter(hash_table, STR(name_buf),
                             mystrdup(STR(str_buf)));
diff --git a/postfix/src/util/attr_scan_plain b/postfix/src/util/attr_scan_plain
new file mode 100755 (executable)
index 0000000..8293c4c
Binary files /dev/null and b/postfix/src/util/attr_scan_plain differ
diff --git a/postfix/src/util/attr_scan_plain.c b/postfix/src/util/attr_scan_plain.c
new file mode 100644 (file)
index 0000000..de3c155
--- /dev/null
@@ -0,0 +1,484 @@
+/*++
+/* NAME
+/*     attr_scan_plain 3
+/* SUMMARY
+/*     recover attributes from byte stream
+/* SYNOPSIS
+/*     #include <attr.h>
+/*
+/*     int     attr_scan_plain(fp, flags, type, name, ..., ATTR_TYPE_END)
+/*     VSTREAM fp;
+/*     int     flags;
+/*     int     type;
+/*     char    *name;
+/*
+/*     int     attr_vscan_plain(fp, flags, ap)
+/*     VSTREAM fp;
+/*     int     flags;
+/*     va_list ap;
+/* DESCRIPTION
+/*     attr_scan_plain() takes zero or more (name, value) request attributes
+/*     and recovers the attribute values from the byte stream that was
+/*     possibly generated by attr_print_plain().
+/*
+/*     attr_vscan_plain() provides an alternative interface that is convenient
+/*     for calling from within a variadic function.
+/*
+/*     The input stream is formatted as follows, where (item)* stands
+/*     for zero or more instances of the specified item, and where
+/*     (item1 | item2) stands for choice:
+/*
+/* .in +5
+/*     attr-list :== simple-attr* newline
+/* .br
+/*     simple-attr :== attr-name "=" attr-value newline
+/* .br
+/*     attr-name :== any string without null or "=" or newline.
+/* .br
+/*     attr-value :== any string without null or newline.
+/* .br
+/*     newline :== the ASCII newline character
+/* .in
+/*
+/*     All attribute names and attribute values are sent as plain
+/*     strings. Each string must be no longer than 4*var_line_limit
+/*     characters. The formatting rules aim to make implementations in PERL
+/*     and other languages easy.
+/*
+/*      Normally, attributes must be received in the sequence as specified
+/*     with the attr_scan_plain() argument list.  The input stream may
+/*     contain additional attributes at any point in the input stream,
+/*     including additional instances of requested attributes.
+/*
+/*     Additional input attributes or input attribute instances are silently
+/*     skipped over, unless the ATTR_FLAG_EXTRA processing flag is specified
+/*     (see below). This allows for some flexibility in the evolution of
+/*     protocols while still providing the option of being strict where
+/*     this is desirable.
+/*
+/*     Arguments:
+/* .IP fp
+/*     Stream to recover the input attributes from.
+/* .IP flags
+/*     The bit-wise OR of zero or more of the following.
+/* .RS
+/* .IP ATTR_FLAG_MISSING
+/*     Log a warning when the input attribute list terminates before all
+/*     requested attributes are recovered. It is always an error when the
+/*     input stream ends without the newline attribute list terminator.
+/* .IP ATTR_FLAG_EXTRA
+/*     Log a warning and stop attribute recovery when the input stream
+/*     contains an attribute that was not requested. This includes the
+/*     case of additional instances of a requested attribute.
+/* .IP ATTR_FLAG_MORE
+/*     After recovering the requested attributes, leave the input stream
+/*     in a state that is usable for more attr_scan_plain() operations
+/*     from the same input attribute list.
+/*     By default, attr_scan_plain() skips forward past the input attribute
+/*     list terminator.
+/* .IP ATTR_FLAG_STRICT
+/*     For convenience, this value combines both ATTR_FLAG_MISSING and
+/*     ATTR_FLAG_EXTRA.
+/* .IP ATTR_FLAG_NONE
+/*     For convenience, this value requests none of the above.
+/* .RE
+/* .IP type
+/*     The type argument determines the arguments that follow.
+/* .RS
+/* .IP "ATTR_TYPE_NUM (char *, int *)"
+/*     This argument is followed by an attribute name and an integer pointer.
+/* .IP "ATTR_TYPE_LONG (char *, long *)"
+/*     This argument is followed by an attribute name and a long pointer.
+/* .IP "ATTR_TYPE_STR (char *, VSTRING *)"
+/*     This argument is followed by an attribute name and a VSTRING pointer.
+/* .IP "ATTR_TYPE_HASH (HTABLE *)"
+/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
+/*     All further input attributes are processed as string attributes.
+/*     No specific attribute sequence is enforced.
+/*     All attributes up to the attribute list terminator are read,
+/*     but only the first instance of each attribute is stored.
+/*     There can be no more than 1024 attributes in a hash table.
+/* .sp
+/*     The attribute string values are stored in the hash table under
+/*     keys equal to the attribute name (obtained from the input stream).
+/*     Values from the input stream are added to the hash table. Existing
+/*     hash table entries are not replaced.
+/* .sp
+/*     N.B. This construct must be followed by an ATTR_TYPE_END argument.
+/* .IP ATTR_TYPE_END
+/*     This argument terminates the requested attribute list.
+/* .RE
+/* BUGS
+/*     ATTR_TYPE_HASH (ATTR_TYPE_NAMEVAL) accepts attributes with arbitrary
+/*     names from possibly untrusted sources.
+/*     This is unsafe, unless the resulting table is queried only with
+/*     known to be good attribute names.
+/* DIAGNOSTICS
+/*     attr_scan_plain() and attr_vscan_plain() return -1 when malformed input
+/*     is detected (string too long, incomplete line, missing end marker).
+/*     Otherwise, the result value is the number of attributes that were
+/*     successfully recovered from the input stream (a hash table counts
+/*     as the number of entries stored into the table).
+/*
+/*     Panic: interface violation. All system call errors are fatal.
+/* SEE ALSO
+/*     attr_print_plain(3) send attributes over byte stream.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <htable.h>
+#include <attr.h>
+
+/* Application specific. */
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* attr_scan_plain_string - pull a string from the input stream */
+
+static int attr_scan_plain_string(VSTREAM *fp, VSTRING *plain_buf,
+                                       int terminator, const char *context)
+{
+    extern int var_line_limit;         /* XXX */
+    int     limit = var_line_limit * 4;
+    int     ch;
+
+    VSTRING_RESET(plain_buf);
+    while ((ch = VSTREAM_GETC(fp)) != '\n'
+       && (terminator == 0 || ch != terminator)) {
+       if (ch == VSTREAM_EOF) {
+           msg_warn("%s on %s while reading %s",
+               vstream_ftimeout(fp) ? "timeout" : "premature end-of-input",
+                    VSTREAM_PATH(fp), context);
+           return (-1);
+       }
+       VSTRING_ADDCH(plain_buf, ch);
+       if (LEN(plain_buf) > limit) {
+           msg_warn("string length > %d characters from %s while reading %s",
+                    limit, VSTREAM_PATH(fp), context);
+           return (-1);
+       }
+    }
+    VSTRING_TERMINATE(plain_buf);
+
+    if (msg_verbose)
+       msg_info("%s: %s", context, *STR(plain_buf) ? STR(plain_buf) : "(end)");
+    return (ch);
+}
+
+/* attr_scan_plain_number - pull a number from the input stream */
+
+static int attr_scan_plain_number(VSTREAM *fp, unsigned *ptr, VSTRING *str_buf,
+                                       int terminator, const char *context)
+{
+    char    junk = 0;
+    int     ch;
+
+    if ((ch = attr_scan_plain_string(fp, str_buf, terminator, context)) < 0)
+       return (-1);
+    if (sscanf(STR(str_buf), "%u%c", ptr, &junk) != 1 || junk != 0) {
+       msg_warn("malformed numerical data from %s while reading %s: %.100s",
+                VSTREAM_PATH(fp), context, STR(str_buf));
+       return (-1);
+    }
+    return (ch);
+}
+
+/* attr_scan_plain_long_number - pull a number from the input stream */
+
+static int attr_scan_plain_long_number(VSTREAM *fp, unsigned long *ptr,
+                                              VSTRING *str_buf,
+                                              int terminator,
+                                              const char *context)
+{
+    char    junk = 0;
+    int     ch;
+
+    if ((ch = attr_scan_plain_string(fp, str_buf, terminator, context)) < 0)
+       return (-1);
+    if (sscanf(STR(str_buf), "%lu%c", ptr, &junk) != 1 || junk != 0) {
+       msg_warn("malformed numerical data from %s while reading %s: %.100s",
+                VSTREAM_PATH(fp), context, STR(str_buf));
+       return (-1);
+    }
+    return (ch);
+}
+
+/* attr_vscan_plain - receive attribute list from stream */
+
+int     attr_vscan_plain(VSTREAM *fp, int flags, va_list ap)
+{
+    const char *myname = "attr_scan_plain";
+    static VSTRING *str_buf = 0;
+    static VSTRING *name_buf = 0;
+    int     wanted_type = -1;
+    char   *wanted_name;
+    unsigned int *number;
+    unsigned long *long_number;
+    VSTRING *string;
+    HTABLE *hash_table;
+    int     ch;
+    int     conversions;
+
+    /*
+     * Sanity check.
+     */
+    if (flags & ~ATTR_FLAG_ALL)
+       msg_panic("%s: bad flags: 0x%x", myname, flags);
+
+    /*
+     * Initialize.
+     */
+    if (str_buf == 0) {
+       str_buf = vstring_alloc(10);
+       name_buf = vstring_alloc(10);
+    }
+
+    /*
+     * Iterate over all (type, name, value) triples.
+     */
+    for (conversions = 0; /* void */ ; conversions++) {
+
+       /*
+        * Determine the next attribute type and attribute name on the
+        * caller's wish list.
+        * 
+        * If we're reading into a hash table, we already know that the
+        * attribute value is string-valued, and we get the attribute name
+        * from the input stream instead. This is secure only when the
+        * resulting table is queried with known to be good attribute names.
+        */
+       if (wanted_type != ATTR_TYPE_HASH) {
+           wanted_type = va_arg(ap, int);
+           if (wanted_type == ATTR_TYPE_END) {
+               if ((flags & ATTR_FLAG_MORE) != 0)
+                   return (conversions);
+               wanted_name = "(list terminator)";
+           } else if (wanted_type == ATTR_TYPE_HASH) {
+               wanted_name = "(any attribute name or list terminator)";
+               hash_table = va_arg(ap, HTABLE *);
+               if (va_arg(ap, int) !=ATTR_TYPE_END)
+                   msg_panic("%s: ATTR_TYPE_HASH not followed by ATTR_TYPE_END",
+                             myname);
+           } else {
+               wanted_name = va_arg(ap, char *);
+           }
+       }
+
+       /*
+        * Locate the next attribute of interest in the input stream.
+        */
+       for (;;) {
+
+           /*
+            * Get the name of the next attribute. Hitting EOF is always bad.
+            * Hitting the end-of-input early is OK if the caller is prepared
+            * to deal with missing inputs.
+            */
+           if (msg_verbose)
+               msg_info("%s: wanted attribute: %s",
+                        VSTREAM_PATH(fp), wanted_name);
+           if ((ch = attr_scan_plain_string(fp, name_buf, '=',
+                                   "input attribute name")) == VSTREAM_EOF)
+               return (-1);
+           if (ch == '\n' && LEN(name_buf) == 0) {
+               if (wanted_type == ATTR_TYPE_END
+                   || wanted_type == ATTR_TYPE_HASH)
+                   return (conversions);
+               if ((flags & ATTR_FLAG_MISSING) != 0)
+                   msg_warn("missing attribute %s in input from %s",
+                            wanted_name, VSTREAM_PATH(fp));
+               return (conversions);
+           }
+
+           /*
+            * See if the caller asks for this attribute.
+            */
+           if (wanted_type == ATTR_TYPE_HASH
+               || (wanted_type != ATTR_TYPE_END
+                   && strcmp(wanted_name, STR(name_buf)) == 0))
+               break;
+           if ((flags & ATTR_FLAG_EXTRA) != 0) {
+               msg_warn("unexpected attribute %s in input from %s",
+                        STR(name_buf), VSTREAM_PATH(fp));
+               return (conversions);
+           }
+
+           /*
+            * Skip over this attribute. The caller does not ask for it.
+            */
+           while (ch != '\n' && (ch = VSTREAM_GETC(fp)) != VSTREAM_EOF)
+                /* void */ ;
+       }
+
+       /*
+        * Do the requested conversion. If the target attribute is a
+        * non-array type, disallow sending a multi-valued attribute, and
+        * disallow sending no value. If the target attribute is an array
+        * type, allow the sender to send a zero-element array (i.e. no value
+        * at all). XXX Need to impose a bound on the number of array
+        * elements.
+        */
+       switch (wanted_type) {
+       case ATTR_TYPE_NUM:
+           if (ch != '=') {
+               msg_warn("missing value for number attribute %s from %s",
+                        STR(name_buf), VSTREAM_PATH(fp));
+               return (-1);
+           }
+           number = va_arg(ap, unsigned int *);
+           if ((ch = attr_scan_plain_number(fp, number, str_buf, 0,
+                                            "input attribute value")) < 0)
+               return (-1);
+           break;
+       case ATTR_TYPE_LONG:
+           if (ch != '=') {
+               msg_warn("missing value for number attribute %s from %s",
+                        STR(name_buf), VSTREAM_PATH(fp));
+               return (-1);
+           }
+           long_number = va_arg(ap, unsigned long *);
+           if ((ch = attr_scan_plain_long_number(fp, long_number, str_buf,
+                                       0, "input attribute value")) < 0)
+               return (-1);
+           break;
+       case ATTR_TYPE_STR:
+           if (ch != '=') {
+               msg_warn("missing value for string attribute %s from %s",
+                        STR(name_buf), VSTREAM_PATH(fp));
+               return (-1);
+           }
+           string = va_arg(ap, VSTRING *);
+           if ((ch = attr_scan_plain_string(fp, string, 0,
+                                            "input attribute value")) < 0)
+               return (-1);
+           break;
+       case ATTR_TYPE_HASH:
+           if (ch != '=') {
+               msg_warn("missing value for string attribute %s from %s",
+                        STR(name_buf), VSTREAM_PATH(fp));
+               return (-1);
+           }
+           if ((ch = attr_scan_plain_string(fp, str_buf, 0,
+                                            "input attribute value")) < 0)
+               return (-1);
+           if (htable_locate(hash_table, STR(name_buf)) != 0) {
+               if ((flags & ATTR_FLAG_EXTRA) != 0) {
+                   msg_warn("duplicate attribute %s in input from %s",
+                            STR(name_buf), VSTREAM_PATH(fp));
+                   return (conversions);
+               }
+           } else if (hash_table->used >= ATTR_HASH_LIMIT) {
+               msg_warn("attribute count exceeds limit %d in input from %s",
+                        ATTR_HASH_LIMIT, VSTREAM_PATH(fp));
+               return (conversions);
+           } else {
+               htable_enter(hash_table, STR(name_buf),
+                            mystrdup(STR(str_buf)));
+           }
+           break;
+       default:
+           msg_panic("%s: unknown type code: %d", myname, wanted_type);
+       }
+    }
+}
+
+/* attr_scan_plain - read attribute list from stream */
+
+int     attr_scan_plain(VSTREAM *fp, int flags,...)
+{
+    va_list ap;
+    int     ret;
+
+    va_start(ap, flags);
+    ret = attr_vscan_plain(fp, flags, ap);
+    va_end(ap);
+    return (ret);
+}
+
+#ifdef TEST
+
+ /*
+  * Proof of concept test program.  Mirror image of the attr_scan_plain test
+  * program.
+  */
+#include <msg_vstream.h>
+
+int     var_line_limit = 2048;
+
+int     main(int unused_argc, char **used_argv)
+{
+    VSTRING *str_val = vstring_alloc(1);
+    HTABLE *table = htable_create(1);
+    HTABLE_INFO **ht_info_list;
+    HTABLE_INFO **ht;
+    int     int_val;
+    long    long_val;
+    int     ret;
+
+    msg_verbose = 1;
+    msg_vstream_init(used_argv[0], VSTREAM_ERR);
+    if ((ret = attr_scan_plain(VSTREAM_IN,
+                              ATTR_FLAG_STRICT,
+                              ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val,
+                              ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val,
+                              ATTR_TYPE_STR, ATTR_NAME_STR, str_val,
+                              ATTR_TYPE_HASH, table,
+                              ATTR_TYPE_END)) > 3) {
+       vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val);
+       vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
+       vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
+       ht_info_list = htable_list(table);
+       for (ht = ht_info_list; *ht; ht++)
+           vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value);
+       myfree((char *) ht_info_list);
+    } else {
+       vstream_printf("return: %d\n", ret);
+    }
+    if ((ret = attr_scan_plain(VSTREAM_IN,
+                              ATTR_FLAG_STRICT,
+                              ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val,
+                              ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val,
+                              ATTR_TYPE_STR, ATTR_NAME_STR, str_val,
+                              ATTR_TYPE_END)) == 3) {
+       vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val);
+       vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
+       vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
+       ht_info_list = htable_list(table);
+       for (ht = ht_info_list; *ht; ht++)
+           vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value);
+       myfree((char *) ht_info_list);
+    } else {
+       vstream_printf("return: %d\n", ret);
+    }
+    if (vstream_fflush(VSTREAM_OUT) != 0)
+       msg_fatal("write error: %m");
+
+    vstring_free(str_val);
+    htable_free(table, myfree);
+
+    return (0);
+}
+
+#endif
diff --git a/postfix/src/util/attr_scan_plain.ref b/postfix/src/util/attr_scan_plain.ref
new file mode 100644 (file)
index 0000000..68aa7d1
--- /dev/null
@@ -0,0 +1,46 @@
+./attr_print_plain: send attr number = 4711
+./attr_print_plain: send attr long_number = 1234
+./attr_print_plain: send attr string = whoopee
+./attr_print_plain: send attr name foo-name value foo-value
+./attr_print_plain: send attr name bar-name value bar-value
+./attr_print_plain: send attr number = 4711
+./attr_print_plain: send attr long_number = 1234
+./attr_print_plain: send attr string = whoopee
+./attr_scan_plain: unknown_stream: wanted attribute: number
+./attr_scan_plain: input attribute name: number
+./attr_scan_plain: input attribute value: 4711
+./attr_scan_plain: unknown_stream: wanted attribute: long_number
+./attr_scan_plain: input attribute name: long_number
+./attr_scan_plain: input attribute value: 1234
+./attr_scan_plain: unknown_stream: wanted attribute: string
+./attr_scan_plain: input attribute name: string
+./attr_scan_plain: input attribute value: whoopee
+./attr_scan_plain: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan_plain: input attribute name: foo-name
+./attr_scan_plain: input attribute value: foo-value
+./attr_scan_plain: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan_plain: input attribute name: bar-name
+./attr_scan_plain: input attribute value: bar-value
+./attr_scan_plain: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan_plain: input attribute name: (end)
+./attr_scan_plain: unknown_stream: wanted attribute: number
+./attr_scan_plain: input attribute name: number
+./attr_scan_plain: input attribute value: 4711
+./attr_scan_plain: unknown_stream: wanted attribute: long_number
+./attr_scan_plain: input attribute name: long_number
+./attr_scan_plain: input attribute value: 1234
+./attr_scan_plain: unknown_stream: wanted attribute: string
+./attr_scan_plain: input attribute name: string
+./attr_scan_plain: input attribute value: whoopee
+./attr_scan_plain: unknown_stream: wanted attribute: (list terminator)
+./attr_scan_plain: input attribute name: (end)
+number 4711
+long_number 1234
+string whoopee
+(hash) foo-name foo-value
+(hash) bar-name bar-value
+number 4711
+long_number 1234
+string whoopee
+(hash) foo-name foo-value
+(hash) bar-name bar-value
diff --git a/postfix/src/util/auto_clnt.c b/postfix/src/util/auto_clnt.c
new file mode 100644 (file)
index 0000000..1a487aa
--- /dev/null
@@ -0,0 +1,246 @@
+/*++
+/* NAME
+/*     auto_clnt 3
+/* SUMMARY
+/*     client endpoint maintenance
+/* SYNOPSIS
+/*     #include <auto_clnt.h>
+/*
+/*     AUTO_CLNT *auto_clnt_create(max_idle, max_ttl, open_action, context)
+/*     int     max_idle;
+/*     int     max_ttl;
+/*     VSTREAM *(open_action)(void *context)
+/*     void    *context;
+/*
+/*     VSTREAM *auto_clnt_access(auto_clnt)
+/*     AUTO_CLNT *auto_clnt;
+/*
+/*     void    auto_clnt_recover(auto_clnt)
+/*     AUTO_CLNT *auto_clnt;
+/*
+/*     void    auto_clnt_free(auto_clnt)
+/*     AUTO_CLNT *auto_clnt;
+/* DESCRIPTION
+/*     This module maintains local IPC client endpoints that automatically
+/*     disconnect after a being idle for a configurable amount of time,
+/*     that disconnect after a configurable time to live,
+/*     and that transparently handle most server-initiated disconnects.
+/*     Server disconnect is detected by read-selecting the client endpoint.
+/*     The code assumes that the server has disconnected when the endpoint
+/*     becomes readable.
+/*
+/*     auto_clnt_create() instantiates a client endpoint.
+/*
+/*     auto_clnt_access() returns an open stream to the service specified
+/*     to auto_clnt_create(). The stream instance may change between calls.
+/*     The result is a null pointer in case of failure.
+/*
+/*     auto_clnt_recover() recovers from a server-initiated disconnect
+/*     that happened in the middle of an I/O operation.
+/*
+/*     auto_clnt_free() destroys of the specified client endpoint.
+/*
+/*     Arguments:
+/* .IP max_idle
+/*     Idle time after which the client disconnects.
+/* .IP max_ttl
+/*     Upper bound on the time that a connection is allowed to persist.
+/* .IP open_action
+/*     Application call-back routine that opens a stream or returns a
+/*     null pointer upon failure. In case of success, the call-back routine
+/*     is expected to set the stream pathname to the server endpoint name.
+/* .IP context
+/*     Application context that is passed to the open_action routine.
+/* DIAGNOSTICS
+/*     Warnings: communication failure. Fatal error: out of memory.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <events.h>
+#include <iostuff.h>
+#include <auto_clnt.h>
+
+/* Application-specific. */
+
+ /*
+  * AUTO_CLNT is an opaque structure. None of the access methods can easily
+  * be implemented as a macro, and access is not performance critical anyway.
+  */
+struct AUTO_CLNT {
+    VSTREAM *vstream;                  /* buffered I/O */
+    int     max_idle;                  /* time before client disconnect */
+    int     max_ttl;                   /* time before client disconnect */
+    VSTREAM *(*open_action) (void *);  /* callback */
+    void   *context;                   /* callback context */
+};
+
+static void auto_clnt_close(AUTO_CLNT *);
+
+/* auto_clnt_event - server-initiated disconnect or client-side max_idle */
+
+static void auto_clnt_event(int unused_event, char *context)
+{
+    AUTO_CLNT *auto_clnt = (AUTO_CLNT *) context;
+
+    /*
+     * Sanity check. This routine causes the stream to be closed, so it
+     * cannot be called when the stream is already closed.
+     */
+    if (auto_clnt->vstream == 0)
+       msg_panic("auto_clnt_event: stream is closed");
+
+    auto_clnt_close(auto_clnt);
+}
+
+/* auto_clnt_ttl_event - client-side expiration */
+
+static void auto_clnt_ttl_event(int event, char *context)
+{
+
+    /*
+     * XXX This function is needed only because event_request_timer() cannot
+     * distinguish between requests that specify the same call-back routine
+     * and call-back context. The fix is obvious: specify a request ID along
+     * with the call-back routine, but there is too much code that would have
+     * to be changed.
+     * 
+     * XXX Should we be concerned that an overly agressive optimizer will
+     * eliminate this function and replace calls to auto_clnt_ttl_event() by
+     * direct calls to auto_clnt_event()? It should not, because there exists
+     * code that takes the address of both functions.
+     */
+    auto_clnt_event(event, context);
+}
+
+/* auto_clnt_open - connect to service */
+
+static void auto_clnt_open(AUTO_CLNT *auto_clnt)
+{
+
+    /*
+     * Sanity check.
+     */
+    if (auto_clnt->vstream)
+       msg_panic("auto_clnt_open: stream is open");
+
+    /*
+     * Schedule a read event so that we can clean up when the remote side
+     * disconnects, and schedule a timer event so that we can cleanup an idle
+     * connection. Note that both events are handled by the same routine.
+     * 
+     * Finally, schedule an event to force disconnection even when the
+     * connection is not idle. This is to prevent one client from clinging on
+     * to a server forever.
+     */
+    auto_clnt->vstream =
+       auto_clnt->open_action(auto_clnt->context);
+
+    if (auto_clnt->vstream != 0) {
+       close_on_exec(vstream_fileno(auto_clnt->vstream), CLOSE_ON_EXEC);
+       event_enable_read(vstream_fileno(auto_clnt->vstream), auto_clnt_event,
+                         (char *) auto_clnt);
+       event_request_timer(auto_clnt_event, (char *) auto_clnt,
+                           auto_clnt->max_idle);
+       event_request_timer(auto_clnt_ttl_event, (char *) auto_clnt,
+                           auto_clnt->max_ttl);
+    }
+}
+
+/* auto_clnt_close - disconnect from service */
+
+static void auto_clnt_close(AUTO_CLNT *auto_clnt)
+{
+
+    /*
+     * Sanity check.
+     */
+    if (auto_clnt->vstream == 0)
+       msg_panic("auto_clnt_close: stream is closed");
+
+    /*
+     * Be sure to disable read and timer events.
+     */
+    if (msg_verbose)
+       msg_info("%s stream disconnect", VSTREAM_PATH(auto_clnt->vstream));
+    event_disable_readwrite(vstream_fileno(auto_clnt->vstream));
+    event_cancel_timer(auto_clnt_event, (char *) auto_clnt);
+    event_cancel_timer(auto_clnt_ttl_event, (char *) auto_clnt);
+    (void) vstream_fclose(auto_clnt->vstream);
+    auto_clnt->vstream = 0;
+}
+
+/* auto_clnt_recover - recover from server-initiated disconnect */
+
+void    auto_clnt_recover(AUTO_CLNT *auto_clnt)
+{
+
+    /*
+     * Clean up. Don't re-connect until the caller needs it.
+     */
+    if (auto_clnt->vstream)
+       auto_clnt_close(auto_clnt);
+}
+
+/* auto_clnt_access - access a client stream */
+
+VSTREAM *auto_clnt_access(AUTO_CLNT *auto_clnt)
+{
+
+    /*
+     * Open a stream or restart the idle timer.
+     * 
+     * Important! Do not restart the TTL timer!
+     */
+    if (auto_clnt->vstream == 0) {
+       auto_clnt_open(auto_clnt);
+    } else {
+       event_request_timer(auto_clnt_event, (char *) auto_clnt,
+                           auto_clnt->max_idle);
+    }
+    return (auto_clnt->vstream);
+}
+
+/* auto_clnt_create - create client stream connection */
+
+AUTO_CLNT *auto_clnt_create(int max_idle, int max_ttl,
+                      VSTREAM *(*open_action) (void *), void *context)
+{
+    AUTO_CLNT *auto_clnt;
+
+    /*
+     * Don't open the stream until the caller needs it.
+     */
+    auto_clnt = (AUTO_CLNT *) mymalloc(sizeof(*auto_clnt));
+    auto_clnt->vstream = 0;
+    auto_clnt->max_idle = max_idle;
+    auto_clnt->max_ttl = max_ttl;
+    auto_clnt->open_action = open_action;
+    auto_clnt->context = context;
+    return (auto_clnt);
+}
+
+/* auto_clnt_free - destroy client stream instance */
+
+void    auto_clnt_free(AUTO_CLNT *auto_clnt)
+{
+    if (auto_clnt->vstream)
+       auto_clnt_close(auto_clnt);
+    myfree((char *) auto_clnt);
+}
diff --git a/postfix/src/util/auto_clnt.h b/postfix/src/util/auto_clnt.h
new file mode 100644 (file)
index 0000000..a4aaec2
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _AUTO_CLNT_H_INCLUDED_
+#define _AUTO_CLNT_H_INCLUDED_
+
+/*++
+/* NAME
+/*     auto_clnt 3h
+/* SUMMARY
+/*     client endpoint maintenance
+/* SYNOPSIS
+/*     #include <auto_clnt.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <vstream.h>
+
+ /*
+  * External interface.
+  */
+typedef struct AUTO_CLNT AUTO_CLNT;
+
+extern AUTO_CLNT *auto_clnt_create(int, int, VSTREAM *(*) (void *), void *);
+extern VSTREAM *auto_clnt_access(AUTO_CLNT *);
+extern void auto_clnt_recover(AUTO_CLNT *);
+extern void auto_clnt_free(AUTO_CLNT *);
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
index 9366915c2a7a100706389cbec5ad4ae646b5e65f..fc502d18c465736bed5e778171ec332a5a1a3949 100644 (file)
 /*     char    *str;
 /*     VSTREAM *stream;
 /*
-/*     long    vstream_ftell(stream)
+/*     off_t   vstream_ftell(stream)
 /*     VSTREAM *stream;
 /*
-/*     long    vstream_fseek(stream, offset, whence)
+/*     off_t   vstream_fseek(stream, offset, whence)
 /*     VSTREAM *stream;
-/*     long    offset;
+/*     off_t   offset;
 /*     int     whence;
 /*
 /*     int     vstream_fflush(stream)
@@ -795,7 +795,7 @@ static int vstream_buf_space(VBUF *bp, int want)
 
 /* vstream_fseek - change I/O position */
 
-long    vstream_fseek(VSTREAM *stream, long offset, int whence)
+off_t   vstream_fseek(VSTREAM *stream, off_t offset, int whence)
 {
     char   *myname = "vstream_fseek";
     VBUF   *bp = &stream->buf;
@@ -857,7 +857,7 @@ long    vstream_fseek(VSTREAM *stream, long offset, int whence)
 
 /* vstream_ftell - return file offset */
 
-long    vstream_ftell(VSTREAM *stream)
+off_t   vstream_ftell(VSTREAM *stream)
 {
     VBUF   *bp = &stream->buf;
 
@@ -874,7 +874,7 @@ long    vstream_ftell(VSTREAM *stream)
      * the last read, write or seek operation.
      */
     if ((bp->flags & VSTREAM_FLAG_SEEK) == 0) {
-       if ((stream->offset = lseek(stream->fd, 0L, SEEK_CUR)) < 0) {
+       if ((stream->offset = lseek(stream->fd, (off_t) 0, SEEK_CUR)) < 0) {
            bp->flags |= VSTREAM_FLAG_NSEEK;
            return (-1);
        }
index 3ec6382956b6a3d087641ef3b1f87a77b41b5979..322cd188e4b0c2c934ec2a272302c712b984834d 100644 (file)
@@ -37,7 +37,7 @@ typedef struct VSTREAM {
     VSTREAM_FN read_fn;                        /* buffer fill action */
     VSTREAM_FN write_fn;               /* buffer fill action */
     void   *context;                   /* application context */
-    long    offset;                    /* cached seek info */
+    off_t   offset;                    /* cached seek info */
     char   *path;                      /* give it at least try */
     int     read_fd;                   /* read channel (double-buffered) */
     int     write_fd;                  /* write channel (double-buffered) */
@@ -72,8 +72,8 @@ extern VSTREAM vstream_fstd[];                /* pre-defined streams */
 
 extern VSTREAM *vstream_fopen(const char *, int, int);
 extern int vstream_fclose(VSTREAM *);
-extern long vstream_fseek(VSTREAM *, long, int);
-extern long vstream_ftell(VSTREAM *);
+extern off_t vstream_fseek(VSTREAM *, off_t, int);
+extern off_t vstream_ftell(VSTREAM *);
 extern int vstream_fflush(VSTREAM *);
 extern int vstream_fputs(const char *, VSTREAM *);
 extern VSTREAM *vstream_fdopen(int, int);
index 0f3e33b5e4006db8c09a2e04b2ef8bcb63cfe2b7..43ffae1e266f3dcdd913b2cf23e25ae86b6016bf 100644 (file)
 /*     of at least "len" bytes. The minimal length is 1. The result
 /*     is a null-terminated string of length zero.
 /*
-/*     vstring_ctl() gives additional control over vstring behavior.
+/*     vstring_ctl() gives additional control over VSTRING behavior.
 /*     The function takes a VSTRING pointer and a list of zero
 /*     or more (name,value) pairs. The expected value type
 /*     depends on the specified name. The value name codes are: