mirror of
https://github.com/vdukhovni/postfix
synced 2025-08-22 09:57:34 +00:00
postfix-3.11-20250418
This commit is contained in:
parent
1b9f8ac2fb
commit
6a87331d09
5
postfix/.indent.pro
vendored
5
postfix/.indent.pro
vendored
@ -260,6 +260,10 @@
|
|||||||
-TPCF_SERVICE_DEF
|
-TPCF_SERVICE_DEF
|
||||||
-TPCF_SERVICE_PATTERN
|
-TPCF_SERVICE_PATTERN
|
||||||
-TPCF_STRING_NV
|
-TPCF_STRING_NV
|
||||||
|
-TPEER_FROM_HAPROXY_CASE
|
||||||
|
-TPEER_FROM_NON_SOCKET_CASE
|
||||||
|
-TPEER_FROM_PASS_ATTR_CASE
|
||||||
|
-TPEER_FROM_UNCONN_SOCKET_CASE
|
||||||
-TPEER_NAME
|
-TPEER_NAME
|
||||||
-TPGSQL_NAME
|
-TPGSQL_NAME
|
||||||
-TPICKUP_INFO
|
-TPICKUP_INFO
|
||||||
@ -358,6 +362,7 @@
|
|||||||
-TSTRING_LIST
|
-TSTRING_LIST
|
||||||
-TSTRING_TABLE
|
-TSTRING_TABLE
|
||||||
-TSYS_EXITS_DETAIL
|
-TSYS_EXITS_DETAIL
|
||||||
|
-TTEST_BASE
|
||||||
-TTEST_CASE
|
-TTEST_CASE
|
||||||
-TTLSMGR_SCACHE
|
-TTLSMGR_SCACHE
|
||||||
-TTLSP_STATE
|
-TTLSP_STATE
|
||||||
|
@ -29081,3 +29081,24 @@ Apologies for any names omitted.
|
|||||||
|
|
||||||
Bit rot: sane_sockaddr_to_hostaddr() may modify its inputs.
|
Bit rot: sane_sockaddr_to_hostaddr() may modify its inputs.
|
||||||
smtp/smtp_tlsrpt.c, postscreen/postscreen_endpt.c
|
smtp/smtp_tlsrpt.c, postscreen/postscreen_endpt.c
|
||||||
|
|
||||||
|
20250411
|
||||||
|
|
||||||
|
Code health: simplified the Postfix SMTP server code to
|
||||||
|
find out the client and server IP addresses for an SMTP
|
||||||
|
connection. This takes advantage of the improved support
|
||||||
|
for address normalization and for haproxy load balancers.
|
||||||
|
Files: smtpd/smtpd_peer.c, smtpd/smtpd_haproxy.c.
|
||||||
|
|
||||||
|
Documentation: XCLIENT attribute availability. File:
|
||||||
|
proto/XCLIENT.
|
||||||
|
|
||||||
|
20250418
|
||||||
|
|
||||||
|
Code health: added unit tests for connection address and
|
||||||
|
port information received through haproxy or postscreen,
|
||||||
|
and improved error handling. Files: smtpd/smtpd_peer.c,
|
||||||
|
smtpd/smtpd_haproxy.c, smtpd/smtpd_peer_test.c.
|
||||||
|
|
||||||
|
Unit tests for 'direct' connections are deferred pending
|
||||||
|
support to mock or intercept system library function calls.
|
||||||
|
@ -115,7 +115,8 @@ manpages:
|
|||||||
done </dev/null
|
done </dev/null
|
||||||
|
|
||||||
# Some checks require a bin/postconf executable.
|
# Some checks require a bin/postconf executable.
|
||||||
pre-release-checks: typo-check double-check missing-proxy-read-maps-check \
|
pre-release-checks: update typo-check double-check \
|
||||||
|
missing-proxy-read-maps-check \
|
||||||
postlink-check postfix-files-check \
|
postlink-check postfix-files-check \
|
||||||
postconf-unimplemented-check postconf-undocumented-check \
|
postconf-unimplemented-check postconf-undocumented-check \
|
||||||
check-table-proto check-see-postconf-d-output \
|
check-table-proto check-see-postconf-d-output \
|
||||||
|
@ -100,8 +100,8 @@ Note 3: Postfix implementations prior to version 2.3 do not xtext encode
|
|||||||
attribute values. Servers that wish to interoperate with these older
|
attribute values. Servers that wish to interoperate with these older
|
||||||
implementations should be prepared to receive unencoded information.
|
implementations should be prepared to receive unencoded information.
|
||||||
|
|
||||||
Note 4: Some Postfix implementations do not implement the PORT or LOGIN
|
Note 4: The PORT attribute is implemented in Postfix 2.5 and later; the LOGIN
|
||||||
attributes.
|
attribute in Postfix 2.9 and later.
|
||||||
|
|
||||||
XXCCLLIIEENNTT SSeerrvveerr rreessppoonnssee
|
XXCCLLIIEENNTT SSeerrvveerr rreessppoonnssee
|
||||||
|
|
||||||
|
@ -145,8 +145,8 @@ xtext encode attribute values. Servers that wish to interoperate
|
|||||||
with these older implementations should be prepared to receive
|
with these older implementations should be prepared to receive
|
||||||
unencoded information. </p>
|
unencoded information. </p>
|
||||||
|
|
||||||
<p> Note 4: Some Postfix implementations do not implement the PORT
|
<p> Note 4: The PORT attribute is implemented in Postfix 2.5 and
|
||||||
or LOGIN attributes. </p>
|
later; the LOGIN attribute in Postfix 2.9 and later. </p>
|
||||||
|
|
||||||
<h2>XCLIENT Server response</h2>
|
<h2>XCLIENT Server response</h2>
|
||||||
|
|
||||||
|
@ -145,8 +145,8 @@ xtext encode attribute values. Servers that wish to interoperate
|
|||||||
with these older implementations should be prepared to receive
|
with these older implementations should be prepared to receive
|
||||||
unencoded information. </p>
|
unencoded information. </p>
|
||||||
|
|
||||||
<p> Note 4: Some Postfix implementations do not implement the PORT
|
<p> Note 4: The PORT attribute is implemented in Postfix 2.5 and
|
||||||
or LOGIN attributes. </p>
|
later; the LOGIN attribute in Postfix 2.9 and later. </p>
|
||||||
|
|
||||||
<h2>XCLIENT Server response</h2>
|
<h2>XCLIENT Server response</h2>
|
||||||
|
|
||||||
|
@ -1673,3 +1673,4 @@ bugfix
|
|||||||
MLKEM
|
MLKEM
|
||||||
cleartext
|
cleartext
|
||||||
redacted
|
redacted
|
||||||
|
subclassed
|
||||||
|
@ -105,3 +105,4 @@ Oemer
|
|||||||
Kozmenko
|
Kozmenko
|
||||||
Oleksandr
|
Oleksandr
|
||||||
Bataille
|
Bataille
|
||||||
|
balancers
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
* Patches change both the patchlevel and the release date. Snapshots have no
|
* Patches change both the patchlevel and the release date. Snapshots have no
|
||||||
* patchlevel; they change the release date only.
|
* patchlevel; they change the release date only.
|
||||||
*/
|
*/
|
||||||
#define MAIL_RELEASE_DATE "20250409"
|
#define MAIL_RELEASE_DATE "20250418"
|
||||||
#define MAIL_VERSION_NUMBER "3.11"
|
#define MAIL_VERSION_NUMBER "3.11"
|
||||||
|
|
||||||
#ifdef SNAPSHOT
|
#ifdef SNAPSHOT
|
||||||
|
@ -13,7 +13,7 @@ HDRS = smtpd_token.h smtpd_check.h smtpd_chat.h smtpd_sasl_proto.h \
|
|||||||
TESTSRC = smtpd_token_test.c
|
TESTSRC = smtpd_token_test.c
|
||||||
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
|
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
|
||||||
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
|
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
|
||||||
TESTPROG= smtpd_token smtpd_check
|
TESTPROG= smtpd_token smtpd_check smtpd_peer_test
|
||||||
PROG = smtpd
|
PROG = smtpd
|
||||||
INC_DIR = ../../include
|
INC_DIR = ../../include
|
||||||
LIBS = ../../lib/lib$(LIB_PREFIX)master$(LIB_SUFFIX) \
|
LIBS = ../../lib/lib$(LIB_PREFIX)master$(LIB_SUFFIX) \
|
||||||
@ -53,18 +53,24 @@ smtpd_check: smtpd_check.o smtpd_check.c $(SMTPD_CHECK_OBJ) $(LIBS)
|
|||||||
$(LIBS) $(SYSLIBS)
|
$(LIBS) $(SYSLIBS)
|
||||||
mv junk $@.o
|
mv junk $@.o
|
||||||
|
|
||||||
|
SMTPD_PEER_OBJ = smtpd_state.o smtpd_peer.o smtpd_haproxy.o
|
||||||
|
|
||||||
|
smtpd_peer_test: smtpd_peer_test.c $(SMTPD_PEER_OBJ) $(LIBS)
|
||||||
|
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(SMTPD_PEER_OBJ) \
|
||||||
|
$(LIBS) $(SYSLIBS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o *core $(PROG) $(TESTPROG) junk *.db *.out *.tmp
|
rm -f *.o *core $(PROG) $(TESTPROG) junk *.db *.out *.tmp
|
||||||
|
|
||||||
tidy: clean
|
tidy: clean
|
||||||
|
|
||||||
broken-tests: smtpd_check_test smtpd_check_test2
|
broken-tests: smtpd_check_test smtpd_check_test2
|
||||||
|
|
||||||
tests: smtpd_acl_test smtpd_addr_valid_test smtpd_exp_test \
|
tests: smtpd_acl_test smtpd_addr_valid_test smtpd_exp_test \
|
||||||
smtpd_token_test smtpd_check_test4 smtpd_check_dsn_test \
|
smtpd_token_test smtpd_check_test4 smtpd_check_dsn_test \
|
||||||
smtpd_check_backup_test smtpd_dnswl_test smtpd_error_test \
|
smtpd_check_backup_test smtpd_dnswl_test smtpd_error_test \
|
||||||
smtpd_server_test smtpd_nullmx_test smtpd_dns_filter_test \
|
smtpd_server_test smtpd_nullmx_test smtpd_dns_filter_test \
|
||||||
smtpd_deprecated_test
|
smtpd_deprecated_test test_smtpd_peer
|
||||||
|
|
||||||
root_tests:
|
root_tests:
|
||||||
|
|
||||||
@ -165,6 +171,9 @@ smtpd_deprecated_test: smtpd_check smtpd_deprecated.in smtpd_deprecated.ref
|
|||||||
diff smtpd_deprecated.ref smtpd_check.tmp
|
diff smtpd_deprecated.ref smtpd_check.tmp
|
||||||
rm -f smtpd_check.tmp
|
rm -f smtpd_check.tmp
|
||||||
|
|
||||||
|
test_smtpd_peer: smtpd_peer_test
|
||||||
|
$(SHLIB_ENV) $(VALGRIND) ./smtpd_peer_test
|
||||||
|
|
||||||
depend: $(MAKES)
|
depend: $(MAKES)
|
||||||
(sed '1,/^# do not edit/!d' Makefile.in; \
|
(sed '1,/^# do not edit/!d' Makefile.in; \
|
||||||
set -e; for i in [a-z][a-z0-9]*.c; do \
|
set -e; for i in [a-z][a-z0-9]*.c; do \
|
||||||
@ -503,6 +512,36 @@ smtpd_peer.o: ../../include/vstream.h
|
|||||||
smtpd_peer.o: ../../include/vstring.h
|
smtpd_peer.o: ../../include/vstring.h
|
||||||
smtpd_peer.o: smtpd.h
|
smtpd_peer.o: smtpd.h
|
||||||
smtpd_peer.o: smtpd_peer.c
|
smtpd_peer.o: smtpd_peer.c
|
||||||
|
smtpd_peer_test.o: ../../include/argv.h
|
||||||
|
smtpd_peer_test.o: ../../include/attr.h
|
||||||
|
smtpd_peer_test.o: ../../include/check_arg.h
|
||||||
|
smtpd_peer_test.o: ../../include/dns.h
|
||||||
|
smtpd_peer_test.o: ../../include/haproxy_srvr.h
|
||||||
|
smtpd_peer_test.o: ../../include/htable.h
|
||||||
|
smtpd_peer_test.o: ../../include/inet_proto.h
|
||||||
|
smtpd_peer_test.o: ../../include/iostuff.h
|
||||||
|
smtpd_peer_test.o: ../../include/mail_params.h
|
||||||
|
smtpd_peer_test.o: ../../include/mail_proto.h
|
||||||
|
smtpd_peer_test.o: ../../include/mail_stream.h
|
||||||
|
smtpd_peer_test.o: ../../include/milter.h
|
||||||
|
smtpd_peer_test.o: ../../include/msg.h
|
||||||
|
smtpd_peer_test.o: ../../include/msg_vstream.h
|
||||||
|
smtpd_peer_test.o: ../../include/myaddrinfo.h
|
||||||
|
smtpd_peer_test.o: ../../include/mymalloc.h
|
||||||
|
smtpd_peer_test.o: ../../include/name_code.h
|
||||||
|
smtpd_peer_test.o: ../../include/name_mask.h
|
||||||
|
smtpd_peer_test.o: ../../include/nvtable.h
|
||||||
|
smtpd_peer_test.o: ../../include/sock_addr.h
|
||||||
|
smtpd_peer_test.o: ../../include/stringops.h
|
||||||
|
smtpd_peer_test.o: ../../include/sys_defs.h
|
||||||
|
smtpd_peer_test.o: ../../include/tls.h
|
||||||
|
smtpd_peer_test.o: ../../include/vbuf.h
|
||||||
|
smtpd_peer_test.o: ../../include/vstream.h
|
||||||
|
smtpd_peer_test.o: ../../include/vstring.h
|
||||||
|
smtpd_peer_test.o: smtpd.h
|
||||||
|
smtpd_peer_test.o: smtpd_chat.h
|
||||||
|
smtpd_peer_test.o: smtpd_peer_test.c
|
||||||
|
smtpd_peer_test.o: smtpd_sasl_glue.h
|
||||||
smtpd_proxy.o: ../../include/argv.h
|
smtpd_proxy.o: ../../include/argv.h
|
||||||
smtpd_proxy.o: ../../include/attr.h
|
smtpd_proxy.o: ../../include/attr.h
|
||||||
smtpd_proxy.o: ../../include/check_arg.h
|
smtpd_proxy.o: ../../include/check_arg.h
|
||||||
|
@ -14,11 +14,16 @@
|
|||||||
/*
|
/*
|
||||||
/* The following summarizes what the Postfix SMTP server expects
|
/* The following summarizes what the Postfix SMTP server expects
|
||||||
/* from an up-stream proxy adapter.
|
/* from an up-stream proxy adapter.
|
||||||
|
/*
|
||||||
|
/* .IP
|
||||||
|
/* Return -1 in case of error, zero otherwise. In case of error,
|
||||||
|
/* the caller will clean up any incomplete endpoint info.
|
||||||
/* .IP \(bu
|
/* .IP \(bu
|
||||||
/* Call smtpd_peer_from_default() if the up-stream proxy
|
/* Call smtpd_peer_from_default() if the up-stream proxy
|
||||||
/* indicates that the connection is not proxied. In that case,
|
/* indicates that the connection is not proxied. In that case,
|
||||||
/* a proxy adapter MUST NOT update any STATE fields: the
|
/* a proxy adapter MUST NOT update dynamically-allocated STATE
|
||||||
/* smtpd_peer_from_default() function will do that instead.
|
/* text fields: the smtpd_peer_from_default() function will do
|
||||||
|
/* that instead.
|
||||||
/* .IP \(bu
|
/* .IP \(bu
|
||||||
/* Validate protocol, address and port syntax. Permit only
|
/* Validate protocol, address and port syntax. Permit only
|
||||||
/* protocols that are configured with the main.cf:inet_protocols
|
/* protocols that are configured with the main.cf:inet_protocols
|
||||||
@ -27,13 +32,17 @@
|
|||||||
/* Convert IPv4-in-IPv6 address syntax to IPv4 syntax when
|
/* Convert IPv4-in-IPv6 address syntax to IPv4 syntax when
|
||||||
/* both IPv6 and IPv4 support are enabled with main.cf:inet_protocols.
|
/* both IPv6 and IPv4 support are enabled with main.cf:inet_protocols.
|
||||||
/* .IP \(bu
|
/* .IP \(bu
|
||||||
/* Update the following session context fields: addr, port,
|
/* Update the following STATE fields: addr, port, rfc_addr,
|
||||||
/* rfc_addr, addr_family, dest_addr, dest_port. The addr_family
|
/* addr_family, dest_addr, dest_port. The addr_family field applies
|
||||||
/* field applies to the client address.
|
/* to the client address.
|
||||||
|
/* .IP \(bu
|
||||||
|
/* Either update the STATE binary fields sockaddr and dest_sockaddr,
|
||||||
|
/* or call smtpd_peer_hostaddr_to_sockaddr() after updating the
|
||||||
|
/* STATE text fields addr, port, dest_addr, and dest_port.
|
||||||
/* .IP \(bu
|
/* .IP \(bu
|
||||||
/* Dynamically allocate storage for string information with
|
/* Dynamically allocate storage for string information with
|
||||||
/* mystrdup(). In case of error, leave unassigned string fields
|
/* mystrdup(). In case of error, leave unassigned string fields
|
||||||
/* at their initial zero value.
|
/* at their initial zero value. The caller will clean up.
|
||||||
/* .IP \(bu
|
/* .IP \(bu
|
||||||
/* Log a clear warning message that explains why a request
|
/* Log a clear warning message that explains why a request
|
||||||
/* fails.
|
/* fails.
|
||||||
@ -62,6 +71,9 @@
|
|||||||
/* Google, Inc.
|
/* Google, Inc.
|
||||||
/* 111 8th Avenue
|
/* 111 8th Avenue
|
||||||
/* New York, NY 10011, USA
|
/* New York, NY 10011, USA
|
||||||
|
/*
|
||||||
|
/* Wietse Venema
|
||||||
|
/* porcupine.org
|
||||||
/*--*/
|
/*--*/
|
||||||
|
|
||||||
/* System library. */
|
/* System library. */
|
||||||
@ -107,9 +119,15 @@ int smtpd_peer_from_haproxy(SMTPD_STATE *state)
|
|||||||
msg_warn("haproxy read: timeout error");
|
msg_warn("haproxy read: timeout error");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
if (haproxy_srvr_receive(vstream_fileno(state->client), &non_proxy,
|
state->sockaddr_len = sizeof(state->sockaddr);
|
||||||
&smtp_client_addr, &smtp_client_port,
|
state->dest_sockaddr_len = sizeof(state->dest_sockaddr);
|
||||||
&smtp_server_addr, &smtp_server_port) < 0) {
|
if (haproxy_srvr_receive_sa(vstream_fileno(state->client), &non_proxy,
|
||||||
|
&smtp_client_addr, &smtp_client_port,
|
||||||
|
&smtp_server_addr, &smtp_server_port,
|
||||||
|
(struct sockaddr *) &state->sockaddr,
|
||||||
|
&state->sockaddr_len,
|
||||||
|
(struct sockaddr *) &state->dest_sockaddr,
|
||||||
|
&state->dest_sockaddr_len) <0) {
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
if (non_proxy) {
|
if (non_proxy) {
|
||||||
|
@ -21,12 +21,16 @@
|
|||||||
/* are set to "unknown".
|
/* are set to "unknown".
|
||||||
/*
|
/*
|
||||||
/* Alternatively, the peer address and port may be obtained
|
/* Alternatively, the peer address and port may be obtained
|
||||||
/* from a proxy server.
|
/* from a proxy server or from attributes that postscreen(8)
|
||||||
|
/* passes to smtpd(8) over local IPC.
|
||||||
/*
|
/*
|
||||||
/* This module uses the local name service via getaddrinfo()
|
/* This module uses the local name service via getaddrinfo()
|
||||||
/* and getnameinfo(). It does not query the DNS directly.
|
/* and getnameinfo(). It does not query the DNS directly.
|
||||||
/*
|
/*
|
||||||
/* smtpd_peer_init() updates the following fields:
|
/* smtpd_peer_init() updates the following fields:
|
||||||
|
/* .IP flags.SMTPD_FLAG_HANGUP
|
||||||
|
/* This flag is raised when the program should hang up
|
||||||
|
/* without reading client input.
|
||||||
/* .IP name
|
/* .IP name
|
||||||
/* The verified client hostname. This name is represented by
|
/* The verified client hostname. This name is represented by
|
||||||
/* the string "unknown" when 1) the address->name lookup failed,
|
/* the string "unknown" when 1) the address->name lookup failed,
|
||||||
@ -36,16 +40,12 @@
|
|||||||
/* The unverified client hostname as found with address->name
|
/* The unverified client hostname as found with address->name
|
||||||
/* lookup; it is not verified for consistency with the client
|
/* lookup; it is not verified for consistency with the client
|
||||||
/* IP address result from name->address lookup.
|
/* IP address result from name->address lookup.
|
||||||
/* .IP forward_name
|
|
||||||
/* The unverified client hostname as found with address->name
|
|
||||||
/* lookup followed by name->address lookup; it is not verified
|
|
||||||
/* for consistency with the result from address->name lookup.
|
|
||||||
/* For example, when the address->name lookup produces as
|
|
||||||
/* hostname an alias, the name->address lookup will produce
|
|
||||||
/* as hostname the expansion of that alias, so that the two
|
|
||||||
/* lookups produce different names.
|
|
||||||
/* .IP addr
|
/* .IP addr
|
||||||
/* Printable representation of the client address.
|
/* Printable representation of the client address.
|
||||||
|
/* .IP addr_family
|
||||||
|
/* AF_INET or AF_INET6 in case of an open TCP connection.
|
||||||
|
/* AF_UNSPEC in all other cases, including an open non-socket
|
||||||
|
/* connection, or a closed connection.
|
||||||
/* .IP namaddr
|
/* .IP namaddr
|
||||||
/* String of the form: "name[addr]:port".
|
/* String of the form: "name[addr]:port".
|
||||||
/* .IP rfc_addr
|
/* .IP rfc_addr
|
||||||
@ -88,19 +88,6 @@
|
|||||||
/* .IP 5
|
/* .IP 5
|
||||||
/* The address->name lookup failed with an unrecoverable error.
|
/* The address->name lookup failed with an unrecoverable error.
|
||||||
/* .RE
|
/* .RE
|
||||||
/* .IP forward_name_status
|
|
||||||
/* The forward_name_status result field specifies how the
|
|
||||||
/* forward_name information should be interpreted:
|
|
||||||
/* .RS
|
|
||||||
/* .IP 2
|
|
||||||
/* The address->name and name->address lookup succeeded.
|
|
||||||
/* .IP 4
|
|
||||||
/* The address->name lookup or name->address failed with a
|
|
||||||
/* recoverable error.
|
|
||||||
/* .IP 5
|
|
||||||
/* The address->name lookup or name->address failed with an
|
|
||||||
/* unrecoverable error.
|
|
||||||
/* .RE
|
|
||||||
/* .PP
|
/* .PP
|
||||||
/* smtpd_peer_reset() releases memory allocated by smtpd_peer_init().
|
/* smtpd_peer_reset() releases memory allocated by smtpd_peer_init().
|
||||||
/*
|
/*
|
||||||
@ -159,8 +146,6 @@
|
|||||||
|
|
||||||
#include "smtpd.h"
|
#include "smtpd.h"
|
||||||
|
|
||||||
static const INET_PROTO_INFO *proto_info;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* XXX If we make local port information available via logging, then we must
|
* XXX If we make local port information available via logging, then we must
|
||||||
* also support these attributes with the XFORWARD command.
|
* also support these attributes with the XFORWARD command.
|
||||||
@ -177,7 +162,7 @@ static int smtpd_peer_sockaddr_to_hostaddr(SMTPD_STATE *state)
|
|||||||
{
|
{
|
||||||
const char *myname = "smtpd_peer_sockaddr_to_hostaddr";
|
const char *myname = "smtpd_peer_sockaddr_to_hostaddr";
|
||||||
struct sockaddr *sa = (struct sockaddr *) &(state->sockaddr);
|
struct sockaddr *sa = (struct sockaddr *) &(state->sockaddr);
|
||||||
SOCKADDR_SIZE sa_length = state->sockaddr_len;
|
SOCKADDR_SIZE *sa_length = &state->sockaddr_len;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* XXX If we're given an IPv6 (or IPv4) connection from, e.g., inetd,
|
* XXX If we're given an IPv6 (or IPv4) connection from, e.g., inetd,
|
||||||
@ -195,12 +180,11 @@ static int smtpd_peer_sockaddr_to_hostaddr(SMTPD_STATE *state)
|
|||||||
MAI_HOSTADDR_STR server_addr;
|
MAI_HOSTADDR_STR server_addr;
|
||||||
MAI_SERVPORT_STR server_port;
|
MAI_SERVPORT_STR server_port;
|
||||||
int aierr;
|
int aierr;
|
||||||
char *colonp;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sanity check: we can't use sockets that we're not configured for.
|
* Sanity check: we can't use sockets that we're not configured for.
|
||||||
*/
|
*/
|
||||||
if (strchr((char *) proto_info->sa_family_list, sa->sa_family) == 0)
|
if (strchr((char *) inet_proto_info()->sa_family_list, sa->sa_family) == 0)
|
||||||
msg_fatal("cannot handle socket type %s with \"%s = %s\"",
|
msg_fatal("cannot handle socket type %s with \"%s = %s\"",
|
||||||
#ifdef AF_INET6
|
#ifdef AF_INET6
|
||||||
sa->sa_family == AF_INET6 ? "AF_INET6" :
|
sa->sa_family == AF_INET6 ? "AF_INET6" :
|
||||||
@ -222,15 +206,15 @@ static int smtpd_peer_sockaddr_to_hostaddr(SMTPD_STATE *state)
|
|||||||
/*
|
/*
|
||||||
* Convert the client address to printable form.
|
* Convert the client address to printable form.
|
||||||
*/
|
*/
|
||||||
if ((aierr = sockaddr_to_hostaddr(sa, sa_length, &client_addr,
|
if ((aierr = sane_sockaddr_to_hostaddr(sa, sa_length, &client_addr,
|
||||||
&client_port, 0)) != 0)
|
&client_port, 0)) != 0)
|
||||||
msg_fatal("%s: cannot convert client sockaddr type %s length %ld "
|
msg_fatal("%s: cannot convert client sockaddr type %s length %ld "
|
||||||
"to string: %s", myname,
|
"to string: %s", myname,
|
||||||
#ifdef AF_INET6
|
#ifdef AF_INET6
|
||||||
sa->sa_family == AF_INET6 ? "AF_INET6" :
|
sa->sa_family == AF_INET6 ? "AF_INET6" :
|
||||||
#endif
|
#endif
|
||||||
sa->sa_family == AF_INET ? "AF_INET" : "other",
|
sa->sa_family == AF_INET ? "AF_INET" : "other",
|
||||||
(long) sa_length, MAI_STRERROR(aierr));
|
(long) *sa_length, MAI_STRERROR(aierr));
|
||||||
state->port = mystrdup(client_port.buf);
|
state->port = mystrdup(client_port.buf);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -241,55 +225,20 @@ static int smtpd_peer_sockaddr_to_hostaddr(SMTPD_STATE *state)
|
|||||||
if (strchr(client_addr.buf, '%') != 0)
|
if (strchr(client_addr.buf, '%') != 0)
|
||||||
msg_panic("%s: address %s has datalink suffix",
|
msg_panic("%s: address %s has datalink suffix",
|
||||||
myname, client_addr.buf);
|
myname, client_addr.buf);
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We convert IPv4-in-IPv6 address to 'true' IPv4 address early on,
|
* Following RFC 2821 section 4.1.3, an IPv6 address literal gets a
|
||||||
* but only if IPv4 support is enabled (why would anyone want to turn
|
* prefix of 'IPv6:'. We do this consistently for all IPv6 addresses
|
||||||
* it off)? With IPv4 support enabled we have no need for the IPv6
|
* that appear in headers or envelopes. The fact that
|
||||||
* form in logging, hostname verification and access checks.
|
* valid_mailhost_addr() enforces the form helps of course. We use
|
||||||
|
* the form without IPV6: prefix when doing access control, or when
|
||||||
|
* accessing the connection cache.
|
||||||
*/
|
*/
|
||||||
#ifdef HAS_IPV6
|
|
||||||
if (sa->sa_family == AF_INET6) {
|
if (sa->sa_family == AF_INET6) {
|
||||||
if (strchr((char *) proto_info->sa_family_list, AF_INET) != 0
|
state->addr = mystrdup(client_addr.buf);
|
||||||
&& IN6_IS_ADDR_V4MAPPED(&SOCK_ADDR_IN6_ADDR(sa))
|
state->rfc_addr =
|
||||||
&& (colonp = strrchr(client_addr.buf, ':')) != 0) {
|
concatenate(IPV6_COL, client_addr.buf, (char *) 0);
|
||||||
struct addrinfo *res0;
|
state->addr_family = sa->sa_family;
|
||||||
|
|
||||||
if (msg_verbose > 1)
|
|
||||||
msg_info("%s: rewriting V4-mapped address \"%s\" to \"%s\"",
|
|
||||||
myname, client_addr.buf, colonp + 1);
|
|
||||||
|
|
||||||
state->addr = mystrdup(colonp + 1);
|
|
||||||
state->rfc_addr = mystrdup(colonp + 1);
|
|
||||||
state->addr_family = AF_INET;
|
|
||||||
aierr =
|
|
||||||
hostaddr_to_sockaddr(state->addr, state->port, 0, &res0);
|
|
||||||
if (aierr)
|
|
||||||
msg_fatal("%s: cannot convert [%s]:%s to binary: %s",
|
|
||||||
myname, state->addr, state->port,
|
|
||||||
MAI_STRERROR(aierr));
|
|
||||||
sa_length = res0->ai_addrlen;
|
|
||||||
if (sa_length > sizeof(state->sockaddr))
|
|
||||||
sa_length = sizeof(state->sockaddr);
|
|
||||||
memcpy((void *) sa, res0->ai_addr, sa_length);
|
|
||||||
freeaddrinfo(res0); /* 200412 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Following RFC 2821 section 4.1.3, an IPv6 address literal gets
|
|
||||||
* a prefix of 'IPv6:'. We do this consistently for all IPv6
|
|
||||||
* addresses that appear in headers or envelopes. The fact that
|
|
||||||
* valid_mailhost_addr() enforces the form helps of course. We
|
|
||||||
* use the form without IPV6: prefix when doing access control,
|
|
||||||
* or when accessing the connection cache.
|
|
||||||
*/
|
|
||||||
else {
|
|
||||||
state->addr = mystrdup(client_addr.buf);
|
|
||||||
state->rfc_addr =
|
|
||||||
concatenate(IPV6_COL, client_addr.buf, (char *) 0);
|
|
||||||
state->addr_family = sa->sa_family;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -306,14 +255,13 @@ static int smtpd_peer_sockaddr_to_hostaddr(SMTPD_STATE *state)
|
|||||||
/*
|
/*
|
||||||
* Convert the server address/port to printable form.
|
* Convert the server address/port to printable form.
|
||||||
*/
|
*/
|
||||||
if ((aierr = sockaddr_to_hostaddr((struct sockaddr *)
|
if ((aierr = sane_sockaddr_to_hostaddr((struct sockaddr *)
|
||||||
&state->dest_sockaddr,
|
&state->dest_sockaddr,
|
||||||
state->dest_sockaddr_len,
|
&state->dest_sockaddr_len,
|
||||||
&server_addr,
|
&server_addr,
|
||||||
&server_port, 0)) != 0)
|
&server_port, 0)) != 0)
|
||||||
/* TODO: convert IPv4-in-IPv6 to IPv4 form. */
|
|
||||||
msg_fatal("%s: cannot convert server sockaddr type %s length %ld "
|
msg_fatal("%s: cannot convert server sockaddr type %s length %ld "
|
||||||
"to string: %s", myname,
|
"to string: %s", myname,
|
||||||
#ifdef AF_INET6
|
#ifdef AF_INET6
|
||||||
state->dest_sockaddr.ss_family == AF_INET6 ? "AF_INET6" :
|
state->dest_sockaddr.ss_family == AF_INET6 ? "AF_INET6" :
|
||||||
#endif
|
#endif
|
||||||
@ -404,7 +352,7 @@ static void smtpd_peer_sockaddr_to_hostname(SMTPD_STATE *state)
|
|||||||
REJECT_PEER_NAME(state, SMTPD_PEER_CODE_FORGED);
|
REJECT_PEER_NAME(state, SMTPD_PEER_CODE_FORGED);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
|
if (strchr((char *) inet_proto_info()->sa_family_list, res->ai_family) == 0) {
|
||||||
msg_info("skipping address family %d for host %s",
|
msg_info("skipping address family %d for host %s",
|
||||||
res->ai_family, state->name);
|
res->ai_family, state->name);
|
||||||
continue;
|
continue;
|
||||||
@ -425,6 +373,11 @@ static void smtpd_peer_hostaddr_to_sockaddr(SMTPD_STATE *state)
|
|||||||
struct addrinfo *res;
|
struct addrinfo *res;
|
||||||
int aierr;
|
int aierr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The client binary address value is provided by non-error peer lookup
|
||||||
|
* methods. It is used to compute the anvil aggregation prefix for client
|
||||||
|
* IP addresses.
|
||||||
|
*/
|
||||||
if ((aierr = hostaddr_to_sockaddr(state->addr, state->port,
|
if ((aierr = hostaddr_to_sockaddr(state->addr, state->port,
|
||||||
SOCK_STREAM, &res)) != 0)
|
SOCK_STREAM, &res)) != 0)
|
||||||
msg_fatal("%s: cannot convert client address '%s' port '%s' to binary: %s",
|
msg_fatal("%s: cannot convert client address '%s' port '%s' to binary: %s",
|
||||||
@ -434,11 +387,26 @@ static void smtpd_peer_hostaddr_to_sockaddr(SMTPD_STATE *state)
|
|||||||
memcpy((void *) &(state->sockaddr), res->ai_addr, res->ai_addrlen);
|
memcpy((void *) &(state->sockaddr), res->ai_addr, res->ai_addrlen);
|
||||||
state->sockaddr_len = res->ai_addrlen;
|
state->sockaddr_len = res->ai_addrlen;
|
||||||
freeaddrinfo(res);
|
freeaddrinfo(res);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The server binary address is provided by non-error peer lookup
|
||||||
|
* methods. It is currently unused, but it is the result of a hermetic
|
||||||
|
* conversion, therefore low-risk.
|
||||||
|
*/
|
||||||
|
if ((aierr = hostaddr_to_sockaddr(state->dest_addr, state->dest_port,
|
||||||
|
SOCK_STREAM, &res)) != 0)
|
||||||
|
msg_fatal("%s: cannot convert server address '%s' port '%s' to binary: %s",
|
||||||
|
myname, state->dest_addr, state->dest_port, MAI_STRERROR(aierr));
|
||||||
|
if (res->ai_addrlen > sizeof(state->dest_sockaddr))
|
||||||
|
msg_panic("%s: address length > struct sockaddr_storage", myname);
|
||||||
|
memcpy((void *) &(state->dest_sockaddr), res->ai_addr, res->ai_addrlen);
|
||||||
|
state->dest_sockaddr_len = res->ai_addrlen;
|
||||||
|
freeaddrinfo(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* smtpd_peer_not_inet - non-socket or non-Internet endpoint */
|
/* smtpd_peer_assume_local_client - non-Internet endpoint */
|
||||||
|
|
||||||
static void smtpd_peer_not_inet(SMTPD_STATE *state)
|
static void smtpd_peer_assume_local_client(SMTPD_STATE *state)
|
||||||
{
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -448,7 +416,7 @@ static void smtpd_peer_not_inet(SMTPD_STATE *state)
|
|||||||
state->name = mystrdup("localhost");
|
state->name = mystrdup("localhost");
|
||||||
state->reverse_name = mystrdup("localhost");
|
state->reverse_name = mystrdup("localhost");
|
||||||
#ifdef AF_INET6
|
#ifdef AF_INET6
|
||||||
if (proto_info->sa_family_list[0] == PF_INET6) {
|
if (inet_proto_info()->sa_family_list[0] == PF_INET6) {
|
||||||
state->addr = mystrdup("::1"); /* XXX bogus. */
|
state->addr = mystrdup("::1"); /* XXX bogus. */
|
||||||
state->rfc_addr = mystrdup(IPV6_COL "::1"); /* XXX bogus. */
|
state->rfc_addr = mystrdup(IPV6_COL "::1"); /* XXX bogus. */
|
||||||
} else
|
} else
|
||||||
@ -464,13 +432,15 @@ static void smtpd_peer_not_inet(SMTPD_STATE *state)
|
|||||||
|
|
||||||
state->dest_addr = mystrdup(state->addr); /* XXX bogus. */
|
state->dest_addr = mystrdup(state->addr); /* XXX bogus. */
|
||||||
state->dest_port = mystrdup(state->port); /* XXX bogus. */
|
state->dest_port = mystrdup(state->port); /* XXX bogus. */
|
||||||
|
|
||||||
|
state->sockaddr_len = 0;
|
||||||
|
state->dest_sockaddr_len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* smtpd_peer_no_client - peer went away, or peer info unavailable */
|
/* smtpd_peer_assume_unknown_client - peer went away, or peer info unavailable */
|
||||||
|
|
||||||
static void smtpd_peer_no_client(SMTPD_STATE *state)
|
static void smtpd_peer_assume_unknown_client(SMTPD_STATE *state)
|
||||||
{
|
{
|
||||||
smtpd_peer_reset(state);
|
|
||||||
state->name = mystrdup(CLIENT_NAME_UNKNOWN);
|
state->name = mystrdup(CLIENT_NAME_UNKNOWN);
|
||||||
state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN);
|
state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN);
|
||||||
state->addr = mystrdup(CLIENT_ADDR_UNKNOWN);
|
state->addr = mystrdup(CLIENT_ADDR_UNKNOWN);
|
||||||
@ -482,58 +452,67 @@ static void smtpd_peer_no_client(SMTPD_STATE *state)
|
|||||||
|
|
||||||
state->dest_addr = mystrdup(SERVER_ADDR_UNKNOWN);
|
state->dest_addr = mystrdup(SERVER_ADDR_UNKNOWN);
|
||||||
state->dest_port = mystrdup(SERVER_PORT_UNKNOWN);
|
state->dest_port = mystrdup(SERVER_PORT_UNKNOWN);
|
||||||
|
|
||||||
|
state->sockaddr_len = 0;
|
||||||
|
state->dest_sockaddr_len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* smtpd_peer_from_pass_attr - initialize from attribute hash */
|
/* smtpd_peer_from_pass_attr - initialize from attribute hash */
|
||||||
|
|
||||||
static void smtpd_peer_from_pass_attr(SMTPD_STATE *state)
|
static int smtpd_peer_from_pass_attr(SMTPD_STATE *state)
|
||||||
{
|
{
|
||||||
HTABLE *attr = (HTABLE *) vstream_context(state->client);
|
HTABLE *attr = (HTABLE *) vstream_context(state->client);
|
||||||
const char *cp;
|
const char *cp;
|
||||||
|
|
||||||
|
#define BAD_PASS_ATTR(...) do { \
|
||||||
|
msg_warn(__VA_ARGS__); \
|
||||||
|
return (-1); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Extract the client endpoint information from the attribute hash.
|
* Extract the client endpoint information from the attribute hash.
|
||||||
*/
|
*/
|
||||||
if ((cp = htable_find(attr, MAIL_ATTR_ACT_CLIENT_ADDR)) == 0)
|
if ((cp = htable_find(attr, MAIL_ATTR_ACT_CLIENT_ADDR)) == 0)
|
||||||
msg_fatal("missing client address from proxy");
|
BAD_PASS_ATTR("missing client address from proxy");
|
||||||
if (strrchr(cp, ':') != 0) {
|
if (strrchr(cp, ':') != 0) {
|
||||||
if (valid_ipv6_hostaddr(cp, DO_GRIPE) == 0)
|
if (valid_ipv6_hostaddr(cp, DO_GRIPE) == 0)
|
||||||
msg_fatal("bad IPv6 client address syntax from proxy: %s", cp);
|
BAD_PASS_ATTR("bad IPv6 client address syntax from proxy: %s", cp);
|
||||||
state->addr = mystrdup(cp);
|
state->addr = mystrdup(cp);
|
||||||
state->rfc_addr = concatenate(IPV6_COL, cp, (char *) 0);
|
state->rfc_addr = concatenate(IPV6_COL, cp, (char *) 0);
|
||||||
state->addr_family = AF_INET6;
|
state->addr_family = AF_INET6;
|
||||||
} else {
|
} else {
|
||||||
if (valid_ipv4_hostaddr(cp, DO_GRIPE) == 0)
|
if (valid_ipv4_hostaddr(cp, DO_GRIPE) == 0)
|
||||||
msg_fatal("bad IPv4 client address syntax from proxy: %s", cp);
|
BAD_PASS_ATTR("bad IPv4 client address syntax from proxy: %s", cp);
|
||||||
state->addr = mystrdup(cp);
|
state->addr = mystrdup(cp);
|
||||||
state->rfc_addr = mystrdup(cp);
|
state->rfc_addr = mystrdup(cp);
|
||||||
state->addr_family = AF_INET;
|
state->addr_family = AF_INET;
|
||||||
}
|
}
|
||||||
if ((cp = htable_find(attr, MAIL_ATTR_ACT_CLIENT_PORT)) == 0)
|
if ((cp = htable_find(attr, MAIL_ATTR_ACT_CLIENT_PORT)) == 0)
|
||||||
msg_fatal("missing client port from proxy");
|
BAD_PASS_ATTR("missing client port from proxy");
|
||||||
if (valid_hostport(cp, DO_GRIPE) == 0)
|
if (valid_hostport(cp, DO_GRIPE) == 0)
|
||||||
msg_fatal("bad TCP client port number syntax from proxy: %s", cp);
|
BAD_PASS_ATTR("bad TCP client port number syntax from proxy: %s", cp);
|
||||||
state->port = mystrdup(cp);
|
state->port = mystrdup(cp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The Dovecot authentication server needs the server IP address.
|
* The Dovecot authentication server needs the server IP address.
|
||||||
*/
|
*/
|
||||||
if ((cp = htable_find(attr, MAIL_ATTR_ACT_SERVER_ADDR)) == 0)
|
if ((cp = htable_find(attr, MAIL_ATTR_ACT_SERVER_ADDR)) == 0)
|
||||||
msg_fatal("missing server address from proxy");
|
BAD_PASS_ATTR("missing server address from proxy");
|
||||||
if (valid_hostaddr(cp, DO_GRIPE) == 0)
|
if (valid_hostaddr(cp, DO_GRIPE) == 0)
|
||||||
msg_fatal("bad IPv6 server address syntax from proxy: %s", cp);
|
BAD_PASS_ATTR("bad IPv6 server address syntax from proxy: %s", cp);
|
||||||
state->dest_addr = mystrdup(cp);
|
state->dest_addr = mystrdup(cp);
|
||||||
|
|
||||||
if ((cp = htable_find(attr, MAIL_ATTR_ACT_SERVER_PORT)) == 0)
|
if ((cp = htable_find(attr, MAIL_ATTR_ACT_SERVER_PORT)) == 0)
|
||||||
msg_fatal("missing server port from proxy");
|
BAD_PASS_ATTR("missing server port from proxy");
|
||||||
if (valid_hostport(cp, DO_GRIPE) == 0)
|
if (valid_hostport(cp, DO_GRIPE) == 0)
|
||||||
msg_fatal("bad TCP server port number syntax from proxy: %s", cp);
|
BAD_PASS_ATTR("bad TCP server port number syntax from proxy: %s", cp);
|
||||||
state->dest_port = mystrdup(cp);
|
state->dest_port = mystrdup(cp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert the client address from string to binary form.
|
* Convert the client address from string to binary form.
|
||||||
*/
|
*/
|
||||||
smtpd_peer_hostaddr_to_sockaddr(state);
|
smtpd_peer_hostaddr_to_sockaddr(state);
|
||||||
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* smtpd_peer_from_default - try to initialize peer information from socket */
|
/* smtpd_peer_from_default - try to initialize peer information from socket */
|
||||||
@ -544,8 +523,8 @@ void smtpd_peer_from_default(SMTPD_STATE *state)
|
|||||||
/*
|
/*
|
||||||
* The "no client" routine provides surrogate information so that the
|
* The "no client" routine provides surrogate information so that the
|
||||||
* application can produce sensible logging when a client disconnects
|
* application can produce sensible logging when a client disconnects
|
||||||
* before the server wakes up. The "not inet" routine provides surrogate
|
* before the server wakes up. The "assume local" routine provides
|
||||||
* state for (presumably) local IPC channels.
|
* surrogate state for open, presumably, local, IPC channels.
|
||||||
*/
|
*/
|
||||||
state->sockaddr_len = sizeof(state->sockaddr);
|
state->sockaddr_len = sizeof(state->sockaddr);
|
||||||
state->dest_sockaddr_len = sizeof(state->dest_sockaddr);
|
state->dest_sockaddr_len = sizeof(state->dest_sockaddr);
|
||||||
@ -556,18 +535,34 @@ void smtpd_peer_from_default(SMTPD_STATE *state)
|
|||||||
(struct sockaddr *) &state->dest_sockaddr,
|
(struct sockaddr *) &state->dest_sockaddr,
|
||||||
&state->dest_sockaddr_len) < 0) {
|
&state->dest_sockaddr_len) < 0) {
|
||||||
if (errno == ENOTSOCK)
|
if (errno == ENOTSOCK)
|
||||||
smtpd_peer_not_inet(state);
|
smtpd_peer_assume_local_client(state);
|
||||||
else
|
else
|
||||||
smtpd_peer_no_client(state);
|
smtpd_peer_assume_unknown_client(state);
|
||||||
} else {
|
} else {
|
||||||
if (smtpd_peer_sockaddr_to_hostaddr(state) < 0)
|
if (smtpd_peer_sockaddr_to_hostaddr(state) < 0)
|
||||||
smtpd_peer_not_inet(state);
|
smtpd_peer_assume_local_client(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* smtpd_peer_fall_back_and_hangup - recover after incomplete peer info */
|
||||||
|
|
||||||
|
static void smtpd_peer_fall_back_and_hangup(SMTPD_STATE *state)
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clear incomplete endpoint info. Populate the SMTPD_STATE with default
|
||||||
|
* endpoint info, so that the caller won't trip over a null pointer. Hang
|
||||||
|
* up before accepting input: we don't know what we're talking to and
|
||||||
|
* what rights they might have.
|
||||||
|
*/
|
||||||
|
smtpd_peer_reset(state);
|
||||||
|
smtpd_peer_assume_unknown_client(state);
|
||||||
|
state->flags |= SMTPD_FLAG_HANGUP;
|
||||||
|
}
|
||||||
|
|
||||||
/* smtpd_peer_from_proxy - get endpoint info from proxy agent */
|
/* smtpd_peer_from_proxy - get endpoint info from proxy agent */
|
||||||
|
|
||||||
static void smtpd_peer_from_proxy(SMTPD_STATE *state)
|
static int smtpd_peer_from_proxy(SMTPD_STATE *state)
|
||||||
{
|
{
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *name;
|
const char *name;
|
||||||
@ -575,6 +570,11 @@ static void smtpd_peer_from_proxy(SMTPD_STATE *state)
|
|||||||
} SMTPD_ENDPT_LOOKUP_INFO;
|
} SMTPD_ENDPT_LOOKUP_INFO;
|
||||||
static const SMTPD_ENDPT_LOOKUP_INFO smtpd_endpt_lookup_info[] = {
|
static const SMTPD_ENDPT_LOOKUP_INFO smtpd_endpt_lookup_info[] = {
|
||||||
HAPROXY_PROTO_NAME, smtpd_peer_from_haproxy,
|
HAPROXY_PROTO_NAME, smtpd_peer_from_haproxy,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See smtpd_haproxy.c for the a summary of the information that a
|
||||||
|
* proxy endpoint lookup function is expected to provide.
|
||||||
|
*/
|
||||||
0,
|
0,
|
||||||
};
|
};
|
||||||
const SMTPD_ENDPT_LOOKUP_INFO *pp;
|
const SMTPD_ENDPT_LOOKUP_INFO *pp;
|
||||||
@ -588,13 +588,7 @@ static void smtpd_peer_from_proxy(SMTPD_STATE *state)
|
|||||||
msg_fatal("unsupported %s value: %s",
|
msg_fatal("unsupported %s value: %s",
|
||||||
VAR_SMTPD_UPROXY_PROTO, var_smtpd_uproxy_proto);
|
VAR_SMTPD_UPROXY_PROTO, var_smtpd_uproxy_proto);
|
||||||
if (strcmp(var_smtpd_uproxy_proto, pp->name) == 0)
|
if (strcmp(var_smtpd_uproxy_proto, pp->name) == 0)
|
||||||
break;
|
return (pp->endpt_lookup(state));
|
||||||
}
|
|
||||||
if (pp->endpt_lookup(state) < 0) {
|
|
||||||
smtpd_peer_from_default(state);
|
|
||||||
state->flags |= SMTPD_FLAG_HANGUP;
|
|
||||||
} else {
|
|
||||||
smtpd_peer_hostaddr_to_sockaddr(state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -602,13 +596,6 @@ static void smtpd_peer_from_proxy(SMTPD_STATE *state)
|
|||||||
|
|
||||||
void smtpd_peer_init(SMTPD_STATE *state)
|
void smtpd_peer_init(SMTPD_STATE *state)
|
||||||
{
|
{
|
||||||
int af;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize.
|
|
||||||
*/
|
|
||||||
if (proto_info == 0)
|
|
||||||
proto_info = inet_proto_info();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prepare for partial initialization after error.
|
* Prepare for partial initialization after error.
|
||||||
@ -629,19 +616,25 @@ void smtpd_peer_init(SMTPD_STATE *state)
|
|||||||
/*
|
/*
|
||||||
* Determine the remote SMTP client address and port.
|
* Determine the remote SMTP client address and port.
|
||||||
*
|
*
|
||||||
|
* If we can't process the connection hand-off info from postscreen or
|
||||||
|
* proxy, fall back to some default endpoint info for logging and force a
|
||||||
|
* hangup. We can't determine what rights the peer should have.
|
||||||
|
*
|
||||||
* XXX In stand-alone mode, don't assume that the peer will be a local
|
* 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
|
* process. That could introduce a gaping hole when the SMTP daemon is
|
||||||
* hooked up to the network via inetd or some other super-server.
|
* hooked up to the network via inetd or some other super-server.
|
||||||
*/
|
*/
|
||||||
if (vstream_context(state->client) != 0) {
|
if (vstream_context(state->client) != 0) {
|
||||||
smtpd_peer_from_pass_attr(state);
|
if (smtpd_peer_from_pass_attr(state) < 0)
|
||||||
|
smtpd_peer_fall_back_and_hangup(state);
|
||||||
if (*var_smtpd_uproxy_proto != 0)
|
if (*var_smtpd_uproxy_proto != 0)
|
||||||
msg_warn("ignoring non-empty %s setting behind postscreen",
|
msg_warn("ignoring non-empty %s setting behind postscreen",
|
||||||
VAR_SMTPD_UPROXY_PROTO);
|
VAR_SMTPD_UPROXY_PROTO);
|
||||||
} else if (SMTPD_STAND_ALONE(state) || *var_smtpd_uproxy_proto == 0) {
|
} else if (SMTPD_STAND_ALONE(state) || *var_smtpd_uproxy_proto == 0) {
|
||||||
smtpd_peer_from_default(state);
|
smtpd_peer_from_default(state);
|
||||||
} else {
|
} else {
|
||||||
smtpd_peer_from_proxy(state);
|
if (smtpd_peer_from_proxy(state) < 0)
|
||||||
|
smtpd_peer_fall_back_and_hangup(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -659,14 +652,14 @@ void smtpd_peer_init(SMTPD_STATE *state)
|
|||||||
state->port);
|
state->port);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate 'address' or 'net/mask' index for anvil event aggregation.
|
* Generate the 'address' or 'net/mask' index for anvil event
|
||||||
* Don't do this for non-socket input. See smtpd_peer_not_inet().
|
* aggregation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (state->addr_family != AF_UNSPEC) {
|
if (state->addr_family != AF_UNSPEC) {
|
||||||
af = SOCK_ADDR_FAMILY(&(state->sockaddr));
|
state->anvil_range = inet_prefix_top(state->addr_family,
|
||||||
state->anvil_range = inet_prefix_top(af,
|
|
||||||
SOCK_ADDR_ADDRP(&(state->sockaddr)),
|
SOCK_ADDR_ADDRP(&(state->sockaddr)),
|
||||||
af == AF_INET ?
|
state->addr_family == AF_INET ?
|
||||||
var_smtpd_cipv4_prefix :
|
var_smtpd_cipv4_prefix :
|
||||||
var_smtpd_cipv6_prefix);
|
var_smtpd_cipv6_prefix);
|
||||||
}
|
}
|
||||||
@ -676,22 +669,24 @@ void smtpd_peer_init(SMTPD_STATE *state)
|
|||||||
|
|
||||||
void smtpd_peer_reset(SMTPD_STATE *state)
|
void smtpd_peer_reset(SMTPD_STATE *state)
|
||||||
{
|
{
|
||||||
|
#define MYFREE_AND_ZERO(e) do { myfree(e); (e) = 0; } while (0);
|
||||||
|
|
||||||
if (state->name)
|
if (state->name)
|
||||||
myfree(state->name);
|
MYFREE_AND_ZERO(state->name);
|
||||||
if (state->reverse_name)
|
if (state->reverse_name)
|
||||||
myfree(state->reverse_name);
|
MYFREE_AND_ZERO(state->reverse_name);
|
||||||
if (state->addr)
|
if (state->addr)
|
||||||
myfree(state->addr);
|
MYFREE_AND_ZERO(state->addr);
|
||||||
if (state->namaddr)
|
if (state->namaddr)
|
||||||
myfree(state->namaddr);
|
MYFREE_AND_ZERO(state->namaddr);
|
||||||
if (state->rfc_addr)
|
if (state->rfc_addr)
|
||||||
myfree(state->rfc_addr);
|
MYFREE_AND_ZERO(state->rfc_addr);
|
||||||
if (state->port)
|
if (state->port)
|
||||||
myfree(state->port);
|
MYFREE_AND_ZERO(state->port);
|
||||||
if (state->dest_addr)
|
if (state->dest_addr)
|
||||||
myfree(state->dest_addr);
|
MYFREE_AND_ZERO(state->dest_addr);
|
||||||
if (state->dest_port)
|
if (state->dest_port)
|
||||||
myfree(state->dest_port);
|
MYFREE_AND_ZERO(state->dest_port);
|
||||||
if (state->anvil_range)
|
if (state->anvil_range)
|
||||||
myfree(state->anvil_range);
|
MYFREE_AND_ZERO(state->anvil_range);
|
||||||
}
|
}
|
||||||
|
715
postfix/src/smtpd/smtpd_peer_test.c
Normal file
715
postfix/src/smtpd/smtpd_peer_test.c
Normal file
@ -0,0 +1,715 @@
|
|||||||
|
/*++
|
||||||
|
/* NAME
|
||||||
|
/* smtpd_peer_test 1t
|
||||||
|
/* SUMMARY
|
||||||
|
/* smtpd_peer_init() unit tests
|
||||||
|
/* SYNOPSIS
|
||||||
|
/* ./smtpd_peer_test
|
||||||
|
/* DESCRIPTION
|
||||||
|
/* Verifies that smtpd_peer_init() will update the SMTPD_STATE
|
||||||
|
/* structure with the expected error or endpoint information for
|
||||||
|
/* different input sources.
|
||||||
|
/* LICENSE
|
||||||
|
/* .ad
|
||||||
|
/* .fi
|
||||||
|
/* The Secure Mailer license must be distributed with this software.
|
||||||
|
/* AUTHOR(S)
|
||||||
|
/* Wietse Venema
|
||||||
|
/* porcupine.org
|
||||||
|
/*--*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* System library.
|
||||||
|
*/
|
||||||
|
#include <sys_defs.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Utility library.
|
||||||
|
*/
|
||||||
|
#include <htable.h>
|
||||||
|
#include <msg.h>
|
||||||
|
#include <msg_vstream.h>
|
||||||
|
#include <stringops.h>
|
||||||
|
#include <vstream.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Global library.
|
||||||
|
*/
|
||||||
|
#include <haproxy_srvr.h>
|
||||||
|
#include <inet_proto.h>
|
||||||
|
#include <mail_params.h>
|
||||||
|
#include <mail_proto.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO(wietse) make this a proper VSTREAM interface or test helper API.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* vstream_swap - capture output for testing */
|
||||||
|
|
||||||
|
static void vstream_swap(VSTREAM *one, VSTREAM *two)
|
||||||
|
{
|
||||||
|
VSTREAM save;
|
||||||
|
|
||||||
|
save = *one;
|
||||||
|
*one = *two;
|
||||||
|
*two = save;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Application-specific.
|
||||||
|
*/
|
||||||
|
#include <smtpd.h>
|
||||||
|
|
||||||
|
int tests_failed = 0;
|
||||||
|
int tests_passed = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fakes to satisfy some dependencies.
|
||||||
|
*/
|
||||||
|
#include <smtpd_chat.h>
|
||||||
|
#include <smtpd_sasl_glue.h>
|
||||||
|
|
||||||
|
#define TEST_TIMEOUT 10
|
||||||
|
char *var_smtpd_uproxy_proto = "";
|
||||||
|
int var_smtpd_uproxy_tmout = TEST_TIMEOUT;
|
||||||
|
bool var_smtpd_peername_lookup;
|
||||||
|
bool var_smtpd_client_port_log;
|
||||||
|
bool var_smtpd_sasl_enable;
|
||||||
|
int var_smtpd_cipv4_prefix = DEF_SMTPD_CIPV4_PREFIX;
|
||||||
|
int var_smtpd_cipv6_prefix = DEF_SMTPD_CIPV6_PREFIX;
|
||||||
|
char *var_notify_classes = DEF_NOTIFY_CLASSES;
|
||||||
|
void smtpd_chat_reset(SMTPD_STATE *state)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void smtpd_sasl_state_init(SMTPD_STATE *state)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void smtpd_xforward_init(SMTPD_STATE *state)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset globals that may be tweaked by individual tests */
|
||||||
|
|
||||||
|
static void reset_global_variables(void)
|
||||||
|
{
|
||||||
|
var_smtpd_uproxy_proto = "";
|
||||||
|
inet_proto_init("reset_global_variables", "all");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Basic tests that smtpd_peer_init() will update the SMTPD_STATE structure
|
||||||
|
* with the expected error info or endpoint info. This needs to be subclassed
|
||||||
|
* to support different input sources (local client, no open connection,
|
||||||
|
* HaProxy, postscreen, etc.).
|
||||||
|
*/
|
||||||
|
typedef struct TEST_BASE {
|
||||||
|
const char *label;
|
||||||
|
int want_hangup;
|
||||||
|
const char *want_warning;
|
||||||
|
const char *want_client_name;
|
||||||
|
int want_client_name_status;
|
||||||
|
const char *want_client_reverse_name;
|
||||||
|
int want_client_reverse_name_status;
|
||||||
|
const char *want_client_addr;
|
||||||
|
const char *want_client_rfc_addr;
|
||||||
|
const char *want_client_port;
|
||||||
|
int want_client_addr_family;
|
||||||
|
const char *want_server_addr;
|
||||||
|
const char *want_server_port;
|
||||||
|
int want_sockaddr_len;
|
||||||
|
int want_dest_sockaddr_len;
|
||||||
|
} TEST_BASE;
|
||||||
|
|
||||||
|
/* test_smtpd_peer_init - enforce smtpd_peer_init() expectations */
|
||||||
|
|
||||||
|
static int test_smtpd_peer_init(const TEST_BASE *tp, VSTREAM *fp,
|
||||||
|
SMTPD_STATE *state)
|
||||||
|
{
|
||||||
|
VSTRING *msg_buf = vstring_alloc(100);
|
||||||
|
VSTREAM *memory_stream;
|
||||||
|
int test_passed = 1;
|
||||||
|
int aierr;
|
||||||
|
MAI_HOSTADDR_STR got_addr;
|
||||||
|
MAI_SERVPORT_STR got_port;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Detonate smtpd_state_init() in a little sandbox.
|
||||||
|
*/
|
||||||
|
VSTRING_RESET(msg_buf);
|
||||||
|
VSTRING_TERMINATE(msg_buf);
|
||||||
|
if ((memory_stream = vstream_memopen(msg_buf, O_WRONLY)) == 0)
|
||||||
|
msg_fatal("open memory stream: %m");
|
||||||
|
vstream_swap(VSTREAM_ERR, memory_stream);
|
||||||
|
smtpd_state_init(state, fp, "something");
|
||||||
|
vstream_swap(memory_stream, VSTREAM_ERR);
|
||||||
|
(void) vstream_fclose(memory_stream);
|
||||||
|
|
||||||
|
/* Verify the results. */
|
||||||
|
if (tp->want_hangup != (state->flags & SMTPD_FLAG_HANGUP)) {
|
||||||
|
msg_warn("got hangup flag '0x%x', want '0x%x'",
|
||||||
|
state->flags & SMTPD_FLAG_HANGUP, tp->want_hangup);
|
||||||
|
test_passed = 0;
|
||||||
|
} else if (tp->want_warning == 0 && VSTRING_LEN(msg_buf) > 0) {
|
||||||
|
msg_warn("got warning ``%s'', want ``null''", vstring_str(msg_buf));
|
||||||
|
test_passed = 0;
|
||||||
|
} else if (tp->want_warning != 0) {
|
||||||
|
if (strstr(vstring_str(msg_buf), tp->want_warning) == 0) {
|
||||||
|
msg_warn("got warning ``%s'', want ``%s''",
|
||||||
|
vstring_str(msg_buf), tp->want_warning);
|
||||||
|
test_passed = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (test_passed == 0) {
|
||||||
|
/* void */ ;
|
||||||
|
} else if (tp->want_client_name
|
||||||
|
&& strcmp(state->name, tp->want_client_name) != 0) {
|
||||||
|
msg_warn("got client name '%s', want '%s'",
|
||||||
|
state->name, tp->want_client_name);
|
||||||
|
test_passed = 0;
|
||||||
|
} else if (tp->want_client_name_status != 0
|
||||||
|
&& state->name_status != tp->want_client_name_status) {
|
||||||
|
msg_warn("got client name status '%d', want '%d'",
|
||||||
|
state->name_status, tp->want_client_name_status);
|
||||||
|
test_passed = 0;
|
||||||
|
} else if (tp->want_client_reverse_name
|
||||||
|
&& strcmp(state->reverse_name, tp->want_client_reverse_name) != 0) {
|
||||||
|
msg_warn("got client reverse name '%s', want '%s'",
|
||||||
|
state->reverse_name, tp->want_client_reverse_name);
|
||||||
|
test_passed = 0;
|
||||||
|
} else if (tp->want_client_reverse_name_status != 0
|
||||||
|
&& state->reverse_name_status != tp->want_client_reverse_name_status) {
|
||||||
|
msg_warn("got client reverse name status '%d', want '%d'",
|
||||||
|
state->reverse_name_status, tp->want_client_reverse_name_status);
|
||||||
|
test_passed = 0;
|
||||||
|
} else if (tp->want_client_addr
|
||||||
|
&& strcmp(state->addr, tp->want_client_addr) != 0) {
|
||||||
|
msg_warn("got text client address '%s', want '%s'",
|
||||||
|
state->addr, tp->want_client_addr);
|
||||||
|
test_passed = 0;
|
||||||
|
} if (tp->want_client_rfc_addr
|
||||||
|
&& strcmp(tp->want_client_rfc_addr, state->rfc_addr) != 0) {
|
||||||
|
msg_warn("got client rfc_addr '%s', want '%s'",
|
||||||
|
state->rfc_addr, tp->want_client_rfc_addr);
|
||||||
|
test_passed = 0;
|
||||||
|
} else if (tp->want_client_port
|
||||||
|
&& strcmp(state->port, tp->want_client_port) != 0) {
|
||||||
|
msg_warn("got text client port '%s', want '%s'",
|
||||||
|
state->port, tp->want_client_port);
|
||||||
|
test_passed = 0;
|
||||||
|
} else if (!state->sockaddr_len != !tp->want_sockaddr_len) {
|
||||||
|
msg_warn("got sockaddr_len '%d', want '%d'",
|
||||||
|
(int) state->sockaddr_len, (int) tp->want_sockaddr_len);
|
||||||
|
test_passed = 0;
|
||||||
|
} else if (tp->want_server_addr
|
||||||
|
&& strcmp(state->dest_addr, tp->want_server_addr) != 0) {
|
||||||
|
msg_warn("got text server address '%s', want '%s'",
|
||||||
|
state->dest_addr, tp->want_server_addr);
|
||||||
|
test_passed = 0;
|
||||||
|
} else if (tp->want_server_port
|
||||||
|
&& strcmp(state->dest_port, tp->want_server_port) != 0) {
|
||||||
|
msg_warn("got text server port '%s', want '%s'",
|
||||||
|
state->dest_port, tp->want_server_port);
|
||||||
|
test_passed = 0;
|
||||||
|
} else if (!state->dest_sockaddr_len != !tp->want_dest_sockaddr_len) {
|
||||||
|
msg_warn("got dest_sockaddr_len '%d', want '%d'",
|
||||||
|
(int) state->dest_sockaddr_len, (int) tp->want_dest_sockaddr_len);
|
||||||
|
test_passed = 0;
|
||||||
|
} else {
|
||||||
|
if (state->sockaddr_len > 0) {
|
||||||
|
if ((aierr =
|
||||||
|
sockaddr_to_hostaddr((struct sockaddr *) &state->sockaddr,
|
||||||
|
state->sockaddr_len, &got_addr, &got_port, 0)) != 0) {
|
||||||
|
msg_warn("sockaddr_to_hostaddr: %s", MAI_STRERROR(aierr));
|
||||||
|
test_passed = 0;
|
||||||
|
} else if (tp->want_client_addr
|
||||||
|
&& strcmp(got_addr.buf, tp->want_client_addr) != 0) {
|
||||||
|
msg_warn("got binary client address '%s', want '%s'",
|
||||||
|
got_addr.buf, tp->want_client_addr);
|
||||||
|
test_passed = 0;
|
||||||
|
} else if (tp->want_client_port
|
||||||
|
&& strcmp(got_port.buf, tp->want_client_port) != 0) {
|
||||||
|
msg_warn("got binary client port '%s', want '%s'",
|
||||||
|
got_port.buf, tp->want_client_port);
|
||||||
|
test_passed = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state->dest_sockaddr_len > 0) {
|
||||||
|
if ((aierr =
|
||||||
|
sockaddr_to_hostaddr((struct sockaddr *) &state->dest_sockaddr,
|
||||||
|
state->dest_sockaddr_len, &got_addr, &got_port, 0)) != 0) {
|
||||||
|
msg_warn("sockaddr_to_hostaddr: %s", MAI_STRERROR(aierr));
|
||||||
|
test_passed = 0;
|
||||||
|
} else if (tp->want_server_addr
|
||||||
|
&& strcmp(got_addr.buf, tp->want_server_addr) != 0) {
|
||||||
|
msg_warn("got binary server address '%s', want '%s'",
|
||||||
|
got_addr.buf, tp->want_server_addr);
|
||||||
|
test_passed = 0;
|
||||||
|
} else if (tp->want_server_port
|
||||||
|
&& strcmp(got_port.buf, tp->want_server_port) != 0) {
|
||||||
|
msg_warn("got binary server port '%s', want '%s'",
|
||||||
|
got_port.buf, tp->want_server_port);
|
||||||
|
test_passed = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(void) vstring_free(msg_buf);
|
||||||
|
smtpd_state_reset(state);
|
||||||
|
return (test_passed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tests that a non-socket client results in fake localhost info.
|
||||||
|
*/
|
||||||
|
typedef struct PEER_FROM_NON_SOCKET_CASE {
|
||||||
|
TEST_BASE base;
|
||||||
|
const char *inet_protocols;
|
||||||
|
} PEER_FROM_NON_SOCKET_CASE;
|
||||||
|
|
||||||
|
static const PEER_FROM_NON_SOCKET_CASE peer_from_non_socket_cases[] = {
|
||||||
|
{
|
||||||
|
.base = {
|
||||||
|
.label = "prefer_ipv4",
|
||||||
|
.want_client_name = "localhost",
|
||||||
|
.want_client_name_status = SMTPD_PEER_CODE_OK,
|
||||||
|
.want_client_addr = "127.0.0.1",
|
||||||
|
.want_client_addr_family = AF_UNSPEC,
|
||||||
|
.want_client_rfc_addr = "127.0.0.1",
|
||||||
|
.want_client_reverse_name_status = SMTPD_PEER_CODE_OK,
|
||||||
|
.want_client_port = "0",
|
||||||
|
.want_server_addr = "127.0.0.1",
|
||||||
|
.want_server_port = "0",
|
||||||
|
},
|
||||||
|
.inet_protocols = "ipv4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.base = {
|
||||||
|
.label = "prefer_ipv6",
|
||||||
|
.want_client_name = "localhost",
|
||||||
|
.want_client_name_status = SMTPD_PEER_CODE_OK,
|
||||||
|
.want_client_reverse_name = "localhost",
|
||||||
|
.want_client_reverse_name_status = SMTPD_PEER_CODE_OK,
|
||||||
|
.want_client_addr = "::1",
|
||||||
|
.want_client_addr_family = AF_UNSPEC,
|
||||||
|
.want_client_rfc_addr = "IPv6:::1",
|
||||||
|
.want_client_port = "0",
|
||||||
|
.want_server_addr = "::1",
|
||||||
|
.want_server_port = "0",
|
||||||
|
},
|
||||||
|
.inet_protocols = "ipv6",
|
||||||
|
},
|
||||||
|
{0},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void test_peer_from_non_socket(void)
|
||||||
|
{
|
||||||
|
int test_passed;
|
||||||
|
const PEER_FROM_NON_SOCKET_CASE *tp;
|
||||||
|
|
||||||
|
reset_global_variables();
|
||||||
|
|
||||||
|
for (tp = peer_from_non_socket_cases; tp->base.label != 0; tp++) {
|
||||||
|
msg_info("RUN test_peer_from_non_socket/%s", tp->base.label);
|
||||||
|
{
|
||||||
|
SMTPD_STATE state;
|
||||||
|
VSTREAM *fp;
|
||||||
|
int pair[2];
|
||||||
|
|
||||||
|
if (pipe(pair) < 0)
|
||||||
|
msg_fatal("pipe: %m");
|
||||||
|
if ((fp = vstream_fdopen(pair[0], O_RDONLY)) == 0)
|
||||||
|
msg_fatal("vstream_fdopen: %m");
|
||||||
|
(void) inet_proto_init("test_peer_from_non_socket",
|
||||||
|
tp->inet_protocols);
|
||||||
|
|
||||||
|
test_passed = test_smtpd_peer_init((TEST_BASE *) tp, fp, &state);
|
||||||
|
|
||||||
|
(void) vstream_fdclose(fp);
|
||||||
|
(void) close(pair[0]);
|
||||||
|
(void) close(pair[1]);
|
||||||
|
}
|
||||||
|
if (test_passed) {
|
||||||
|
msg_info("PASS test_peer_from_non_socket/%s", tp->base.label);
|
||||||
|
tests_passed += 1;
|
||||||
|
} else {
|
||||||
|
msg_info("FAIL test_peer_from_non_socket/%s", tp->base.label);
|
||||||
|
tests_failed += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tests that a non-connected socket results in 'unknown' endpoint info.
|
||||||
|
*/
|
||||||
|
typedef struct PEER_FROM_UNCONN_SOCKET_CASE {
|
||||||
|
TEST_BASE base;
|
||||||
|
int proto_family;
|
||||||
|
} PEER_FROM_UNCONN_SOCKET_CASE;
|
||||||
|
|
||||||
|
static const PEER_FROM_UNCONN_SOCKET_CASE peer_from_unconn_socket_cases[] = {
|
||||||
|
{
|
||||||
|
.base = {
|
||||||
|
.label = "tcp4",
|
||||||
|
.want_client_name = CLIENT_NAME_UNKNOWN,
|
||||||
|
.want_client_name_status = SMTPD_PEER_CODE_PERM,
|
||||||
|
.want_client_addr = CLIENT_ADDR_UNKNOWN,
|
||||||
|
.want_client_addr_family = AF_UNSPEC,
|
||||||
|
.want_client_rfc_addr = CLIENT_ADDR_UNKNOWN,
|
||||||
|
.want_client_reverse_name = CLIENT_NAME_UNKNOWN,
|
||||||
|
.want_client_reverse_name_status = SMTPD_PEER_CODE_PERM,
|
||||||
|
.want_client_port = CLIENT_PORT_UNKNOWN,
|
||||||
|
.want_server_addr = SERVER_ADDR_UNKNOWN,
|
||||||
|
.want_server_port = SERVER_PORT_UNKNOWN,
|
||||||
|
},
|
||||||
|
.proto_family = PF_INET,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.base = {
|
||||||
|
.label = "tcp6",
|
||||||
|
.want_client_name = CLIENT_NAME_UNKNOWN,
|
||||||
|
.want_client_name_status = SMTPD_PEER_CODE_PERM,
|
||||||
|
.want_client_addr = CLIENT_ADDR_UNKNOWN,
|
||||||
|
.want_client_addr_family = AF_UNSPEC,
|
||||||
|
.want_client_rfc_addr = CLIENT_ADDR_UNKNOWN,
|
||||||
|
.want_client_reverse_name = CLIENT_NAME_UNKNOWN,
|
||||||
|
.want_client_reverse_name_status = SMTPD_PEER_CODE_PERM,
|
||||||
|
.want_client_port = CLIENT_PORT_UNKNOWN,
|
||||||
|
.want_server_addr = SERVER_ADDR_UNKNOWN,
|
||||||
|
.want_server_port = SERVER_PORT_UNKNOWN,
|
||||||
|
},
|
||||||
|
.proto_family = PF_INET6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.base = {
|
||||||
|
.label = "unix",
|
||||||
|
.want_client_name = CLIENT_NAME_UNKNOWN,
|
||||||
|
.want_client_name_status = SMTPD_PEER_CODE_PERM,
|
||||||
|
.want_client_addr = CLIENT_ADDR_UNKNOWN,
|
||||||
|
.want_client_addr_family = AF_UNSPEC,
|
||||||
|
.want_client_rfc_addr = CLIENT_ADDR_UNKNOWN,
|
||||||
|
.want_client_reverse_name = CLIENT_NAME_UNKNOWN,
|
||||||
|
.want_client_reverse_name_status = SMTPD_PEER_CODE_PERM,
|
||||||
|
.want_client_port = CLIENT_PORT_UNKNOWN,
|
||||||
|
.want_server_addr = SERVER_ADDR_UNKNOWN,
|
||||||
|
.want_server_port = SERVER_PORT_UNKNOWN,
|
||||||
|
},
|
||||||
|
.proto_family = PF_UNIX,
|
||||||
|
},
|
||||||
|
{0},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void test_peer_from_unconn_socket(void)
|
||||||
|
{
|
||||||
|
int test_passed;
|
||||||
|
const PEER_FROM_UNCONN_SOCKET_CASE *tp;
|
||||||
|
|
||||||
|
reset_global_variables();
|
||||||
|
|
||||||
|
for (tp = peer_from_unconn_socket_cases; tp->base.label != 0; tp++) {
|
||||||
|
msg_info("RUN test_peer_from_unconn_socket/%s", tp->base.label);
|
||||||
|
{
|
||||||
|
SMTPD_STATE state;
|
||||||
|
VSTREAM *fp;
|
||||||
|
int sock;
|
||||||
|
|
||||||
|
if ((sock = socket(tp->proto_family, SOCK_STREAM, 0)) < 0)
|
||||||
|
msg_fatal("socketpair: %m");
|
||||||
|
if ((fp = vstream_fdopen(sock, O_RDONLY)) == 0)
|
||||||
|
msg_fatal("vstream_fdopen: %m");
|
||||||
|
|
||||||
|
test_passed = test_smtpd_peer_init((TEST_BASE *) tp, fp, &state);
|
||||||
|
|
||||||
|
(void) vstream_fclose(fp);
|
||||||
|
}
|
||||||
|
if (test_passed) {
|
||||||
|
msg_info("PASS test_peer_from_unconn_socket/%s", tp->base.label);
|
||||||
|
tests_passed += 1;
|
||||||
|
} else {
|
||||||
|
msg_info("FAIL test_peer_from_unconn_socket/%s", tp->base.label);
|
||||||
|
tests_failed += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tests that smtpd_peer_from_pass_attr() updates the SMTPD_STATE structure
|
||||||
|
* with the expected error or endpoint information.
|
||||||
|
*/
|
||||||
|
#define PASS_ATTR_COUNT 5
|
||||||
|
struct PASS_ATTR {
|
||||||
|
const char *key;
|
||||||
|
const char *value;
|
||||||
|
};
|
||||||
|
typedef struct PEER_FROM_PASS_ATTR_CASE {
|
||||||
|
const TEST_BASE base; /* parent class */
|
||||||
|
struct PASS_ATTR attrs[PASS_ATTR_COUNT];
|
||||||
|
} PEER_FROM_PASS_ATTR_CASE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need one test for every constraint in smtpd_peer_from_pass(), to
|
||||||
|
* demonstrate that smtpd_pass_attr.c propagates errors and endpoint info.
|
||||||
|
*/
|
||||||
|
static const PEER_FROM_PASS_ATTR_CASE peer_from_pass_attr_cases[] = {
|
||||||
|
{
|
||||||
|
.base = {
|
||||||
|
.label = "propagates_endpoint_info_from_good_pass_attr",
|
||||||
|
.want_client_addr = "1.2.3.4",
|
||||||
|
.want_client_port = "123",
|
||||||
|
.want_client_addr_family = AF_INET,
|
||||||
|
.want_server_addr = "4.3.2.1",
|
||||||
|
.want_server_port = "321",
|
||||||
|
.want_sockaddr_len = 1,
|
||||||
|
.want_dest_sockaddr_len = 1,
|
||||||
|
},
|
||||||
|
.attrs = {
|
||||||
|
MAIL_ATTR_ACT_CLIENT_ADDR, "1.2.3.4",
|
||||||
|
MAIL_ATTR_ACT_CLIENT_PORT, "123",
|
||||||
|
MAIL_ATTR_ACT_SERVER_ADDR, "4.3.2.1",
|
||||||
|
MAIL_ATTR_ACT_SERVER_PORT, "321",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.base = {
|
||||||
|
.label = "propagates_error_from_bad_IPv4_client_addr",
|
||||||
|
.want_hangup = 1,
|
||||||
|
.want_warning = "bad IPv4 client address",
|
||||||
|
/* TODO(wietse) Should we verify the surrogate endpoint info? */
|
||||||
|
},
|
||||||
|
.attrs = {
|
||||||
|
MAIL_ATTR_ACT_CLIENT_ADDR, "1.1.2.3.4",
|
||||||
|
MAIL_ATTR_ACT_CLIENT_PORT, "123",
|
||||||
|
MAIL_ATTR_ACT_SERVER_ADDR, "4.3.2.1",
|
||||||
|
MAIL_ATTR_ACT_SERVER_PORT, "321",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.base = {
|
||||||
|
.label = "propagates_error_from_missing_client_addr",
|
||||||
|
.want_hangup = 1,
|
||||||
|
.want_warning = "missing client address",
|
||||||
|
},
|
||||||
|
.attrs = {
|
||||||
|
MAIL_ATTR_ACT_CLIENT_PORT, "123",
|
||||||
|
MAIL_ATTR_ACT_SERVER_ADDR, "4.3.2.1",
|
||||||
|
MAIL_ATTR_ACT_SERVER_PORT, "321",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.base = {
|
||||||
|
.label = "propagates_error_from_bad_TCP_client_port",
|
||||||
|
.want_hangup = 1,
|
||||||
|
.want_warning = "bad TCP client port",
|
||||||
|
},
|
||||||
|
.attrs = {
|
||||||
|
MAIL_ATTR_ACT_CLIENT_ADDR, "1.2.3.4",
|
||||||
|
MAIL_ATTR_ACT_CLIENT_PORT, "A23",
|
||||||
|
MAIL_ATTR_ACT_SERVER_ADDR, "4.3.2.1",
|
||||||
|
MAIL_ATTR_ACT_SERVER_PORT, "321",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.base = {
|
||||||
|
.label = "propagates_error_from_missing_client_port",
|
||||||
|
.want_hangup = 1,
|
||||||
|
.want_warning = "missing client port",
|
||||||
|
},
|
||||||
|
.attrs = {
|
||||||
|
MAIL_ATTR_ACT_CLIENT_ADDR, "1.2.3.4",
|
||||||
|
MAIL_ATTR_ACT_SERVER_ADDR, "4.3.2.1",
|
||||||
|
MAIL_ATTR_ACT_SERVER_PORT, "321",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.base = {
|
||||||
|
.label = "propagates_error_from_bad_IPv6_server_addr",
|
||||||
|
.want_hangup = 1,
|
||||||
|
.want_warning = "bad IPv6 server address",
|
||||||
|
},
|
||||||
|
.attrs = {
|
||||||
|
MAIL_ATTR_ACT_CLIENT_ADDR, "1.2.3.4",
|
||||||
|
MAIL_ATTR_ACT_CLIENT_PORT, "123",
|
||||||
|
MAIL_ATTR_ACT_SERVER_ADDR, ":::4.3.2.1",
|
||||||
|
MAIL_ATTR_ACT_SERVER_PORT, "321",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.base = {
|
||||||
|
.label = "propagates_error_from_missing_server_addr",
|
||||||
|
.want_hangup = 1,
|
||||||
|
.want_warning = "missing server address",
|
||||||
|
},
|
||||||
|
.attrs = {
|
||||||
|
MAIL_ATTR_ACT_CLIENT_ADDR, "1.2.3.4",
|
||||||
|
MAIL_ATTR_ACT_CLIENT_PORT, "123",
|
||||||
|
MAIL_ATTR_ACT_SERVER_PORT, "321",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.base = {
|
||||||
|
.label = "propagates_error_from_bad_TCP_server_port",
|
||||||
|
.want_hangup = 1,
|
||||||
|
.want_warning = "bad TCP server port",
|
||||||
|
},
|
||||||
|
.attrs = {
|
||||||
|
MAIL_ATTR_ACT_CLIENT_ADDR, "1.2.3.4",
|
||||||
|
MAIL_ATTR_ACT_CLIENT_PORT, "123",
|
||||||
|
MAIL_ATTR_ACT_SERVER_ADDR, "4.3.2.1",
|
||||||
|
MAIL_ATTR_ACT_SERVER_PORT, "A21",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.base = {
|
||||||
|
.label = "propagates_error_from_missing_server_port",
|
||||||
|
.want_hangup = 1,
|
||||||
|
.want_warning = "missing server port",
|
||||||
|
},
|
||||||
|
.attrs = {
|
||||||
|
MAIL_ATTR_ACT_CLIENT_ADDR, "1.2.3.4",
|
||||||
|
MAIL_ATTR_ACT_CLIENT_PORT, "123",
|
||||||
|
MAIL_ATTR_ACT_SERVER_ADDR, "4.3.2.1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void test_peer_from_pass_attr(void)
|
||||||
|
{
|
||||||
|
int test_passed;
|
||||||
|
const PEER_FROM_PASS_ATTR_CASE *tp;
|
||||||
|
|
||||||
|
reset_global_variables();
|
||||||
|
|
||||||
|
for (tp = peer_from_pass_attr_cases; tp->base.label != 0; tp++) {
|
||||||
|
msg_info("RUN test_peer_from_pass_attr/%s", tp->base.label);
|
||||||
|
{
|
||||||
|
SMTPD_STATE state;
|
||||||
|
VSTREAM *fp;
|
||||||
|
int sock;
|
||||||
|
HTABLE *attr_table = htable_create(PASS_ATTR_COUNT);
|
||||||
|
const struct PASS_ATTR *p;
|
||||||
|
|
||||||
|
if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
|
||||||
|
msg_fatal("socket: %m");
|
||||||
|
if ((fp = vstream_fdopen(sock, O_RDWR)) == 0)
|
||||||
|
msg_fatal("vstream_fdopen: %m");
|
||||||
|
for (p = tp->attrs; p < tp->attrs + PASS_ATTR_COUNT && p->key != 0; p++)
|
||||||
|
htable_enter(attr_table, p->key, (void *) p->value);
|
||||||
|
vstream_control(fp,
|
||||||
|
VSTREAM_CTL_CONTEXT, (void *) attr_table,
|
||||||
|
VSTREAM_CTL_END);
|
||||||
|
|
||||||
|
test_passed = test_smtpd_peer_init((TEST_BASE *) tp, fp, &state);
|
||||||
|
|
||||||
|
htable_free(attr_table, (void (*) (void *)) 0);
|
||||||
|
(void) vstream_fclose(fp);
|
||||||
|
}
|
||||||
|
if (test_passed) {
|
||||||
|
msg_info("PASS test_peer_from_pass_attr/%s", tp->base.label);
|
||||||
|
tests_passed += 1;
|
||||||
|
} else {
|
||||||
|
msg_info("FAIL test_peer_from_pass_attr/%s", tp->base.label);
|
||||||
|
tests_failed += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tests that smtpd_peer_from_haproxy() updates the SMTPD_STATE structure
|
||||||
|
* with the expected error or endpoint information.
|
||||||
|
*/
|
||||||
|
typedef struct PEER_FROM_HAPROXY_CASE {
|
||||||
|
const TEST_BASE base;
|
||||||
|
const char *proxy_header;
|
||||||
|
} PEER_FROM_HAPROXY_CASE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need only two tests to show that smtpd_haproxy.c propagates errors and
|
||||||
|
* non-error endpoint info. We don't need to duplicate each individual test in
|
||||||
|
* haproxy_srvr_test.c for different IP protocols, HaProxy protocol
|
||||||
|
* versions, and error modes.
|
||||||
|
*/
|
||||||
|
static const PEER_FROM_HAPROXY_CASE peer_from_haproxy_caes[] = {
|
||||||
|
{
|
||||||
|
.base = {
|
||||||
|
.label = "propagates_endpoint_info_from_good_proxy_header",
|
||||||
|
.want_client_addr = "1.2.3.4",
|
||||||
|
.want_client_port = "123",
|
||||||
|
.want_client_addr_family = AF_INET,
|
||||||
|
.want_server_addr = "4.3.2.1",
|
||||||
|
.want_server_port = "321",
|
||||||
|
.want_sockaddr_len = 1,
|
||||||
|
.want_dest_sockaddr_len = 1,
|
||||||
|
},
|
||||||
|
.proxy_header = "PROXY TCP4 1.2.3.4 4.3.2.1 123 321\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.base = {
|
||||||
|
.label = "propagates_error_from_bad_proxy_header",
|
||||||
|
.want_hangup = 1,
|
||||||
|
.want_warning = "short protocol header",
|
||||||
|
/* TODO(wietse) Should we verify the surrogate endpoint info? */
|
||||||
|
},
|
||||||
|
.proxy_header = "bad",
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void test_peer_from_haproxy(void)
|
||||||
|
{
|
||||||
|
int test_passed;
|
||||||
|
const PEER_FROM_HAPROXY_CASE *tp;
|
||||||
|
|
||||||
|
reset_global_variables();
|
||||||
|
var_smtpd_uproxy_proto = HAPROXY_PROTO_NAME;
|
||||||
|
|
||||||
|
for (tp = peer_from_haproxy_caes; tp->proxy_header != 0; tp++) {
|
||||||
|
msg_info("RUN test_peer_from_haproxy/%s", tp->base.label);
|
||||||
|
{
|
||||||
|
SMTPD_STATE state;
|
||||||
|
VSTREAM *fp;
|
||||||
|
int pair[2];
|
||||||
|
|
||||||
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) < 0)
|
||||||
|
msg_fatal("socketpair: %m");
|
||||||
|
if (write_buf(pair[1], tp->proxy_header, strlen(tp->proxy_header),
|
||||||
|
TEST_TIMEOUT) < 0)
|
||||||
|
msg_fatal("write_buf: %m");
|
||||||
|
if ((fp = vstream_fdopen(pair[0], O_RDONLY)) == 0)
|
||||||
|
msg_fatal("vstream_fdopen: %m");
|
||||||
|
|
||||||
|
test_passed = test_smtpd_peer_init((TEST_BASE *) tp, fp, &state);
|
||||||
|
|
||||||
|
(void) vstream_fdclose(fp);
|
||||||
|
(void) close(pair[0]);
|
||||||
|
(void) close(pair[1]);
|
||||||
|
}
|
||||||
|
if (test_passed) {
|
||||||
|
msg_info("PASS test_peer_from_haproxy/%s", tp->base.label);
|
||||||
|
tests_passed += 1;
|
||||||
|
} else {
|
||||||
|
msg_info("FAIL test_peer_from_haproxy/%s", tp->base.label);
|
||||||
|
tests_failed += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR);
|
||||||
|
|
||||||
|
test_peer_from_non_socket();
|
||||||
|
|
||||||
|
test_peer_from_unconn_socket();
|
||||||
|
|
||||||
|
test_peer_from_pass_attr();
|
||||||
|
|
||||||
|
test_peer_from_haproxy();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO(wietse) tests for a connected socket. This will require mock
|
||||||
|
* get_peername/get/sockname() and getnameinfo/getaddrinfo()
|
||||||
|
* infrastructure.
|
||||||
|
*/
|
||||||
|
|
||||||
|
msg_info("PASS=%d FAIL=%d", tests_passed, tests_failed);
|
||||||
|
exit(tests_failed != 0);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user