diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 634794f72..df478eb1f 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -213,6 +213,8 @@ -TPSC_DNSBL_HEAD -TPSC_DNSBL_SCORE -TPSC_DNSBL_SITE +-TPSC_ENDPT_LOOKUP_INFO +-TPSC_HAPROXY_STATE -TPSC_SMTPD_COMMAND -TPSC_STARTTLS -TPSC_STATE @@ -258,6 +260,7 @@ -TSMFICTX -TSMTPD_CMD -TSMTPD_DEFER +-TSMTPD_ENDPT_LOOKUP_INFO -TSMTPD_PROXY -TSMTPD_RBL_EXPAND_CONTEXT -TSMTPD_RBL_STATE diff --git a/postfix/HISTORY b/postfix/HISTORY index 57ab40cc4..611c9d409 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -17811,3 +17811,51 @@ Apologies for any names omitted. Bugfix (introduced: 20031216-21): with soft_bounce=yes, the SMTP client did not move on to the next MX host or fallback relay after a 5xx reply. File: smtp/smtp_trouble.c. + +20120527-8 + + Infrastructure: limited support to shrink VSTREAM buffers. + The change takes place when reading from (a stream for the + first time | an empty buffer) or when writing to (a stream + for the first time | a full buffer). TODO: the change should + also happen after purging or flushing a buffer. File: + util/vstream.c. + +20120531-617 + + Feature: haproxy support in postscreen(8) and smtpd(8). To + enable, specify "smtpd_upstream_proxy_protocol = haproxy" + or "postscreen_upstream_proxy_protocol = haproxy". Files: + mantools/postlink, proto/postconf.proto, global/Makefile.in, + global/haproxy_srvr.c, global/haproxy_srvr.h, global/mail_params.h, + global/mail_proto.h, master/single_server.c, master/multi_server.c, + master/event_server.c, postscreen/Makefile.in, + postscreen/postscreen.c, postscreen/postscreen.h, + postscreen/postscreen_endpt.c, postscreen/postscreen_haproxy.c, + postscreen/postscreen_haproxy.h, postscreen/postscreen_send.c, + postscreen/postscreen_state.c, smtpd/Makefile.in, smtpd/smtpd.h, + smtpd/smtpd_peer.c, smtpd/smtpd_sasl_glue.c, smtpd/smtpd_haproxy.c, + util/Makefile.in, util/listen.h, util/recv_pass_attr.c, + util/stream_listen.c, util/sys_defs.h, util/unix_pass_listen.c. + + +20120618 + + Cleanup: made the postscreen-to-smtpd haproxy attribute + transmission more robust for Solaris. Files: util/sys_defs.h, + util/connect.h, util/steam_listen.c, postscreen/postscreen_send.c. + + Cleanup: simplified the "stream used" workaround. Files: + util/vstream.h, master/event_server.c, master/multi_server.c. + +20120621 + + Cleanup: simplified workarounds for Solaris streams versus + UNIX-domain sockets. Files: util/pass_accept.c (new), + util/pass_trigger.c (new), util/stream_pass_connect.c + (deleted), util/unix_pass_listen.c (deleted), + util/unix_pass_trigger.c (deleted), updated header files, + and replaced PASS_XXX macros by pass_xxx function calls. + + Cleanup: don't clobber errno when logging a problem. + File util/msg_output.c. diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index 096b2d08a..0fb18079c 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -14,6 +14,30 @@ specifies the release date of a stable release or snapshot release. If you upgrade from Postfix 2.8 or earlier, read RELEASE_NOTES-2.9 before proceeding. +Incompatible changes with snapshot 20120625 +=========================================== + +The postscreen(8)-to-smtpd(8) protocol has changed. To avoid "cannot +receive connection attributes" warnings and dropped connections, +execute the command "postfix reload". No mail will be lost as long +as the remote SMTP client tries again later. + +Major changes with snapshot 20120625 +==================================== + +Support for upstream proxy agent in the postscreen(8) and smtpd(8) +daemons. To enable the haproxy protocol, specify one of the +following: + + postscreen_upstream_proxy_protocol = haproxy + smtpd_upstream_proxy_protocol = haproxy + +Note 1: smtpd_upstream_proxy_protocol can't be used behind postscreen. + +Note 2: To use the nginx proxy with smtpd(8), enable the XCLIENT +protocol with smtpd_authorized_xclient_hosts. This supports SASL +authentication in the proxy agent (Postfix 2.9 and later). + Major changes with snapshot 20120422 ==================================== diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index 06f74178d..072b7be32 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -7639,6 +7639,31 @@ for details.

This feature is available in Postfix 2.8 and later.

+ + +
postscreen_upstream_proxy_protocol +(default: empty)
+ +

The name of the proxy protocol used by an optional before-postscreen +proxy agent. When a proxy agent is used, this protocol conveys local +and remote address and port information. Specify +"postscreen_upstream_proxy_protocol = haproxy" to enable the haproxy +protocol.

+ +

This feature is available in Postfix 2.10 and later.

+ + +
+ +
postscreen_upstream_proxy_timeout +(default: 5s)
+ +

The time limit for the proxy protocol specified with the +postscreen_upstream_proxy_protocol parameter.

+ +

This feature is available in Postfix 2.10 and later.

+ +
postscreen_use_tls @@ -15142,6 +15167,35 @@ purpose.

This feature is available in Postfix 2.2 and later.

+ + +
smtpd_upstream_proxy_protocol +(default: empty)
+ +

The name of the proxy protocol used by an optional before-smtpd +proxy agent. When a proxy agent is used, this protocol conveys local +and remote address and port information. Specify +"smtpd_upstream_proxy_protocol = haproxy" to enable the haproxy +protocol.

+ +

NOTE: To use the nginx proxy with smtpd(8), enable the XCLIENT +protocol with smtpd_authorized_xclient_hosts. This supports SASL +authentication in the proxy agent (Postfix 2.9 and later).

+ +

This feature is available in Postfix 2.10 and later.

+ + +
+ +
smtpd_upstream_proxy_timeout +(default: 5s)
+ +

The time limit for the proxy protocol specified with the +smtpd_upstream_proxy_protocol parameter.

+ +

This feature is available in Postfix 2.10 and later.

+ +
smtpd_use_tls diff --git a/postfix/html/postscreen.8.html b/postfix/html/postscreen.8.html index 4ef299d7b..901b71631 100644 --- a/postfix/html/postscreen.8.html +++ b/postfix/html/postscreen.8.html @@ -146,10 +146,22 @@ POSTSCREEN(8) POSTSCREEN(8) Safety net to keep mail queued that would otherwise be returned to the sender. +BEFORE-POSTSCREEN PROXY AGENT + Available in Postfix version 2.10 and later: + + postscreen_upstream_proxy_protocol (empty) + The name of the proxy protocol used by an optional + before-postscreen proxy agent. + + postscreen_upstream_proxy_timeout (5s) + The time limit for the proxy protocol specified + with the postscreen_upstream_proxy_protocol parame- + ter. + PERMANENT WHITE/BLACKLIST TEST - This test is executed immediately after a remote SMTP - client connects. If a client is permanently whitelisted, - the client will be handed off immediately to a Postfix + This test is executed immediately after a remote SMTP + client connects. If a client is permanently whitelisted, + the client will be handed off immediately to a Postfix SMTP server process. postscreen_access_list (permit_mynetworks) @@ -157,99 +169,99 @@ POSTSCREEN(8) POSTSCREEN(8) addresses. postscreen_blacklist_action (ignore) - The action that postscreen(8) takes when a remote - SMTP client is permanently blacklisted with the + The action that postscreen(8) takes when a remote + SMTP client is permanently blacklisted with the postscreen_access_list parameter. MAIL EXCHANGER POLICY TESTS - When postscreen(8) is configured to monitor all primary - and backup MX addresses, it can refuse to whitelist - clients that connect to a backup MX address only. For - small sites, this requires configuring primary and backup - MX addresses on the same MTA. Larger sites would have to - share the postscreen(8) cache between primary and backup + When postscreen(8) is configured to monitor all primary + and backup MX addresses, it can refuse to whitelist + clients that connect to a backup MX address only. For + small sites, this requires configuring primary and backup + MX addresses on the same MTA. Larger sites would have to + share the postscreen(8) cache between primary and backup MTAs, which would introduce a common point of failure. postscreen_whitelist_interfaces (static:all) - A list of local postscreen(8) server IP addresses - where a non-whitelisted remote SMTP client can - obtain postscreen(8)'s temporary whitelist status. + A list of local postscreen(8) server IP addresses + where a non-whitelisted remote SMTP client can + obtain postscreen(8)'s temporary whitelist status. BEFORE-GREETING TESTS - These tests are executed before the remote SMTP client + These tests are executed before the remote SMTP client receives the "220 servername" greeting. If no tests remain - after the successful completion of this phase, the client - will be handed off immediately to a Postfix SMTP server + after the successful completion of this phase, the client + will be handed off immediately to a Postfix SMTP server process. dnsblog_service_name (dnsblog) - The name of the dnsblog(8) service entry in mas- + The name of the dnsblog(8) service entry in mas- ter.cf. postscreen_dnsbl_action (ignore) - The action that postscreen(8) takes when a remote - SMTP client's combined DNSBL score is equal to or - greater than a threshold (as defined with the + The action that postscreen(8) takes when a remote + SMTP client's combined DNSBL score is equal to or + greater than a threshold (as defined with the postscreen_dnsbl_sites and postscreen_dnsbl_thresh- old parameters). postscreen_dnsbl_reply_map (empty) - A mapping from actual DNSBL domain name which - includes a secret password, to the DNSBL domain + A mapping from actual DNSBL domain name which + includes a secret password, to the DNSBL domain name that postscreen will reply with when it rejects mail. postscreen_dnsbl_sites (empty) - Optional list of DNS white/blacklist domains, fil- + Optional list of DNS white/blacklist domains, fil- ters and weight factors. postscreen_dnsbl_threshold (1) - The inclusive lower bound for blocking a remote - SMTP client, based on its combined DNSBL score as - defined with the postscreen_dnsbl_sites parameter. + The inclusive lower bound for blocking a remote + SMTP client, based on its combined DNSBL score as + defined with the postscreen_dnsbl_sites parameter. postscreen_greet_action (ignore) - The action that postscreen(8) takes when a remote - SMTP client speaks before its turn within the time + The action that postscreen(8) takes when a remote + SMTP client speaks before its turn within the time specified with the postscreen_greet_wait parameter. postscreen_greet_banner ($smtpd_banner) The text in the optional "220-text..." server response that postscreen(8) sends ahead of the real Postfix SMTP server's "220 text..." response, in an - attempt to confuse bad SMTP clients so that they + attempt to confuse bad SMTP clients so that they speak before their turn (pre-greet). postscreen_greet_wait (${stress?2}${stress:6}s) The amount of time that postscreen(8) will wait for - an SMTP client to send a command before its turn, - and for DNS blocklist lookup results to arrive - (default: up to 2 seconds under stress, up to 6 + an SMTP client to send a command before its turn, + and for DNS blocklist lookup results to arrive + (default: up to 2 seconds under stress, up to 6 seconds otherwise). smtpd_service_name (smtpd) - The internal service that postscreen(8) hands off + The internal service that postscreen(8) hands off allowed connections to. AFTER-GREETING TESTS - These tests are executed after the remote SMTP client + These tests are executed after the remote SMTP client receives the "220 servername" greeting. If a client passes - all tests during this phase, it will receive a 4XX - response to RCPT TO commands until the client hangs up. + all tests during this phase, it will receive a 4XX + response to RCPT TO commands until the client hangs up. After this, the client will be allowed to talk directly to a Postfix SMTP server process. postscreen_bare_newline_action (ignore) - The action that postscreen(8) takes when a remote - SMTP client sends a bare newline character, that + The action that postscreen(8) takes when a remote + SMTP client sends a bare newline character, that is, a newline not preceded by carriage return. postscreen_bare_newline_enable (no) - Enable "bare newline" SMTP protocol tests in the + Enable "bare newline" SMTP protocol tests in the postscreen(8) server. postscreen_disable_vrfy_command ($disable_vrfy_command) - Disable the SMTP VRFY command in the postscreen(8) + Disable the SMTP VRFY command in the postscreen(8) daemon. postscreen_forbidden_commands ($smtpd_forbidden_commands) @@ -257,159 +269,159 @@ POSTSCREEN(8) POSTSCREEN(8) siders in violation of the SMTP protocol. postscreen_helo_required ($smtpd_helo_required) - Require that a remote SMTP client sends HELO or + Require that a remote SMTP client sends HELO or EHLO before commencing a MAIL transaction. postscreen_non_smtp_command_action (drop) - The action that postscreen(8) takes when a remote - SMTP client sends non-SMTP commands as specified + The action that postscreen(8) takes when a remote + SMTP client sends non-SMTP commands as specified with the postscreen_forbidden_commands parameter. postscreen_non_smtp_command_enable (no) - Enable "non-SMTP command" tests in the + Enable "non-SMTP command" tests in the postscreen(8) server. postscreen_pipelining_action (enforce) - The action that postscreen(8) takes when a remote - SMTP client sends multiple commands instead of - sending one command and waiting for the server to + The action that postscreen(8) takes when a remote + SMTP client sends multiple commands instead of + sending one command and waiting for the server to respond. postscreen_pipelining_enable (no) - Enable "pipelining" SMTP protocol tests in the + Enable "pipelining" SMTP protocol tests in the postscreen(8) server. CACHE CONTROLS postscreen_cache_cleanup_interval (12h) - The amount of time between postscreen(8) cache + The amount of time between postscreen(8) cache cleanup runs. postscreen_cache_map (btree:$data_direc- tory/postscreen_cache) - Persistent storage for the postscreen(8) server + Persistent storage for the postscreen(8) server decisions. postscreen_cache_retention_time (7d) The amount of time that postscreen(8) will cache an - expired temporary whitelist entry before it is + expired temporary whitelist entry before it is removed. postscreen_bare_newline_ttl (30d) - The amount of time that postscreen(8) will use the + The amount of time that postscreen(8) will use the result from a successful "bare newline" SMTP proto- col test. postscreen_dnsbl_ttl (1h) - The amount of time that postscreen(8) will use the + The amount of time that postscreen(8) will use the result from a successful DNS blocklist test. postscreen_greet_ttl (1d) - The amount of time that postscreen(8) will use the + The amount of time that postscreen(8) will use the result from a successful PREGREET test. postscreen_non_smtp_command_ttl (30d) - The amount of time that postscreen(8) will use the - result from a successful "non_smtp_command" SMTP + The amount of time that postscreen(8) will use the + result from a successful "non_smtp_command" SMTP protocol test. postscreen_pipelining_ttl (30d) - The amount of time that postscreen(8) will use the + The amount of time that postscreen(8) will use the result from a successful "pipelining" SMTP protocol test. RESOURCE CONTROLS line_length_limit (2048) - Upon input, long lines are chopped up into pieces - of at most this length; upon delivery, long lines + Upon input, long lines are chopped up into pieces + of at most this length; upon delivery, long lines are reconstructed. postscreen_client_connection_count_limit ($smtpd_client_connection_count_limit) - How many simultaneous connections any remote SMTP - client is allowed to have with the postscreen(8) + How many simultaneous connections any remote SMTP + client is allowed to have with the postscreen(8) daemon. postscreen_command_count_limit (20) - The limit on the total number of commands per SMTP - session for postscreen(8)'s built-in SMTP protocol + The limit on the total number of commands per SMTP + session for postscreen(8)'s built-in SMTP protocol engine. postscreen_command_time_limit (${stress?10}${stress:300}s) - The time limit to read an entire command line with + The time limit to read an entire command line with postscreen(8)'s built-in SMTP protocol engine. postscreen_post_queue_limit ($default_process_limit) - The number of clients that can be waiting for ser- + The number of clients that can be waiting for ser- vice from a real Postfix SMTP server process. postscreen_pre_queue_limit ($default_process_limit) - The number of non-whitelisted clients that can be - waiting for a decision whether they will receive + The number of non-whitelisted clients that can be + waiting for a decision whether they will receive service from a real Postfix SMTP server process. postscreen_watchdog_timeout (10s) - How much time a postscreen(8) process may take to - respond to a remote SMTP client command or to per- + How much time a postscreen(8) process may take to + respond to a remote SMTP client command or to per- form a cache operation before it is terminated by a built-in watchdog timer. STARTTLS CONTROLS postscreen_tls_security_level ($smtpd_tls_security_level) - The SMTP TLS security level for the postscreen(8) - server; when a non-empty value is specified, this + The SMTP TLS security level for the postscreen(8) + server; when a non-empty value is specified, this overrides the obsolete parameters postscreen_use_tls and postscreen_enforce_tls. tlsproxy_service_name (tlsproxy) - The name of the tlsproxy(8) service entry in mas- + The name of the tlsproxy(8) service entry in mas- ter.cf. OBSOLETE STARTTLS SUPPORT CONTROLS - These parameters are supported for compatibility with + These parameters are supported for compatibility with smtpd(8) legacy parameters. postscreen_use_tls ($smtpd_use_tls) - Opportunistic TLS: announce STARTTLS support to + Opportunistic TLS: announce STARTTLS support to remote SMTP clients, but do not require that clients use TLS encryption. postscreen_enforce_tls ($smtpd_enforce_tls) - Mandatory TLS: announce STARTTLS support to remote - SMTP clients, and require that clients use TLS + Mandatory TLS: announce STARTTLS support to remote + SMTP clients, and require that clients use TLS encryption. MISCELLANEOUS CONTROLS config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and + The default location of the Postfix main.cf and master.cf configuration files. delay_logging_resolution_limit (2) - The maximal number of digits after the decimal + The maximal number of digits after the decimal point when logging sub-second delay values. command_directory (see 'postconf -d' output) - The location of all postfix administrative com- + The location of all postfix administrative com- mands. max_idle (100s) - The maximum amount of time that an idle Postfix - daemon process waits for an incoming connection + The maximum amount of time that an idle Postfix + daemon process waits for an incoming connection before terminating voluntarily. process_id (read-only) - The process ID of a Postfix command or daemon + The process ID of a Postfix command or daemon process. process_name (read-only) - The process name of a Postfix command or daemon + The process name of a Postfix command or daemon process. syslog_facility (mail) The syslog facility of Postfix logging. syslog_name (see 'postconf -d' output) - The mail system name that is prepended to the - process name in syslog records, so that "smtpd" + The mail system name that is prepended to the + process name in syslog records, so that "smtpd" becomes, for example, "postfix/smtpd". SEE ALSO @@ -422,14 +434,14 @@ POSTSCREEN(8) POSTSCREEN(8) POSTSCREEN_README, Postfix Postscreen Howto LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. HISTORY This service was introduced with Postfix version 2.8. - Many ideas in postscreen(8) were explored in earlier work - by Michael Tokarev, in OpenBSD spamd, and in MailChannels + Many ideas in postscreen(8) were explored in earlier work + by Michael Tokarev, in OpenBSD spamd, and in MailChannels Traffic Control. AUTHOR(S) diff --git a/postfix/html/smtpd.8.html b/postfix/html/smtpd.8.html index f814378bc..ee9ae6a92 100644 --- a/postfix/html/smtpd.8.html +++ b/postfix/html/smtpd.8.html @@ -183,6 +183,17 @@ SMTPD(8) SMTPD(8) addresses with the domain specified in the remote_header_rewrite_domain parameter. +BEFORE-SMTPD PROXY AGENT + Available in Postfix version 2.10 and later: + + smtpd_upstream_proxy_protocol (empty) + The name of the proxy protocol used by an optional + before-smtpd proxy agent. + + smtpd_upstream_proxy_timeout (5s) + The time limit for the proxy protocol specified + with the smtpd_upstream_proxy_protocol parameter. + AFTER QUEUE EXTERNAL CONTENT INSPECTION CONTROLS As of version 1.0, Postfix can be configured to send new mail to an external content filter AFTER the mail is diff --git a/postfix/makedefs b/postfix/makedefs index 92a9717c3..875ecc213 100644 --- a/postfix/makedefs +++ b/postfix/makedefs @@ -666,7 +666,7 @@ CCARGS="$CCARGS -DSNAPSHOT" # Non-production: needs thorough testing, or major changes are still # needed before the code stabilizes. -#CCARGS="$CCARGS -DNONPROD" +CCARGS="$CCARGS -DNONPROD" sed 's/ / /g' <) { s;\bsmtpd_use_tls\b;$&;g; s;\bsmtpd_reject_footer\b;$&;g; s;\bsmtpd_per_record_deadline\b;$&;g; + s;\bsmtpd_upstream_proxy_protocol\b;$&;g; + s;\bsmtpd_upstream_proxy_timeout\b;$&;g; s;\btls_daemon_random_bytes\b;$&;g; s;\btls_daemon_random_source\b;$&;g; s;\btls_ran[-]*\n* *[]*dom_bytes\b;$&;g; @@ -969,6 +971,8 @@ while (<>) { s;\bpostscreen_reject_footer\b;$&;g; s;\bpostscreen_command_filter\b;$&;g; s;\bpostscreen_whitelist_interfaces\b;$&;g; + s;\bpostscreen_upstream_proxy_protocol\b;$&;g; + s;\bpostscreen_upstream_proxy_timeout\b;$&;g; s;\btlsproxy_watchdog_timeout\b;$&;g; s;\btlsproxy_enforce_tls\b;$&;g; diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index 24fd6b5db..416ed5b4a 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -14369,6 +14369,44 @@ and will never be allowed to talk to a Postfix SMTP server process.

This feature is available in Postfix 2.9 and later.

+%PARAM postscreen_upstream_proxy_protocol + +

The name of the proxy protocol used by an optional before-postscreen +proxy agent. When a proxy agent is used, this protocol conveys local +and remote address and port information. Specify +"postscreen_upstream_proxy_protocol = haproxy" to enable the haproxy +protocol.

+ +

This feature is available in Postfix 2.10 and later.

+ +%PARAM postscreen_upstream_proxy_timeout 5s + +

The time limit for the proxy protocol specified with the +postscreen_upstream_proxy_protocol parameter.

+ +

This feature is available in Postfix 2.10 and later.

+ +%PARAM smtpd_upstream_proxy_protocol + +

The name of the proxy protocol used by an optional before-smtpd +proxy agent. When a proxy agent is used, this protocol conveys local +and remote address and port information. Specify +"smtpd_upstream_proxy_protocol = haproxy" to enable the haproxy +protocol.

+ +

NOTE: To use the nginx proxy with smtpd(8), enable the XCLIENT +protocol with smtpd_authorized_xclient_hosts. This supports SASL +authentication in the proxy agent (Postfix 2.9 and later).

+ +

This feature is available in Postfix 2.10 and later.

+ +%PARAM smtpd_upstream_proxy_timeout 5s + +

The time limit for the proxy protocol specified with the +smtpd_upstream_proxy_protocol parameter.

+ +

This feature is available in Postfix 2.10 and later.

+ %PARAM enable_long_queue_ids no

Enable long, non-repeating, queue IDs (queue file names). The diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in index d8fb3dbd5..8e11da58d 100644 --- a/postfix/src/global/Makefile.in +++ b/postfix/src/global/Makefile.in @@ -32,7 +32,7 @@ SRCS = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.c \ match_service.c mail_conf_nint.c addr_match_list.c mail_conf_nbool.c \ smtp_reply_footer.c safe_ultostr.c verify_sender_addr.c \ dict_memcache.c mail_version.c memcache_proto.c server_acl.c \ - mkmap_fail.c + mkmap_fail.c haproxy_srvr.c OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \ canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \ clnt_stream.o conv_time.o db_common.o debug_peer.o debug_process.o \ @@ -66,7 +66,7 @@ OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \ match_service.o mail_conf_nint.o addr_match_list.o mail_conf_nbool.o \ smtp_reply_footer.o safe_ultostr.o verify_sender_addr.o \ dict_memcache.o mail_version.o memcache_proto.o server_acl.o \ - mkmap_fail.o + mkmap_fail.o haproxy_srvr.o HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \ canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \ conv_time.h db_common.h debug_peer.h debug_process.h defer.h \ @@ -92,7 +92,8 @@ HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \ verp_sender.h wildcard_inet_addr.h xtext.h delivered_hdr.h \ fold_addr.h header_body_checks.h data_redirect.h match_service.h \ addr_match_list.h smtp_reply_footer.h safe_ultostr.h \ - verify_sender_addr.h dict_memcache.h memcache_proto.h server_acl.h + verify_sender_addr.h dict_memcache.h memcache_proto.h server_acl.h \ + haproxy_srvr.h TESTSRC = rec2stream.c stream2rec.c recdump.c DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) CFLAGS = $(DEBUG) $(OPT) $(DEFS) @@ -1100,6 +1101,15 @@ fold_addr.o: ../../include/vbuf.h fold_addr.o: ../../include/vstring.h fold_addr.o: fold_addr.c fold_addr.o: fold_addr.h +haproxy_srvr.o: ../../include/msg.h +haproxy_srvr.o: ../../include/myaddrinfo.h +haproxy_srvr.o: ../../include/stringops.h +haproxy_srvr.o: ../../include/sys_defs.h +haproxy_srvr.o: ../../include/valid_hostname.h +haproxy_srvr.o: ../../include/vbuf.h +haproxy_srvr.o: ../../include/vstring.h +haproxy_srvr.o: haproxy_srvr.c +haproxy_srvr.o: haproxy_srvr.h header_body_checks.o: ../../include/argv.h header_body_checks.o: ../../include/dict.h header_body_checks.o: ../../include/msg.h diff --git a/postfix/src/global/haproxy_srvr.c b/postfix/src/global/haproxy_srvr.c new file mode 100644 index 000000000..db3d0c1da --- /dev/null +++ b/postfix/src/global/haproxy_srvr.c @@ -0,0 +1,197 @@ +/*++ +/* NAME +/* haproxy_srvr 3 +/* SUMMARY +/* server-side haproxy protocol support +/* SYNOPSIS +/* #include +/* +/* const char *haproxy_srvr_parse(str, +/* smtp_client_addr, smtp_client_port, +/* smtp_server_addr, smtp_server_port) +/* const char *str; +/* MAI_HOSTADDR_STR *smtp_client_addr, +/* MAI_SERVPORT_STR *smtp_client_port, +/* MAI_HOSTADDR_STR *smtp_server_addr, +/* MAI_SERVPORT_STR *smtp_server_port; +/* DESCRIPTION +/* haproxy_srvr_parse() parses a haproxy line. The result is +/* null in case of success, a pointer to text (with the error +/* type) in case of error. If both IPv6 and IPv4 support are +/* enabled, IPV4_IN_IPV6 address syntax (::ffff:1.2.3.4) is +/* converted to IPV4 syntax. +/* 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 +#include +#include +#include + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include + +/* Application-specific. */ + +static INET_PROTO_INFO *proto_info; + +/* haproxy_srvr_parse_lit - extract and validate string literal */ + +static int haproxy_srvr_parse_lit(const char *str,...) +{ + va_list ap; + const char *cp; + int result = -1; + + if (msg_verbose) + msg_info("haproxy_srvr_parse: %s", str); + + if (str != 0) { + va_start(ap, str); + while (result < 0 && (cp = va_arg(ap, const char *)) != 0) + if (strcmp(str, cp) == 0) + result = 0; + va_end(ap); + } + return (result); +} + +/* haproxy_srvr_parse_proto - parse and validate the protocol type */ + +static int haproxy_srvr_parse_proto(const char *str, int *addr_family) +{ + if (msg_verbose) + msg_info("haproxy_srvr_parse: proto=%s", str); + +#ifdef AF_INET6 + if (strcasecmp(str, "TCP6") == 0) { + if (strchr((char *) proto_info->sa_family_list, AF_INET6) != 0) { + *addr_family = AF_INET6; + return (0); + } + } else +#endif + if (strcasecmp(str, "TCP4") == 0) { + if (strchr((char *) proto_info->sa_family_list, AF_INET) != 0) { + *addr_family = AF_INET; + return (0); + } + } + return (-1); +} + +/* haproxy_srvr_parse_addr - extract and validate IP address */ + +static int haproxy_srvr_parse_addr(const char *str, MAI_HOSTADDR_STR *addr, + int addr_family) +{ + if (msg_verbose) + msg_info("haproxy_srvr_parse: addr=%s proto=%d", str, addr_family); + + if (str == 0 || strlen(str) >= sizeof(MAI_HOSTADDR_STR)) + return (-1); + + switch (addr_family) { +#ifdef AF_INET6 + case AF_INET6: + if (!valid_ipv6_hostaddr(str, DONT_GRIPE)) + return (-1); + if (strncasecmp("::ffff:", str, 7) == 0 + && strchr((char *) proto_info->sa_family_list, AF_INET) != 0) { + memcpy(addr->buf, str + 7, strlen(str) + 1 - 7); + return (0); + } else { + memcpy(addr->buf, str, strlen(str) + 1); + return (0); + } +#endif + case AF_INET: + if (!valid_ipv4_hostaddr(str, DONT_GRIPE)) + return (-1); + memcpy(addr->buf, str, strlen(str) + 1); + return (0); + default: + msg_panic("haproxy_srvr_parse: unexpected address family: %d", + addr_family); + } +} + +/* haproxy_srvr_parse_port - extract and validate TCP port */ + +static int haproxy_srvr_parse_port(const char *str, MAI_SERVPORT_STR *port) +{ + if (msg_verbose) + msg_info("haproxy_srvr_parse: port=%s", str); + if (str == 0 || strlen(str) >= sizeof(MAI_SERVPORT_STR) + || !valid_hostport(str, DONT_GRIPE)) { + return (-1); + } else { + memcpy(port->buf, str, strlen(str) + 1); + return (0); + } +} + +/* haproxy_srvr_parse - parse haproxy line */ + +const char *haproxy_srvr_parse(const char *str, + MAI_HOSTADDR_STR *smtp_client_addr, + MAI_SERVPORT_STR *smtp_client_port, + MAI_HOSTADDR_STR *smtp_server_addr, + MAI_SERVPORT_STR *smtp_server_port) +{ + char *saved_str = mystrdup(str); + char *cp = saved_str; + const char *err; + int addr_family; + + if (proto_info == 0) + proto_info = inet_proto_info(); + + /* + * XXX We don't accept connections with the "UNKNOWN" protocol type, + * because those would sidestep address-based access control mechanisms. + */ +#define NEXT_TOKEN mystrtok(&cp, " \r\n") + if (haproxy_srvr_parse_lit(NEXT_TOKEN, "PROXY", (char *) 0) < 0) + err = "unexpected protocol header"; + else if (haproxy_srvr_parse_proto(NEXT_TOKEN, &addr_family) < 0) + err = "unsupported protocol type"; + else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_client_addr, + addr_family) < 0) + err = "unexpected client address syntax"; + else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_server_addr, + addr_family) < 0) + err = "unexpected server address syntax"; + else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_client_port) < 0) + err = "unexpected client port syntax"; + else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_server_port) < 0) + err = "unexpected server port syntax"; + else + err = 0; + myfree(saved_str); + return (err); +} diff --git a/postfix/src/global/haproxy_srvr.h b/postfix/src/global/haproxy_srvr.h new file mode 100644 index 000000000..7cd3262a2 --- /dev/null +++ b/postfix/src/global/haproxy_srvr.h @@ -0,0 +1,45 @@ +#ifndef _HAPROXY_SRVR_H_INCLUDED_ +#define _HAPROXY_SRVR_H_INCLUDED_ + +/*++ +/* NAME +/* haproxy_srvr 3h +/* SUMMARY +/* server-side haproxy protocol support +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern const char *haproxy_srvr_parse(const char *, + MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *, + MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *); + +#define HAPROXY_PROTO_NAME "haproxy" +#define HAPROXY_MAX_LEN (256 + 2) + +#ifndef DO_GRIPE +#define DO_GRIPE 1 +#define DONT_GRIPE 0 +#endif + +/* 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 diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index ae960984b..8913eed9c 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -3458,6 +3458,14 @@ extern char *var_psc_acl; #define DEF_PSC_WLIST_IF "static:all" extern char *var_psc_wlist_if; +#define VAR_PSC_UPROXY_PROTO "postscreen_upstream_proxy_protocol" +#define DEF_PSC_UPROXY_PROTO "" +extern char *var_psc_uproxy_proto; + +#define VAR_PSC_UPROXY_TMOUT "postscreen_upstream_proxy_timeout" +#define DEF_PSC_UPROXY_TMOUT "5s" +extern int var_psc_uproxy_tmout; + #define VAR_DNSBLOG_SERVICE "dnsblog_service_name" #define DEF_DNSBLOG_SERVICE MAIL_SERVICE_DNSBLOG extern char *var_dnsblog_service; @@ -3620,6 +3628,17 @@ extern bool var_smtp_rec_deadline; #define DEF_SMTPD_ACL_PERM_LOG "" extern char *var_smtpd_acl_perm_log; + /* + * Before-smtpd proxy support. + */ +#define VAR_SMTPD_UPROXY_PROTO "smtpd_upstream_proxy_protocol" +#define DEF_SMTPD_UPROXY_PROTO "" +extern char *var_smtpd_uproxy_proto; + +#define VAR_SMTPD_UPROXY_TMOUT "smtpd_upstream_proxy_timeout" +#define DEF_SMTPD_UPROXY_TMOUT "5s" +extern int var_smtpd_uproxy_tmout; + /* * Postfix sendmail command compatibility features. */ diff --git a/postfix/src/global/mail_proto.h b/postfix/src/global/mail_proto.h index 538b01e57..a4dd8d4c4 100644 --- a/postfix/src/global/mail_proto.h +++ b/postfix/src/global/mail_proto.h @@ -193,6 +193,9 @@ extern char *mail_pathname(const char *, const char *); #define MAIL_ATTR_ACT_REVERSE_CLIENT_NAME "reverse_client_name" #define MAIL_ATTR_ACT_FORWARD_CLIENT_NAME "forward_client_name" +#define MAIL_ATTR_ACT_SERVER_ADDR "server_address" /* server address */ +#define MAIL_ATTR_ACT_SERVER_PORT "server_port" /* server TCP port */ + #define MAIL_ATTR_PROTO_STATE "protocol_state" /* MAIL/RCPT/... */ #define MAIL_ATTR_ORG_NONE "unknown" /* origin unknown */ #define MAIL_ATTR_ORG_LOCAL "local" /* local submission */ diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 81910ced0..f4306c63f 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20120617" +#define MAIL_RELEASE_DATE "20120621" #define MAIL_VERSION_NUMBER "2.10" #ifdef SNAPSHOT diff --git a/postfix/src/master/Makefile.in b/postfix/src/master/Makefile.in index 2305da346..9a74c6917 100644 --- a/postfix/src/master/Makefile.in +++ b/postfix/src/master/Makefile.in @@ -90,6 +90,7 @@ event_server.o: ../../include/chroot_uid.h event_server.o: ../../include/debug_process.h event_server.o: ../../include/dict.h event_server.o: ../../include/events.h +event_server.o: ../../include/htable.h event_server.o: ../../include/iostuff.h event_server.o: ../../include/listen.h event_server.o: ../../include/mail_conf.h @@ -193,6 +194,7 @@ master_flow.o: ../../include/sys_defs.h master_flow.o: master.h master_flow.o: master_flow.c master_flow.o: master_proto.h +master_listen.o: ../../include/htable.h master_listen.o: ../../include/inet_addr_list.h master_listen.o: ../../include/iostuff.h master_listen.o: ../../include/listen.h @@ -286,6 +288,7 @@ multi_server.o: ../../include/chroot_uid.h multi_server.o: ../../include/debug_process.h multi_server.o: ../../include/dict.h multi_server.o: ../../include/events.h +multi_server.o: ../../include/htable.h multi_server.o: ../../include/iostuff.h multi_server.o: ../../include/listen.h multi_server.o: ../../include/mail_conf.h @@ -318,6 +321,7 @@ single_server.o: ../../include/chroot_uid.h single_server.o: ../../include/debug_process.h single_server.o: ../../include/dict.h single_server.o: ../../include/events.h +single_server.o: ../../include/htable.h single_server.o: ../../include/iostuff.h single_server.o: ../../include/listen.h single_server.o: ../../include/mail_conf.h @@ -350,6 +354,7 @@ trigger_server.o: ../../include/chroot_uid.h trigger_server.o: ../../include/debug_process.h trigger_server.o: ../../include/dict.h trigger_server.o: ../../include/events.h +trigger_server.o: ../../include/htable.h trigger_server.o: ../../include/iostuff.h trigger_server.o: ../../include/listen.h trigger_server.o: ../../include/mail_conf.h diff --git a/postfix/src/master/event_server.c b/postfix/src/master/event_server.c index b58d22578..aa6f97d1b 100644 --- a/postfix/src/master/event_server.c +++ b/postfix/src/master/event_server.c @@ -38,7 +38,10 @@ /* its privileges. The application is responsible for managing /* subsequent I/O events on the stream, and is responsible for /* calling event_server_disconnect() when the stream is closed. -/* The stream initial state is non-blocking mode. The service +/* The stream initial state is non-blocking mode. +/* Optional connection attributes are provided as a hash that +/* is attached as stream context. NOTE: the attributes are +/* destroyed after this function is called. The service /* name argument corresponds to the service name in the master.cf /* file. The argv argument specifies command-line arguments /* left over after options processing. @@ -255,6 +258,7 @@ static unsigned event_server_generation; static void (*event_server_pre_disconn) (VSTREAM *, char *, char **); static void (*event_server_slow_exit) (char *, char **); static int event_server_watchdog = 1000; +static int event_server_saved_flags; /* event_server_exit - normal termination */ @@ -339,6 +343,8 @@ void event_server_disconnect(VSTREAM *stream) static void event_server_execute(int unused_event, char *context) { VSTREAM *stream = (VSTREAM *) context; + HTABLE *attr = (vstream_flags(stream) == event_server_saved_flags ? + (HTABLE *) vstream_context(stream) : 0); if (event_server_lock != 0 && myflock(vstream_fileno(event_server_lock), INTERNAL_LOCK, @@ -355,11 +361,13 @@ static void event_server_execute(int unused_event, char *context) event_server_service(stream, event_server_name, event_server_argv); if (master_notify(var_pid, event_server_generation, MASTER_STAT_AVAIL) < 0) event_server_abort(EVENT_NULL_TYPE, EVENT_NULL_CONTEXT); + if (attr) + htable_free(attr, myfree); } /* event_server_wakeup - wake up application */ -static void event_server_wakeup(int fd) +static void event_server_wakeup(int fd, HTABLE *attr) { VSTREAM *stream; char *tmp; @@ -388,9 +396,13 @@ static void event_server_wakeup(int fd) client_count++; stream = vstream_fdopen(fd, O_RDWR); tmp = concatenate(event_server_name, " socket", (char *) 0); - vstream_control(stream, VSTREAM_CTL_PATH, tmp, VSTREAM_CTL_END); + vstream_control(stream, + VSTREAM_CTL_PATH, tmp, + VSTREAM_CTL_CONTEXT, (char *) attr, + VSTREAM_CTL_END); myfree(tmp); timed_ipc_setup(stream); + event_server_saved_flags = vstream_flags(stream); if (event_server_in_flow_delay && mail_flow_get(1) < 0) event_request_timer(event_server_execute, (char *) stream, var_in_flow_delay); @@ -430,7 +442,7 @@ static void event_server_accept_local(int unused_event, char *context) event_request_timer(event_server_timeout, (char *) 0, time_left); return; } - event_server_wakeup(fd); + event_server_wakeup(fd, (HTABLE *) 0); } #ifdef MASTER_XPORT_NAME_PASS @@ -442,6 +454,7 @@ static void event_server_accept_pass(int unused_event, char *context) int listen_fd = CAST_CHAR_PTR_TO_INT(context); int time_left = -1; int fd; + HTABLE *attr = 0; /* * Be prepared for accept() to fail because some other process already @@ -455,7 +468,7 @@ static void event_server_accept_pass(int unused_event, char *context) if (event_server_pre_accept) event_server_pre_accept(event_server_name, event_server_argv); - fd = PASS_ACCEPT(listen_fd); + fd = pass_accept_attr(listen_fd, &attr); if (event_server_lock != 0 && myflock(vstream_fileno(event_server_lock), INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0) @@ -467,7 +480,7 @@ static void event_server_accept_pass(int unused_event, char *context) event_request_timer(event_server_timeout, (char *) 0, time_left); return; } - event_server_wakeup(fd); + event_server_wakeup(fd, attr); } #endif @@ -504,7 +517,7 @@ static void event_server_accept_inet(int unused_event, char *context) event_request_timer(event_server_timeout, (char *) 0, time_left); return; } - event_server_wakeup(fd); + event_server_wakeup(fd, (HTABLE *) 0); } /* event_server_main - the real main program */ diff --git a/postfix/src/master/master_listen.c b/postfix/src/master/master_listen.c index 494d429f7..a17bde1cb 100644 --- a/postfix/src/master/master_listen.c +++ b/postfix/src/master/master_listen.c @@ -136,7 +136,7 @@ void master_listen_init(MASTER_SERV *serv) case MASTER_SERV_TYPE_PASS: set_eugid(var_owner_uid, var_owner_gid); serv->listen_fd[0] = - PASS_LISTEN(serv->name, serv->max_proc > var_proc_limit ? + LOCAL_LISTEN(serv->name, serv->max_proc > var_proc_limit ? serv->max_proc : var_proc_limit, NON_BLOCKING); close_on_exec(serv->listen_fd[0], CLOSE_ON_EXEC); set_ugid(getuid(), getgid()); diff --git a/postfix/src/master/master_wakeup.c b/postfix/src/master/master_wakeup.c index ee6062023..47c2e4134 100644 --- a/postfix/src/master/master_wakeup.c +++ b/postfix/src/master/master_wakeup.c @@ -107,7 +107,7 @@ static void master_wakeup_timer_event(int unused_event, char *context) break; #ifdef MASTER_SERV_TYPE_PASS case MASTER_SERV_TYPE_PASS: - status = PASS_TRIGGER(serv->name, &wakeup, sizeof(wakeup), BRIEFLY); + status = pass_trigger(serv->name, &wakeup, sizeof(wakeup), BRIEFLY); break; #endif diff --git a/postfix/src/master/multi_server.c b/postfix/src/master/multi_server.c index c16118db3..19c04040d 100644 --- a/postfix/src/master/multi_server.c +++ b/postfix/src/master/multi_server.c @@ -35,6 +35,9 @@ /* function is run after the program has optionally dropped its /* privileges. This function should not attempt to preserve state /* across calls. The stream initial state is non-blocking mode. +/* Optional connection attributes are provided as a hash that +/* is attached as stream context. NOTE: the attributes are +/* destroyed after this function is called. /* The service name argument corresponds to the service name in the /* master.cf file. /* The argv argument specifies command-line arguments left over @@ -241,6 +244,7 @@ static VSTREAM *multi_server_lock; static int multi_server_in_flow_delay; static unsigned multi_server_generation; static void (*multi_server_pre_disconn) (VSTREAM *, char *, char **); +static int multi_server_saved_flags; /* multi_server_exit - normal termination */ @@ -322,6 +326,8 @@ void multi_server_disconnect(VSTREAM *stream) static void multi_server_execute(int unused_event, char *context) { VSTREAM *stream = (VSTREAM *) context; + HTABLE *attr = (vstream_flags(stream) == multi_server_saved_flags ? + (HTABLE *) vstream_context(stream) : 0); if (multi_server_lock != 0 && myflock(vstream_fileno(multi_server_lock), INTERNAL_LOCK, @@ -342,6 +348,8 @@ static void multi_server_execute(int unused_event, char *context) } else { multi_server_disconnect(stream); } + if (attr) + htable_free(attr, myfree); } /* multi_server_enable_read - enable read events */ @@ -355,7 +363,7 @@ static void multi_server_enable_read(int unused_event, char *context) /* multi_server_wakeup - wake up application */ -static void multi_server_wakeup(int fd) +static void multi_server_wakeup(int fd, HTABLE *attr) { VSTREAM *stream; char *tmp; @@ -384,9 +392,13 @@ static void multi_server_wakeup(int fd) client_count++; stream = vstream_fdopen(fd, O_RDWR); tmp = concatenate(multi_server_name, " socket", (char *) 0); - vstream_control(stream, VSTREAM_CTL_PATH, tmp, VSTREAM_CTL_END); + vstream_control(stream, + VSTREAM_CTL_PATH, tmp, + VSTREAM_CTL_CONTEXT, (char *) attr, + VSTREAM_CTL_END); myfree(tmp); timed_ipc_setup(stream); + multi_server_saved_flags = vstream_flags(stream); if (multi_server_in_flow_delay && mail_flow_get(1) < 0) event_request_timer(multi_server_enable_read, (char *) stream, var_in_flow_delay); @@ -426,7 +438,7 @@ static void multi_server_accept_local(int unused_event, char *context) event_request_timer(multi_server_timeout, (char *) 0, time_left); return; } - multi_server_wakeup(fd); + multi_server_wakeup(fd, (HTABLE *) 0); } #ifdef MASTER_XPORT_NAME_PASS @@ -438,6 +450,7 @@ static void multi_server_accept_pass(int unused_event, char *context) int listen_fd = CAST_CHAR_PTR_TO_INT(context); int time_left = -1; int fd; + HTABLE *attr = 0; /* * Be prepared for accept() to fail because some other process already @@ -451,7 +464,7 @@ static void multi_server_accept_pass(int unused_event, char *context) if (multi_server_pre_accept) multi_server_pre_accept(multi_server_name, multi_server_argv); - fd = PASS_ACCEPT(listen_fd); + fd = pass_accept_attr(listen_fd, &attr); if (multi_server_lock != 0 && myflock(vstream_fileno(multi_server_lock), INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0) @@ -463,7 +476,7 @@ static void multi_server_accept_pass(int unused_event, char *context) event_request_timer(multi_server_timeout, (char *) 0, time_left); return; } - multi_server_wakeup(fd); + multi_server_wakeup(fd, attr); } #endif @@ -500,7 +513,7 @@ static void multi_server_accept_inet(int unused_event, char *context) event_request_timer(multi_server_timeout, (char *) 0, time_left); return; } - multi_server_wakeup(fd); + multi_server_wakeup(fd, (HTABLE *) 0); } /* multi_server_main - the real main program */ diff --git a/postfix/src/master/single_server.c b/postfix/src/master/single_server.c index 7b6d8bfd7..01bc89218 100644 --- a/postfix/src/master/single_server.c +++ b/postfix/src/master/single_server.c @@ -29,6 +29,8 @@ /* a client connects to the program's service port. The function is /* run after the program has irrevocably dropped its privileges. /* The stream initial state is non-blocking mode. +/* Optional connection attributes are provided as a hash that +/* is attached as stream context. /* The service name argument corresponds to the service name in the /* master.cf file. /* The argv argument specifies command-line arguments left over @@ -245,7 +247,7 @@ static void single_server_timeout(int unused_event, char *unused_context) /* single_server_wakeup - wake up application */ -static void single_server_wakeup(int fd) +static void single_server_wakeup(int fd, HTABLE *attr) { VSTREAM *stream; char *tmp; @@ -263,7 +265,10 @@ static void single_server_wakeup(int fd) close_on_exec(fd, CLOSE_ON_EXEC); stream = vstream_fdopen(fd, O_RDWR); tmp = concatenate(single_server_name, " socket", (char *) 0); - vstream_control(stream, VSTREAM_CTL_PATH, tmp, VSTREAM_CTL_END); + vstream_control(stream, + VSTREAM_CTL_PATH, tmp, + VSTREAM_CTL_CONTEXT, (char *) attr, + VSTREAM_CTL_END); myfree(tmp); timed_ipc_setup(stream); if (master_notify(var_pid, single_server_generation, MASTER_STAT_TAKEN) < 0) @@ -281,6 +286,8 @@ static void single_server_wakeup(int fd) use_count++; if (var_idle_limit > 0) event_request_timer(single_server_timeout, (char *) 0, var_idle_limit); + if (attr) + htable_free(attr, myfree); } /* single_server_accept_local - accept client connection request */ @@ -314,7 +321,7 @@ static void single_server_accept_local(int unused_event, char *context) event_request_timer(single_server_timeout, (char *) 0, time_left); return; } - single_server_wakeup(fd); + single_server_wakeup(fd, (HTABLE *) 0); } #ifdef MASTER_XPORT_NAME_PASS @@ -326,6 +333,7 @@ static void single_server_accept_pass(int unused_event, char *context) int listen_fd = CAST_CHAR_PTR_TO_INT(context); int time_left = -1; int fd; + HTABLE *attr = 0; /* * Be prepared for accept() to fail because some other process already @@ -338,7 +346,7 @@ static void single_server_accept_pass(int unused_event, char *context) if (single_server_pre_accept) single_server_pre_accept(single_server_name, single_server_argv); - fd = PASS_ACCEPT(listen_fd); + fd = pass_accept_attr(listen_fd, &attr); if (single_server_lock != 0 && myflock(vstream_fileno(single_server_lock), INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0) @@ -350,7 +358,7 @@ static void single_server_accept_pass(int unused_event, char *context) event_request_timer(single_server_timeout, (char *) 0, time_left); return; } - single_server_wakeup(fd); + single_server_wakeup(fd, attr); } #endif @@ -386,7 +394,7 @@ static void single_server_accept_inet(int unused_event, char *context) event_request_timer(single_server_timeout, (char *) 0, time_left); return; } - single_server_wakeup(fd); + single_server_wakeup(fd, (HTABLE *) 0); } /* single_server_main - the real main program */ @@ -472,7 +480,7 @@ NORETURN single_server_main(int argc, char **argv, SINGLE_SERVER_FN service,...) * Register dictionaries that use higher-level interfaces and protocols. */ mail_dict_init(); - + /* * After database open error, continue execution with reduced * functionality. diff --git a/postfix/src/master/trigger_server.c b/postfix/src/master/trigger_server.c index bc4a16cea..cdfdd75bb 100644 --- a/postfix/src/master/trigger_server.c +++ b/postfix/src/master/trigger_server.c @@ -376,7 +376,7 @@ static void trigger_server_accept_pass(int unused_event, char *context) if (trigger_server_pre_accept) trigger_server_pre_accept(trigger_server_name, trigger_server_argv); - fd = PASS_ACCEPT(listen_fd); + fd = pass_accept(listen_fd); if (trigger_server_lock != 0 && myflock(vstream_fileno(trigger_server_lock), INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0) diff --git a/postfix/src/postscreen/Makefile.in b/postfix/src/postscreen/Makefile.in index a7a61837f..d83a10bba 100644 --- a/postfix/src/postscreen/Makefile.in +++ b/postfix/src/postscreen/Makefile.in @@ -2,11 +2,13 @@ SHELL = /bin/sh SRCS = postscreen.c postscreen_dict.c postscreen_dnsbl.c \ postscreen_early.c postscreen_smtpd.c postscreen_misc.c \ postscreen_state.c postscreen_tests.c postscreen_send.c \ - postscreen_starttls.c postscreen_expand.c + postscreen_starttls.c postscreen_expand.c postscreen_endpt.c \ + postscreen_haproxy.c OBJS = postscreen.o postscreen_dict.o postscreen_dnsbl.o \ postscreen_early.o postscreen_smtpd.o postscreen_misc.o \ postscreen_state.o postscreen_tests.o postscreen_send.o \ - postscreen_starttls.o postscreen_expand.o + postscreen_starttls.o postscreen_expand.o postscreen_endpt.o \ + postscreen_haproxy.o HDRS = TESTSRC = DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) @@ -103,6 +105,7 @@ postscreen_dict.o: ../../include/htable.h postscreen_dict.o: ../../include/maps.h postscreen_dict.o: ../../include/match_list.h postscreen_dict.o: ../../include/msg.h +postscreen_dict.o: ../../include/myaddrinfo.h postscreen_dict.o: ../../include/server_acl.h postscreen_dict.o: ../../include/string_list.h postscreen_dict.o: ../../include/sys_defs.h @@ -149,6 +152,7 @@ postscreen_early.o: ../../include/mail_params.h postscreen_early.o: ../../include/maps.h postscreen_early.o: ../../include/match_list.h postscreen_early.o: ../../include/msg.h +postscreen_early.o: ../../include/myaddrinfo.h postscreen_early.o: ../../include/mymalloc.h postscreen_early.o: ../../include/server_acl.h postscreen_early.o: ../../include/string_list.h @@ -159,6 +163,27 @@ postscreen_early.o: ../../include/vstream.h postscreen_early.o: ../../include/vstring.h postscreen_early.o: postscreen.h postscreen_early.o: postscreen_early.c +postscreen_endpt.o: ../../include/addr_match_list.h +postscreen_endpt.o: ../../include/argv.h +postscreen_endpt.o: ../../include/dict.h +postscreen_endpt.o: ../../include/dict_cache.h +postscreen_endpt.o: ../../include/events.h +postscreen_endpt.o: ../../include/haproxy_srvr.h +postscreen_endpt.o: ../../include/htable.h +postscreen_endpt.o: ../../include/mail_params.h +postscreen_endpt.o: ../../include/maps.h +postscreen_endpt.o: ../../include/match_list.h +postscreen_endpt.o: ../../include/msg.h +postscreen_endpt.o: ../../include/myaddrinfo.h +postscreen_endpt.o: ../../include/server_acl.h +postscreen_endpt.o: ../../include/string_list.h +postscreen_endpt.o: ../../include/sys_defs.h +postscreen_endpt.o: ../../include/vbuf.h +postscreen_endpt.o: ../../include/vstream.h +postscreen_endpt.o: ../../include/vstring.h +postscreen_endpt.o: postscreen.h +postscreen_endpt.o: postscreen_endpt.c +postscreen_endpt.o: postscreen_haproxy.h postscreen_expand.o: ../../include/addr_match_list.h postscreen_expand.o: ../../include/argv.h postscreen_expand.o: ../../include/attr.h @@ -172,6 +197,7 @@ postscreen_expand.o: ../../include/mail_proto.h postscreen_expand.o: ../../include/maps.h postscreen_expand.o: ../../include/match_list.h postscreen_expand.o: ../../include/msg.h +postscreen_expand.o: ../../include/myaddrinfo.h postscreen_expand.o: ../../include/server_acl.h postscreen_expand.o: ../../include/string_list.h postscreen_expand.o: ../../include/stringops.h @@ -181,6 +207,28 @@ postscreen_expand.o: ../../include/vstream.h postscreen_expand.o: ../../include/vstring.h postscreen_expand.o: postscreen.h postscreen_expand.o: postscreen_expand.c +postscreen_haproxy.o: ../../include/addr_match_list.h +postscreen_haproxy.o: ../../include/argv.h +postscreen_haproxy.o: ../../include/dict.h +postscreen_haproxy.o: ../../include/dict_cache.h +postscreen_haproxy.o: ../../include/events.h +postscreen_haproxy.o: ../../include/haproxy_srvr.h +postscreen_haproxy.o: ../../include/htable.h +postscreen_haproxy.o: ../../include/maps.h +postscreen_haproxy.o: ../../include/match_list.h +postscreen_haproxy.o: ../../include/msg.h +postscreen_haproxy.o: ../../include/myaddrinfo.h +postscreen_haproxy.o: ../../include/mymalloc.h +postscreen_haproxy.o: ../../include/server_acl.h +postscreen_haproxy.o: ../../include/string_list.h +postscreen_haproxy.o: ../../include/stringops.h +postscreen_haproxy.o: ../../include/sys_defs.h +postscreen_haproxy.o: ../../include/vbuf.h +postscreen_haproxy.o: ../../include/vstream.h +postscreen_haproxy.o: ../../include/vstring.h +postscreen_haproxy.o: postscreen.h +postscreen_haproxy.o: postscreen_haproxy.c +postscreen_haproxy.o: postscreen_haproxy.h postscreen_misc.o: ../../include/addr_match_list.h postscreen_misc.o: ../../include/argv.h postscreen_misc.o: ../../include/dict.h @@ -193,6 +241,7 @@ postscreen_misc.o: ../../include/mail_params.h postscreen_misc.o: ../../include/maps.h postscreen_misc.o: ../../include/match_list.h postscreen_misc.o: ../../include/msg.h +postscreen_misc.o: ../../include/myaddrinfo.h postscreen_misc.o: ../../include/server_acl.h postscreen_misc.o: ../../include/string_list.h postscreen_misc.o: ../../include/sys_defs.h @@ -203,6 +252,7 @@ postscreen_misc.o: postscreen.h postscreen_misc.o: postscreen_misc.c postscreen_send.o: ../../include/addr_match_list.h postscreen_send.o: ../../include/argv.h +postscreen_send.o: ../../include/attr.h postscreen_send.o: ../../include/connect.h postscreen_send.o: ../../include/dict.h postscreen_send.o: ../../include/dict_cache.h @@ -212,9 +262,11 @@ postscreen_send.o: ../../include/iostuff.h postscreen_send.o: ../../include/mac_expand.h postscreen_send.o: ../../include/mac_parse.h postscreen_send.o: ../../include/mail_params.h +postscreen_send.o: ../../include/mail_proto.h postscreen_send.o: ../../include/maps.h postscreen_send.o: ../../include/match_list.h postscreen_send.o: ../../include/msg.h +postscreen_send.o: ../../include/myaddrinfo.h postscreen_send.o: ../../include/server_acl.h postscreen_send.o: ../../include/smtp_reply_footer.h postscreen_send.o: ../../include/string_list.h @@ -240,6 +292,7 @@ postscreen_smtpd.o: ../../include/mail_proto.h postscreen_smtpd.o: ../../include/maps.h postscreen_smtpd.o: ../../include/match_list.h postscreen_smtpd.o: ../../include/msg.h +postscreen_smtpd.o: ../../include/myaddrinfo.h postscreen_smtpd.o: ../../include/mymalloc.h postscreen_smtpd.o: ../../include/name_code.h postscreen_smtpd.o: ../../include/name_mask.h @@ -267,6 +320,7 @@ postscreen_starttls.o: ../../include/mail_proto.h postscreen_starttls.o: ../../include/maps.h postscreen_starttls.o: ../../include/match_list.h postscreen_starttls.o: ../../include/msg.h +postscreen_starttls.o: ../../include/myaddrinfo.h postscreen_starttls.o: ../../include/mymalloc.h postscreen_starttls.o: ../../include/name_code.h postscreen_starttls.o: ../../include/name_mask.h @@ -294,6 +348,7 @@ postscreen_state.o: ../../include/mail_server.h postscreen_state.o: ../../include/maps.h postscreen_state.o: ../../include/match_list.h postscreen_state.o: ../../include/msg.h +postscreen_state.o: ../../include/myaddrinfo.h postscreen_state.o: ../../include/mymalloc.h postscreen_state.o: ../../include/name_mask.h postscreen_state.o: ../../include/server_acl.h @@ -314,6 +369,7 @@ postscreen_tests.o: ../../include/mail_params.h postscreen_tests.o: ../../include/maps.h postscreen_tests.o: ../../include/match_list.h postscreen_tests.o: ../../include/msg.h +postscreen_tests.o: ../../include/myaddrinfo.h postscreen_tests.o: ../../include/server_acl.h postscreen_tests.o: ../../include/string_list.h postscreen_tests.o: ../../include/sys_defs.h diff --git a/postfix/src/postscreen/postscreen.c b/postfix/src/postscreen/postscreen.c index fa85d6d48..2c55a660c 100644 --- a/postfix/src/postscreen/postscreen.c +++ b/postfix/src/postscreen/postscreen.c @@ -132,6 +132,16 @@ /* .IP "\fBsoft_bounce (no)\fR" /* Safety net to keep mail queued that would otherwise be returned to /* the sender. +/* BEFORE-POSTSCREEN PROXY AGENT +/* .ad +/* .fi +/* Available in Postfix version 2.10 and later: +/* .IP "\fBpostscreen_upstream_proxy_protocol (empty)\fR" +/* The name of the proxy protocol used by an optional before-postscreen +/* proxy agent. +/* .IP "\fBpostscreen_upstream_proxy_timeout (5s)\fR" +/* The time limit for the proxy protocol specified with the +/* postscreen_upstream_proxy_protocol parameter. /* PERMANENT WHITE/BLACKLIST TEST /* .ad /* .fi @@ -484,6 +494,8 @@ char *var_smtpd_exp_filter; char *var_psc_exp_filter; char *var_psc_wlist_if; +char *var_psc_uproxy_proto; +int var_psc_uproxy_tmout; /* * Global variables. @@ -512,12 +524,16 @@ DICT *psc_dnsbl_reply; /* DNSBL name mapper */ HTABLE *psc_client_concurrency; /* per-client concurrency */ /* - * Local variables. + * Local variables and functions. */ static ARGV *psc_acl; /* permanent white/backlist */ static int psc_blist_action; /* PSC_ACT_DROP/ENFORCE/etc */ static ADDR_MATCH_LIST *psc_wlist_if; /* whitelist interfaces */ +static void psc_endpt_lookup_done(int, VSTREAM *, + MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *, + MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *); + /* psc_dump - dump some statistics before exit */ static void psc_dump(void) @@ -581,17 +597,6 @@ static void psc_service(VSTREAM *smtp_client_stream, char *unused_service, char **unused_argv) { - const char *myname = "psc_service"; - PSC_STATE *state; - struct sockaddr_storage addr_storage; - SOCKADDR_SIZE addr_storage_len = sizeof(addr_storage); - MAI_HOSTADDR_STR smtp_client_addr; - MAI_SERVPORT_STR smtp_client_port; - MAI_HOSTADDR_STR smtp_server_addr; - MAI_SERVPORT_STR smtp_server_port; - int aierr; - const char *stamp_str; - int saved_flags; /* * For sanity, require that at least one of INET or INET6 is enabled. @@ -611,85 +616,53 @@ static void psc_service(VSTREAM *smtp_client_stream, */ non_blocking(vstream_fileno(smtp_client_stream), NON_BLOCKING); - /* - * We use the event_server framework. This means we get already-accepted - * connections so we have to invoke getpeername() to find out the remote - * address and port. - */ - - /* Best effort - if this non-blocking write(2) fails, so be it. */ -#define PSC_SERVICE_DISCONNECT_AND_RETURN(stream) do { \ - (void) write(vstream_fileno(stream), \ - "421 4.3.2 No system resources\r\n", \ - sizeof("421 4.3.2 No system resources\r\n") - 1); \ - event_server_disconnect(stream); \ - return; \ - } while (0); - /* * Look up the remote SMTP client address and port. */ - if (getpeername(vstream_fileno(smtp_client_stream), (struct sockaddr *) - & addr_storage, &addr_storage_len) < 0) { - msg_warn("getpeername: %m -- dropping this connection"); - PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream); - } + psc_endpt_lookup(smtp_client_stream, psc_endpt_lookup_done); +} + +/* psc_endpt_lookup_done - endpoint lookup completed */ + +static void psc_endpt_lookup_done(int endpt_status, + VSTREAM *smtp_client_stream, + MAI_HOSTADDR_STR *smtp_client_addr, + MAI_SERVPORT_STR *smtp_client_port, + MAI_HOSTADDR_STR *smtp_server_addr, + MAI_SERVPORT_STR *smtp_server_port) +{ + const char *myname = "psc_endpt_lookup_done"; + PSC_STATE *state; + const char *stamp_str; + int saved_flags; /* - * Convert the remote SMTP client address and port to printable form for - * logging and access control. + * Best effort - if this non-blocking write(2) fails, so be it. */ - if ((aierr = sockaddr_to_hostaddr((struct sockaddr *) & addr_storage, - addr_storage_len, &smtp_client_addr, - &smtp_client_port, 0)) != 0) { - msg_warn("cannot convert client address/port to string: %s" - " -- dropping this connection", - MAI_STRERROR(aierr)); - PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream); + if (endpt_status < 0) { + (void) write(vstream_fileno(smtp_client_stream), + "421 4.3.2 No system resources\r\n", + sizeof("421 4.3.2 No system resources\r\n") - 1); + event_server_disconnect(smtp_client_stream); + return; } - if (strncasecmp("::ffff:", smtp_client_addr.buf, 7) == 0) - memmove(smtp_client_addr.buf, smtp_client_addr.buf + 7, - sizeof(smtp_client_addr.buf) - 7); if (msg_verbose > 1) msg_info("%s: sq=%d cq=%d connect from [%s]:%s", myname, psc_post_queue_length, psc_check_queue_length, - smtp_client_addr.buf, smtp_client_port.buf); - - /* - * Look up the local SMTP server address and port. - */ - if (getsockname(vstream_fileno(smtp_client_stream), (struct sockaddr *) - & addr_storage, &addr_storage_len) < 0) { - msg_warn("getsockname: %m -- dropping this connection"); - PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream); - } - - /* - * Convert the local SMTP server address and port to printable form for - * logging and access control. - */ - if ((aierr = sockaddr_to_hostaddr((struct sockaddr *) & addr_storage, - addr_storage_len, &smtp_server_addr, - &smtp_server_port, 0)) != 0) { - msg_warn("cannot convert server address/port to string: %s" - " -- dropping this connection", - MAI_STRERROR(aierr)); - PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream); - } - if (strncasecmp("::ffff:", smtp_server_addr.buf, 7) == 0) - memmove(smtp_server_addr.buf, smtp_server_addr.buf + 7, - sizeof(smtp_server_addr.buf) - 7); + smtp_client_addr->buf, smtp_client_port->buf); msg_info("CONNECT from [%s]:%s to [%s]:%s", - smtp_client_addr.buf, smtp_client_port.buf, - smtp_server_addr.buf, smtp_server_port.buf); + smtp_client_addr->buf, smtp_client_port->buf, + smtp_server_addr->buf, smtp_server_port->buf); /* * Bundle up all the loose session pieces. This zeroes all flags and time * stamps. */ - state = psc_new_session_state(smtp_client_stream, smtp_client_addr.buf, - smtp_client_port.buf); + state = psc_new_session_state(smtp_client_stream, smtp_client_addr->buf, + smtp_client_port->buf, + smtp_server_addr->buf, + smtp_server_port->buf); /* * Reply with 421 when the client has too many open connections. @@ -799,7 +772,7 @@ static void psc_service(VSTREAM *smtp_client_stream, * Don't whitelist clients that connect to backup MX addresses. Fail * "closed" on error. */ - if (addr_match_list_match(psc_wlist_if, smtp_server_addr.buf) == 0) { + if (addr_match_list_match(psc_wlist_if, smtp_server_addr->buf) == 0) { state->flags |= (PSC_STATE_FLAG_WLIST_FAIL | PSC_STATE_FLAG_NOFORWARD); msg_info("WHITELIST VETO [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); } @@ -1114,6 +1087,7 @@ int main(int argc, char **argv) VAR_DNSBLOG_SERVICE, DEF_DNSBLOG_SERVICE, &var_dnsblog_service, 1, 0, VAR_TLSPROXY_SERVICE, DEF_TLSPROXY_SERVICE, &var_tlsproxy_service, 1, 0, VAR_PSC_WLIST_IF, DEF_PSC_WLIST_IF, &var_psc_wlist_if, 0, 0, + VAR_PSC_UPROXY_PROTO, DEF_PSC_UPROXY_PROTO, &var_psc_uproxy_proto, 0, 0, 0, }; static const CONFIG_INT_TABLE int_table[] = { @@ -1139,6 +1113,7 @@ int main(int argc, char **argv) VAR_PSC_CACHE_RET, DEF_PSC_CACHE_RET, &var_psc_cache_ret, 1, 0, VAR_PSC_CACHE_SCAN, DEF_PSC_CACHE_SCAN, &var_psc_cache_scan, 0, 0, VAR_PSC_WATCHDOG, DEF_PSC_WATCHDOG, &var_psc_watchdog, 10, 0, + VAR_PSC_UPROXY_TMOUT, DEF_PSC_UPROXY_TMOUT, &var_psc_uproxy_tmout, 1, 0, 0, }; static const CONFIG_BOOL_TABLE bool_table[] = { diff --git a/postfix/src/postscreen/postscreen.h b/postfix/src/postscreen/postscreen.h index 87cc2c130..860a134b6 100644 --- a/postfix/src/postscreen/postscreen.h +++ b/postfix/src/postscreen/postscreen.h @@ -20,6 +20,7 @@ #include #include #include +#include /* * Global library. @@ -44,6 +45,8 @@ typedef struct { int smtp_server_fd; /* real SMTP server */ char *smtp_client_addr; /* client address */ char *smtp_client_port; /* client port */ + char *smtp_server_addr; /* server address */ + char *smtp_server_port; /* server port */ int client_concurrency; /* per-client */ const char *final_reply; /* cause for hanging up */ VSTRING *send_buf; /* pending output */ @@ -379,7 +382,7 @@ extern HTABLE *psc_client_concurrency; /* per-client concurrency */ (state)->smtp_client_stream = 0; \ psc_check_queue_length--; \ } while (0) -extern PSC_STATE *psc_new_session_state(VSTREAM *, const char *, const char *); +extern PSC_STATE *psc_new_session_state(VSTREAM *, const char *, const char *, const char *, const char *); extern void psc_free_session_state(PSC_STATE *); extern const char *psc_print_state_flags(int, const char *); @@ -468,6 +471,14 @@ extern VSTRING *psc_expand_filter; extern void psc_expand_init(void); extern const char *psc_expand_lookup(const char *, int, char *); + /* + * postscreen_endpt.c + */ +typedef void (*PSC_ENDPT_LOOKUP_FN) (int, VSTREAM *, + MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *, + MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *); +extern void psc_endpt_lookup(VSTREAM *, PSC_ENDPT_LOOKUP_FN); + /* * postscreen_access emulation. */ diff --git a/postfix/src/postscreen/postscreen_endpt.c b/postfix/src/postscreen/postscreen_endpt.c new file mode 100644 index 000000000..44f332e77 --- /dev/null +++ b/postfix/src/postscreen/postscreen_endpt.c @@ -0,0 +1,186 @@ +/*++ +/* NAME +/* postscreen_endpt 3 +/* SUMMARY +/* look up connection endpoint information +/* SYNOPSIS +/* #include +/* +/* void psc_endpt_lookup(smtp_client_stream, +/* void *lookup_done(status, smtp_client_stream, +/* smtp_client_addr, smtp_client_port, +/* smtp_server_addr, smtp_server_port)) +/* VSTRING *smtp_client_stream; +/* int status; +/* MAI_HOSTADDR_STR *smtp_client_addr; +/* MAI_SERVPORT_STR *smtp_client_port; +/* MAI_HOSTADDR_STR *smtp_server_addr; +/* MAI_SERVPORT_STR *smtp_server_port; +/* DESCRIPTION +/* psc_endpt_lookup() looks up remote and local connection +/* endpoint information through local system calls or through +/* a remote proxy protocol. The lookup_done() call-back routine +/* passes the result status, address and port information. The +/* result status is -1 in case of error, 0 in case of success. +/* This function (and its supporting routines) logs a warning +/* in case of error, and never communicates with a remote SMTP +/* client. +/* 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 +#include + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include + +/* Global library. */ + +#include +#include + +/* Application-specific. */ + +#include +#include + +static INET_PROTO_INFO *proto_info; + +/* psc_sockaddr_to_hostaddr - transform endpoint address and port to string */ + +static int psc_sockaddr_to_hostaddr(struct sockaddr * addr_storage, + SOCKADDR_SIZE addr_storage_len, + MAI_HOSTADDR_STR *addr_buf, + MAI_SERVPORT_STR *port_buf, + int socktype) +{ + int aierr; + + if ((aierr = sockaddr_to_hostaddr(addr_storage, addr_storage_len, + addr_buf, port_buf, socktype)) == 0 + && strncasecmp("::ffff:", addr_buf->buf, 7) == 0 + && strchr((char *) proto_info->sa_family_list, AF_INET) != 0) + memmove(addr_buf->buf, addr_buf->buf + 7, + sizeof(addr_buf->buf) - 7); + return (aierr); +} + +/* psc_endpt_local_lookup - look up local system connection information */ + +static void psc_endpt_local_lookup(VSTREAM *smtp_client_stream, + PSC_ENDPT_LOOKUP_FN lookup_done) +{ + struct sockaddr_storage addr_storage; + SOCKADDR_SIZE addr_storage_len = sizeof(addr_storage); + int status; + MAI_HOSTADDR_STR smtp_client_addr; + MAI_SERVPORT_STR smtp_client_port; + MAI_HOSTADDR_STR smtp_server_addr; + MAI_SERVPORT_STR smtp_server_port; + int aierr; + + /* + * Look up the remote SMTP client address and port. + */ + if (getpeername(vstream_fileno(smtp_client_stream), (struct sockaddr *) + & addr_storage, &addr_storage_len) < 0) { + msg_warn("getpeername: %m -- dropping this connection"); + status = -1; + } + + /* + * Convert the remote SMTP client address and port to printable form for + * logging and access control. + */ + else if ((aierr = psc_sockaddr_to_hostaddr( + (struct sockaddr *) & addr_storage, + addr_storage_len, &smtp_client_addr, + &smtp_client_port, SOCK_STREAM)) != 0) { + msg_warn("cannot convert client address/port to string: %s" + " -- dropping this connection", + MAI_STRERROR(aierr)); + status = -1; + } + + /* + * Look up the local SMTP server address and port. + */ + else if (getsockname(vstream_fileno(smtp_client_stream), + (struct sockaddr *) & addr_storage, + &addr_storage_len) < 0) { + msg_warn("getsockname: %m -- dropping this connection"); + status = -1; + } + + /* + * Convert the local SMTP server address and port to printable form for + * logging. + */ + else if ((aierr = psc_sockaddr_to_hostaddr( + (struct sockaddr *) & addr_storage, + addr_storage_len, &smtp_server_addr, + &smtp_server_port, SOCK_STREAM)) != 0) { + msg_warn("cannot convert server address/port to string: %s" + " -- dropping this connection", + MAI_STRERROR(aierr)); + status = -1; + } else { + status = 0; + } + lookup_done(status, smtp_client_stream, + &smtp_client_addr, &smtp_client_port, + &smtp_server_addr, &smtp_server_port); +} + + /* + * Lookup table for available proxy protocols. + */ +typedef struct { + const char *name; + void (*endpt_lookup) (VSTREAM *, PSC_ENDPT_LOOKUP_FN); +} PSC_ENDPT_LOOKUP_INFO; + +static const PSC_ENDPT_LOOKUP_INFO psc_endpt_lookup_info[] = { + DEF_PSC_UPROXY_PROTO, psc_endpt_local_lookup, + HAPROXY_PROTO_NAME, psc_endpt_haproxy_lookup, + 0, +}; + +/* psc_endpt_lookup - look up connection endpoint information */ + +void psc_endpt_lookup(VSTREAM *smtp_client_stream, + PSC_ENDPT_LOOKUP_FN notify) +{ + const PSC_ENDPT_LOOKUP_INFO *pp; + + if (proto_info == 0) + proto_info = inet_proto_info(); + + for (pp = psc_endpt_lookup_info; /* see below */ ; pp++) { + if (pp->name == 0) + msg_fatal("unsupported %s value: %s", + VAR_PSC_UPROXY_PROTO, var_psc_uproxy_proto); + if (strcmp(var_psc_uproxy_proto, pp->name) == 0) { + pp->endpt_lookup(smtp_client_stream, notify); + return; + } + } +} diff --git a/postfix/src/postscreen/postscreen_haproxy.c b/postfix/src/postscreen/postscreen_haproxy.c new file mode 100644 index 000000000..a8cb73689 --- /dev/null +++ b/postfix/src/postscreen/postscreen_haproxy.c @@ -0,0 +1,194 @@ +/*++ +/* NAME +/* postscreen_haproxy 3 +/* SUMMARY +/* haproxy protocol adapter +/* SYNOPSIS +/* #include +/* +/* void psc_endpt_haproxy_lookup(smtp_client_stream, +/* void *lookup_done(status, smtp_client_stream, +/* smtp_client_addr, smtp_client_port, +/* smtp_server_addr, smtp_server_port)) +/* VSTRING *smtp_client_stream; +/* int status; +/* MAI_HOSTADDR_STR *smtp_client_addr; +/* MAI_SERVPORT_STR *smtp_client_port; +/* MAI_HOSTADDR_STR *smtp_server_addr; +/* MAI_SERVPORT_STR *smtp_server_port; +/* DESCRIPTION +/* psc_endpt_haproxy_lookup() looks up connection endpoint +/* information via the haproxy protocol. Arguments and results +/* conform to the postscreen_endpt(3) API. +/* +/* The following summarizes what the Postfix SMTP server expects +/* from an up-stream proxy adapter. +/* .IP \(bu +/* Validate address and port syntax. Permit only protocols +/* that are configured with the main.cf:inet_protocols +/* setting. +/* .IP \(bu +/* Convert IPv4-in-IPv6 address syntax to IPv4 form, when both +/* IPv4 and IPv6 support are enabled with main.cf:inet_protocols. +/* 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 +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include + +/* Application-specific. */ + +#include +#include + + /* + * Per-session state. + */ +typedef struct { + VSTREAM *stream; + PSC_ENDPT_LOOKUP_FN notify; + VSTRING *buffer; +} PSC_HAPROXY_STATE; + +/* psc_endpt_haproxy_event - read or time event */ + +static void psc_endpt_haproxy_event(int event, char *context) +{ + const char *myname = "psc_endpt_haproxy_event"; + PSC_HAPROXY_STATE *state = (PSC_HAPROXY_STATE *) context; + int status = 0; + MAI_HOSTADDR_STR smtp_client_addr; + MAI_SERVPORT_STR smtp_client_port; + MAI_HOSTADDR_STR smtp_server_addr; + MAI_SERVPORT_STR smtp_server_port; + int last_char = 0; + const char *err; + VSTRING *escape_buf; + + /* + * Basic event processing. + */ + switch (event) { + case EVENT_TIME: + msg_warn("haproxy read: time limit exceeded"); + status = -1; + break; + case EVENT_READ: + if ((last_char = VSTREAM_GETC(state->stream)) == VSTREAM_EOF) { + if (vstream_ferror(state->stream)) + msg_warn("haproxy read: %m"); + else + msg_warn("haproxy read: lost connection"); + status = -1; + break; + } + if (VSTRING_LEN(state->buffer) >= HAPROXY_MAX_LEN) { + msg_warn("haproxy read: line too long"); + status = -1; + break; + } + VSTRING_ADDCH(state->buffer, last_char); + break; + } + + /* + * Parse the haproxy line. Note: the haproxy_srvr_parse() routine + * performs address protocol checks, address and port syntax checks, and + * converts IPv4-in-IPv6 address string syntax (:ffff::1.2.3.4) to IPv4 + * syntax where permitted by the main.cf:inet_protocols setting. + */ + if (status == 0 && last_char == '\n') { + VSTRING_TERMINATE(state->buffer); + if ((err = haproxy_srvr_parse(vstring_str(state->buffer), + &smtp_client_addr, &smtp_client_port, + &smtp_server_addr, &smtp_server_port)) != 0) { + escape_buf = vstring_alloc(HAPROXY_MAX_LEN + 2); + escape(escape_buf, vstring_str(state->buffer), + VSTRING_LEN(state->buffer)); + msg_warn("haproxy read: %s: %s", err, vstring_str(escape_buf)); + status = -1; + vstring_free(escape_buf); + } + } + + /* + * Are we done yet? + */ + if (status < 0 || last_char == '\n') { + PSC_CLEAR_EVENT_REQUEST(vstream_fileno(state->stream), + psc_endpt_haproxy_event, context); + vstream_control(state->stream, + VSTREAM_CTL_BUFSIZE, VSTREAM_BUFSIZE, + VSTREAM_CTL_END); + state->notify(status, state->stream, + &smtp_client_addr, &smtp_client_port, + &smtp_server_addr, &smtp_server_port); + /* Note: the stream may be closed at this point. */ + vstring_free(state->buffer); + myfree((char *) state); + } +} + +/* psc_endpt_haproxy_lookup - event-driven haproxy client */ + +void psc_endpt_haproxy_lookup(VSTREAM *stream, + PSC_ENDPT_LOOKUP_FN notify) +{ + const char *myname = "psc_endpt_haproxy_lookup"; + PSC_HAPROXY_STATE *state; + + /* + * Prepare the per-session state. XXX To improve overload behavior, + * maintain a pool of these so that we can reduce memory allocator + * activity. + */ + state = (PSC_HAPROXY_STATE *) mymalloc(sizeof(*state)); + state->stream = stream; + state->notify = notify; + state->buffer = vstring_alloc(100); + + /* + * We don't assume that the haproxy line will be unfragmented. Therefore, + * we use read(2) instead of recv(..., MSG_PEEK). + * + * We must not read(2) past the that terminates the haproxy line. + * Therefore we force one-character read(2) calls. + * + * We want to (eventually) build this on top of a reusable line read + * routine, once we have figured out an easy-to-use and efficient API. + */ + vstream_control(stream, VSTREAM_CTL_BUFSIZE, 1, VSTREAM_CTL_END); + + /* + * Read the haproxy line. + */ + PSC_READ_EVENT_REQUEST(vstream_fileno(stream), psc_endpt_haproxy_event, + (char *) state, var_psc_uproxy_tmout); +} diff --git a/postfix/src/postscreen/postscreen_haproxy.h b/postfix/src/postscreen/postscreen_haproxy.h new file mode 100644 index 000000000..2691e481f --- /dev/null +++ b/postfix/src/postscreen/postscreen_haproxy.h @@ -0,0 +1,25 @@ +/*++ +/* NAME +/* postscreen_haproxy 3h +/* SUMMARY +/* postscreen haproxy protocol support +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * haproxy protocol interface. + */ +extern void psc_endpt_haproxy_lookup(VSTREAM *, PSC_ENDPT_LOOKUP_FN); + +/* 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 +/*--*/ diff --git a/postfix/src/postscreen/postscreen_send.c b/postfix/src/postscreen/postscreen_send.c index cbbbf97c7..61a5f0cf2 100644 --- a/postfix/src/postscreen/postscreen_send.c +++ b/postfix/src/postscreen/postscreen_send.c @@ -61,11 +61,14 @@ #include #include #include +#include +#include /* Global library. */ #include #include +#include /* Application-specific. */ @@ -163,6 +166,8 @@ void psc_send_socket(PSC_STATE *state) { const char *myname = "psc_send_socket"; int server_fd; + int pass_err; + VSTREAM *fp; if (msg_verbose > 1) msg_info("%s: sq=%d cq=%d send socket %d from [%s]:%s", @@ -187,8 +192,8 @@ void psc_send_socket(PSC_STATE *state) * Postfix-specific. */ if ((server_fd = - PASS_CONNECT(psc_smtpd_service_name, NON_BLOCKING, - PSC_SEND_SOCK_CONNECT_TIMEOUT)) < 0) { + LOCAL_CONNECT(psc_smtpd_service_name, NON_BLOCKING, + PSC_SEND_SOCK_CONNECT_TIMEOUT)) < 0) { msg_warn("cannot connect to service %s: %m", psc_smtpd_service_name); if (state->flags & PSC_STATE_FLAG_PREGR_TODO) { PSC_SMTPD_X21(state, "421 4.3.2 No system resources\r\n"); @@ -198,8 +203,19 @@ void psc_send_socket(PSC_STATE *state) } return; } - if (LOCAL_SEND_FD(server_fd, - vstream_fileno(state->smtp_client_stream)) < 0) { + /* XXX Note: no dummy read between LOCAL_SEND_FD() and attr_print(). */ + fp = vstream_fdopen(server_fd, O_RDWR); + pass_err = + (LOCAL_SEND_FD(server_fd, + vstream_fileno(state->smtp_client_stream)) < 0 + || (attr_print(fp, ATTR_FLAG_NONE, + ATTR_TYPE_STR, MAIL_ATTR_ACT_CLIENT_ADDR, state->smtp_client_addr, + ATTR_TYPE_STR, MAIL_ATTR_ACT_CLIENT_PORT, state->smtp_client_port, + ATTR_TYPE_STR, MAIL_ATTR_ACT_SERVER_ADDR, state->smtp_server_addr, + ATTR_TYPE_STR, MAIL_ATTR_ACT_SERVER_PORT, state->smtp_server_port, + ATTR_TYPE_END) || vstream_fflush(fp))); + (void) vstream_fdclose(fp); + if (pass_err != 0) { msg_warn("cannot pass connection to service %s: %m", psc_smtpd_service_name); (void) close(server_fd); diff --git a/postfix/src/postscreen/postscreen_state.c b/postfix/src/postscreen/postscreen_state.c index 581647b6f..e199eb885 100644 --- a/postfix/src/postscreen/postscreen_state.c +++ b/postfix/src/postscreen/postscreen_state.c @@ -6,10 +6,13 @@ /* SYNOPSIS /* #include /* -/* PSC_STATE *psc_new_session_state(stream, addr, port) +/* PSC_STATE *psc_new_session_state(stream, client_addr, client_port, +/* server_addr, server_port) /* VSTREAM *stream; -/* const char *addr; -/* const char *port; +/* const char *client_addr; +/* const char *client_port; +/* const char *server_addr; +/* const char *server_port; /* /* void psc_free_session_state(state) /* PSC_STATE *state; @@ -140,8 +143,10 @@ /* psc_new_session_state - fill in connection state for event processing */ PSC_STATE *psc_new_session_state(VSTREAM *stream, - const char *addr, - const char *port) + const char *client_addr, + const char *client_port, + const char *server_addr, + const char *server_port) { PSC_STATE *state; HTABLE_INFO *ht; @@ -151,8 +156,10 @@ PSC_STATE *psc_new_session_state(VSTREAM *stream, if ((state->smtp_client_stream = stream) != 0) psc_check_queue_length++; state->smtp_server_fd = (-1); - state->smtp_client_addr = mystrdup(addr); - state->smtp_client_port = mystrdup(port); + state->smtp_client_addr = mystrdup(client_addr); + state->smtp_client_port = mystrdup(client_port); + state->smtp_server_addr = mystrdup(server_addr); + state->smtp_server_port = mystrdup(server_port); state->send_buf = vstring_alloc(100); state->test_name = "TEST NAME HERE"; state->dnsbl_reply = 0; @@ -180,8 +187,8 @@ PSC_STATE *psc_new_session_state(VSTREAM *stream, /* * Update the per-client session count. */ - if ((ht = htable_locate(psc_client_concurrency, addr)) == 0) - ht = htable_enter(psc_client_concurrency, addr, (char *) 0); + if ((ht = htable_locate(psc_client_concurrency, client_addr)) == 0) + ht = htable_enter(psc_client_concurrency, client_addr, (char *) 0); ht->value += 1; state->client_concurrency = CAST_CHAR_PTR_TO_INT(ht->value); @@ -218,6 +225,8 @@ void psc_free_session_state(PSC_STATE *state) state->send_buf = vstring_free(state->send_buf); myfree(state->smtp_client_addr); myfree(state->smtp_client_port); + myfree(state->smtp_server_addr); + myfree(state->smtp_server_port); if (state->dnsbl_reply) vstring_free(state->dnsbl_reply); if (state->helo_name) diff --git a/postfix/src/smtpd/Makefile.in b/postfix/src/smtpd/Makefile.in index a311c77ec..3178a0a20 100644 --- a/postfix/src/smtpd/Makefile.in +++ b/postfix/src/smtpd/Makefile.in @@ -2,11 +2,11 @@ SHELL = /bin/sh SRCS = smtpd.c smtpd_token.c smtpd_check.c smtpd_chat.c smtpd_state.c \ smtpd_peer.c smtpd_sasl_proto.c smtpd_sasl_glue.c smtpd_proxy.c \ smtpd_xforward.c smtpd_dsn_fix.c smtpd_milter.c smtpd_resolve.c \ - smtpd_expand.c + smtpd_expand.c smtpd_haproxy.c OBJS = smtpd.o smtpd_token.o smtpd_check.o smtpd_chat.o smtpd_state.o \ smtpd_peer.o smtpd_sasl_proto.o smtpd_sasl_glue.o smtpd_proxy.o \ smtpd_xforward.o smtpd_dsn_fix.o smtpd_milter.o smtpd_resolve.o \ - smtpd_expand.o + smtpd_expand.o smtpd_haproxy.o HDRS = smtpd_token.h smtpd_check.h smtpd_chat.h smtpd_sasl_proto.h \ smtpd_sasl_glue.h smtpd_proxy.h smtpd_dsn_fix.h smtpd_milter.h \ smtpd_resolve.h smtpd_expand.h @@ -346,6 +346,28 @@ smtpd_expand.o: ../../include/vstring.h smtpd_expand.o: smtpd.h smtpd_expand.o: smtpd_expand.c smtpd_expand.o: smtpd_expand.h +smtpd_haproxy.o: ../../include/argv.h +smtpd_haproxy.o: ../../include/attr.h +smtpd_haproxy.o: ../../include/haproxy_srvr.h +smtpd_haproxy.o: ../../include/mail_params.h +smtpd_haproxy.o: ../../include/mail_stream.h +smtpd_haproxy.o: ../../include/milter.h +smtpd_haproxy.o: ../../include/msg.h +smtpd_haproxy.o: ../../include/myaddrinfo.h +smtpd_haproxy.o: ../../include/mymalloc.h +smtpd_haproxy.o: ../../include/name_code.h +smtpd_haproxy.o: ../../include/name_mask.h +smtpd_haproxy.o: ../../include/smtp_stream.h +smtpd_haproxy.o: ../../include/stringops.h +smtpd_haproxy.o: ../../include/sys_defs.h +smtpd_haproxy.o: ../../include/tls.h +smtpd_haproxy.o: ../../include/valid_hostname.h +smtpd_haproxy.o: ../../include/valid_mailhost_addr.h +smtpd_haproxy.o: ../../include/vbuf.h +smtpd_haproxy.o: ../../include/vstream.h +smtpd_haproxy.o: ../../include/vstring.h +smtpd_haproxy.o: smtpd.h +smtpd_haproxy.o: smtpd_haproxy.c smtpd_milter.o: ../../include/argv.h smtpd_milter.o: ../../include/attr.h smtpd_milter.o: ../../include/mail_params.h @@ -370,6 +392,8 @@ smtpd_milter.o: smtpd_resolve.h smtpd_milter.o: smtpd_sasl_glue.h smtpd_peer.o: ../../include/argv.h smtpd_peer.o: ../../include/attr.h +smtpd_peer.o: ../../include/haproxy_srvr.h +smtpd_peer.o: ../../include/htable.h smtpd_peer.o: ../../include/inet_proto.h smtpd_peer.o: ../../include/iostuff.h smtpd_peer.o: ../../include/mail_params.h diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 6651424b4..df6ee36ee 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -154,6 +154,16 @@ /* at all, or rewrite message headers and update incomplete addresses /* with the domain specified in the remote_header_rewrite_domain /* parameter. +/* BEFORE-SMTPD PROXY AGENT +/* .ad +/* .fi +/* Available in Postfix version 2.10 and later: +/* .IP "\fBsmtpd_upstream_proxy_protocol (empty)\fR" +/* The name of the proxy protocol used by an optional before-smtpd +/* proxy agent. +/* .IP "\fBsmtpd_upstream_proxy_timeout (5s)\fR" +/* The time limit for the proxy protocol specified with the +/* smtpd_upstream_proxy_protocol parameter. /* AFTER QUEUE EXTERNAL CONTENT INSPECTION CONTROLS /* .ad /* .fi @@ -1291,6 +1301,9 @@ char *var_tlsproxy_service; #endif +char *var_smtpd_uproxy_proto; +int var_smtpd_uproxy_tmout; + /* * Silly little macros. */ @@ -4924,7 +4937,8 @@ static void smtpd_service(VSTREAM *stream, char *service, char **argv) /* * Provide the SMTP service. */ - smtpd_proto(&state); + if ((state.flags & SMTPD_FLAG_HANGUP) == 0) + smtpd_proto(&state); /* * After the client has gone away, clean up whatever we have set up at @@ -5273,6 +5287,7 @@ int main(int argc, char **argv) VAR_MILT_CMD_TIME, DEF_MILT_CMD_TIME, &var_milt_cmd_time, 1, 0, VAR_MILT_MSG_TIME, DEF_MILT_MSG_TIME, &var_milt_msg_time, 1, 0, VAR_VERIFY_SENDER_TTL, DEF_VERIFY_SENDER_TTL, &var_verify_sender_ttl, 0, 0, + VAR_SMTPD_UPROXY_TMOUT, DEF_SMTPD_UPROXY_TMOUT, &var_smtpd_uproxy_tmout, 1, 0, 0, }; static const CONFIG_BOOL_TABLE bool_table[] = { @@ -5401,6 +5416,7 @@ int main(int argc, char **argv) VAR_TLSPROXY_SERVICE, DEF_TLSPROXY_SERVICE, &var_tlsproxy_service, 1, 0, #endif VAR_SMTPD_ACL_PERM_LOG, DEF_SMTPD_ACL_PERM_LOG, &var_smtpd_acl_perm_log, 0, 0, + VAR_SMTPD_UPROXY_PROTO, DEF_SMTPD_UPROXY_PROTO, &var_smtpd_uproxy_proto, 0, 0, 0, }; static const CONFIG_RAW_TABLE raw_table[] = { diff --git a/postfix/src/smtpd/smtpd.h b/postfix/src/smtpd/smtpd.h index cc4590682..a4932ac88 100644 --- a/postfix/src/smtpd/smtpd.h +++ b/postfix/src/smtpd/smtpd.h @@ -79,7 +79,9 @@ typedef struct { char *namaddr; /* name[address]:port */ char *rfc_addr; /* address for RFC 2821 */ int addr_family; /* address family */ + char *dest_addr; /* for Dovecot AUTH */ struct sockaddr_storage sockaddr; /* binary client endpoint */ + SOCKADDR_SIZE sockaddr_len; /* binary client endpoint */ int name_status; /* 2=ok 4=soft 5=hard 6=forged */ int reverse_name_status; /* 2=ok 4=soft 5=hard */ int conn_count; /* connections from this client */ @@ -308,6 +310,7 @@ extern void smtpd_state_reset(SMTPD_STATE *); */ extern void smtpd_peer_init(SMTPD_STATE *state); extern void smtpd_peer_reset(SMTPD_STATE *state); +extern int smtpd_peer_from_haproxy(SMTPD_STATE *state); #define SMTPD_PEER_CODE_OK 2 #define SMTPD_PEER_CODE_TEMP 4 diff --git a/postfix/src/smtpd/smtpd_haproxy.c b/postfix/src/smtpd/smtpd_haproxy.c new file mode 100644 index 000000000..d104d9dca --- /dev/null +++ b/postfix/src/smtpd/smtpd_haproxy.c @@ -0,0 +1,144 @@ +/*++ +/* NAME +/* smtpd_haproxy 3 +/* SUMMARY +/* Postfix SMTP server haproxy adapter +/* SYNOPSIS +/* #include "smtpd.h" +/* +/* int smtpd_peer_from_haproxy(state) +/* SMTPD_STATE *state; +/* DESCRIPTION +/* smtpd_peer_from_haproxy() receives endpoint address and +/* port information via the haproxy protocol. +/* +/* The following summarizes what the Postfix SMTP server expects +/* from an up-stream proxy adapter. +/* .IP \(bu +/* Validate address and port syntax. Permit only protocols +/* that are configured with the main.cf:inet_protocols +/* setting. +/* .IP \(bu +/* Convert IPv4-in-IPv6 address syntax to IPv4 syntax, when +/* both IPv4 and IPv6 support are enabled with main.cf:inet_protocols. +/* .IP \(bu +/* Update the following session context fields: addr, port, +/* rfc_addr, addr_family, dest_addr. The addr_family field +/* applies to the client address. +/* .IP \(bu +/* Dynamically allocate storage for string information with +/* mystrdup(). In case of error, leave unassigned string fields +/* at their initial zero value. +/* .IP \(bu +/* Log warnings in case of data format error. +/* .PP +/* Arguments: +/* .IP state +/* Session context. +/* DIAGNOSTICS +/* Warnings: I/O errors, malformed haproxy line. +/* +/* The result value is 0 in case of success, -1 in case of +/* error. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include + +/* Application-specific. */ + +#include + +/* SLMs. */ + +#define STR(x) vstring_str(x) +#define LEN(x) VSTRING_LEN(x) + +/* smtpd_peer_from_haproxy - initialize peer information from haproxy */ + +int smtpd_peer_from_haproxy(SMTPD_STATE *state) +{ + const char *myname = "smtpd_peer_from_haproxy"; + MAI_HOSTADDR_STR smtp_client_addr; + MAI_SERVPORT_STR smtp_client_port; + MAI_HOSTADDR_STR smtp_server_addr; + MAI_SERVPORT_STR smtp_server_port; + const char *proxy_err; + int io_err; + VSTRING *escape_buf; + + /* + * Note: the haproxy_srvr_parse() routine performs address protocol + * checks, address and port syntax checks, and converts IPv4-in-IPv6 + * address string syntax (:ffff::1.2.3.4) to IPv4 syntax where permitted + * by the main.cf:inet_protocols setting, but logs no warnings. + */ +#define ENABLE_DEADLINE 1 + + smtp_stream_setup(state->client, var_smtpd_uproxy_tmout, ENABLE_DEADLINE); + switch (io_err = vstream_setjmp(state->client)) { + default: + msg_panic("%s: unhandled I/O error %d", myname, io_err); + case SMTP_ERR_EOF: + msg_warn("haproxy read: unexpected EOF"); + return (-1); + case SMTP_ERR_TIME: + msg_warn("haproxy read: timeout error"); + return (-1); + case 0: + if (smtp_get(state->buffer, state->client, HAPROXY_MAX_LEN, + SMTP_GET_FLAG_NONE) != '\n') { + msg_warn("haproxy line > %d characters", HAPROXY_MAX_LEN); + return (-1); + } + if ((proxy_err = haproxy_srvr_parse(STR(state->buffer), + &smtp_client_addr, &smtp_client_port, + &smtp_server_addr, &smtp_server_port)) != 0) { + escape_buf = vstring_alloc(HAPROXY_MAX_LEN + 2); + escape(escape_buf, STR(state->buffer), LEN(state->buffer)); + msg_warn("haproxy read: %s: %s", proxy_err, STR(escape_buf)); + vstring_free(escape_buf); + return (-1); + } + state->addr = mystrdup(smtp_client_addr.buf); + if (strrchr(state->addr, ':') != 0) { + state->rfc_addr = concatenate(IPV6_COL, state->addr, (char *) 0); + state->addr_family = AF_INET6; + } else { + state->rfc_addr = mystrdup(state->addr); + state->addr_family = AF_INET; + } + state->port = mystrdup(smtp_client_port.buf); + + /* + * Avoid surprises in the Dovecot authentication server. + */ + state->dest_addr = mystrdup(smtp_server_addr.buf); + return (0); + } +} diff --git a/postfix/src/smtpd/smtpd_peer.c b/postfix/src/smtpd/smtpd_peer.c index 330e07938..5faa1e445 100644 --- a/postfix/src/smtpd/smtpd_peer.c +++ b/postfix/src/smtpd/smtpd_peer.c @@ -17,6 +17,9 @@ /* Where information is unavailable, the name and/or address /* are set to "unknown". /* +/* Alternatively, the peer address and port may be obtained +/* from a proxy server. +/* /* This module uses the local name service via getaddrinfo() /* and getnameinfo(). It does not query the DNS directly. /* @@ -45,6 +48,8 @@ /* .IP rfc_addr /* String of the form "ipv4addr" or "ipv6:ipv6addr" for use /* in Received: message headers. +/* .IP dest_addr +/* Server address, used by the Dovecot authentication server. /* .IP name_status /* The name_status result field specifies how the name /* information should be interpreted: @@ -104,6 +109,7 @@ #include #include #include +#include /* Utility library. */ @@ -120,75 +126,51 @@ #include #include #include +#include /* Application-specific. */ #include "smtpd.h" -/* smtpd_peer_init - initialize peer information */ +static INET_PROTO_INFO *proto_info; -void smtpd_peer_init(SMTPD_STATE *state) + /* + * XXX If we make local endpoint (getsockname) information available to + * Milter applications as {if_name} and {if_addr}, then we also must be able + * to provide this via the XCLIENT command for Milter testing. + * + * XXX If we make local port information available to policy servers or Milter + * applications, then we must also make this testable with the XCLIENT + * command, otherwise there will be confusion. + * + * XXX If we make local port information available via logging, then we must + * also support these attributes with the XFORWARD command. + * + * XXX If support were to be added for Milter applications in down-stream MTAs, + * then consistency demands that we propagate a lot of Sendmail macro + * information via the XFORWARD command. Otherwise we could end up with a + * very confusing situation. + */ + +/* smtpd_peer_sockaddr_to_hostaddr - client address/port to printable form */ + +static int smtpd_peer_sockaddr_to_hostaddr(SMTPD_STATE *state) { - const char *myname = "smtpd_peer_init"; - SOCKADDR_SIZE sa_length; - struct sockaddr *sa; - INET_PROTO_INFO *proto_info = inet_proto_info(); - - sa = (struct sockaddr *) & (state->sockaddr); - sa_length = sizeof(state->sockaddr); + const char *myname = "smtpd_peer_sockaddr_to_hostaddr"; + struct sockaddr *sa = (struct sockaddr *) & (state->sockaddr); + SOCKADDR_SIZE sa_length = state->sockaddr_len; /* - * Look up the peer address information. - * - * XXX If we make local endpoint (getsockname) information available to - * Milter applications as {if_name} and {if_addr}, then we also must be - * able to provide this via the XCLIENT command for Milter testing. - * - * XXX If we make local or remote port information available to policy - * servers or Milter applications, then we must also make this testable - * with the XCLIENT command, otherwise there will be confusion. - * - * XXX If we make local or remote port information available via logging, - * then we must also support these attributes with the XFORWARD command. - * - * XXX If support were to be added for Milter applications in down-stream - * MTAs, then consistency demands that we propagate a lot of Sendmail - * macro information via the XFORWARD command. Otherwise we could end up - * with a very confusing situation. + * XXX If we're given an IPv6 (or IPv4) connection from, e.g., inetd, + * while Postfix IPv6 (or IPv4) support is turned off, don't (skip to the + * final else clause, pretend the origin is localhost[127.0.0.1], and + * become an open relay). */ - if (getpeername(vstream_fileno(state->client), sa, &sa_length) >= 0) { - errno = 0; - } - - /* - * If peer went away, give up. - */ - if (errno != 0 && errno != ENOTSOCK) { - state->name = mystrdup(CLIENT_NAME_UNKNOWN); - state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN); - state->addr = mystrdup(CLIENT_ADDR_UNKNOWN); - state->rfc_addr = mystrdup(CLIENT_ADDR_UNKNOWN); - state->addr_family = AF_UNSPEC; - state->name_status = SMTPD_PEER_CODE_PERM; - state->reverse_name_status = SMTPD_PEER_CODE_PERM; - state->port = mystrdup(CLIENT_PORT_UNKNOWN); - } - - /* - * Convert the client address to printable address and hostname. - * - * XXX If we're given an IPv6 (or IPv4) connection from, e.g., inetd, while - * Postfix IPv6 (or IPv4) support is turned off, don't (skip to the final - * else clause, pretend the origin is localhost[127.0.0.1], and become an - * open relay). - */ - else if (errno == 0 - && (sa->sa_family == AF_INET + if (sa->sa_family == AF_INET #ifdef AF_INET6 - || sa->sa_family == AF_INET6 + || sa->sa_family == AF_INET6 #endif - )) { - MAI_HOSTNAME_STR client_name; + ) { MAI_HOSTADDR_STR client_addr; MAI_SERVPORT_STR client_port; int aierr; @@ -290,16 +272,35 @@ void smtpd_peer_init(SMTPD_STATE *state) state->rfc_addr = mystrdup(client_addr.buf); state->addr_family = sa->sa_family; } + return (0); + } - /* - * Look up and sanity check the client hostname. - * - * It is unsafe to allow numeric hostnames, especially because there - * exists pressure to turn off the name->addr double check. In that - * case an attacker could trivally bypass access restrictions. - * - * sockaddr_to_hostname() already rejects malformed or numeric names. - */ + /* + * It's not Internet. + */ + else { + return (-1); + } +} + +/* smtpd_peer_sockaddr_to_hostname - client hostname lookup */ + +static void smtpd_peer_sockaddr_to_hostname(SMTPD_STATE *state) +{ + struct sockaddr *sa = (struct sockaddr *) & (state->sockaddr); + SOCKADDR_SIZE sa_length = state->sockaddr_len; + MAI_HOSTNAME_STR client_name; + int aierr; + + /* + * Look up and sanity check the client hostname. + * + * It is unsafe to allow numeric hostnames, especially because there exists + * pressure to turn off the name->addr double check. In that case an + * attacker could trivally bypass access restrictions. + * + * sockaddr_to_hostname() already rejects malformed or numeric names. + */ #define TEMP_AI_ERROR(e) \ ((e) == EAI_AGAIN || (e) == EAI_MEMORY || (e) == EAI_SYSTEM) @@ -309,81 +310,276 @@ void smtpd_peer_init(SMTPD_STATE *state) state->name_status = code; \ } - if (var_smtpd_peername_lookup == 0) { - state->name = mystrdup(CLIENT_NAME_UNKNOWN); - state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN); - state->name_status = SMTPD_PEER_CODE_PERM; - state->reverse_name_status = SMTPD_PEER_CODE_PERM; - } else if ((aierr = sockaddr_to_hostname(sa, sa_length, &client_name, + if (var_smtpd_peername_lookup == 0) { + state->name = mystrdup(CLIENT_NAME_UNKNOWN); + state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN); + state->name_status = SMTPD_PEER_CODE_PERM; + state->reverse_name_status = SMTPD_PEER_CODE_PERM; + } else if ((aierr = sockaddr_to_hostname(sa, sa_length, &client_name, (MAI_SERVNAME_STR *) 0, 0)) != 0) { - state->name = mystrdup(CLIENT_NAME_UNKNOWN); - state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN); - state->name_status = (TEMP_AI_ERROR(aierr) ? + state->name = mystrdup(CLIENT_NAME_UNKNOWN); + state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN); + state->name_status = (TEMP_AI_ERROR(aierr) ? + SMTPD_PEER_CODE_TEMP : SMTPD_PEER_CODE_PERM); + state->reverse_name_status = (TEMP_AI_ERROR(aierr) ? SMTPD_PEER_CODE_TEMP : SMTPD_PEER_CODE_PERM); - state->reverse_name_status = (TEMP_AI_ERROR(aierr) ? - SMTPD_PEER_CODE_TEMP : SMTPD_PEER_CODE_PERM); - } else { - struct addrinfo *res0; - struct addrinfo *res; + } else { + struct addrinfo *res0; + struct addrinfo *res; - state->name = mystrdup(client_name.buf); - state->reverse_name = mystrdup(client_name.buf); - state->name_status = SMTPD_PEER_CODE_OK; - state->reverse_name_status = SMTPD_PEER_CODE_OK; + state->name = mystrdup(client_name.buf); + state->reverse_name = mystrdup(client_name.buf); + state->name_status = SMTPD_PEER_CODE_OK; + state->reverse_name_status = SMTPD_PEER_CODE_OK; - /* - * Reject the hostname if it does not list the peer address. - * Without further validation or qualification, such information - * must not be allowed to enter the audit trail, as people would - * draw false conclusions. - */ - aierr = hostname_to_sockaddr_pf(state->name, state->addr_family, - (char *) 0, 0, &res0); - if (aierr) { - msg_warn("hostname %s does not resolve to address %s: %s", - state->name, state->addr, MAI_STRERROR(aierr)); - REJECT_PEER_NAME(state, (TEMP_AI_ERROR(aierr) ? + /* + * Reject the hostname if it does not list the peer address. Without + * further validation or qualification, such information must not be + * allowed to enter the audit trail, as people would draw false + * conclusions. + */ + aierr = hostname_to_sockaddr_pf(state->name, state->addr_family, + (char *) 0, 0, &res0); + if (aierr) { + msg_warn("hostname %s does not resolve to address %s: %s", + state->name, state->addr, MAI_STRERROR(aierr)); + REJECT_PEER_NAME(state, (TEMP_AI_ERROR(aierr) ? SMTPD_PEER_CODE_TEMP : SMTPD_PEER_CODE_FORGED)); - } else { - for (res = res0; /* void */ ; res = res->ai_next) { - if (res == 0) { - msg_warn("hostname %s does not resolve to address %s", - state->name, state->addr); - REJECT_PEER_NAME(state, SMTPD_PEER_CODE_FORGED); - break; - } - if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) { - msg_info("skipping address family %d for host %s", - res->ai_family, state->name); - continue; - } - if (sock_addr_cmp_addr(res->ai_addr, sa) == 0) - break; /* keep peer name */ + } else { + for (res = res0; /* void */ ; res = res->ai_next) { + if (res == 0) { + msg_warn("hostname %s does not resolve to address %s", + state->name, state->addr); + REJECT_PEER_NAME(state, SMTPD_PEER_CODE_FORGED); + break; } - freeaddrinfo(res0); + if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) { + msg_info("skipping address family %d for host %s", + res->ai_family, state->name); + continue; + } + if (sock_addr_cmp_addr(res->ai_addr, sa) == 0) + break; /* keep peer name */ } + freeaddrinfo(res0); } } +} + +/* smtpd_peer_hostaddr_to_sockaddr - convert numeric string to binary */ + +static void smtpd_peer_hostaddr_to_sockaddr(SMTPD_STATE *state) +{ + const char *myname = "smtpd_peer_hostaddr_to_sockaddr"; + struct addrinfo *res; + int aierr; + + if ((aierr = hostaddr_to_sockaddr(state->addr, state->port, + SOCK_STREAM, &res)) != 0) + msg_fatal("%s: cannot convert client address/port to string: %s", + myname, MAI_STRERROR(aierr)); + if (res->ai_addrlen > sizeof(state->sockaddr)) + msg_panic("%s: address length > struct sockaddr_storage", myname); + memcpy((char *) &(state->sockaddr), res->ai_addr, res->ai_addrlen); + state->sockaddr_len = res->ai_addrlen; + freeaddrinfo(res); +} + +/* smtpd_peer_not_inet - non-socket or non-Internet endpoint */ + +static void smtpd_peer_not_inet(SMTPD_STATE *state) +{ /* * If it's not Internet, assume the client is local, and avoid using the * naming service because that can hang when the machine is disconnected. */ - else { - state->name = mystrdup("localhost"); - state->reverse_name = mystrdup("localhost"); - if (proto_info->sa_family_list[0] == PF_INET6) { - state->addr = mystrdup("::1"); /* XXX bogus. */ - state->rfc_addr = mystrdup(IPV6_COL "::1"); /* XXX bogus. */ - } else { - state->addr = mystrdup("127.0.0.1");/* XXX bogus. */ - state->rfc_addr = mystrdup("127.0.0.1"); /* XXX bogus. */ - } - state->addr_family = AF_UNSPEC; - state->name_status = SMTPD_PEER_CODE_OK; - state->reverse_name_status = SMTPD_PEER_CODE_OK; - state->port = mystrdup("0"); /* XXX bogus. */ + state->name = mystrdup("localhost"); + state->reverse_name = mystrdup("localhost"); +#ifdef AF_INET6 + if (proto_info->sa_family_list[0] == PF_INET6) { + state->addr = mystrdup("::1"); /* XXX bogus. */ + state->rfc_addr = mystrdup(IPV6_COL "::1"); /* XXX bogus. */ + } else +#endif + { + state->addr = mystrdup("127.0.0.1"); /* XXX bogus. */ + state->rfc_addr = mystrdup("127.0.0.1");/* XXX bogus. */ } + state->addr_family = AF_UNSPEC; + state->name_status = SMTPD_PEER_CODE_OK; + state->reverse_name_status = SMTPD_PEER_CODE_OK; + state->port = mystrdup("0"); /* XXX bogus. */ +} + +/* smtpd_peer_no_client - peer went away, or peer info unavailable */ + +static void smtpd_peer_no_client(SMTPD_STATE *state) +{ + smtpd_peer_reset(state); + state->name = mystrdup(CLIENT_NAME_UNKNOWN); + state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN); + state->addr = mystrdup(CLIENT_ADDR_UNKNOWN); + state->rfc_addr = mystrdup(CLIENT_ADDR_UNKNOWN); + state->addr_family = AF_UNSPEC; + state->name_status = SMTPD_PEER_CODE_PERM; + state->reverse_name_status = SMTPD_PEER_CODE_PERM; + state->port = mystrdup(CLIENT_PORT_UNKNOWN); +} + +/* smtpd_peer_from_pass_attr - initialize from attribute hash */ + +static void smtpd_peer_from_pass_attr(SMTPD_STATE *state) +{ + HTABLE *attr = (HTABLE *) vstream_context(state->client); + const char *cp; + + /* + * Extract the client endpoint information from the attribute hash. + */ + if ((cp = htable_find(attr, MAIL_ATTR_ACT_CLIENT_ADDR)) == 0) + msg_fatal("missing client address from proxy"); + if (strrchr(cp, ':') != 0) { + if (valid_ipv6_hostaddr(cp, DO_GRIPE) == 0) + msg_fatal("bad IPv6 client address syntax from proxy: %s", cp); + state->addr = mystrdup(cp); + state->rfc_addr = concatenate(IPV6_COL, cp, (char *) 0); + state->addr_family = AF_INET6; + } else { + if (valid_ipv4_hostaddr(cp, DO_GRIPE) == 0) + msg_fatal("bad IPv4 client address syntax from proxy: %s", cp); + state->addr = mystrdup(cp); + state->rfc_addr = mystrdup(cp); + state->addr_family = AF_INET; + } + if ((cp = htable_find(attr, MAIL_ATTR_ACT_CLIENT_PORT)) == 0) + msg_fatal("missing client port from proxy"); + if (valid_hostport(cp, DO_GRIPE) == 0) + msg_fatal("bad TCP client port number syntax from proxy: %s", cp); + state->port = mystrdup(cp); + + /* + * Avoid surprises in the Dovecot authentication server. + */ + if ((cp = htable_find(attr, MAIL_ATTR_ACT_SERVER_ADDR)) == 0) + msg_fatal("missing server address from proxy"); + if (valid_hostaddr(cp, DO_GRIPE) == 0) + msg_fatal("bad IPv6 client address syntax from proxy: %s", cp); + state->dest_addr = mystrdup(cp); + + /* + * Convert the client address from string to binary form. + */ + smtpd_peer_hostaddr_to_sockaddr(state); +} + +/* smtpd_peer_from_default - try to initialize peer information from socket */ + +static void smtpd_peer_from_default(SMTPD_STATE *state) +{ + SOCKADDR_SIZE sa_length = sizeof(state->sockaddr); + struct sockaddr *sa = (struct sockaddr *) & (state->sockaddr); + + /* + * The "no client" routine provides surrogate information so that the + * application can produce sensible logging when a client disconnects + * before the server wakes up. The "not inet" routine provides surrogate + * state for (presumably) local IPC channels. + */ + if (getpeername(vstream_fileno(state->client), sa, &sa_length) < 0) { + if (errno == ENOTSOCK) + smtpd_peer_not_inet(state); + else + smtpd_peer_no_client(state); + } else { + state->sockaddr_len = sa_length; + if (smtpd_peer_sockaddr_to_hostaddr(state) < 0) + smtpd_peer_not_inet(state); + } +} + +/* smtpd_peer_from_proxy - get endpoint info from proxy agent */ + +static void smtpd_peer_from_proxy(SMTPD_STATE *state) +{ + typedef struct { + const char *name; + int (*endpt_lookup) (SMTPD_STATE *); + } SMTPD_ENDPT_LOOKUP_INFO; + static const SMTPD_ENDPT_LOOKUP_INFO smtpd_endpt_lookup_info[] = { + HAPROXY_PROTO_NAME, smtpd_peer_from_haproxy, + 0, + }; + const SMTPD_ENDPT_LOOKUP_INFO *pp; + + /* + * When the proxy information is unavailable, we can't maintain an audit + * trail or enforce access control, therefore we forcibly hang up. + */ + for (pp = smtpd_endpt_lookup_info; /* see below */ ; pp++) { + if (pp->name == 0) + msg_fatal("unsupported %s value: %s", + VAR_SMTPD_UPROXY_PROTO, var_smtpd_uproxy_proto); + if (strcmp(var_smtpd_uproxy_proto, pp->name) == 0) + break; + } + if (pp->endpt_lookup(state) < 0) { + smtpd_peer_no_client(state); + state->flags |= SMTPD_FLAG_HANGUP; + } else { + smtpd_peer_hostaddr_to_sockaddr(state); + } +} + +/* smtpd_peer_init - initialize peer information */ + +void smtpd_peer_init(SMTPD_STATE *state) +{ + + /* + * Initialize. + */ + if (proto_info == 0) + proto_info = inet_proto_info(); + + /* + * Prepare for partial initialization after error. + */ + memset((char *) &(state->sockaddr), 0, sizeof(state->sockaddr)); + state->sockaddr_len = 0; + state->name = 0; + state->reverse_name = 0; + state->addr = 0; + state->namaddr = 0; + state->rfc_addr = 0; + state->port = 0; + state->dest_addr = 0; + + /* + * Determine the remote SMTP client address and port. + * + * XXX In stand-alone mode, don't assume that the peer will be a local + * process. That could introduce a gaping hole when the SMTP daemon is + * hooked up to the network via inetd or some other super-server. + */ + if (vstream_context(state->client) != 0) { + smtpd_peer_from_pass_attr(state); + if (*var_smtpd_uproxy_proto != 0) + msg_warn("ignoring non-empty %s setting behind postscreen", + VAR_SMTPD_UPROXY_PROTO); + } else if (SMTPD_STAND_ALONE(state) || *var_smtpd_uproxy_proto == 0) { + smtpd_peer_from_default(state); + } else { + smtpd_peer_from_proxy(state); + } + + /* + * Determine the remote SMTP client hostname. Note: some of the handlers + * above provide surrogate endpoint information in case of error. In that + * case, leave the surrogate information alone. + */ + if (state->name == 0) + smtpd_peer_sockaddr_to_hostname(state); /* * Do the name[addr]:port formatting for pretty reports. @@ -396,10 +592,18 @@ void smtpd_peer_init(SMTPD_STATE *state) void smtpd_peer_reset(SMTPD_STATE *state) { - myfree(state->name); - myfree(state->reverse_name); - myfree(state->addr); - myfree(state->namaddr); - myfree(state->rfc_addr); - myfree(state->port); + if (state->name) + myfree(state->name); + if (state->reverse_name) + myfree(state->reverse_name); + if (state->addr) + myfree(state->addr); + if (state->namaddr) + myfree(state->namaddr); + if (state->rfc_addr) + myfree(state->rfc_addr); + if (state->port) + myfree(state->port); + if (state->dest_addr) + myfree(state->dest_addr); } diff --git a/postfix/src/smtpd/smtpd_sasl_glue.c b/postfix/src/smtpd/smtpd_sasl_glue.c index 5062ee9ac..29021cd0d 100644 --- a/postfix/src/smtpd/smtpd_sasl_glue.c +++ b/postfix/src/smtpd/smtpd_sasl_glue.c @@ -214,7 +214,8 @@ void smtpd_sasl_activate(SMTPD_STATE *state, const char *sasl_opts_name, if ((state->sasl_server = XSASL_SERVER_CREATE(smtpd_sasl_impl, &create_args, stream = state->client, - server_addr = "", /* need smtpd_peer.c update */ + server_addr = (state->dest_addr ? + state->dest_addr : ""), client_addr = ADDR_OR_EMPTY(state->addr, CLIENT_ADDR_UNKNOWN), service = SMTPD_SASL_SERVICE, diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index f5119a32c..59bbc3587 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -31,11 +31,11 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \ vstream_popen.c vstring.c vstring_vstream.c watchdog.c writable.c \ write_buf.c write_wait.c sane_basename.c format_tv.c allspace.c \ allascii.c load_file.c killme_after.c vstream_tweak.c \ - unix_pass_listen.c unix_pass_trigger.c edit_file.c inet_windowsize.c \ + pass_trigger.c edit_file.c inet_windowsize.c \ unix_pass_fd_fix.c dict_cache.c valid_utf_8.c dict_thash.c \ - ip_match.c nbbio.c stream_pass_connect.c base32_code.c dict_test.c \ + ip_match.c nbbio.c base32_code.c dict_test.c \ dict_fail.c msg_rate_delay.c dict_surrogate.c warn_stat.c \ - dict_sockmap.c line_number.c + dict_sockmap.c line_number.c recv_pass_attr.c pass_accept.c OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \ attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \ @@ -68,11 +68,11 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ vstream_popen.o vstring.o vstring_vstream.o watchdog.o writable.o \ write_buf.o write_wait.o sane_basename.o format_tv.o allspace.o \ allascii.o load_file.o killme_after.o vstream_tweak.o \ - unix_pass_listen.o unix_pass_trigger.o edit_file.o inet_windowsize.o \ + pass_trigger.o edit_file.o inet_windowsize.o \ unix_pass_fd_fix.o dict_cache.o valid_utf_8.o dict_thash.o \ - ip_match.o nbbio.o stream_pass_connect.o base32_code.o dict_test.o \ + ip_match.o nbbio.o base32_code.o dict_test.o \ dict_fail.o msg_rate_delay.o dict_surrogate.o warn_stat.o \ - dict_sockmap.o line_number.o + dict_sockmap.o line_number.o recv_pass_attr.o pass_accept.o HDRS = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \ chroot_uid.h cidr_match.h clean_env.h connect.h ctable.h dict.h \ dict_cdb.h dict_cidr.h dict_db.h dict_dbm.h dict_env.h dict_ht.h \ @@ -111,7 +111,8 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \ attr_scan0 host_port attr_scan_plain attr_print_plain htable \ unix_recv_fd unix_send_fd stream_recv_fd stream_send_fd hex_code \ myaddrinfo myaddrinfo4 inet_proto sane_basename format_tv \ - valid_utf_8 ip_match base32_code msg_rate_delay netstring + valid_utf_8 ip_match base32_code msg_rate_delay netstring \ + vstream LIB_DIR = ../../lib INC_DIR = ../../include @@ -446,6 +447,11 @@ netstring: $(LIB) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) mv junk $@.o +vstream: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + 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 \ @@ -1203,6 +1209,7 @@ exec_command.o: exec_command.h exec_command.o: msg.h exec_command.o: sys_defs.h fifo_listen.o: fifo_listen.c +fifo_listen.o: htable.h fifo_listen.o: iostuff.h fifo_listen.o: listen.h fifo_listen.o: msg.h @@ -1329,6 +1336,7 @@ inet_connect.o: sock_addr.h inet_connect.o: sys_defs.h inet_connect.o: timed_connect.h inet_listen.o: host_port.h +inet_listen.o: htable.h inet_listen.o: inet_listen.c inet_listen.o: inet_proto.h inet_listen.o: iostuff.h @@ -1572,6 +1580,23 @@ open_lock.o: sys_defs.h open_lock.o: vbuf.h open_lock.o: vstream.h open_lock.o: vstring.h +pass_accept.o: attr.h +pass_accept.o: htable.h +pass_accept.o: iostuff.h +pass_accept.o: listen.h +pass_accept.o: msg.h +pass_accept.o: pass_accept.c +pass_accept.o: sys_defs.h +pass_accept.o: vbuf.h +pass_accept.o: vstream.h +pass_trigger.o: connect.h +pass_trigger.o: events.h +pass_trigger.o: iostuff.h +pass_trigger.o: msg.h +pass_trigger.o: mymalloc.h +pass_trigger.o: pass_trigger.c +pass_trigger.o: sys_defs.h +pass_trigger.o: trigger.h peekfd.o: iostuff.h peekfd.o: peekfd.c peekfd.o: sys_defs.h @@ -1608,6 +1633,15 @@ readlline.o: sys_defs.h readlline.o: vbuf.h readlline.o: vstream.h readlline.o: vstring.h +recv_pass_attr.o: attr.h +recv_pass_attr.o: htable.h +recv_pass_attr.o: iostuff.h +recv_pass_attr.o: listen.h +recv_pass_attr.o: mymalloc.h +recv_pass_attr.o: recv_pass_attr.c +recv_pass_attr.o: sys_defs.h +recv_pass_attr.o: vbuf.h +recv_pass_attr.o: vstream.h ring.o: ring.c ring.o: ring.h safe_getenv.o: safe.h @@ -1720,16 +1754,12 @@ stream_connect.o: iostuff.h stream_connect.o: msg.h stream_connect.o: stream_connect.c stream_connect.o: sys_defs.h +stream_listen.o: htable.h stream_listen.o: iostuff.h stream_listen.o: listen.h stream_listen.o: msg.h stream_listen.o: stream_listen.c stream_listen.o: sys_defs.h -stream_pass_connect.o: connect.h -stream_pass_connect.o: iostuff.h -stream_pass_connect.o: msg.h -stream_pass_connect.o: stream_pass_connect.c -stream_pass_connect.o: sys_defs.h stream_recv_fd.o: iostuff.h stream_recv_fd.o: msg.h stream_recv_fd.o: stream_recv_fd.c @@ -1739,6 +1769,7 @@ stream_send_fd.o: msg.h stream_send_fd.o: stream_send_fd.c stream_send_fd.o: sys_defs.h stream_test.o: connect.h +stream_test.o: htable.h stream_test.o: iostuff.h stream_test.o: listen.h stream_test.o: msg.h @@ -1798,6 +1829,7 @@ unix_connect.o: sane_connect.h unix_connect.o: sys_defs.h unix_connect.o: timed_connect.h unix_connect.o: unix_connect.c +unix_listen.o: htable.h unix_listen.o: iostuff.h unix_listen.o: listen.h unix_listen.o: msg.h @@ -1810,20 +1842,6 @@ unix_pass_fd_fix.o: sys_defs.h unix_pass_fd_fix.o: unix_pass_fd_fix.c unix_pass_fd_fix.o: vbuf.h unix_pass_fd_fix.o: vstring.h -unix_pass_listen.o: iostuff.h -unix_pass_listen.o: listen.h -unix_pass_listen.o: msg.h -unix_pass_listen.o: sane_accept.h -unix_pass_listen.o: sys_defs.h -unix_pass_listen.o: unix_pass_listen.c -unix_pass_trigger.o: connect.h -unix_pass_trigger.o: events.h -unix_pass_trigger.o: iostuff.h -unix_pass_trigger.o: msg.h -unix_pass_trigger.o: mymalloc.h -unix_pass_trigger.o: sys_defs.h -unix_pass_trigger.o: trigger.h -unix_pass_trigger.o: unix_pass_trigger.c unix_recv_fd.o: iostuff.h unix_recv_fd.o: msg.h unix_recv_fd.o: sys_defs.h diff --git a/postfix/src/util/connect.h b/postfix/src/util/connect.h index 9ec9c5883..080b99c06 100644 --- a/postfix/src/util/connect.h +++ b/postfix/src/util/connect.h @@ -22,9 +22,6 @@ extern int unix_connect(const char *, int, int); extern int inet_connect(const char *, int, int); extern int stream_connect(const char *, int, int); -extern int stream_pass_connect(const char *, int, int); - -#define unix_pass_connect unix_connect /* LICENSE /* .ad diff --git a/postfix/src/util/listen.h b/postfix/src/util/listen.h index 848543752..ccd45bc73 100644 --- a/postfix/src/util/listen.h +++ b/postfix/src/util/listen.h @@ -15,6 +15,7 @@ * Utility library. */ #include +#include /* * Listener external interface. @@ -24,15 +25,13 @@ extern int inet_listen(const char *, int, int); extern int fifo_listen(const char *, int, int); extern int stream_listen(const char *, int, int); -#define unix_pass_listen unix_listen -#define stream_pass_listen stream_listen - extern int inet_accept(int); extern int unix_accept(int); extern int stream_accept(int); -extern int unix_pass_accept(int); -#define stream_pass_accept stream_accept +extern int recv_pass_attr(int, HTABLE **, int, ssize_t); +extern int pass_accept(int); +extern int pass_accept_attr(int, HTABLE **); /* LICENSE /* .ad diff --git a/postfix/src/util/msg_output.c b/postfix/src/util/msg_output.c index 0c34bb5db..be2ceaabb 100644 --- a/postfix/src/util/msg_output.c +++ b/postfix/src/util/msg_output.c @@ -148,6 +148,8 @@ void msg_printf(int level, const char *format,...) void msg_vprintf(int level, const char *format, va_list ap) { + int saved_errno = errno; + if (msg_vprintf_lock == 0) { msg_vprintf_lock = 1; /* On-the-fly initialization for debugging test programs only. */ @@ -158,6 +160,7 @@ void msg_vprintf(int level, const char *format, va_list ap) msg_text(level, vstring_str(msg_buffer)); msg_vprintf_lock = 0; } + errno = saved_errno; } /* msg_text - sanitize and log pre-formatted text */ diff --git a/postfix/src/util/pass_accept.c b/postfix/src/util/pass_accept.c new file mode 100644 index 000000000..3e1504986 --- /dev/null +++ b/postfix/src/util/pass_accept.c @@ -0,0 +1,106 @@ +/*++ +/* NAME +/* pass_accept 3 +/* SUMMARY +/* start UNIX-domain file descriptor listener +/* SYNOPSIS +/* #include +/* +/* int pass_accept(listen_fd) +/* int listen_fd; +/* +/* int pass_accept_attr(listen_fd, attr) +/* int listen_fd; +/* HTABLE **attr; +/* DESCRIPTION +/* This module implements a listener that receives one attribute list +/* and file descriptor over each a local connection that is made to it. +/* +/* Arguments: +/* .IP attr +/* Pointer to attribute list pointer. In case of error, or +/* no attributes, the attribute list pointer is set to null. +/* .IP listen_fd +/* File descriptor returned by LOCAL_LISTEN(). +/* DIAGNOSTICS +/* Warnings: I/O errors, timeout. +/* 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 +#include +#include + +/* Utility library. */ + +#include +#include +#include + +#define PASS_ACCEPT_TMOUT 100 + +/* pass_accept - accept descriptor */ + +int pass_accept(int listen_fd) +{ + const char *myname = "pass_accept"; + int accept_fd; + int recv_fd = -1; + + accept_fd = LOCAL_ACCEPT(listen_fd); + if (accept_fd < 0) { + if (errno != EAGAIN) + msg_warn("%s: cannot accept connection: %m", myname); + return (-1); + } else { + if (read_wait(accept_fd, PASS_ACCEPT_TMOUT) < 0) + msg_warn("%s: timeout receiving file descriptor: %m", myname); + else if ((recv_fd = LOCAL_RECV_FD(accept_fd)) < 0) + msg_warn("%s: cannot receive file descriptor: %m", myname); + if (close(accept_fd) < 0) + msg_warn("%s: close: %m", myname); + return (recv_fd); + } +} + +/* pass_accept_attr - accept attribute list and descriptor */ + +int pass_accept_attr(int listen_fd, HTABLE **attr) +{ + const char *myname = "pass_accept_attr"; + int accept_fd; + int recv_fd = -1; + + *attr = 0; + accept_fd = LOCAL_ACCEPT(listen_fd); + if (accept_fd < 0) { + if (errno != EAGAIN) + msg_warn("%s: cannot accept connection: %m", myname); + return (-1); + } else { + if (read_wait(accept_fd, PASS_ACCEPT_TMOUT) < 0) + msg_warn("%s: timeout receiving file descriptor: %m", myname); + else if ((recv_fd = LOCAL_RECV_FD(accept_fd)) < 0) + msg_warn("%s: cannot receive file descriptor: %m", myname); + else if (read_wait(accept_fd, PASS_ACCEPT_TMOUT) < 0 + || recv_pass_attr(accept_fd, attr, PASS_ACCEPT_TMOUT, 0) < 0) { + msg_warn("%s: cannot receive connection attributes: %m", myname); + if (close(recv_fd) < 0) + msg_warn("%s: close: %m", myname); + recv_fd = -1; + } + if (close(accept_fd) < 0) + msg_warn("%s: close: %m", myname); + return (recv_fd); + } +} diff --git a/postfix/src/util/unix_pass_trigger.c b/postfix/src/util/pass_trigger.c similarity index 51% rename from postfix/src/util/unix_pass_trigger.c rename to postfix/src/util/pass_trigger.c index 612300924..9567abc0f 100644 --- a/postfix/src/util/unix_pass_trigger.c +++ b/postfix/src/util/pass_trigger.c @@ -1,19 +1,19 @@ /*++ /* NAME -/* unix_pass_trigger 3 +/* pass_trigger 3 /* SUMMARY -/* wakeup UNIX-domain file descriptor listener +/* trigger file descriptor listener /* SYNOPSIS /* #include /* -/* int unix_pass_trigger(service, buf, len, timeout) +/* int pass_trigger(service, buf, len, timeout) /* const char *service; /* const char *buf; /* ssize_t len; /* int timeout; /* DESCRIPTION -/* unix_pass_trigger() wakes up the named UNIX-domain server by sending -/* a brief connection to it and writing the named buffer. +/* pass_trigger() connects to the named local server by sending +/* a file descriptor to it and writing the named buffer. /* /* The connection is closed by a background thread. Some kernels /* cannot handle client-side disconnect before the server has @@ -32,7 +32,8 @@ /* DIAGNOSTICS /* The result is zero in case of success, -1 in case of problems. /* SEE ALSO -/* unix_pass_connect(3), UNIX-domain client +/* unix_connect(3), local client +/* stream_connect(3), streams-based client /* LICENSE /* .ad /* .fi @@ -60,45 +61,45 @@ #include #include -struct unix_pass_trigger { - int fd; +struct pass_trigger { + int connect_fd; char *service; - int *pair; + int pass_fd[2]; }; -/* unix_pass_trigger_event - disconnect from peer */ +/* pass_trigger_event - disconnect from peer */ -static void unix_pass_trigger_event(int event, char *context) +static void pass_trigger_event(int event, char *context) { - struct unix_pass_trigger *up = (struct unix_pass_trigger *) context; - static const char *myname = "unix_pass_trigger_event"; + struct pass_trigger *pp = (struct pass_trigger *) context; + static const char *myname = "pass_trigger_event"; /* * Disconnect. */ if (event == EVENT_TIME) - msg_warn("%s: read timeout for service %s", myname, up->service); - event_disable_readwrite(up->fd); - event_cancel_timer(unix_pass_trigger_event, context); + msg_warn("%s: read timeout for service %s", myname, pp->service); + event_disable_readwrite(pp->connect_fd); + event_cancel_timer(pass_trigger_event, context); /* Don't combine multiple close() calls into one boolean expression. */ - if (close(up->fd) < 0) - msg_warn("%s: close %s: %m", myname, up->service); - if (close(up->pair[0]) < 0) + if (close(pp->connect_fd) < 0) + msg_warn("%s: close %s: %m", myname, pp->service); + if (close(pp->pass_fd[0]) < 0) msg_warn("%s: close pipe: %m", myname); - if (close(up->pair[1]) < 0) + if (close(pp->pass_fd[1]) < 0) msg_warn("%s: close pipe: %m", myname); - myfree(up->service); - myfree((char *) up); + myfree(pp->service); + myfree((char *) pp); } -/* unix_pass_trigger - wakeup UNIX-domain server */ +/* pass_trigger - wakeup local server */ -int unix_pass_trigger(const char *service, const char *buf, ssize_t len, int timeout) +int pass_trigger(const char *service, const char *buf, ssize_t len, int timeout) { - const char *myname = "unix_pass_trigger"; - int pair[2]; - struct unix_pass_trigger *up; - int fd; + const char *myname = "pass_trigger"; + int pass_fd[2]; + struct pass_trigger *pp; + int connect_fd; if (msg_verbose > 1) msg_info("%s: service %s", myname, service); @@ -106,36 +107,37 @@ int unix_pass_trigger(const char *service, const char *buf, ssize_t len, int /* * Connect... */ - if ((fd = unix_pass_connect(service, BLOCKING, timeout)) < 0) { + if ((connect_fd = LOCAL_CONNECT(service, BLOCKING, timeout)) < 0) { if (msg_verbose) msg_warn("%s: connect to %s: %m", myname, service); return (-1); } - close_on_exec(fd, CLOSE_ON_EXEC); + close_on_exec(connect_fd, CLOSE_ON_EXEC); /* * Create a pipe, and send one pipe end to the server. */ - if (pipe(pair) < 0) + if (pipe(pass_fd) < 0) msg_fatal("%s: pipe: %m", myname); - close_on_exec(pair[0], CLOSE_ON_EXEC); - close_on_exec(pair[1], CLOSE_ON_EXEC); - if (unix_send_fd(fd, pair[0]) < 0) + close_on_exec(pass_fd[0], CLOSE_ON_EXEC); + close_on_exec(pass_fd[1], CLOSE_ON_EXEC); + if (LOCAL_SEND_FD(connect_fd, pass_fd[0]) < 0) msg_fatal("%s: send file descriptor: %m", myname); /* * Stash away context. */ - up = (struct unix_pass_trigger *) mymalloc(sizeof(*up)); - up->fd = fd; - up->service = mystrdup(service); - up->pair = pair; + pp = (struct pass_trigger *) mymalloc(sizeof(*pp)); + pp->connect_fd = connect_fd; + pp->service = mystrdup(service); + pp->pass_fd[0] = pass_fd[0]; + pp->pass_fd[1] = pass_fd[1]; /* * Write the request... */ - if (write_buf(pair[1], buf, len, timeout) < 0 - || write_buf(pair[1], "", 1, timeout) < 0) + if (write_buf(pass_fd[1], buf, len, timeout) < 0 + || write_buf(pass_fd[1], "", 1, timeout) < 0) if (msg_verbose) msg_warn("%s: write to %s: %m", myname, service); @@ -143,7 +145,7 @@ int unix_pass_trigger(const char *service, const char *buf, ssize_t len, int * Wakeup when the peer disconnects, or when we lose patience. */ if (timeout > 0) - event_request_timer(unix_pass_trigger_event, (char *) up, timeout + 100); - event_enable_read(fd, unix_pass_trigger_event, (char *) up); + event_request_timer(pass_trigger_event, (char *) pp, timeout + 100); + event_enable_read(connect_fd, pass_trigger_event, (char *) pp); return (0); } diff --git a/postfix/src/util/recv_pass_attr.c b/postfix/src/util/recv_pass_attr.c new file mode 100644 index 000000000..3e7a9d0c0 --- /dev/null +++ b/postfix/src/util/recv_pass_attr.c @@ -0,0 +1,93 @@ +/*++ +/* NAME +/* recv_pass_attr 3 +/* SUMMARY +/* predicate if string is all numerical +/* SYNOPSIS +/* #include +/* +/* int recv_pass_attr(fd, attr, timeout, bufsize) +/* int fd; +/* HTABLE **attr; +/* int timeout; +/* ssize_t bufsize; +/* DESCRIPTION +/* recv_pass_attr() receives named attributes over the specified +/* The result value is zero for success, -1 for error. +/* +/* Arguments: +/* .IP fd +/* The file descriptor to read from. +/* .IP attr +/* Pointer to attribute list pointer. The target is set to +/* zero on error or when the received attribute list is empty, +/* ohterwise it is assigned a pointer to non-empty attribute +/* list. +/* .IP timeout +/* The deadline for receiving all attributes. +/* .IP bufsize +/* The read buffer size. Specify 1 to avoid reading past the +/* end of the attribute list. +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* recv_pass_attr - receive connection attributes */ + +int recv_pass_attr(int fd, HTABLE **attr, int timeout, ssize_t bufsize) +{ + VSTREAM *fp; + int stream_err; + + /* + * Set up a temporary VSTREAM to receive the attributes. + * + * XXX We use one-character reads to simplify the implementation. + */ + fp = vstream_fdopen(fd, O_RDWR); + vstream_control(fp, + VSTREAM_CTL_BUFSIZE, bufsize, + VSTREAM_CTL_TIMEOUT, timeout, + VSTREAM_CTL_START_DEADLINE, + VSTREAM_CTL_END); + (void) attr_scan(fp, ATTR_FLAG_NONE, + ATTR_TYPE_HASH, *attr = htable_create(1), + ATTR_TYPE_END); + stream_err = (vstream_feof(fp) || vstream_ferror(fp)); + vstream_fdclose(fp); + + /* + * Error reporting and recovery. + */ + if (stream_err) { + htable_free(*attr, myfree); + *attr = 0; + return (-1); + } else { + if ((*attr)->used == 0) { + htable_free(*attr, myfree); + *attr = 0; + } + return (0); + } +} diff --git a/postfix/src/util/stream_listen.c b/postfix/src/util/stream_listen.c index 5882c30d9..b522b764d 100644 --- a/postfix/src/util/stream_listen.c +++ b/postfix/src/util/stream_listen.c @@ -97,6 +97,6 @@ int stream_accept(int fd) return (-1); return (fdinfo.fd); #else - msg_fatal("stream connections are not implemented"); + msg_fatal("stream connections are not implemented"); #endif } diff --git a/postfix/src/util/stream_pass_connect.c b/postfix/src/util/stream_pass_connect.c deleted file mode 100644 index 9bacdf4ae..000000000 --- a/postfix/src/util/stream_pass_connect.c +++ /dev/null @@ -1,86 +0,0 @@ -/*++ -/* NAME -/* stream_pass_connect 3 -/* SUMMARY -/* connect to stream-based descriptor listener -/* SYNOPSIS -/* #include -/* -/* int stream_pass_connect(path, block_mode, timeout) -/* const char *path; -/* int block_mode; -/* int timeout; -/* DESCRIPTION -/* stream_pass_connect() connects to a stream-based descriptor -/* listener for the specified pathname, and returns the resulting -/* file descriptor. The next operation is to stream_send_fd() -/* a file descriptor and then close() the connection once the -/* server has received the file descriptor. -/* -/* Arguments: -/* .IP path -/* Null-terminated string with listener endpoint name. -/* .IP block_mode -/* Either NON_BLOCKING for a non-blocking stream, or BLOCKING for -/* blocking mode. However, a stream connection succeeds or fails -/* immediately. -/* .IP timeout -/* This argument is ignored; it is present for compatibility with -/* other interfaces. Stream connections succeed or fail immediately. -/* DIAGNOSTICS -/* The result is -1 in case the connection could not be made. -/* Fatal errors: other system call failures. -/* 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 - -#ifdef STREAM_CONNECTIONS - -#include -#include -#include -#include - -#endif - -/* Utility library. */ - -#include -#include - -/* stream_pass_connect - connect to stream-based descriptor listener */ - -int stream_pass_connect(const char *path, int block_mode, int unused_timeout) -{ -#ifdef STREAM_CONNECTIONS - const char *myname = "stream_pass_connect"; - int fifo; - - /* - * The requested file system object must exist, otherwise we can't reach - * the server. - */ - if ((fifo = open(path, O_WRONLY | O_NONBLOCK, 0)) < 0) - return (-1); - - /* - * This is for {unix,inet}_connect() compatibility. - */ - non_blocking(fifo, block_mode); - - return (fifo); -#else - msg_fatal("stream connections are not implemented"); -#endif -} diff --git a/postfix/src/util/sys_defs.h b/postfix/src/util/sys_defs.h index 482fa7327..5c5b9d784 100644 --- a/postfix/src/util/sys_defs.h +++ b/postfix/src/util/sys_defs.h @@ -436,10 +436,6 @@ extern int opterr; #define LOCAL_TRIGGER stream_trigger #define LOCAL_SEND_FD stream_send_fd #define LOCAL_RECV_FD stream_recv_fd -#define PASS_CONNECT stream_pass_connect -#define PASS_LISTEN stream_pass_listen -#define PASS_ACCEPT stream_pass_accept -#define PASS_TRIGGER stream_pass_trigger #define HAS_VOLATILE_LOCKS #define BROKEN_READ_SELECT_ON_TCP_SOCKET #define CANT_WRITE_BEFORE_SENDING_FD @@ -1410,13 +1406,6 @@ extern int inet_pton(int, const char *, void *); #define LOCAL_RECV_FD unix_recv_fd #endif -#ifndef PASS_LISTEN -#define PASS_CONNECT unix_pass_connect -#define PASS_LISTEN unix_pass_listen -#define PASS_ACCEPT unix_pass_accept -#define PASS_TRIGGER unix_pass_trigger -#endif - #if !defined (HAVE_SYS_NDIR_H) && !defined (HAVE_SYS_DIR_H) \ && !defined (HAVE_NDIR_H) #define HAVE_DIRENT_H diff --git a/postfix/src/util/trigger.h b/postfix/src/util/trigger.h index 5e578d2b4..e716d5374 100644 --- a/postfix/src/util/trigger.h +++ b/postfix/src/util/trigger.h @@ -18,9 +18,7 @@ extern int unix_trigger(const char *, const char *, ssize_t, int); extern int inet_trigger(const char *, const char *, ssize_t, int); extern int fifo_trigger(const char *, const char *, ssize_t, int); extern int stream_trigger(const char *, const char *, ssize_t, int); -extern int unix_pass_trigger(const char *, const char *, ssize_t, int); - -#define stream_pass_trigger stream_trigger +extern int pass_trigger(const char *, const char *, ssize_t, int); /* LICENSE /* .ad diff --git a/postfix/src/util/unix_pass_listen.c b/postfix/src/util/unix_pass_listen.c deleted file mode 100644 index 822615977..000000000 --- a/postfix/src/util/unix_pass_listen.c +++ /dev/null @@ -1,85 +0,0 @@ -/*++ -/* NAME -/* unix_pass_listen 3 -/* SUMMARY -/* start UNIX-domain file descriptor listener -/* SYNOPSIS -/* #include -/* -/* int unix_pass_listen(path, backlog, block_mode) -/* const char *path; -/* int backlog; -/* int block_mode; -/* -/* int unix_pass_accept(fd) -/* int fd; -/* DESCRIPTION -/* This module implements a listener that receives one file descriptor -/* across each UNIX-domain connection that is made to it. -/* -/* unix_pass_listen() creates a listener endpoint with the specified -/* permissions, and returns a file descriptor to be used for accepting -/* descriptors. -/* -/* unix_pass_accept() accepts a descriptor. -/* -/* Arguments: -/* .IP path -/* Null-terminated string with connection destination. -/* .IP backlog -/* This argument exists for compatibility and is ignored. -/* .IP block_mode -/* Either NON_BLOCKING or BLOCKING. This does not affect the -/* mode of accepted connections. -/* .IP fd -/* File descriptor returned by unix_pass_listen(). -/* DIAGNOSTICS -/* Fatal errors: unix_pass_listen() aborts upon any system call failure. -/* unix_pass_accept() leaves all error handling up to the caller. -/* 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 -#include -#include -#include - -/* Utility library. */ - -#include -#include -#include - -/* unix_pass_accept - accept descriptor */ - -int unix_pass_accept(int listen_fd) -{ - const char *myname = "unix_pass_accept"; - int accept_fd; - int recv_fd = -1; - - accept_fd = sane_accept(listen_fd, (struct sockaddr *) 0, (SOCKADDR_SIZE *) 0); - if (accept_fd < 0) { - if (errno != EAGAIN) - msg_warn("%s: accept connection: %m", myname); - return (-1); - } else { - if (read_wait(accept_fd, 100) < 0) - msg_warn("%s: timeout receiving file descriptor: %m", myname); - else if ((recv_fd = unix_recv_fd(accept_fd)) < 0) - msg_warn("%s: cannot receive file descriptor: %m", myname); - if (close(accept_fd) < 0) - msg_warn("%s: close: %m", myname); - return (recv_fd); - } -} diff --git a/postfix/src/util/valid_hostname.c b/postfix/src/util/valid_hostname.c index 1beeb9af4..7a40d6e4b 100644 --- a/postfix/src/util/valid_hostname.c +++ b/postfix/src/util/valid_hostname.c @@ -21,6 +21,10 @@ /* int valid_ipv6_hostaddr(addr, gripe) /* const char *addr; /* int gripe; +/* +/* int valid_hostport(port, gripe) +/* const char *port; +/* int gripe; /* DESCRIPTION /* valid_hostname() scrutinizes a hostname: the name should /* be no longer than VALID_HOSTNAME_LEN characters, should @@ -42,6 +46,9 @@ /* These routines operate silently unless the gripe parameter /* specifies a non-zero value. The macros DO_GRIPE and DONT_GRIPE /* provide suitable constants. +/* +/* valid_hostport() requires that the input is a valid string +/* representation of a TCP or UDP port number. /* BUGS /* valid_hostmumble() does not guarantee that string lengths /* fit the buffer sizes defined in myaddrinfo(3h). @@ -65,6 +72,7 @@ #include #include #include +#include /* Utility library. */ @@ -337,6 +345,32 @@ int valid_ipv6_hostaddr(const char *addr, int gripe) } } +/* valid_hostport - validate numeric port */ + +int valid_hostport(const char *str, int gripe) +{ + const char *myname = "valid_hostport"; + int port; + + if (str[0] == '0' && str[1] != 0) { + if (gripe) + msg_warn("%s: leading zero in port number: %.100s", myname, str); + return (0); + } + if (alldig(str) == 0) { + if (gripe) + msg_warn("%s: non-numeric port number: %.100s", myname, str); + return (0); + } + if (strlen(str) > strlen("65535") + || (port = atoi(str)) > 65535 || port < 0) { + if (gripe) + msg_warn("%s: out-of-range port number: %.100s", myname, str); + return (0); + } + return (1); +} + #ifdef TEST /* diff --git a/postfix/src/util/valid_hostname.h b/postfix/src/util/valid_hostname.h index 8860153b5..b06fc1758 100644 --- a/postfix/src/util/valid_hostname.h +++ b/postfix/src/util/valid_hostname.h @@ -23,6 +23,7 @@ extern int valid_hostname(const char *, int); extern int valid_hostaddr(const char *, int); extern int valid_ipv4_hostaddr(const char *, int); extern int valid_ipv6_hostaddr(const char *, int); +extern int valid_hostport(const char *, int); /* LICENSE /* .ad diff --git a/postfix/src/util/vstream.c b/postfix/src/util/vstream.c index 0f12d203c..ee718b457 100644 --- a/postfix/src/util/vstream.c +++ b/postfix/src/util/vstream.c @@ -324,16 +324,21 @@ /* This involves allocation of additional memory that normally isn't /* used. /* .IP "VSTREAM_CTL_BUFSIZE (ssize_t)" -/* Specify a non-default write buffer size, or zero to implement -/* a no-op. Requests to shrink an existing buffer size are -/* ignored. Requests to change a fixed-size buffer (stdin, -/* stdout, stderr) are not allowed. +/* Specify a non-default buffer size, or zero to implement +/* a no-op. Requests to resize a fixed-size buffer (stderr) +/* are not allowed. /* /* NOTE: the VSTREAM_CTL_BUFSIZE request specifies intent, not /* reality. Actual buffer sizes are not updated immediately. -/* Instead, an existing write buffer will be resized when it -/* is full, and an existing read buffer will be resized when -/* the buffer is filled. +/* Instead, a write buffer size will be updated when writing +/* to a stream for the first time, or when writing to a full +/* buffer, and a read buffer size will be updated when reading +/* from a stream for the first time, or when reading from an +/* empty buffer. +/* +/* NOTE: the vstream_*printf() routines may silently expand a +/* buffer, so that the result of some %letter specifiers can +/* be written to contiguous memory. /* /* NOTE: the VSTREAM_CTL_BUFSIZE argument type is ssize_t, not /* int. Use an explicit cast to avoid problems on LP64 @@ -621,10 +626,17 @@ static void vstream_buf_alloc(VBUF *bp, ssize_t len) ssize_t used = bp->ptr - bp->data; const char *myname = "vstream_buf_alloc"; - if (len < bp->len) - msg_panic("%s: attempt to shrink buffer", myname); + /* + * Don't shrink a non-empty read buffer, or a non-flushed write buffer. + */ + if (len <= 0) + msg_panic("%s: bad buffer length: %ld", myname, (long) len); + if (len < bp->len + && (((bp->flags & VSTREAM_FLAG_READ) && bp->cnt != 0) + || ((bp->flags & VSTREAM_FLAG_WRITE) && bp->cnt != bp->len))) + msg_panic("%s: attempt to shrink non-empty buffer", myname); if (bp->flags & VSTREAM_FLAG_FIXED) - msg_panic("%s: unable to extend fixed-size buffer", myname); + msg_panic("%s: attempt to resize fixed-length buffer", myname); /* * Late buffer allocation allows the user to override the default policy. @@ -842,7 +854,7 @@ static int vstream_buf_get_ready(VBUF *bp) * allocation gives the application a chance to override the default * buffering policy. */ - if (bp->len < stream->req_bufsize) + if (bp->len != stream->req_bufsize) vstream_buf_alloc(bp, stream->req_bufsize); /* @@ -956,6 +968,8 @@ static int vstream_buf_put_ready(VBUF *bp) if (VSTREAM_FFLUSH_SOME(stream)) return (VSTREAM_EOF); } + if (bp->len > stream->req_bufsize) + vstream_buf_alloc(bp, stream->req_bufsize); return (0); } @@ -1467,8 +1481,7 @@ void vstream_control(VSTREAM *stream, int name,...) if (req_bufsize < 0) msg_panic("VSTREAM_CTL_BUFSIZE with negative size: %ld", (long) req_bufsize); - if (stream != VSTREAM_ERR - && req_bufsize > stream->req_bufsize) + if (req_bufsize > 0 && stream != VSTREAM_ERR) stream->req_bufsize = req_bufsize; break; @@ -1578,3 +1591,44 @@ const char *vstream_peek_data(VSTREAM *vp) return (0); } } + +#ifdef TEST + +static void copy_line(ssize_t bufsize) +{ + int c; + + vstream_control(VSTREAM_IN, VSTREAM_CTL_BUFSIZE, bufsize, VSTREAM_CTL_END); + vstream_control(VSTREAM_OUT, VSTREAM_CTL_BUFSIZE, bufsize, VSTREAM_CTL_END); + while ((c = VSTREAM_GETC(VSTREAM_IN)) != VSTREAM_EOF) { + VSTREAM_PUTC(c, VSTREAM_OUT); + if (c == '\n') + break; + } + vstream_fflush(VSTREAM_OUT); +} + +static void printf_number(void) +{ + vstream_printf("%d\n", __MAXINT__(int)); + vstream_fflush(VSTREAM_OUT); +} + + /* + * Exercise some of the features. + */ +int main(int argc, char **argv) +{ + + /* + * Test buffer expansion and shrinking. Formatted print may silently + * expand the write buffer and cause multiple bytes to be written. + */ + copy_line(1); /* one-byte read/write */ + copy_line(2); /* two-byte read/write */ + copy_line(1); /* one-byte read/write */ + printf_number(); /* multi-byte write */ + exit(0); +} + +#endif diff --git a/postfix/src/util/vstream.h b/postfix/src/util/vstream.h index ddb8fda6a..ee8d84347 100644 --- a/postfix/src/util/vstream.h +++ b/postfix/src/util/vstream.h @@ -207,6 +207,8 @@ extern const char *vstream_peek_data(VSTREAM *); extern int vstream_tweak_sock(VSTREAM *); extern int vstream_tweak_tcp(VSTREAM *); +#define vstream_flags(stream) ((const int) (stream)->buf.flags) + /* LICENSE /* .ad /* .fi