-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
--- /dev/null
+1 - Purpose of this document
+============================
+
+This document describes how to debug parts of the Postfix mail
+system, either by making the software log a lot of detail to the
+syslog daemon, or by running some daemon processes under control
+of an interactive debugger.
+
+2 - Verbose logging for specific SMTP connections
+=================================================
+
+In /etc/postfix/main.cf, list the remote site name or address in
+the "debug_peer_list" parameter. For example, in order to make the
+software log a lot of information to the syslog daemon for connections
+from or to the loopback interface:
+
+ debug_peer_list = 127.0.0.1
+
+3 - Making daemon programs more verbose
+=======================================
+
+Append one or more -v options to commands in /etc/postfix/master.cf
+and type "postfix reload". This will cause a lot of activity to be
+logged to the syslog daemon.
+
+4 - Tracing a Postfix daemon process
+====================================
+
+Some systems allow you to inspect a running process with a system
+call tracer. For example:
+
+ # trace -p process-id
+ # strace -p process-id
+ # truss -p process-id
+ # ktrace -p process-id
+
+This can give valuable information about what a process is attempting
+to do. This is as much information as you can get without running
+a debugger program, as described in the next section.
+
+5 - Running daemon programs under an interactive debugger
+=========================================================
+
+Append a -D option to the suspect command in /etc/postfix/master.cf,
+for example:
+
+ smtp inet n - n - - smtpd -D
+
+Edit the debugger_command definition in /etc/postfix/main.cf so
+that it invokes the debugger of your choice, for example:
+
+ debugger_command =
+ PATH=/usr/bin:/usr/X11R6/bin
+ xxgdb $daemon_directory/$process_name $process_id & sleep 5
+
+Export XAUTHORITY so that X access control works, for example:
+
+ % setenv XAUTHORITY ~/.Xauthority
+
+Stop and start the Postfix system.
+
+Whenever the suspect daemon process is started, a debugger window
+pops up and you can watch in detail what happens.
mailbox_command of $user, $home, $shell, $recipient,
$extension, $domain, and $recipient_delimiter. Files:
local/command.c, local/dotforward.c, local/local_expand.c.
+
+19990506
+
+ Cleanup: eliminated misleading warnings about unknown HELO
+ etc. SMTPD restrictions when the HELO etc. information is
+ not available. File: smtpd/smtpd_check.c.
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
mail_name = Postfix
mail_owner = postfix
mail_spool_directory = /var/mail
-mail_version = Snapshot-19990504
+mail_version = Snapshot-19990505
mailbox_command =
mailbox_transport =
maps_rbl_domains = rbl.maps.vix.com
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
-#define DEF_MAIL_VERSION "Snapshot-19990504"
+#define DEF_MAIL_VERSION "Snapshot-19990505"
extern char *var_mail_version;
/* LICENSE
/* A timeout error is reported to the vstream module as an I/O error.
/* .PP
/* smtp_printf() formats its arguments and writes the result to
-/* the named stream, followed by a CR LF stream. The stream is flushed.
+/* the named stream, followed by a CR LF pair. The stream is flushed.
/* Long lines of text are not broken.
/*
/* smtp_get() reads the named stream up to and including
* XXX The timeout etc. state is static, so a process can have at most
* one SMTP session at a time. We could use the VSTREAM file descriptor
* number as key into a BINHASH table with per-stream contexts. This
- * would allow us to keep talk to multiple SMTP streams at the same time.
- * Another possibilty is to use the file descriptor as an index into a
+ * would allow us to talk to multiple SMTP streams at the same time.
+ * Another possibility is to use the file descriptor as an index into a
* linear table of structure pointers. In either case we would need to
* provide an smtp_timeout_cleanup() routine to dispose of memory that is
* no longer needed.
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
/*
* HELO/EHLO parameter restrictions.
*/
- if (state->helo_name) {
- if (is_map_command(name, CHECK_HELO_ACL, cpp) && state->helo_name) {
+ if (is_map_command(name, CHECK_HELO_ACL, cpp) && state->helo_name) {
+ if (state->helo_name)
*status = check_domain_access(state, **cpp, state->helo_name, FULL);
- return (1);
- }
- if (strcasecmp(name, REJECT_INVALID_HOSTNAME) == 0) {
+ return (1);
+ }
+ if (strcasecmp(name, REJECT_INVALID_HOSTNAME) == 0) {
+ if (state->helo_name) {
if (*state->helo_name != '[')
*status = reject_invalid_hostname(state, state->helo_name);
else
*status = reject_invalid_hostaddr(state, state->helo_name);
- return (1);
}
- if (strcasecmp(name, REJECT_UNKNOWN_HOSTNAME) == 0) {
+ return (1);
+ }
+ if (strcasecmp(name, REJECT_UNKNOWN_HOSTNAME) == 0) {
+ if (state->helo_name) {
if (*state->helo_name != '[')
*status = reject_unknown_hostname(state, state->helo_name);
else
*status = reject_invalid_hostaddr(state, state->helo_name);
- return (1);
}
- if (strcasecmp(name, PERMIT_NAKED_IP_ADDR) == 0) {
+ return (1);
+ }
+ if (strcasecmp(name, PERMIT_NAKED_IP_ADDR) == 0) {
+ if (state->helo_name) {
if (state->helo_name[strspn(state->helo_name, "0123456789.")] == 0
&& (*status = reject_invalid_hostaddr(state, state->helo_name)) == 0)
*status = SMTPD_CHECK_OK;
- return (1);
}
- if (strcasecmp(name, REJECT_NON_FQDN_HOSTNAME) == 0) {
+ return (1);
+ }
+ if (strcasecmp(name, REJECT_NON_FQDN_HOSTNAME) == 0) {
+ if (state->helo_name) {
if (*state->helo_name != '[')
*status = reject_non_fqdn_hostname(state, state->helo_name);
else
*status = reject_invalid_hostaddr(state, state->helo_name);
- return (1);
}
+ return (1);
}
/*
* Sender mail address restrictions.
*/
- if (state->sender) {
- if (is_map_command(name, CHECK_SENDER_ACL, cpp) && state->sender) {
+ if (is_map_command(name, CHECK_SENDER_ACL, cpp) && state->sender) {
+ if (state->sender)
*status = check_mail_access(state, **cpp, state->sender);
- return (1);
- }
- if (strcasecmp(name, REJECT_UNKNOWN_ADDRESS) == 0) {
+ return (1);
+ }
+ if (strcasecmp(name, REJECT_UNKNOWN_ADDRESS) == 0) {
+ if (state->sender)
*status = reject_unknown_address(state, state->sender);
- return (1);
- }
- if (strcasecmp(name, REJECT_UNKNOWN_SENDDOM) == 0) {
+ return (1);
+ }
+ if (strcasecmp(name, REJECT_UNKNOWN_SENDDOM) == 0) {
+ if (state->sender)
*status = reject_unknown_address(state, state->sender);
- return (1);
- }
- if (strcasecmp(name, REJECT_NON_FQDN_SENDER) == 0) {
- if (*state->sender)
- *status = reject_non_fqdn_address(state, state->sender);
- return (1);
- }
+ return (1);
+ }
+ if (strcasecmp(name, REJECT_NON_FQDN_SENDER) == 0) {
+ if (*state->sender)
+ *status = reject_non_fqdn_address(state, state->sender);
+ return (1);
}
return (0);
}
#
# unknown sender/recipient domain
#
-unknown_address_reject_code 550
+unknown_address_reject_code 554
recipient_restrictions reject_unknown_recipient_domain,reject_unknown_sender_domain
mail wietse@porcupine.org
rcpt wietse@porcupine.org
>>> client unknown 168.100.189.13
OK
>>> client random.bad.domain 123.123.123.123
-./smtpd_check: reject: CONNECT from random.bad.domain[123.123.123.123]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: CONNECT from random.bad.domain[123.123.123.123]: 554 match bad.domain
+554 match bad.domain
>>> client friend.bad.domain 123.123.123.123
OK
>>> client bad.domain 123.123.123.123
-./smtpd_check: reject: CONNECT from bad.domain[123.123.123.123]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: CONNECT from bad.domain[123.123.123.123]: 554 match bad.domain
+554 match bad.domain
>>> client wzv.win.tue.nl 131.155.210.17
OK
>>> client aa.win.tue.nl 131.155.210.18
-./smtpd_check: reject: CONNECT from aa.win.tue.nl[131.155.210.18]: 550 match 131.155.210
-550 match 131.155.210
+./smtpd_check: reject: CONNECT from aa.win.tue.nl[131.155.210.18]: 554 match 131.155.210
+554 match 131.155.210
>>> client_restrictions permit_mynetworks
OK
>>> #
>>> helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,hash:./smtpd_check_access
OK
>>> helo random.bad.domain
-./smtpd_check: reject: HELO from foo[123.123.123.123]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: HELO from foo[123.123.123.123]: 554 match bad.domain
+554 match bad.domain
>>> helo friend.bad.domain
OK
>>> helo_restrictions reject_invalid_hostname,reject_unknown_hostname
>>> sender_restrictions hash:./smtpd_check_access
OK
>>> mail bad-sender@any.domain
-./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad-sender@
-550 match bad-sender@
+./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 match bad-sender@
+554 match bad-sender@
>>> mail bad-sender@good.domain
OK
>>> mail reject@this.address
-./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match reject@this.address
-550 match reject@this.address
+./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 match reject@this.address
+554 match reject@this.address
>>> mail Reject@this.address
-./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match reject@this.address
-550 match reject@this.address
+./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 match reject@this.address
+554 match reject@this.address
>>> mail foo@bad.domain
-./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 match bad.domain
+554 match bad.domain
>>> mail foo@Bad.domain
-./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 match bad.domain
+554 match bad.domain
>>> mail foo@random.bad.domain
-./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 match bad.domain
+554 match bad.domain
>>> mail foo@friend.bad.domain
OK
>>> #
>>> client foo 123.123.123.123
OK
>>> rcpt foo@watson.ibm.com
-./smtpd_check: reject: RCPT from foo[123.123.123.123]: 550 <foo@watson.ibm.com>: Relay access denied
-550 <foo@watson.ibm.com>: Relay access denied
+./smtpd_check: reject: RCPT from foo[123.123.123.123]: 554 <foo@watson.ibm.com>: Relay access denied
+554 <foo@watson.ibm.com>: Relay access denied
>>> rcpt foo@porcupine.org
OK
>>> recipient_restrictions check_relay_domains
>>> client foo 123.123.123.123
OK
>>> rcpt foo@watson.ibm.com
-./smtpd_check: reject: RCPT from foo[123.123.123.123]: 550 <foo@watson.ibm.com>: Relay access denied
-550 <foo@watson.ibm.com>: Relay access denied
+./smtpd_check: reject: RCPT from foo[123.123.123.123]: 554 <foo@watson.ibm.com>: Relay access denied
+554 <foo@watson.ibm.com>: Relay access denied
>>> rcpt foo@porcupine.org
OK
>>> recipient_restrictions hash:./smtpd_check_access
OK
>>> mail bad-sender@any.domain
-./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad-sender@
-550 match bad-sender@
+./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 match bad-sender@
+554 match bad-sender@
>>> mail bad-sender@good.domain
OK
>>> mail reject@this.address
-./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match reject@this.address
-550 match reject@this.address
+./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 match reject@this.address
+554 match reject@this.address
>>> mail foo@bad.domain
-./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 match bad.domain
+554 match bad.domain
>>> mail foo@random.bad.domain
-./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 match bad.domain
+554 match bad.domain
>>> mail foo@friend.bad.domain
OK
>>> #
>>> client spike.porcupine.org 168.100.189.2
OK
>>> client foo 127.0.0.2
-./smtpd_check: reject: CONNECT from foo[127.0.0.2]: 550 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com
-550 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com
+./smtpd_check: reject: CONNECT from foo[127.0.0.2]: 554 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com
+554 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com
>>> #
>>> # Hybrids
>>> #
>>> client foo 131.155.210.17
OK
>>> rcpt foo@watson.ibm.com
-./smtpd_check: reject: RCPT from foo[131.155.210.17]: 550 <foo@watson.ibm.com>: Relay access denied
-550 <foo@watson.ibm.com>: Relay access denied
+./smtpd_check: reject: RCPT from foo[131.155.210.17]: 554 <foo@watson.ibm.com>: Relay access denied
+554 <foo@watson.ibm.com>: Relay access denied
>>> recipient_restrictions check_client_access,hash:./smtpd_check_access,check_relay_domains
OK
>>> client foo 131.155.210.17
>>> recipient_restrictions check_helo_access,hash:./smtpd_check_access,check_relay_domains
OK
>>> helo bad.domain
-./smtpd_check: reject: HELO from foo[131.155.210.17]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: HELO from foo[131.155.210.17]: 554 match bad.domain
+554 match bad.domain
>>> rcpt foo@porcupine.org
-./smtpd_check: reject: RCPT from foo[131.155.210.17]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: RCPT from foo[131.155.210.17]: 554 match bad.domain
+554 match bad.domain
>>> helo 131.155.210.17
./smtpd_check: warning: valid_hostname: numeric hostname: 131.155.210.17
OK
>>> recipient_restrictions check_sender_access,hash:./smtpd_check_access,check_relay_domains
OK
>>> mail foo@bad.domain
-./smtpd_check: reject: MAIL from foo[131.155.210.17]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: MAIL from foo[131.155.210.17]: 554 match bad.domain
+554 match bad.domain
>>> rcpt foo@porcupine.org
-./smtpd_check: reject: RCPT from foo[131.155.210.17]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: RCPT from foo[131.155.210.17]: 554 match bad.domain
+554 match bad.domain
>>> mail foo@friend.bad.domain
OK
>>> rcpt foo@porcupine.org
>>> rcpt wietse@wzv.win.tue.nl
OK
>>> rcpt wietse@trouble.org
-./smtpd_check: reject: RCPT from foo[131.155.210.17]: 550 <wietse@trouble.org> Access denied
-550 <wietse@trouble.org> Access denied
+./smtpd_check: reject: RCPT from foo[131.155.210.17]: 554 <wietse@trouble.org> Access denied
+554 <wietse@trouble.org> Access denied
>>> rcpt wietse@porcupine.org
OK
>>> #
>>> mail foo@good.domain
OK
>>> rcpt foo@porcupine.org
-./smtpd_check: reject: RCPT from foo[131.155.210.17]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: RCPT from foo[131.155.210.17]: 554 match bad.domain
+554 match bad.domain
>>> helo good.domain
OK
>>> mail foo@bad.domain
OK
>>> rcpt foo@porcupine.org
-./smtpd_check: reject: RCPT from foo[131.155.210.17]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: RCPT from foo[131.155.210.17]: 554 match bad.domain
+554 match bad.domain
>>> #
>>> # FQDN restrictions
>>> #
>>> client unknown 168.100.189.13
OK
>>> client random.bad.domain 123.123.123.123
-./smtpd_check: reject: CONNECT from random.bad.domain[123.123.123.123]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: CONNECT from random.bad.domain[123.123.123.123]: 554 match bad.domain
+554 match bad.domain
>>> client friend.bad.domain 123.123.123.123
OK
>>> client bad.domain 123.123.123.123
-./smtpd_check: reject: CONNECT from bad.domain[123.123.123.123]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: CONNECT from bad.domain[123.123.123.123]: 554 match bad.domain
+554 match bad.domain
>>> client wzv.win.tue.nl 131.155.210.17
OK
>>> client aa.win.tue.nl 131.155.210.18
-./smtpd_check: reject: CONNECT from aa.win.tue.nl[131.155.210.18]: 550 match 131.155.210
-550 match 131.155.210
+./smtpd_check: reject: CONNECT from aa.win.tue.nl[131.155.210.18]: 554 match 131.155.210
+554 match 131.155.210
>>> client_restrictions permit_mynetworks
OK
>>> #
>>> helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,check_helo_access,hash:./smtpd_check_access
OK
>>> helo random.bad.domain
-./smtpd_check: reject: HELO from foo[123.123.123.123]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: HELO from foo[123.123.123.123]: 554 match bad.domain
+554 match bad.domain
>>> helo friend.bad.domain
OK
>>> #
>>> sender_restrictions check_sender_access,hash:./smtpd_check_access
OK
>>> mail bad-sender@any.domain
-./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad-sender@
-550 match bad-sender@
+./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 match bad-sender@
+554 match bad-sender@
>>> mail bad-sender@good.domain
OK
>>> mail reject@this.address
-./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match reject@this.address
-550 match reject@this.address
+./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 match reject@this.address
+554 match reject@this.address
>>> mail Reject@this.address
-./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match reject@this.address
-550 match reject@this.address
+./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 match reject@this.address
+554 match reject@this.address
>>> mail foo@bad.domain
-./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 match bad.domain
+554 match bad.domain
>>> mail foo@Bad.domain
-./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 match bad.domain
+554 match bad.domain
>>> mail foo@random.bad.domain
-./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 match bad.domain
+554 match bad.domain
>>> mail foo@friend.bad.domain
OK
>>> #
>>> client foo 123.123.123.123
OK
>>> rcpt foo@watson.ibm.com
-./smtpd_check: reject: RCPT from foo[123.123.123.123]: 550 <foo@watson.ibm.com>: Relay access denied
-550 <foo@watson.ibm.com>: Relay access denied
+./smtpd_check: reject: RCPT from foo[123.123.123.123]: 554 <foo@watson.ibm.com>: Relay access denied
+554 <foo@watson.ibm.com>: Relay access denied
>>> rcpt foo@porcupine.org
OK
>>> recipient_restrictions check_relay_domains
>>> client foo 123.123.123.123
OK
>>> rcpt foo@watson.ibm.com
-./smtpd_check: reject: RCPT from foo[123.123.123.123]: 550 <foo@watson.ibm.com>: Relay access denied
-550 <foo@watson.ibm.com>: Relay access denied
+./smtpd_check: reject: RCPT from foo[123.123.123.123]: 554 <foo@watson.ibm.com>: Relay access denied
+554 <foo@watson.ibm.com>: Relay access denied
>>> rcpt foo@porcupine.org
OK
>>> recipient_restrictions check_recipient_access,hash:./smtpd_check_access
OK
>>> mail bad-sender@any.domain
-./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad-sender@
-550 match bad-sender@
+./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 match bad-sender@
+554 match bad-sender@
>>> mail bad-sender@good.domain
OK
>>> mail reject@this.address
-./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match reject@this.address
-550 match reject@this.address
+./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 match reject@this.address
+554 match reject@this.address
>>> mail foo@bad.domain
-./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 match bad.domain
+554 match bad.domain
>>> mail foo@random.bad.domain
-./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad.domain
-550 match bad.domain
+./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 match bad.domain
+554 match bad.domain
>>> mail foo@friend.bad.domain
OK
>>> #
>>> client spike.porcupine.org 168.100.189.2
OK
>>> client foo 127.0.0.2
-./smtpd_check: reject: CONNECT from foo[127.0.0.2]: 550 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com
-550 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com
+./smtpd_check: reject: CONNECT from foo[127.0.0.2]: 554 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com
+554 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com
>>> #
>>> # unknown sender/recipient domain
>>> #
->>> unknown_address_reject_code 550
+>>> unknown_address_reject_code 554
OK
>>> recipient_restrictions reject_unknown_recipient_domain,reject_unknown_sender_domain
OK
>>> rcpt wietse@porcupine.org
OK
>>> rcpt wietse@no.recipient.domain
-./smtpd_check: reject: RCPT from foo[127.0.0.2]: 550 <no.recipient.domain>: Domain not found
-550 <no.recipient.domain>: Domain not found
+./smtpd_check: reject: RCPT from foo[127.0.0.2]: 554 <no.recipient.domain>: Domain not found
+554 <no.recipient.domain>: Domain not found
>>> mail wietse@no.sender.domain
OK
>>> rcpt wietse@porcupine.org
-./smtpd_check: reject: RCPT from foo[127.0.0.2]: 550 <no.sender.domain>: Domain not found
-550 <no.sender.domain>: Domain not found
+./smtpd_check: reject: RCPT from foo[127.0.0.2]: 554 <no.sender.domain>: Domain not found
+554 <no.sender.domain>: Domain not found
-bad.domain 550 match bad.domain
+bad.domain 554 match bad.domain
friend.bad.domain OK
-bad-sender@ 550 match bad-sender@
+bad-sender@ 554 match bad-sender@
bad-sender@good.domain OK
-131.155.210 550 match 131.155.210
+131.155.210 554 match 131.155.210
131.155.210.17 OK
-reject@this.address 550 match reject@this.address
+reject@this.address 554 match reject@this.address
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TINET_ADDR_LIST
-TINT_TABLE
-TLOCAL_STATE
--TMAC_EXP_CONTEXT
+-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
/* SYNOPSIS
/* #include <mac_expand.h>
/*
+/* MAC_EXP *mac_exp_update(handle, key, ...)
+/* MAC_EXP *handle;
+/* int key;
+/*
+/* int mac_expand_use(handle, result, pattern, flags)
+/* MAC_EXP *handle;
+/* VSTRING *result;
+/* const char *pattern;
+/* int flags;
+/*
+/* void mac_expand_free(handle)
+/* MAC_EXP *handle;
+/*
/* int mac_expand(result, pattern, flags, key, ...)
/* VSTRING *result;
/* const char *pattern;
/* int flags;
/* int key;
/* DESCRIPTION
-/* mac_expand() expands $name instances in \fBpattern\fR
-/* and stores the result into \fBresult\fR.
-/*
-/* The following expansions are done:
+/* This module maintains a private attribute-value list and implements
+/* the following expansions:
/* .IP "$name, ${name}, $(name)"
-/* The result is the value of the named parameter. Optionally, the
+/* The result is the value of the named attribute. Optionally, the
/* result is subjected to further expansions.
/* .IP "${name?text}, $(name?text)"
-/* If the named parameter is defined, the result is the given text,
+/* If the named attribute is defined, the result is the given text,
/* after another iteration of $name expansion. Otherwise, the result is
/* empty.
/* .IP "${name:text}, $(name:text)"
-/* If the named parameter is undefined, the result is the given text,
+/* If the named attribute is undefined, the result is the given text,
/* after another iteration of $name expansion. Otherwise, the result is
/* empty.
/* .PP
+/* max_expand_update() updates an existing macro expansion context
+/* or instantiates a new one when given a null handle. The result
+/* is a handle that can be used by other mac_expand_xxx() routines.
+/*
+/* mac_expand_use() uses a macro expansion context to replace $name etc.
+/* instances in \fBpattern\fR and stores the result into \fBresult\fR.
+/*
+/* mac_expand_free() destroys a macro expansion context.
+/*
+/* mac_expand() is a convenience routine that combines all of the
+/* above in one function call.
+/*
/* Arguments:
+/* .IP mc
+/* Macro expansion context created or update with mac_expand_update().
/* .IP result
-/* Storage for the result of expansion. The result is not truncated
+/* Storage for the result of expansion. The result is truncated
/* upon entry.
/* .IP pattern
/* The string to be expanded.
/* .IP "MAC_EXP_ARG_ATTR (char *, char *)"
/* The next two arguments specify an attribute name and its attribute
/* string value. Specify a null string value for an attribute that is
-/* known but unset.
+/* known but unset. Attribute string values are not copied.
/* .IP "MAC_EXP_ARG_TABLE (HTABLE *)"
/* The next argument is a hash table with attribute names and values.
-/* Specify a null string value for an attribute that is known but unset.
+/* Specify a null string value for an attribute that is known but unset.
+/* Attribute string values are not copied.
/* .IP "MAC_EXP_ARG_FILTER (char *)"
/* The next argument specifies a null-terminated list of characters
/* that are allowed to appear in $name expansions. By default, illegal
/* characters are replaced by underscore. Only the last specified
-/* filter takes effect.
+/* filter takes effect. Specify a null pointer to disable filtering.
/* .IP "MAC_EXP_ARG_CLOBBER (int)"
/* Character value to be used when the result of expansion is not
/* allowed according to the MAC_EXP_ARG_FILTER argument. Only the
/*
* Little helper structure.
*/
-typedef struct {
+struct MAC_EXP {
HTABLE *table; /* private symbol table */
VSTRING *result; /* result buffer */
const char *filter; /* safe character list */
int flags; /* findings, features */
int level; /* nesting level */
jmp_buf jbuf; /* escape */
-} MAC_EXP_CONTEXT;
+};
/* mac_expand_callback - callback for mac_parse */
static void mac_expand_callback(int type, VSTRING *buf, char *ptr)
{
char *myname = "mac_expand_callback";
- MAC_EXP_CONTEXT *context = (MAC_EXP_CONTEXT *) ptr;
+ MAC_EXP *mc = (MAC_EXP *) ptr;
HTABLE_INFO *ht;
char *text;
char *cp;
int ch;
- if (context->level++ > 100) {
+ if (mc->level++ > 100) {
msg_warn("unreasonable macro call nesting: \"%s\"", vstring_str(buf));
- longjmp(context->jbuf, 1);
+ longjmp(mc->jbuf, 1);
}
/*
}
if (!ISALNUM(ch) && ch != '_') {
msg_warn("macro name syntax error: \"%s\"", vstring_str(buf));
- longjmp(context->jbuf, 1);
+ longjmp(mc->jbuf, 1);
}
}
/*
* Look up the named parameter.
*/
- text = (ht = htable_locate(context->table, vstring_str(buf))) == 0 ?
+ text = (ht = htable_locate(mc->table, vstring_str(buf))) == 0 ?
0 : ht->value;
/*
switch (ch) {
case '?':
if (text != 0)
- mac_parse(cp, mac_expand_callback, (char *) context);
+ mac_parse(cp, mac_expand_callback, (char *) mc);
break;
case ':':
if (text == 0)
- mac_parse(cp, mac_expand_callback, (char *) context);
+ mac_parse(cp, mac_expand_callback, (char *) mc);
break;
default:
if (text == 0) {
- context->flags |= MAC_EXP_FLAG_UNDEF;
+ mc->flags |= MAC_EXP_FLAG_UNDEF;
break;
}
- if (context->filter) {
+ if (mc->filter) {
vstring_strcpy(buf, text);
text = vstring_str(buf);
- for (cp = text; (cp += strspn(cp, context->filter))[0];)
- *cp++ = context->clobber;
+ for (cp = text; (cp += strspn(cp, mc->filter))[0];)
+ *cp++ = mc->clobber;
}
- if (context->flags & MAC_EXP_FLAG_RECURSE)
- mac_parse(text, mac_expand_callback, (char *) context);
+ if (mc->flags & MAC_EXP_FLAG_RECURSE)
+ mac_parse(text, mac_expand_callback, (char *) mc);
else
- vstring_strcat(context->result, text);
+ vstring_strcat(mc->result, text);
break;
}
}
*/
else {
text = vstring_str(buf);
- vstring_strcat(context->result, text);
+ vstring_strcat(mc->result, text);
}
/*
msg_info("%s: %s = %s", myname, vstring_str(buf),
text ? text : "(undef)");
- context->level--;
+ mc->level--;
}
-/* mac_expand - expand $name instances */
+/* mac_expand_update_va - update engine */
-int mac_expand(VSTRING *result, const char *pattern, int flags, int key,...)
+static MAC_EXP *mac_expand_update_va(MAC_EXP *mc, int key, va_list ap)
{
- MAC_EXP_CONTEXT context;
- va_list ap;
HTABLE_INFO **ht_info;
HTABLE_INFO **ht;
HTABLE *table;
} while(0);
/*
- * Inititalize.
+ * Optionally create expansion context.
*/
- context.table = htable_create(0);
- context.result = result;
- context.flags = flags;
- context.filter = 0;
- context.clobber = '_';
- context.level = 0;
+ if (mc == 0) {
+ mc = (MAC_EXP *) mymalloc(sizeof(*mc));
+ mc->table = htable_create(0);
+ mc->result = 0;
+ mc->flags = 0;
+ mc->filter = 0;
+ mc->clobber = '_';
+ mc->level = 0;
+ }
/*
* Stash away the attributes.
*/
- for (va_start(ap, key); key != 0; key = va_arg(ap, int)) {
+ for ( /* void */ ; key != 0; key = va_arg(ap, int)) {
switch (key) {
case MAC_EXP_ARG_ATTR:
name = va_arg(ap, char *);
value = va_arg(ap, char *);
- HTABLE_CLOBBER(context.table, name, value);
+ HTABLE_CLOBBER(mc->table, name, value);
break;
case MAC_EXP_ARG_TABLE:
table = va_arg(ap, HTABLE *);
ht_info = htable_list(table);
for (ht = ht_info; *ht; ht++)
- HTABLE_CLOBBER(context.table, ht[0]->key, ht[0]->value);
+ HTABLE_CLOBBER(mc->table, ht[0]->key, ht[0]->value);
myfree((char *) ht_info);
break;
case MAC_EXP_ARG_FILTER:
- context.filter = va_arg(ap, char *);
+ mc->filter = va_arg(ap, char *);
break;
case MAC_EXP_ARG_CLOBBER:
- context.clobber = va_arg(ap, int);
+ mc->clobber = va_arg(ap, int);
break;
}
}
+ return (mc);
+}
+
+/* mac_expand_update - update or create macro expansion context */
+
+MAC_EXP *mac_expand_update(MAC_EXP *mc, int key,...)
+{
+ va_list ap;
+
+ va_start(ap, key);
+ mc = mac_expand_update(mc, key, ap);
+ va_end(ap);
+ return (mc);
+}
+
+/* mac_expand_use - string expansion */
+
+int mac_expand_use(MAC_EXP *mc, VSTRING *result, const char *pattern, int flags)
+{
+ VSTRING_RESET(result);
+ mc->result = result;
+ mc->level = 0;
+ mc->flags = flags;
+ if (setjmp(mc->jbuf) == 0)
+ mac_parse(pattern, mac_expand_callback, (char *) mc);
+ VSTRING_TERMINATE(result);
+ return (mc->flags & MAC_EXP_FLAG_UNDEF);
+}
+
+/* mac_expand_free - destroy macro expansion context */
+
+void mac_expand_free(MAC_EXP *mc)
+{
+ htable_free(mc->table, (void (*) (char *)) 0);
+ myfree((char *) mc);
+}
+
+/* mac_expand - expand $name instances */
+
+int mac_expand(VSTRING *result, const char *pattern, int flags, int key,...)
+{
+ MAC_EXP *mc = 0;
+ va_list ap;
+ int status;
+
+ /*
+ * Stash away the attributes.
+ */
+ va_start(ap, key);
+ mc = mac_expand_update_va(mc, key, ap);
va_end(ap);
/*
* Do the substitutions.
*/
- if (setjmp(context.jbuf) == 0)
- mac_parse(pattern, mac_expand_callback, (char *) &context);
- VSTRING_TERMINATE(result);
+ status = mac_expand_use(mc, result, pattern, flags);
/*
* Clean up.
*/
- htable_free(context.table, (void (*) (char *)) 0);
- return (context.flags & MAC_EXP_FLAG_UNDEF);
+ mac_expand_free(mc);
+ return (status);
}
#ifdef TEST
/*
* External interface.
*/
+typedef struct MAC_EXP MAC_EXP;
+
#define MAC_EXP_FLAG_NONE (0)
#define MAC_EXP_FLAG_UNDEF (1<<0)
#define MAC_EXP_FLAG_RECURSE (1<<1)
#define MAC_EXP_ARG_FILTER 3
#define MAC_EXP_ARG_CLOBBER 4
+extern MAC_EXP *mac_expand_update(MAC_EXP *, int,...);
+extern int mac_expand_use(MAC_EXP *, VSTRING *, const char *, int);
+extern void mac_expand_free(MAC_EXP *);
+
extern int mac_expand(VSTRING *, const char *, int, int,...);
/* LICENSE