]*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