2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-22 09:57:34 +00:00

postfix-3.11-20250409

This commit is contained in:
Wietse Z Venema 2025-04-09 00:00:00 -05:00 committed by Viktor Dukhovni
parent ba7dbdd88a
commit 1b9f8ac2fb
26 changed files with 1551 additions and 610 deletions

View File

@ -29044,3 +29044,40 @@ Apologies for any names omitted.
quotes. Oscar Bataille reported that the non-recommended
form is not protected against SQL injection. Files:
global/dict_sqlite.c, global/dict_sqlite_test.c.
20250326
Updated myaddrinfo tests to also cover service/port conversion.
Files: util/myaddrinfo4.ref, util/myaddrinfo4.ref2,
util/myaddrinfo.c, util/myaddrinfo.ref, util/myaddrinfo.ref2.
20260402
Documentation: updated guidance for using DNS-based reputation
services. File: proto/postconf.proto.
20250404
Code health: simplified the conversions from IPv4 mapped
IPv6 addresses (::ffff:d.d.d.d) to their IPv4 form (d.d.d.d),
for both the binary form and human-readable form. Added
unit tests to show that the conversions work as expected.
Files: util/normalize_v4mapped_addr.[hc],
util/normalize_v4mapped_addr_test.c.
20260406
Code health: overhauled the haproxy adapter to simplify
code and to avoid unnecessary conversions between binary
and human-readable forms. Added more unit tests for the v1
and v2 proxy protocols. A separate update will overhaul
the smtpd 'peer' lookups. Files: global/haproxy_srvr.c,
global/haproxy_srvr_test.c.
20250408
Code health: replace explicit code with normalize_v4mapped_xxx()
call. File: util/sane_sockaddr_to_hostaddr.c.
Bit rot: sane_sockaddr_to_hostaddr() may modify its inputs.
smtp/smtp_tlsrpt.c, postscreen/postscreen_endpt.c

View File

@ -8878,6 +8878,10 @@ clients,
and <a href="postscreen.8.html">postscreen(8)</a> will update an SMTP client's DNSBL score with
each non-error reply as described below. </p>
<p> NOTE: Always respect the usage policies of reputation services.
Avoid public or ISP resolvers, unless the queries use your unique
API key. </p>
<p> Caution: when postscreen rejects mail, its SMTP response contains
the DNSBL
domain name. Use the <a href="postconf.5.html#postscreen_dnsbl_reply_map">postscreen_dnsbl_reply_map</a> feature to hide
@ -15553,7 +15557,12 @@ The <a href="postconf.5.html#maps_rbl_reject_code">maps_rbl_reject_code</a> para
rejected requests (default: 554), the <a href="postconf.5.html#default_rbl_reply">default_rbl_reply</a> parameter
specifies the default server reply, and the <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> parameter
specifies tables with server replies indexed by <i>rbl_domain</i>.
This feature is available in Postfix 2.0 and later. </dd>
<br>
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> for how to avoid leaking the key in SMTP server
responses). <br>
This feature is available in Postfix 2.0 and later. </dd>
<dt><b><a name="permit_dnswl_client">permit_dnswl_client <i>dnswl_domain=d.d.d.d</i></a></b></dt>
@ -15565,8 +15574,12 @@ If no "<i>=d.d.d.d</i>" is specified, accept the request when the
reversed client network address is listed with any A record under
<i>dnswl_domain</i>. <br> For safety, <a href="postconf.5.html#permit_dnswl_client">permit_dnswl_client</a> is silently
ignored when it would override <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a>. The
result is DEFER_IF_REJECT when allowlist lookup fails. This feature
is available in Postfix 2.8 and later. </dd>
result is DEFER_IF_REJECT when allowlist lookup fails. <br>
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> for how to avoid leaking an API key in SMTP
server responses). <br>
This feature is available in Postfix 2.8 and later. </dd>
<dt><b><a name="reject_rhsbl_client">reject_rhsbl_client <i>rbl_domain=d.d.d.d</i></a></b></dt>
@ -15579,9 +15592,14 @@ number..number ranges (Postfix version 2.8 and later). If no
hostname is listed with
any A record under <i>rbl_domain</i>. See the <a href="postconf.5.html#reject_rbl_client">reject_rbl_client</a>
description above for additional RBL related configuration parameters.
<br>
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> for how to avoid leaking an API key in SMTP
server responses). <br>
This feature is available in Postfix 2.0 and later; with Postfix
version 2.8 and later, <a href="postconf.5.html#reject_rhsbl_reverse_client">reject_rhsbl_reverse_client</a> will usually
produce better results. </dd>
produce better results. </dd>
<dt><b><a name="permit_rhswl_client">permit_rhswl_client <i>rhswl_domain=d.d.d.d</i></a></b></dt>
@ -15597,8 +15615,12 @@ allowlisting should be used only to reduce false positives in e.g.
DNS-based blocklists, and not for making access rule exceptions.
<br> For safety, <a href="postconf.5.html#permit_rhswl_client">permit_rhswl_client</a> is silently ignored when it
would override <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a>. The result is DEFER_IF_REJECT
when allowlist lookup fails. This feature is available in Postfix
2.8 and later. </dd>
when allowlist lookup fails. <br>
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> for how to avoid leaking an API key in SMTP
server responses). <br>
This feature is available in Postfix 2.8 and later.</dd>
<dt><b><a name="reject_rhsbl_reverse_client">reject_rhsbl_reverse_client <i>rbl_domain=d.d.d.d</i></a></b></dt>
@ -15609,8 +15631,12 @@ one or more ";"-separated numbers or number..number ranges.
If no "<i>=d.d.d.d</i>" is specified, reject the request when the
unverified reverse client hostname is listed with any A record under
<i>rbl_domain</i>. See the <a href="postconf.5.html#reject_rbl_client">reject_rbl_client</a> description above for
additional RBL related configuration parameters. This feature is
available in Postfix 2.8 and later. </dd>
additional RBL related configuration parameters. <br>
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> for how to avoid leaking an API key in SMTP
server responses). <br>
This feature is available in Postfix 2.8 and later. </dd>
<dt><b><a name="reject_unknown_client_hostname">reject_unknown_client_hostname</a></b> (with Postfix &lt; 2.3: reject_unknown_client)</dt>
@ -16512,8 +16538,12 @@ listed with any A record under <i>rbl_domain</i>. See the
parameters. Note: specify "<a href="postconf.5.html#smtpd_helo_required">smtpd_helo_required</a> = yes" to fully
enforce this restriction (without "<a href="postconf.5.html#smtpd_helo_required">smtpd_helo_required</a> = yes", a
client can simply skip <a href="postconf.5.html#reject_rhsbl_helo">reject_rhsbl_helo</a> by not sending HELO or
EHLO). This feature is available in Postfix 2.0
and later. </dd>
EHLO). <br>
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> for how to avoid leaking an API key in SMTP
server responses). <br>
This feature is available in Postfix 2.0 and later. </dd>
<dt><b><a name="reject_unknown_helo_hostname">reject_unknown_helo_hostname</a></b> (with Postfix &lt; 2.3: reject_unknown_hostname)</dt>
@ -17231,8 +17261,12 @@ any A record under <i>rbl_domain</i>. <br> The <a href="postconf.5.html#maps_rbl
parameter specifies the response code for rejected requests (default:
554); the <a href="postconf.5.html#default_rbl_reply">default_rbl_reply</a> parameter specifies the default server
reply; and the <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> parameter specifies tables with server
replies indexed by <i>rbl_domain</i>. This feature is available
in Postfix version 2.0 and later.</dd>
replies indexed by <i>rbl_domain</i>. <br>
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> for how to avoid leaking an API key in SMTP
server responses). <br>
This feature is available in Postfix version 2.0 and later.</dd>
<dt><b><a name="reject_unauth_destination">reject_unauth_destination</a></b></dt>
@ -18082,6 +18116,11 @@ listed with any A record under <i>rbl_domain</i>. <br> The
rejected requests (default: 554); the <a href="postconf.5.html#default_rbl_reply">default_rbl_reply</a> parameter
specifies the default server reply; and the <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> parameter
specifies tables with server replies indexed by <i>rbl_domain</i>.
<br>
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> for how to avoid leaking an API key in SMTP
server responses). <br>
This feature is available in Postfix 2.0 and later.</dd>
<dt><b><a name="reject_sender_login_mismatch">reject_sender_login_mismatch</a></b></dt>

View File

@ -5505,6 +5505,10 @@ clients,
and \fBpostscreen\fR(8) will update an SMTP client's DNSBL score with
each non\-error reply as described below.
.PP
NOTE: Always respect the usage policies of reputation services.
Avoid public or ISP resolvers, unless the queries use your unique
API key.
.PP
Caution: when postscreen rejects mail, its SMTP response contains
the DNSBL
domain name. Use the postscreen_dnsbl_reply_map feature to hide
@ -10381,6 +10385,12 @@ The maps_rbl_reject_code parameter specifies the response code for
rejected requests (default: 554), the default_rbl_reply parameter
specifies the default server reply, and the rbl_reply_maps parameter
specifies tables with server replies indexed by \fIrbl_domain\fR.
.br
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see rbl_reply_maps for how to avoid leaking the key in SMTP server
responses).
.br
This feature is available in Postfix 2.0 and later.
.br
.IP "\fBpermit_dnswl_client \fIdnswl_domain=d.d.d.d\fR\fR"
@ -10394,8 +10404,14 @@ reversed client network address is listed with any A record under
.br
For safety, permit_dnswl_client is silently
ignored when it would override reject_unauth_destination. The
result is DEFER_IF_REJECT when allowlist lookup fails. This feature
is available in Postfix 2.8 and later.
result is DEFER_IF_REJECT when allowlist lookup fails.
.br
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
server responses).
.br
This feature is available in Postfix 2.8 and later.
.br
.IP "\fBreject_rhsbl_client \fIrbl_domain=d.d.d.d\fR\fR"
Reject the request when the client hostname is listed with the
@ -10407,6 +10423,12 @@ number..number ranges (Postfix version 2.8 and later). If no
hostname is listed with
any A record under \fIrbl_domain\fR. See the reject_rbl_client
description above for additional RBL related configuration parameters.
.br
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
server responses).
.br
This feature is available in Postfix 2.0 and later; with Postfix
version 2.8 and later, reject_rhsbl_reverse_client will usually
produce better results.
@ -10426,8 +10448,14 @@ DNS\-based blocklists, and not for making access rule exceptions.
.br
For safety, permit_rhswl_client is silently ignored when it
would override reject_unauth_destination. The result is DEFER_IF_REJECT
when allowlist lookup fails. This feature is available in Postfix
2.8 and later.
when allowlist lookup fails.
.br
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
server responses).
.br
This feature is available in Postfix 2.8 and later.
.br
.IP "\fBreject_rhsbl_reverse_client \fIrbl_domain=d.d.d.d\fR\fR"
Reject the request when the unverified reverse client hostname
@ -10437,8 +10465,14 @@ one or more ";"\-separated numbers or number..number ranges.
If no "\fI=d.d.d.d\fR" is specified, reject the request when the
unverified reverse client hostname is listed with any A record under
\fIrbl_domain\fR. See the reject_rbl_client description above for
additional RBL related configuration parameters. This feature is
available in Postfix 2.8 and later.
additional RBL related configuration parameters.
.br
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
server responses).
.br
This feature is available in Postfix 2.8 and later.
.br
.IP "\fBreject_unknown_client_hostname\fR (with Postfix < 2.3: reject_unknown_client)"
Reject the request when 1) the client IP address\->name mapping
@ -11155,8 +11189,14 @@ reject_rbl_client description for additional RBL related configuration
parameters. Note: specify "smtpd_helo_required = yes" to fully
enforce this restriction (without "smtpd_helo_required = yes", a
client can simply skip reject_rhsbl_helo by not sending HELO or
EHLO). This feature is available in Postfix 2.0
and later.
EHLO).
.br
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
server responses).
.br
This feature is available in Postfix 2.0 and later.
.br
.IP "\fBreject_unknown_helo_hostname\fR (with Postfix < 2.3: reject_unknown_hostname)"
Reject the request when the HELO or EHLO hostname has no DNS A
@ -11653,8 +11693,14 @@ The maps_rbl_reject_code
parameter specifies the response code for rejected requests (default:
554); the default_rbl_reply parameter specifies the default server
reply; and the rbl_reply_maps parameter specifies tables with server
replies indexed by \fIrbl_domain\fR. This feature is available
in Postfix version 2.0 and later.
replies indexed by \fIrbl_domain\fR.
.br
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
server responses).
.br
This feature is available in Postfix version 2.0 and later.
.br
.IP "\fBreject_unauth_destination\fR"
Reject the request unless one of the following is true:
@ -12298,6 +12344,12 @@ maps_rbl_reject_code parameter specifies the response code for
rejected requests (default: 554); the default_rbl_reply parameter
specifies the default server reply; and the rbl_reply_maps parameter
specifies tables with server replies indexed by \fIrbl_domain\fR.
.br
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
server responses).
.br
This feature is available in Postfix 2.0 and later.
.br
.IP "\fBreject_sender_login_mismatch\fR"

View File

@ -5471,7 +5471,12 @@ The maps_rbl_reject_code parameter specifies the response code for
rejected requests (default: 554), the default_rbl_reply parameter
specifies the default server reply, and the rbl_reply_maps parameter
specifies tables with server replies indexed by <i>rbl_domain</i>.
This feature is available in Postfix 2.0 and later. </dd>
<br>
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see rbl_reply_maps for how to avoid leaking the key in SMTP server
responses). <br>
This feature is available in Postfix 2.0 and later. </dd>
<dt><b><a name="permit_dnswl_client">permit_dnswl_client <i>dnswl_domain=d.d.d.d</i></a></b></dt>
@ -5483,8 +5488,12 @@ If no "<i>=d.d.d.d</i>" is specified, accept the request when the
reversed client network address is listed with any A record under
<i>dnswl_domain</i>. <br> For safety, permit_dnswl_client is silently
ignored when it would override reject_unauth_destination. The
result is DEFER_IF_REJECT when allowlist lookup fails. This feature
is available in Postfix 2.8 and later. </dd>
result is DEFER_IF_REJECT when allowlist lookup fails. <br>
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
server responses). <br>
This feature is available in Postfix 2.8 and later. </dd>
<dt><b><a name="reject_rhsbl_client">reject_rhsbl_client <i>rbl_domain=d.d.d.d</i></a></b></dt>
@ -5497,9 +5506,14 @@ number..number ranges (Postfix version 2.8 and later). If no
hostname is listed with
any A record under <i>rbl_domain</i>. See the reject_rbl_client
description above for additional RBL related configuration parameters.
<br>
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
server responses). <br>
This feature is available in Postfix 2.0 and later; with Postfix
version 2.8 and later, reject_rhsbl_reverse_client will usually
produce better results. </dd>
produce better results. </dd>
<dt><b><a name="permit_rhswl_client">permit_rhswl_client <i>rhswl_domain=d.d.d.d</i></a></b></dt>
@ -5515,8 +5529,12 @@ allowlisting should be used only to reduce false positives in e.g.
DNS-based blocklists, and not for making access rule exceptions.
<br> For safety, permit_rhswl_client is silently ignored when it
would override reject_unauth_destination. The result is DEFER_IF_REJECT
when allowlist lookup fails. This feature is available in Postfix
2.8 and later. </dd>
when allowlist lookup fails. <br>
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
server responses). <br>
This feature is available in Postfix 2.8 and later.</dd>
<dt><b><a name="reject_rhsbl_reverse_client">reject_rhsbl_reverse_client <i>rbl_domain=d.d.d.d</i></a></b></dt>
@ -5527,8 +5545,12 @@ one or more ";"-separated numbers or number..number ranges.
If no "<i>=d.d.d.d</i>" is specified, reject the request when the
unverified reverse client hostname is listed with any A record under
<i>rbl_domain</i>. See the reject_rbl_client description above for
additional RBL related configuration parameters. This feature is
available in Postfix 2.8 and later. </dd>
additional RBL related configuration parameters. <br>
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
server responses). <br>
This feature is available in Postfix 2.8 and later. </dd>
<dt><b><a name="reject_unknown_client_hostname">reject_unknown_client_hostname</a></b> (with Postfix &lt; 2.3: reject_unknown_client)</dt>
@ -6034,8 +6056,12 @@ reject_rbl_client description for additional RBL related configuration
parameters. Note: specify "smtpd_helo_required = yes" to fully
enforce this restriction (without "smtpd_helo_required = yes", a
client can simply skip reject_rhsbl_helo by not sending HELO or
EHLO). This feature is available in Postfix 2.0
and later. </dd>
EHLO). <br>
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
server responses). <br>
This feature is available in Postfix 2.0 and later. </dd>
<dt><b><a name="reject_unknown_helo_hostname">reject_unknown_helo_hostname</a></b> (with Postfix &lt; 2.3: reject_unknown_hostname)</dt>
@ -6343,8 +6369,12 @@ any A record under <i>rbl_domain</i>. <br> The maps_rbl_reject_code
parameter specifies the response code for rejected requests (default:
554); the default_rbl_reply parameter specifies the default server
reply; and the rbl_reply_maps parameter specifies tables with server
replies indexed by <i>rbl_domain</i>. This feature is available
in Postfix version 2.0 and later.</dd>
replies indexed by <i>rbl_domain</i>. <br>
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
server responses). <br>
This feature is available in Postfix version 2.0 and later.</dd>
<dt><b><a name="reject_unauth_destination">reject_unauth_destination</a></b></dt>
@ -6861,6 +6891,11 @@ maps_rbl_reject_code parameter specifies the response code for
rejected requests (default: 554); the default_rbl_reply parameter
specifies the default server reply; and the rbl_reply_maps parameter
specifies tables with server replies indexed by <i>rbl_domain</i>.
<br>
NOTE: Always respect the usage policies of reputation services. Avoid
public or ISP resolvers, unless the queries use your unique API key
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
server responses). <br>
This feature is available in Postfix 2.0 and later.</dd>
<dt><b><a name="reject_sender_login_mismatch">reject_sender_login_mismatch</a></b></dt>
@ -14699,6 +14734,10 @@ clients,
and postscreen(8) will update an SMTP client's DNSBL score with
each non-error reply as described below. </p>
<p> NOTE: Always respect the usage policies of reputation services.
Avoid public or ISP resolvers, unless the queries use your unique
API key. </p>
<p> Caution: when postscreen rejects mail, its SMTP response contains
the DNSBL
domain name. Use the postscreen_dnsbl_reply_map feature to hide

View File

@ -1672,3 +1672,4 @@ URIs
bugfix
MLKEM
cleartext
redacted

View File

@ -1858,3 +1858,5 @@ TINYCDB
getdata
XXXSENDOPTS
xtra
HAPROXY
SRVR

View File

@ -129,7 +129,7 @@ TESTPROG= domain_list dot_lockfile mail_addr_crunch mail_addr_find \
data_redirect addr_match_list safe_ultostr verify_sender_addr \
mail_version mail_dict server_acl uxtext mail_parm_split \
fold_addr smtp_reply_footer mail_addr_map normalize_mailhost_addr \
haproxy_srvr map_search delivered_hdr login_sender_match \
haproxy_srvr_test map_search delivered_hdr login_sender_match \
compat_level config_known_tcp_ports hfrom_format rfc2047_code \
ascii_header_text sendopts_test dict_sqlite_test
@ -381,7 +381,7 @@ smtp_reply_footer: smtp_reply_footer.c $(LIB) $(LIBS)
normalize_mailhost_addr: normalize_mailhost_addr.c $(LIB) $(LIBS)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
haproxy_srvr: haproxy_srvr.c $(LIB) $(LIBS)
haproxy_srvr_test: haproxy_srvr_test.c $(LIB) $(LIBS)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
map_search: map_search.c $(LIB) $(LIBS)
@ -422,7 +422,7 @@ tests: tok822_test mime_tests strip_addr_test tok822_limit_test \
safe_ultostr_test mail_parm_split_test fold_addr_test \
smtp_reply_footer_test off_cvt_test mail_addr_crunch_test \
mail_addr_find_test mail_addr_map_test quote_822_local_test \
normalize_mailhost_addr_test haproxy_srvr_test map_search_test \
normalize_mailhost_addr_test test_haproxy_srvr map_search_test \
delivered_hdr_test login_sender_match_test compat_level_test \
config_known_tcp_ports_test hfrom_format_test rfc2047_code_test \
ascii_header_text_test test_sendopts test_dict_sqlite
@ -743,10 +743,8 @@ normalize_mailhost_addr_test: update normalize_mailhost_addr
diff /dev/null normalize_mailhost_addr.tmp
rm -f normalize_mailhost_addr.tmp
haproxy_srvr_test: update haproxy_srvr
-$(SHLIB_ENV) $(VALGRIND) ./haproxy_srvr >haproxy_srvr.tmp 2>&1
diff /dev/null haproxy_srvr.tmp
rm -f haproxy_srvr.tmp
test_haproxy_srvr: update haproxy_srvr_test
$(SHLIB_ENV) $(VALGRIND) ./haproxy_srvr_test
map_search_test: update map_search map_search.ref
-$(SHLIB_ENV) $(VALGRIND) ./map_search >map_search.tmp 2>&1
@ -1532,6 +1530,7 @@ haproxy_srvr.o: ../../include/inet_proto.h
haproxy_srvr.o: ../../include/msg.h
haproxy_srvr.o: ../../include/myaddrinfo.h
haproxy_srvr.o: ../../include/mymalloc.h
haproxy_srvr.o: ../../include/normalize_v4mapped_addr.h
haproxy_srvr.o: ../../include/sock_addr.h
haproxy_srvr.o: ../../include/split_at.h
haproxy_srvr.o: ../../include/stringops.h
@ -1541,6 +1540,18 @@ haproxy_srvr.o: ../../include/vbuf.h
haproxy_srvr.o: ../../include/vstring.h
haproxy_srvr.o: haproxy_srvr.c
haproxy_srvr.o: haproxy_srvr.h
haproxy_srvr_test.o: ../../include/check_arg.h
haproxy_srvr_test.o: ../../include/msg.h
haproxy_srvr_test.o: ../../include/msg_vstream.h
haproxy_srvr_test.o: ../../include/myaddrinfo.h
haproxy_srvr_test.o: ../../include/sock_addr.h
haproxy_srvr_test.o: ../../include/stringops.h
haproxy_srvr_test.o: ../../include/sys_defs.h
haproxy_srvr_test.o: ../../include/vbuf.h
haproxy_srvr_test.o: ../../include/vstream.h
haproxy_srvr_test.o: ../../include/vstring.h
haproxy_srvr_test.o: haproxy_srvr.h
haproxy_srvr_test.o: haproxy_srvr_test.c
header_body_checks.o: ../../include/argv.h
header_body_checks.o: ../../include/check_arg.h
header_body_checks.o: ../../include/dict.h

View File

@ -6,6 +6,31 @@
/* SYNOPSIS
/* #include <haproxy_srvr.h>
/*
/* const char *haproxy_srvr_parse_sa(
/* const char *str,
/* ssize_t *str_len,
/* int *non_proxy,
/* MAI_HOSTADDR_STR *smtp_client_addr,
/* MAI_SERVPORT_STR *smtp_client_port,
/* MAI_HOSTADDR_STR *smtp_server_addr,
/* MAI_SERVPORT_STR *smtp_server_port,
/* struct sockaddr *client_sa,
/* SOCKADDR_SIZE *client_sa_len,
/* struct sockaddr *server_sa,
/* SOCKADDR_SIZE *server_sa_len)
/*
/* const char *haproxy_srvr_receive_sa(
/* int fd,
/* int *non_proxy,
/* MAI_HOSTADDR_STR *smtp_client_addr,
/* MAI_SERVPORT_STR *smtp_client_port,
/* MAI_HOSTADDR_STR *smtp_server_addr,
/* MAI_SERVPORT_STR *smtp_server_port,
/* struct sockaddr *client_sa,
/* SOCKADDR_SIZE *client_sa_len,
/* struct sockaddr *server_sa,
/* SOCKADDR_SIZE *server_sa_len)
/* ABI COMPATIBILITY
/* const char *haproxy_srvr_parse(str, str_len, non_proxy,
/* smtp_client_addr, smtp_client_port,
/* smtp_server_addr, smtp_server_port)
@ -27,7 +52,7 @@
/* MAI_HOSTADDR_STR *smtp_server_addr,
/* MAI_SERVPORT_STR *smtp_server_port;
/* DESCRIPTION
/* haproxy_srvr_parse() parses a haproxy v1 or v2 protocol
/* haproxy_srvr_parse_sa() parses a haproxy v1 or v2 protocol
/* message. 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
@ -36,7 +61,7 @@
/* of bytes parsed, and the non_proxy argument is true or false
/* if the haproxy message specifies a non-proxied connection.
/*
/* haproxy_srvr_receive() receives and parses a haproxy protocol
/* haproxy_srvr_receive_sa() receives and parses a haproxy protocol
/* handshake. This must be called before any I/O is done on
/* the specified file descriptor. The result is 0 in case of
/* success, -1 in case of error. All errors are logged.
@ -45,6 +70,13 @@
/* TCP over IPv6, and non-proxied connections. In the latter
/* case, the caller is responsible for any local or remote
/* address/port lookup.
/*
/* The client or server sockaddr and length storage are updated
/* when their pointers are non-null.
/*
/* haproxy_srvr_parse() and haproxy_srvr_receive() provide ABI
/* backwards compatibility, passing null pointers for the sockaddr
/* and length storage arguments.
/* LICENSE
/* .ad
/* .fi
@ -59,6 +91,9 @@
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
/*
/* Wietse Venema
/* porcupine.org
/*--*/
/* System library. */
@ -82,89 +117,13 @@
#include <inet_proto.h>
#include <split_at.h>
#include <sock_addr.h>
#include <normalize_v4mapped_addr.h>
/* Global library. */
#define _HAPROXY_SRVR_INTERNAL_
#include <haproxy_srvr.h>
/* Application-specific. */
/*
* The haproxy protocol assumes that a haproxy header will normally not
* exceed the default IPv4 TCP MSS, i.e. 576-40=536 bytes (the IPv6 default
* is larger: 1280-60=1220). With a proxy header that contains IPv6
* addresses, that leaves room for 536-52=484 bytes of TLVs. The Postfix
* implementation does not support headers with UNIX-domain addresses.
*/
#define HAPROXY_HEADER_MAX_LEN 536
/*
* Begin protocol v2 definitions from haproxy/include/types/connection.h.
*/
#define PP2_SIGNATURE "\r\n\r\n\0\r\nQUIT\n"
#define PP2_SIGNATURE_LEN 12
#define PP2_HEADER_LEN 16
/* ver_cmd byte */
#define PP2_CMD_LOCAL 0x00
#define PP2_CMD_PROXY 0x01
#define PP2_CMD_MASK 0x0F
#define PP2_VERSION 0x20
#define PP2_VERSION_MASK 0xF0
/* fam byte */
#define PP2_TRANS_UNSPEC 0x00
#define PP2_TRANS_STREAM 0x01
#define PP2_TRANS_DGRAM 0x02
#define PP2_TRANS_MASK 0x0F
#define PP2_FAM_UNSPEC 0x00
#define PP2_FAM_INET 0x10
#define PP2_FAM_INET6 0x20
#define PP2_FAM_UNIX 0x30
#define PP2_FAM_MASK 0xF0
/* len field (2 bytes) */
#define PP2_ADDR_LEN_UNSPEC (0)
#define PP2_ADDR_LEN_INET (4 + 4 + 2 + 2)
#define PP2_ADDR_LEN_INET6 (16 + 16 + 2 + 2)
#define PP2_ADDR_LEN_UNIX (108 + 108)
#define PP2_HDR_LEN_UNSPEC (PP2_HEADER_LEN + PP2_ADDR_LEN_UNSPEC)
#define PP2_HDR_LEN_INET (PP2_HEADER_LEN + PP2_ADDR_LEN_INET)
#define PP2_HDR_LEN_INET6 (PP2_HEADER_LEN + PP2_ADDR_LEN_INET6)
#define PP2_HDR_LEN_UNIX (PP2_HEADER_LEN + PP2_ADDR_LEN_UNIX)
struct proxy_hdr_v2 {
uint8_t sig[PP2_SIGNATURE_LEN]; /* PP2_SIGNATURE */
uint8_t ver_cmd; /* protocol version | command */
uint8_t fam; /* protocol family and transport */
uint16_t len; /* length of remainder */
union {
struct { /* for TCP/UDP over IPv4, len = 12 */
uint32_t src_addr;
uint32_t dst_addr;
uint16_t src_port;
uint16_t dst_port;
} ip4;
struct { /* for TCP/UDP over IPv6, len = 36 */
uint8_t src_addr[16];
uint8_t dst_addr[16];
uint16_t src_port;
uint16_t dst_port;
} ip6;
struct { /* for AF_UNIX sockets, len = 216 */
uint8_t src_addr[108];
uint8_t dst_addr[108];
} unx;
} addr;
};
/*
* End protocol v2 definitions from haproxy/include/types/connection.h.
*/
static const INET_PROTO_INFO *proto_info;
#define STR_OR_NULL(str) ((str) ? (str) : "(null)")
@ -223,10 +182,14 @@ static int haproxy_srvr_parse_proto(const char *str, int *addr_family)
/* 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)
int addr_family,
struct sockaddr *sa,
SOCKADDR_SIZE *sa_len)
{
struct addrinfo *res = 0;
struct addrinfo *res;
int err;
struct sockaddr_storage ss;
SOCKADDR_SIZE ss_len;
if (msg_verbose)
msg_info("haproxy_srvr_parse: addr=%s proto=%d",
@ -238,30 +201,60 @@ static int haproxy_srvr_parse_addr(const char *str, MAI_HOSTADDR_STR *addr,
switch (addr_family) {
#ifdef AF_INET6
case AF_INET6:
err = !valid_ipv6_hostaddr(str, DONT_GRIPE);
if (!valid_ipv6_hostaddr(str, DONT_GRIPE))
return (-1);
break;
#endif
case AF_INET:
err = !valid_ipv4_hostaddr(str, DONT_GRIPE);
if (!valid_ipv4_hostaddr(str, DONT_GRIPE))
return (-1);
break;
default:
msg_panic("haproxy_srvr_parse: unexpected address family: %d",
addr_family);
}
if (err == 0)
err = (hostaddr_to_sockaddr(str, (char *) 0, 0, &res)
|| sane_sockaddr_to_hostaddr(res->ai_addr, res->ai_addrlen,
addr, (MAI_SERVPORT_STR *) 0, 0));
if (res)
freeaddrinfo(res);
if (err)
/*
* Convert the printable address to canonical form. Don't rely on the
* proxy. This requires a conversion to binary form and back, even if a
* caller such as postscreen does not need the binary form.
*/
if ((err = hostaddr_to_sockaddr(str, (char *) 0, 0, &res)) != 0) {
msg_warn("haproxy_srvr_parse: hostaddr_to_sockaddr(\"%s\") failed: %s",
str, MAI_STRERROR(err));
return (-1);
}
if (sa == 0) {
sa = (struct sockaddr *) &ss;
ss_len = sizeof(ss);
sa_len = &ss_len;
} else {
if (sa_len == 0)
msg_panic("haproxy_srvr_parse: sockaddr length not specified");
}
if (*sa_len < res->ai_addrlen)
msg_panic("haproxy_srvr_parse: sockaddr size %d too small",
(int) *sa_len);
*sa_len = res->ai_addrlen;
memcpy((void *) sa, res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
#ifdef AF_INET6
if (sa->sa_family == AF_INET6)
normalize_v4mapped_sockaddr(sa, sa_len);
#endif
if ((err = sockaddr_to_hostaddr(sa, *sa_len,
addr, (MAI_SERVPORT_STR *) 0, 0)) != 0) {
msg_warn("haproxy_srvr_parse: sockaddr_to_hostaddr() failed: %s",
MAI_STRERROR(err));
return (-1);
}
return (0);
}
/* haproxy_srvr_parse_port - extract and validate TCP port */
static int haproxy_srvr_parse_port(const char *str, MAI_SERVPORT_STR *port)
static int haproxy_srvr_parse_port(const char *str, MAI_SERVPORT_STR *port,
struct sockaddr *sa)
{
if (msg_verbose)
msg_info("haproxy_srvr_parse: port=%s", STR_OR_NULL(str));
@ -270,6 +263,21 @@ static int haproxy_srvr_parse_port(const char *str, MAI_SERVPORT_STR *port)
return (-1);
} else {
memcpy(port->buf, str, strlen(str) + 1);
if (sa != 0) {
switch (sa->sa_family) {
#ifdef AF_INET6
case AF_INET6:
SOCK_ADDR_IN6_PORT(sa) = htons(atoi(str));
break;
#endif
case AF_INET:
SOCK_ADDR_IN_PORT(sa) = htons(atoi(str));
break;
default:
msg_panic("haproxy_srvr_parse: unexpected address family: %d",
sa->sa_family);
}
}
return (0);
}
}
@ -279,16 +287,33 @@ static int haproxy_srvr_parse_port(const char *str, MAI_SERVPORT_STR *port)
static int haproxy_srvr_parse_v2_addr_v4(uint32_t sin_addr,
unsigned sin_port,
MAI_HOSTADDR_STR *addr,
MAI_SERVPORT_STR *port)
MAI_SERVPORT_STR *port,
struct sockaddr *sa,
SOCKADDR_SIZE *sa_len)
{
struct sockaddr_in sin;
SOCKADDR_SIZE sin_len;
memset((void *) &sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = sin_addr;
sin.sin_port = sin_port;
if (sockaddr_to_hostaddr((struct sockaddr *) &sin, sizeof(sin),
addr, port, 0) < 0)
/*
* Convert the binary address and port to printable form.
*/
if (sa == 0) {
sa = (struct sockaddr *) &sin;
sin_len = sizeof(sin);
sa_len = &sin_len;
} else {
if (sa_len == 0)
msg_panic("haproxy_srvr_parse: sockaddr length not specified");
if (*sa_len < sizeof(sin))
msg_panic("haproxy_srvr_parse: sockaddr size %d too small",
(int) *sa_len);
*sa_len = sizeof(sin);
}
memset((void *) sa, 0, *sa_len);
SOCK_ADDR_IN_FAMILY(sa) = AF_INET;
SOCK_ADDR_IN_ADDR(sa).s_addr = sin_addr;
SOCK_ADDR_IN_PORT(sa) = sin_port;
if (sockaddr_to_hostaddr(sa, *sa_len, addr, port, 0) < 0)
return (-1);
return (0);
}
@ -300,22 +325,35 @@ static int haproxy_srvr_parse_v2_addr_v4(uint32_t sin_addr,
static int haproxy_srvr_parse_v2_addr_v6(uint8_t *sin6_addr,
unsigned sin6_port,
MAI_HOSTADDR_STR *addr,
MAI_SERVPORT_STR *port)
MAI_SERVPORT_STR *port,
struct sockaddr *sa,
SOCKADDR_SIZE *sa_len)
{
struct sockaddr_in6 sin6;
SOCKADDR_SIZE sin6_len;
memset((void *) &sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
memcpy(&sin6.sin6_addr, sin6_addr, 16);
sin6.sin6_port = sin6_port;
if (sockaddr_to_hostaddr((struct sockaddr *) &sin6,
sizeof(sin6), addr, port, 0) < 0)
/*
* Convert the binary address and port to printable form.
*/
if (sa == 0) {
sa = (struct sockaddr *) &sin6;
sin6_len = sizeof(sin6);
sa_len = &sin6_len;
} else {
if (sa_len == 0)
msg_panic("haproxy_srvr_parse: sockaddr length not specified");
if (*sa_len < sizeof(sin6))
msg_panic("haproxy_srvr_parse: sockaddr size %d too small",
(int) *sa_len);
*sa_len = sizeof(sin6);
}
memset((void *) sa, 0, *sa_len);
SOCK_ADDR_IN6_FAMILY(sa) = AF_INET6;
memcpy(&SOCK_ADDR_IN6_ADDR(sa), sin6_addr, sizeof(SOCK_ADDR_IN6_ADDR(sa)));
SOCK_ADDR_IN6_PORT(sa) = sin6_port;
normalize_v4mapped_sockaddr(sa, sa_len);
if (sockaddr_to_hostaddr(sa, *sa_len, addr, port, 0) < 0)
return (-1);
if (addr->buf[0] == ':'
&& strncasecmp("::ffff:", addr->buf, 7) == 0
&& strchr((char *) proto_info->sa_family_list, AF_INET) != 0)
memmove(addr->buf, addr->buf + 7,
strlen(addr->buf) + 1 - 7);
return (0);
}
@ -328,7 +366,11 @@ static const char *haproxy_srvr_parse_v2_hdr(const char *str, ssize_t *str_len,
MAI_HOSTADDR_STR *smtp_client_addr,
MAI_SERVPORT_STR *smtp_client_port,
MAI_HOSTADDR_STR *smtp_server_addr,
MAI_SERVPORT_STR *smtp_server_port)
MAI_SERVPORT_STR *smtp_server_port,
struct sockaddr *client_sa,
SOCKADDR_SIZE *client_sa_len,
struct sockaddr *server_sa,
SOCKADDR_SIZE *server_sa_len)
{
const char myname[] = "haproxy_srvr_parse_v2_hdr";
struct proxy_hdr_v2 *hdr_v2;
@ -357,14 +399,18 @@ static const char *haproxy_srvr_parse_v2_hdr(const char *str, ssize_t *str_len,
return ("short address field");
if (haproxy_srvr_parse_v2_addr_v4(hdr_v2->addr.ip4.src_addr,
hdr_v2->addr.ip4.src_port,
smtp_client_addr, smtp_client_port) < 0)
smtp_client_addr, smtp_client_port,
client_sa, client_sa_len) < 0)
return ("client network address conversion error");
if (msg_verbose)
msg_info("%s: smtp_client_addr=%s smtp_client_port=%s",
myname, smtp_client_addr->buf, smtp_client_port->buf);
if (haproxy_srvr_parse_v2_addr_v4(hdr_v2->addr.ip4.dst_addr,
hdr_v2->addr.ip4.dst_port,
smtp_server_addr, smtp_server_port) < 0)
smtp_server_addr,
smtp_server_port,
server_sa,
server_sa_len) < 0)
return ("server network address conversion error");
if (msg_verbose)
msg_info("%s: smtp_server_addr=%s smtp_server_port=%s",
@ -380,7 +426,9 @@ static const char *haproxy_srvr_parse_v2_hdr(const char *str, ssize_t *str_len,
if (haproxy_srvr_parse_v2_addr_v6(hdr_v2->addr.ip6.src_addr,
hdr_v2->addr.ip6.src_port,
smtp_client_addr,
smtp_client_port) < 0)
smtp_client_port,
client_sa,
client_sa_len) < 0)
return ("client network address conversion error");
if (msg_verbose)
msg_info("%s: smtp_client_addr=%s smtp_client_port=%s",
@ -388,7 +436,9 @@ static const char *haproxy_srvr_parse_v2_hdr(const char *str, ssize_t *str_len,
if (haproxy_srvr_parse_v2_addr_v6(hdr_v2->addr.ip6.dst_addr,
hdr_v2->addr.ip6.dst_port,
smtp_server_addr,
smtp_server_port) < 0)
smtp_server_port,
server_sa,
server_sa_len) < 0)
return ("server network address conversion error");
if (msg_verbose)
msg_info("%s: smtp_server_addr=%s smtp_server_port=%s",
@ -418,14 +468,18 @@ static const char *haproxy_srvr_parse_v2_hdr(const char *str, ssize_t *str_len,
}
}
/* haproxy_srvr_parse - parse haproxy line */
/* haproxy_srvr_parse_sa - parse haproxy line */
const char *haproxy_srvr_parse(const char *str, ssize_t *str_len,
int *non_proxy,
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 *haproxy_srvr_parse_sa(const char *str, ssize_t *str_len,
int *non_proxy,
MAI_HOSTADDR_STR *smtp_client_addr,
MAI_SERVPORT_STR *smtp_client_port,
MAI_HOSTADDR_STR *smtp_server_addr,
MAI_SERVPORT_STR *smtp_server_port,
struct sockaddr *client_sa,
SOCKADDR_SIZE *client_sa_len,
struct sockaddr *server_sa,
SOCKADDR_SIZE *server_sa_len)
{
const char *err;
@ -456,14 +510,18 @@ const char *haproxy_srvr_parse(const char *str, ssize_t *str_len,
else if (haproxy_srvr_parse_proto(NEXT_TOKEN, &addr_family) < 0)
err = "bad or missing protocol type";
else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_client_addr,
addr_family) < 0)
addr_family, client_sa,
client_sa_len) < 0)
err = "bad or missing client address";
else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_server_addr,
addr_family) < 0)
addr_family, server_sa,
server_sa_len) < 0)
err = "bad or missing server address";
else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_client_port) < 0)
else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_client_port,
client_sa) < 0)
err = "bad or missing client port";
else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_server_port) < 0)
else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_server_port,
server_sa) < 0)
err = "bad or missing server port";
else {
err = 0;
@ -480,17 +538,48 @@ const char *haproxy_srvr_parse(const char *str, ssize_t *str_len,
else {
return (haproxy_srvr_parse_v2_hdr(str, str_len, non_proxy,
smtp_client_addr, smtp_client_port,
smtp_server_addr, smtp_server_port));
smtp_server_addr, smtp_server_port,
client_sa, client_sa_len,
server_sa, server_sa_len));
}
}
/* haproxy_srvr_receive - receive and parse haproxy protocol handshake */
/* haproxy_srvr_parse - ABI compatibility */
int haproxy_srvr_receive(int fd, int *non_proxy,
MAI_HOSTADDR_STR *smtp_client_addr,
MAI_SERVPORT_STR *smtp_client_port,
MAI_HOSTADDR_STR *smtp_server_addr,
MAI_SERVPORT_STR *smtp_server_port)
#undef haproxy_srvr_parse
const char *haproxy_srvr_parse(const char *str, ssize_t *str_len,
int *non_proxy,
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 *haproxy_srvr_parse(const char *str, ssize_t *str_len,
int *non_proxy,
MAI_HOSTADDR_STR *smtp_client_addr,
MAI_SERVPORT_STR *smtp_client_port,
MAI_HOSTADDR_STR *smtp_server_addr,
MAI_SERVPORT_STR *smtp_server_port)
{
return (haproxy_srvr_parse_sa(str, str_len, non_proxy,
smtp_client_addr, smtp_client_port,
smtp_server_addr, smtp_server_port,
(struct sockaddr *) 0, (SOCKADDR_SIZE *) 0,
(struct sockaddr *) 0, (SOCKADDR_SIZE *) 0));
}
/* haproxy_srvr_receive_sa - receive and parse haproxy protocol handshake */
int haproxy_srvr_receive_sa(int fd, int *non_proxy,
MAI_HOSTADDR_STR *smtp_client_addr,
MAI_SERVPORT_STR *smtp_client_port,
MAI_HOSTADDR_STR *smtp_server_addr,
MAI_SERVPORT_STR *smtp_server_port,
struct sockaddr *client_sa,
SOCKADDR_SIZE *client_sa_len,
struct sockaddr *server_sa,
SOCKADDR_SIZE *server_sa_len)
{
const char *err;
VSTRING *escape_buf;
@ -513,9 +602,11 @@ int haproxy_srvr_receive(int fd, int *non_proxy,
*/
read_buf[read_len] = 0;
if ((err = haproxy_srvr_parse(read_buf, &read_len, non_proxy,
smtp_client_addr, smtp_client_port,
smtp_server_addr, smtp_server_port)) != 0) {
if ((err = haproxy_srvr_parse_sa(read_buf, &read_len, non_proxy,
smtp_client_addr, smtp_client_port,
smtp_server_addr, smtp_server_port,
client_sa, client_sa_len,
server_sa, server_sa_len)) != 0) {
escape_buf = vstring_alloc(read_len * 2);
escape(escape_buf, read_buf, read_len);
msg_warn("haproxy read: %s: %s", err, vstring_str(escape_buf));
@ -533,356 +624,27 @@ int haproxy_srvr_receive(int fd, int *non_proxy,
return (0);
}
/*
* Test program.
*/
#ifdef TEST
/* haproxy_srvr_receive - ABI compatibility */
/*
* Test cases with inputs and expected outputs. A request may contain
* trailing garbage, and it may be too short. A v1 request may also contain
* malformed address or port information.
*/
typedef struct TEST_CASE {
const char *haproxy_request; /* v1 or v2 request including thrash */
ssize_t haproxy_req_len; /* request length including thrash */
ssize_t exp_req_len; /* parsed request length */
int exp_non_proxy; /* request is not proxied */
const char *exp_return; /* expected error string */
const char *exp_client_addr; /* expected client address string */
const char *exp_server_addr; /* expected client port string */
const char *exp_client_port; /* expected client address string */
const char *exp_server_port; /* expected server port string */
} TEST_CASE;
static TEST_CASE v1_test_cases[] = {
/* IPv6. */
{"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1 123 321\n", 0, 0, 0, 0, "fc::1:2:3:4", "fc::4:3:2:1", "123", "321"},
{"PROXY TCP6 FC:00:00:00:1:2:3:4 FC:00:00:00:4:3:2:1 123 321\n", 0, 0, 0, 0, "fc::1:2:3:4", "fc::4:3:2:1", "123", "321"},
{"PROXY TCP6 1.2.3.4 4.3.2.1 123 321\n", 0, 0, 0, "bad or missing client address"},
{"PROXY TCP6 fc:00:00:00:1:2:3:4 4.3.2.1 123 321\n", 0, 0, 0, "bad or missing server address"},
/* IPv4 in IPv6. */
{"PROXY TCP6 ::ffff:1.2.3.4 ::ffff:4.3.2.1 123 321\n", 0, 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"},
{"PROXY TCP6 ::FFFF:1.2.3.4 ::FFFF:4.3.2.1 123 321\n", 0, 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"},
{"PROXY TCP4 ::ffff:1.2.3.4 ::ffff:4.3.2.1 123 321\n", 0, 0, 0, "bad or missing client address"},
{"PROXY TCP4 1.2.3.4 ::ffff:4.3.2.1 123 321\n", 0, 0, 0, "bad or missing server address"},
/* IPv4. */
{"PROXY TCP4 1.2.3.4 4.3.2.1 123 321\n", 0, 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"},
{"PROXY TCP4 01.02.03.04 04.03.02.01 123 321\n", 0, 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"},
{"PROXY TCP4 1.2.3.4 4.3.2.1 123456 321\n", 0, 0, 0, "bad or missing client port"},
{"PROXY TCP4 1.2.3.4 4.3.2.1 123 654321\n", 0, 0, 0, "bad or missing server port"},
{"PROXY TCP4 1.2.3.4 4.3.2.1 0123 321\n", 0, 0, 0, "bad or missing client port"},
{"PROXY TCP4 1.2.3.4 4.3.2.1 123 0321\n", 0, 0, 0, "bad or missing server port"},
/* Missing fields. */
{"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1 123\n", 0, 0, 0, "bad or missing server port"},
{"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1\n", 0, 0, 0, "bad or missing client port"},
{"PROXY TCP6 fc:00:00:00:1:2:3:4\n", 0, 0, 0, "bad or missing server address"},
{"PROXY TCP6\n", 0, 0, 0, "bad or missing client address"},
{"PROXY TCP4 1.2.3.4 4.3.2.1 123\n", 0, 0, 0, "bad or missing server port"},
{"PROXY TCP4 1.2.3.4 4.3.2.1\n", 0, 0, 0, "bad or missing client port"},
{"PROXY TCP4 1.2.3.4\n", 0, 0, 0, "bad or missing server address"},
{"PROXY TCP4\n", 0, 0, 0, "bad or missing client address"},
/* Other. */
{"PROXY BLAH\n", 0, 0, 0, "bad or missing protocol type"},
{"PROXY\n", 0, 0, 0, "short protocol header"},
{"BLAH\n", 0, 0, 0, "short protocol header"},
{"\n", 0, 0, 0, "short protocol header"},
{"", 0, 0, 0, "short protocol header"},
0,
};
#undef haproxy_srvr_receive
static struct proxy_hdr_v2 v2_local_request = {
PP2_SIGNATURE, PP2_VERSION | PP2_CMD_LOCAL,
};
static TEST_CASE v2_non_proxy_test = {
(char *) &v2_local_request, PP2_HEADER_LEN, PP2_HEADER_LEN, 1,
};
int haproxy_srvr_receive(int fd, int *non_proxy,
MAI_HOSTADDR_STR *smtp_client_addr,
MAI_SERVPORT_STR *smtp_client_port,
MAI_HOSTADDR_STR *smtp_server_addr,
MAI_SERVPORT_STR *smtp_server_port);
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
/* evaluate_test_case - evaluate one test case */
static int evaluate_test_case(const char *test_label,
const TEST_CASE *test_case)
int haproxy_srvr_receive(int fd, int *non_proxy,
MAI_HOSTADDR_STR *smtp_client_addr,
MAI_SERVPORT_STR *smtp_client_port,
MAI_HOSTADDR_STR *smtp_server_addr,
MAI_SERVPORT_STR *smtp_server_port)
{
/* Actual results. */
const char *act_return;
ssize_t act_req_len;
int act_non_proxy;
MAI_HOSTADDR_STR act_smtp_client_addr;
MAI_HOSTADDR_STR act_smtp_server_addr;
MAI_SERVPORT_STR act_smtp_client_port;
MAI_SERVPORT_STR act_smtp_server_port;
int test_failed;
if (msg_verbose)
msg_info("test case=%s exp_client_addr=%s exp_server_addr=%s "
"exp_client_port=%s exp_server_port=%s",
test_label, STR_OR_NULL(test_case->exp_client_addr),
STR_OR_NULL(test_case->exp_server_addr),
STR_OR_NULL(test_case->exp_client_port),
STR_OR_NULL(test_case->exp_server_port));
/*
* Start the test.
*/
test_failed = 0;
act_req_len = test_case->haproxy_req_len;
act_return =
haproxy_srvr_parse(test_case->haproxy_request, &act_req_len,
&act_non_proxy,
&act_smtp_client_addr, &act_smtp_client_port,
&act_smtp_server_addr, &act_smtp_server_port);
if (act_return != test_case->exp_return) {
msg_warn("test case %s return expected=%s actual=%s",
test_label, STR_OR_NULL(test_case->exp_return),
STR_OR_NULL(act_return));
test_failed = 1;
return (test_failed);
}
if (act_req_len != test_case->exp_req_len) {
msg_warn("test case %s str_len expected=%ld actual=%ld",
test_label,
(long) test_case->exp_req_len, (long) act_req_len);
test_failed = 1;
return (test_failed);
}
if (act_non_proxy != test_case->exp_non_proxy) {
msg_warn("test case %s non_proxy expected=%d actual=%d",
test_label,
test_case->exp_non_proxy, act_non_proxy);
test_failed = 1;
return (test_failed);
}
if (test_case->exp_non_proxy || test_case->exp_return != 0)
/* No expected address/port results. */
return (test_failed);
/*
* Compare address/port results against expected results.
*/
if (strcmp(test_case->exp_client_addr, act_smtp_client_addr.buf)) {
msg_warn("test case %s client_addr expected=%s actual=%s",
test_label,
test_case->exp_client_addr, act_smtp_client_addr.buf);
test_failed = 1;
}
if (strcmp(test_case->exp_server_addr, act_smtp_server_addr.buf)) {
msg_warn("test case %s server_addr expected=%s actual=%s",
test_label,
test_case->exp_server_addr, act_smtp_server_addr.buf);
test_failed = 1;
}
if (strcmp(test_case->exp_client_port, act_smtp_client_port.buf)) {
msg_warn("test case %s client_port expected=%s actual=%s",
test_label,
test_case->exp_client_port, act_smtp_client_port.buf);
test_failed = 1;
}
if (strcmp(test_case->exp_server_port, act_smtp_server_port.buf)) {
msg_warn("test case %s server_port expected=%s actual=%s",
test_label,
test_case->exp_server_port, act_smtp_server_port.buf);
test_failed = 1;
}
return (test_failed);
return (haproxy_srvr_receive_sa(fd, non_proxy,
smtp_client_addr, smtp_client_port,
smtp_server_addr, smtp_server_port,
(struct sockaddr *) 0,
(SOCKADDR_SIZE *) 0,
(struct sockaddr *) 0,
(SOCKADDR_SIZE *) 0));
}
/* convert_v1_proxy_req_to_v2 - convert well-formed v1 proxy request to v2 */
static void convert_v1_proxy_req_to_v2(VSTRING *buf, const char *req,
ssize_t req_len)
{
const char myname[] = "convert_v1_proxy_req_to_v2";
const char *err;
int non_proxy;
MAI_HOSTADDR_STR smtp_client_addr;
MAI_SERVPORT_STR smtp_client_port;
MAI_HOSTADDR_STR smtp_server_addr;
MAI_SERVPORT_STR smtp_server_port;
struct proxy_hdr_v2 *hdr_v2;
struct addrinfo *src_res;
struct addrinfo *dst_res;
/*
* Allocate buffer space for the largest possible protocol header, so we
* don't have to worry about hidden realloc() calls.
*/
VSTRING_RESET(buf);
VSTRING_SPACE(buf, sizeof(struct proxy_hdr_v2));
hdr_v2 = (struct proxy_hdr_v2 *) STR(buf);
/*
* Fill in the header,
*/
memcpy(hdr_v2->sig, PP2_SIGNATURE, PP2_SIGNATURE_LEN);
hdr_v2->ver_cmd = PP2_VERSION | PP2_CMD_PROXY;
if ((err = haproxy_srvr_parse(req, &req_len, &non_proxy, &smtp_client_addr,
&smtp_client_port, &smtp_server_addr,
&smtp_server_port)) != 0 || non_proxy)
msg_fatal("%s: malformed or non-proxy request: %s",
myname, req);
if (hostaddr_to_sockaddr(smtp_client_addr.buf, smtp_client_port.buf, 0,
&src_res) != 0)
msg_fatal("%s: unable to convert source address %s port %s",
myname, smtp_client_addr.buf, smtp_client_port.buf);
if (hostaddr_to_sockaddr(smtp_server_addr.buf, smtp_server_port.buf, 0,
&dst_res) != 0)
msg_fatal("%s: unable to convert destination address %s port %s",
myname, smtp_server_addr.buf, smtp_server_port.buf);
if (src_res->ai_family != dst_res->ai_family)
msg_fatal("%s: mixed source/destination address families", myname);
#ifdef AF_INET6
if (src_res->ai_family == PF_INET6) {
hdr_v2->fam = PP2_FAM_INET6 | PP2_TRANS_STREAM;
hdr_v2->len = htons(PP2_ADDR_LEN_INET6);
memcpy(hdr_v2->addr.ip6.src_addr,
&SOCK_ADDR_IN6_ADDR(src_res->ai_addr),
sizeof(hdr_v2->addr.ip6.src_addr));
hdr_v2->addr.ip6.src_port = SOCK_ADDR_IN6_PORT(src_res->ai_addr);
memcpy(hdr_v2->addr.ip6.dst_addr,
&SOCK_ADDR_IN6_ADDR(dst_res->ai_addr),
sizeof(hdr_v2->addr.ip6.dst_addr));
hdr_v2->addr.ip6.dst_port = SOCK_ADDR_IN6_PORT(dst_res->ai_addr);
} else
#endif
if (src_res->ai_family == PF_INET) {
hdr_v2->fam = PP2_FAM_INET | PP2_TRANS_STREAM;
hdr_v2->len = htons(PP2_ADDR_LEN_INET);
hdr_v2->addr.ip4.src_addr = SOCK_ADDR_IN_ADDR(src_res->ai_addr).s_addr;
hdr_v2->addr.ip4.src_port = SOCK_ADDR_IN_PORT(src_res->ai_addr);
hdr_v2->addr.ip4.dst_addr = SOCK_ADDR_IN_ADDR(dst_res->ai_addr).s_addr;
hdr_v2->addr.ip4.dst_port = SOCK_ADDR_IN_PORT(dst_res->ai_addr);
} else {
msg_panic("unknown address family 0x%x", src_res->ai_family);
}
vstring_set_payload_size(buf, PP2_SIGNATURE_LEN + ntohs(hdr_v2->len));
freeaddrinfo(src_res);
freeaddrinfo(dst_res);
}
int main(int argc, char **argv)
{
VSTRING *test_label;
TEST_CASE *v1_test_case;
TEST_CASE v2_test_case;
TEST_CASE mutated_test_case;
VSTRING *v2_request_buf;
VSTRING *mutated_request_buf;
/* Findings. */
int tests_failed = 0;
int test_failed;
test_label = vstring_alloc(100);
v2_request_buf = vstring_alloc(100);
mutated_request_buf = vstring_alloc(100);
for (tests_failed = 0, v1_test_case = v1_test_cases;
v1_test_case->haproxy_request != 0;
tests_failed += test_failed, v1_test_case++) {
/*
* Fill in missing string length info in v1 test data.
*/
if (v1_test_case->haproxy_req_len == 0)
v1_test_case->haproxy_req_len =
strlen(v1_test_case->haproxy_request);
if (v1_test_case->exp_req_len == 0)
v1_test_case->exp_req_len = v1_test_case->haproxy_req_len;
/*
* Evaluate each v1 test case.
*/
vstring_sprintf(test_label, "%d", (int) (v1_test_case - v1_test_cases));
test_failed = evaluate_test_case(STR(test_label), v1_test_case);
/*
* If the v1 test input is malformed, skip the mutation tests.
*/
if (v1_test_case->exp_return != 0)
continue;
/*
* Mutation test: a well-formed v1 test case should still pass after
* appending a byte, and should return the actual parsed header
* length. The test uses the implicit VSTRING null safety byte.
*/
vstring_sprintf(test_label, "%d (one byte appended)",
(int) (v1_test_case - v1_test_cases));
mutated_test_case = *v1_test_case;
mutated_test_case.haproxy_req_len += 1;
/* reuse v1_test_case->exp_req_len */
test_failed += evaluate_test_case(STR(test_label), &mutated_test_case);
/*
* Mutation test: a well-formed v1 test case should fail after
* stripping the terminator.
*/
vstring_sprintf(test_label, "%d (last byte stripped)",
(int) (v1_test_case - v1_test_cases));
mutated_test_case = *v1_test_case;
mutated_test_case.exp_return = "missing protocol header terminator";
mutated_test_case.haproxy_req_len -= 1;
mutated_test_case.exp_req_len = mutated_test_case.haproxy_req_len;
test_failed += evaluate_test_case(STR(test_label), &mutated_test_case);
/*
* A 'well-formed' v1 test case should pass after conversion to v2.
*/
vstring_sprintf(test_label, "%d (converted to v2)",
(int) (v1_test_case - v1_test_cases));
v2_test_case = *v1_test_case;
convert_v1_proxy_req_to_v2(v2_request_buf,
v1_test_case->haproxy_request,
v1_test_case->haproxy_req_len);
v2_test_case.haproxy_request = STR(v2_request_buf);
v2_test_case.haproxy_req_len = PP2_HEADER_LEN
+ ntohs(((struct proxy_hdr_v2 *) STR(v2_request_buf))->len);
v2_test_case.exp_req_len = v2_test_case.haproxy_req_len;
test_failed += evaluate_test_case(STR(test_label), &v2_test_case);
/*
* Mutation test: a well-formed v2 test case should still pass after
* appending a byte, and should return the actual parsed header
* length. The test uses the implicit VSTRING null safety byte.
*/
vstring_sprintf(test_label, "%d (converted to v2, one byte appended)",
(int) (v1_test_case - v1_test_cases));
mutated_test_case = v2_test_case;
mutated_test_case.haproxy_req_len += 1;
/* reuse v2_test_case->exp_req_len */
test_failed += evaluate_test_case(STR(test_label), &mutated_test_case);
/*
* Mutation test: a well-formed v2 test case should fail after
* stripping one byte
*/
vstring_sprintf(test_label, "%d (converted to v2, last byte stripped)",
(int) (v1_test_case - v1_test_cases));
mutated_test_case = v2_test_case;
mutated_test_case.haproxy_req_len -= 1;
mutated_test_case.exp_req_len = mutated_test_case.haproxy_req_len;
mutated_test_case.exp_return = "short version 2 protocol header";
test_failed += evaluate_test_case(STR(test_label), &mutated_test_case);
}
/*
* Additional V2-only tests.
*/
test_failed +=
evaluate_test_case("v2 non-proxy request", &v2_non_proxy_test);
/*
* Clean up.
*/
vstring_free(v2_request_buf);
vstring_free(mutated_request_buf);
vstring_free(test_label);
if (tests_failed)
msg_info("tests failed: %d", tests_failed);
exit(tests_failed != 0);
}
#endif

View File

@ -19,12 +19,32 @@
/*
* External interface.
*/
extern const char *haproxy_srvr_parse(const char *, ssize_t *, int *,
extern const char *haproxy_srvr_parse_sa(const char *, ssize_t *, int *,
MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *,
MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *);
extern int haproxy_srvr_receive(int, int *,
MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *,
MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *);
struct sockaddr *, SOCKADDR_SIZE *,
struct sockaddr *, SOCKADDR_SIZE *);
#define haproxy_srvr_parse(str, len, non_proxy, client_addr, client_port, \
server_addr, server_port) \
haproxy_srvr_parse_sa((str), (len), (non_proxy), \
(client_addr), (client_port), \
(server_addr), (server_port), \
(struct sockaddr *) 0, (SOCKADDR_SIZE *) 0, \
(struct sockaddr *) 0, (SOCKADDR_SIZE *) 0)
extern int haproxy_srvr_receive_sa(int, int *,
MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *,
MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *,
struct sockaddr *, SOCKADDR_SIZE *,
struct sockaddr *, SOCKADDR_SIZE *);
#define haproxy_srvr_receive(fd, non_proxy, client_addr, client_port, \
server_addr, server_port) \
haproxy_srvr_receive_sa((fd), (non_proxy), \
(client_addr), (client_port), \
(server_addr), (server_port), \
(struct sockaddr *) 0, (SOCKADDR_SIZE *) 0, \
(struct sockaddr *) 0, (SOCKADDR_SIZE *) 0)
#define HAPROXY_PROTO_NAME "haproxy"
@ -33,6 +53,86 @@ extern int haproxy_srvr_receive(int, int *,
#define DONT_GRIPE 0
#endif
#ifdef _HAPROXY_SRVR_INTERNAL_
/*
* The haproxy protocol assumes that a haproxy header will normally not
* exceed the default IPv4 TCP MSS, i.e. 576-40=536 bytes (the IPv6 default
* is larger: 1280-60=1220). With a proxy header that contains IPv6
* addresses, that leaves room for 536-52=484 bytes of TLVs. The Postfix
* implementation does not support headers with UNIX-domain addresses.
*/
#define HAPROXY_HEADER_MAX_LEN 536
/*
* Begin protocol v2 definitions from haproxy/include/types/connection.h.
*/
#define PP2_SIGNATURE "\r\n\r\n\0\r\nQUIT\n"
#define PP2_SIGNATURE_LEN 12
#define PP2_HEADER_LEN 16
/* ver_cmd byte */
#define PP2_CMD_LOCAL 0x00
#define PP2_CMD_PROXY 0x01
#define PP2_CMD_MASK 0x0F
#define PP2_VERSION 0x20
#define PP2_VERSION_MASK 0xF0
/* fam byte */
#define PP2_TRANS_UNSPEC 0x00
#define PP2_TRANS_STREAM 0x01
#define PP2_TRANS_DGRAM 0x02
#define PP2_TRANS_MASK 0x0F
#define PP2_FAM_UNSPEC 0x00
#define PP2_FAM_INET 0x10
#define PP2_FAM_INET6 0x20
#define PP2_FAM_UNIX 0x30
#define PP2_FAM_MASK 0xF0
/* len field (2 bytes) */
#define PP2_ADDR_LEN_UNSPEC (0)
#define PP2_ADDR_LEN_INET (4 + 4 + 2 + 2)
#define PP2_ADDR_LEN_INET6 (16 + 16 + 2 + 2)
#define PP2_ADDR_LEN_UNIX (108 + 108)
#define PP2_HDR_LEN_UNSPEC (PP2_HEADER_LEN + PP2_ADDR_LEN_UNSPEC)
#define PP2_HDR_LEN_INET (PP2_HEADER_LEN + PP2_ADDR_LEN_INET)
#define PP2_HDR_LEN_INET6 (PP2_HEADER_LEN + PP2_ADDR_LEN_INET6)
#define PP2_HDR_LEN_UNIX (PP2_HEADER_LEN + PP2_ADDR_LEN_UNIX)
struct proxy_hdr_v2 {
uint8_t sig[PP2_SIGNATURE_LEN]; /* PP2_SIGNATURE */
uint8_t ver_cmd; /* protocol version | command */
uint8_t fam; /* protocol family and transport */
uint16_t len; /* length of remainder */
union {
struct { /* for TCP/UDP over IPv4, len = 12 */
uint32_t src_addr;
uint32_t dst_addr;
uint16_t src_port;
uint16_t dst_port;
} ip4;
struct { /* for TCP/UDP over IPv6, len = 36 */
uint8_t src_addr[16];
uint8_t dst_addr[16];
uint16_t src_port;
uint16_t dst_port;
} ip6;
struct { /* for AF_UNIX sockets, len = 216 */
uint8_t src_addr[108];
uint8_t dst_addr[108];
} unx;
} addr;
};
/*
* End protocol v2 definitions from haproxy/include/types/connection.h.
*/
#endif /* _HAPROXY_SRVR_INTERNAL_ */
/* LICENSE
/* .ad
/* .fi

View File

@ -0,0 +1,534 @@
/*
* System library.
*/
#include <sys_defs.h>
#include <stdlib.h>
/*
* Utility library.
*/
#include <msg.h>
#include <msg_vstream.h>
#include <myaddrinfo.h>
#include <sock_addr.h>
#include <stringops.h>
#include <vstring.h>
/*
* Global library.
*/
#define _HAPROXY_SRVR_INTERNAL_
#include <haproxy_srvr.h>
/*
* Application-specific.
*/
#define STR_OR_NULL(str) ((str) ? (str) : "(null)")
/*
* Test cases with inputs and expected outputs. A request may contain
* trailing garbage, and it may be too short. A v1 request may also contain
* malformed address or port information. TODO(wietse) add unit test with
* different inet_protocols setting. See normalize_v4mapped_addr_test.c.
*/
typedef struct TEST_CASE {
const char *haproxy_request; /* v1 or v2 request including thrash */
ssize_t haproxy_req_len; /* request length including thrash */
ssize_t exp_req_len; /* parsed request length */
int exp_non_proxy; /* request is not proxied */
const char *exp_return; /* expected error string */
const char *exp_client_addr; /* expected client address string */
const char *exp_server_addr; /* expected client port string */
const char *exp_client_port; /* expected client address string */
const char *exp_server_port; /* expected server port string */
} TEST_CASE;
static TEST_CASE v1_test_cases[] = {
/* IPv6. */
{"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1 123 321\n", 0, 0, 0, 0, "fc::1:2:3:4", "fc::4:3:2:1", "123", "321"},
{"PROXY TCP6 FC:00:00:00:1:2:3:4 FC:00:00:00:4:3:2:1 123 321\n", 0, 0, 0, 0, "fc::1:2:3:4", "fc::4:3:2:1", "123", "321"},
{"PROXY TCP6 1.2.3.4 4.3.2.1 123 321\n", 0, 0, 0, "bad or missing client address"},
{"PROXY TCP6 fc:00:00:00:1:2:3:4 4.3.2.1 123 321\n", 0, 0, 0, "bad or missing server address"},
/* IPv4 in IPv6. */
{"PROXY TCP6 ::ffff:1.2.3.4 ::ffff:4.3.2.1 123 321\n", 0, 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"},
{"PROXY TCP6 ::FFFF:1.2.3.4 ::FFFF:4.3.2.1 123 321\n", 0, 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"},
{"PROXY TCP4 ::ffff:1.2.3.4 ::ffff:4.3.2.1 123 321\n", 0, 0, 0, "bad or missing client address"},
{"PROXY TCP4 1.2.3.4 ::ffff:4.3.2.1 123 321\n", 0, 0, 0, "bad or missing server address"},
/* IPv4. */
{"PROXY TCP4 1.2.3.4 4.3.2.1 123 321\n", 0, 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"},
{"PROXY TCP4 01.02.03.04 04.03.02.01 123 321\n", 0, 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"},
{"PROXY TCP4 1.2.3.4 4.3.2.1 123456 321\n", 0, 0, 0, "bad or missing client port"},
{"PROXY TCP4 1.2.3.4 4.3.2.1 123 654321\n", 0, 0, 0, "bad or missing server port"},
{"PROXY TCP4 1.2.3.4 4.3.2.1 0123 321\n", 0, 0, 0, "bad or missing client port"},
{"PROXY TCP4 1.2.3.4 4.3.2.1 123 0321\n", 0, 0, 0, "bad or missing server port"},
/* Missing fields. */
{"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1 123\n", 0, 0, 0, "bad or missing server port"},
{"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1\n", 0, 0, 0, "bad or missing client port"},
{"PROXY TCP6 fc:00:00:00:1:2:3:4\n", 0, 0, 0, "bad or missing server address"},
{"PROXY TCP6\n", 0, 0, 0, "bad or missing client address"},
{"PROXY TCP4 1.2.3.4 4.3.2.1 123\n", 0, 0, 0, "bad or missing server port"},
{"PROXY TCP4 1.2.3.4 4.3.2.1\n", 0, 0, 0, "bad or missing client port"},
{"PROXY TCP4 1.2.3.4\n", 0, 0, 0, "bad or missing server address"},
{"PROXY TCP4\n", 0, 0, 0, "bad or missing client address"},
/* Other. */
{"PROXY BLAH\n", 0, 0, 0, "bad or missing protocol type"},
{"PROXY\n", 0, 0, 0, "short protocol header"},
{"BLAH\n", 0, 0, 0, "short protocol header"},
{"\n", 0, 0, 0, "short protocol header"},
{"", 0, 0, 0, "short protocol header"},
0,
};
static struct proxy_hdr_v2 v2_local_request = {
PP2_SIGNATURE, PP2_VERSION | PP2_CMD_LOCAL,
};
static TEST_CASE v2_non_proxy_test = {
(char *) &v2_local_request, PP2_HEADER_LEN, PP2_HEADER_LEN, 1,
};
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
#define DO_SOCKADDR_OUTPUT 1
#define NO_SOCKADDR_OUTPUT 0
static int evaluate_sockaddr(const char *which, struct sockaddr *sa,
SOCKADDR_SIZE sa_len, const char *want_addr,
const char *want_port)
{
MAI_HOSTADDR_STR act_addr;
MAI_SERVPORT_STR act_port;
int err;
int failed = 0;
if ((err = sockaddr_to_hostaddr(sa, sa_len, &act_addr, &act_port, 0)) != 0) {
msg_warn("sockaddr_to_hostaddr: %s", MAI_STRERROR(err));
return (1);
}
if (strcmp(act_addr.buf, want_addr) != 0) {
msg_warn("got %s address '%s', want '%s'",
which, act_addr.buf, want_addr);
failed = 1;
}
if (strcmp(act_port.buf, want_port) != 0) {
msg_warn("got %s port '%s', want '%s'",
which, act_port.buf, want_port);
failed = 1;
}
return (failed);
}
/* evaluate_test_case - evaluate one base or mutated test case */
static int evaluate_test_case(const char *test_label,
const TEST_CASE *test_case,
int want_sockaddr_output)
{
/* Actual results. */
const char *act_return;
ssize_t act_req_len;
int act_non_proxy;
MAI_HOSTADDR_STR act_smtp_client_addr;
MAI_HOSTADDR_STR act_smtp_server_addr;
MAI_SERVPORT_STR act_smtp_client_port;
MAI_SERVPORT_STR act_smtp_server_port;
int test_failed;
struct sockaddr_storage client_ss;
struct sockaddr *client_sa;
SOCKADDR_SIZE client_sa_len;
struct sockaddr_storage server_ss;
struct sockaddr *server_sa;
SOCKADDR_SIZE server_sa_len;
if (msg_verbose)
msg_info("test case=%s exp_client_addr=%s exp_server_addr=%s "
"exp_client_port=%s exp_server_port=%s",
test_label, STR_OR_NULL(test_case->exp_client_addr),
STR_OR_NULL(test_case->exp_server_addr),
STR_OR_NULL(test_case->exp_client_port),
STR_OR_NULL(test_case->exp_server_port));
if (want_sockaddr_output) {
client_sa = (struct sockaddr *) &client_ss;
client_sa_len = sizeof(client_ss);
server_sa = (struct sockaddr *) &server_ss;
server_sa_len = sizeof(server_ss);
} else {
client_sa = 0;
client_sa_len = 0;
server_sa = 0;
server_sa_len = 0;
}
/*
* Start the test.
*/
test_failed = 0;
act_req_len = test_case->haproxy_req_len;
act_return =
haproxy_srvr_parse_sa(test_case->haproxy_request, &act_req_len,
&act_non_proxy,
&act_smtp_client_addr, &act_smtp_client_port,
&act_smtp_server_addr, &act_smtp_server_port,
client_sa, &client_sa_len,
server_sa, &server_sa_len);
if (act_return != test_case->exp_return
&& strcmp(STR_OR_NULL(act_return), STR_OR_NULL(test_case->exp_return))) {
msg_warn("test case %s return expected=>%s< actual=>%s<",
test_label, STR_OR_NULL(test_case->exp_return),
STR_OR_NULL(act_return));
test_failed = 1;
return (test_failed);
}
if (act_req_len != test_case->exp_req_len) {
msg_warn("test case %s str_len expected=%ld actual=%ld",
test_label,
(long) test_case->exp_req_len, (long) act_req_len);
test_failed = 1;
return (test_failed);
}
if (act_non_proxy != test_case->exp_non_proxy) {
msg_warn("test case %s non_proxy expected=%d actual=%d",
test_label,
test_case->exp_non_proxy, act_non_proxy);
test_failed = 1;
return (test_failed);
}
if (test_case->exp_non_proxy || test_case->exp_return != 0)
/* No expected address/port results. */
return (test_failed);
/*
* Compare address/port results against expected results.
*/
if (strcmp(test_case->exp_client_addr, act_smtp_client_addr.buf)) {
msg_warn("test case %s client_addr expected=%s actual=%s",
test_label,
test_case->exp_client_addr, act_smtp_client_addr.buf);
test_failed = 1;
}
if (strcmp(test_case->exp_server_addr, act_smtp_server_addr.buf)) {
msg_warn("test case %s server_addr expected=%s actual=%s",
test_label,
test_case->exp_server_addr, act_smtp_server_addr.buf);
test_failed = 1;
}
if (strcmp(test_case->exp_client_port, act_smtp_client_port.buf)) {
msg_warn("test case %s client_port expected=%s actual=%s",
test_label,
test_case->exp_client_port, act_smtp_client_port.buf);
test_failed = 1;
}
if (strcmp(test_case->exp_server_port, act_smtp_server_port.buf)) {
msg_warn("test case %s server_port expected=%s actual=%s",
test_label,
test_case->exp_server_port, act_smtp_server_port.buf);
test_failed = 1;
}
if (want_sockaddr_output) {
if (evaluate_sockaddr("client", client_sa, client_sa_len,
test_case->exp_client_addr,
test_case->exp_client_port) != 0
|| evaluate_sockaddr("server", server_sa, server_sa_len,
test_case->exp_server_addr,
test_case->exp_server_port) != 0)
test_failed = 1;
}
return (test_failed);
}
/* convert_v1_proxy_req_to_v2 - convert well-formed v1 proxy request to v2 */
static void convert_v1_proxy_req_to_v2(VSTRING *buf, const char *req,
ssize_t req_len)
{
const char myname[] = "convert_v1_proxy_req_to_v2";
const char *err;
int non_proxy;
MAI_HOSTADDR_STR smtp_client_addr;
MAI_SERVPORT_STR smtp_client_port;
MAI_HOSTADDR_STR smtp_server_addr;
MAI_SERVPORT_STR smtp_server_port;
struct proxy_hdr_v2 *hdr_v2;
struct addrinfo *src_res;
struct addrinfo *dst_res;
/*
* Allocate buffer space for the largest possible protocol header, so we
* don't have to worry about hidden realloc() calls.
*/
VSTRING_RESET(buf);
VSTRING_SPACE(buf, sizeof(struct proxy_hdr_v2));
hdr_v2 = (struct proxy_hdr_v2 *) STR(buf);
/*
* Fill in the header,
*/
memcpy(hdr_v2->sig, PP2_SIGNATURE, PP2_SIGNATURE_LEN);
hdr_v2->ver_cmd = PP2_VERSION | PP2_CMD_PROXY;
if ((err = haproxy_srvr_parse(req, &req_len, &non_proxy, &smtp_client_addr,
&smtp_client_port, &smtp_server_addr,
&smtp_server_port)) != 0 || non_proxy)
msg_fatal("%s: malformed or non-proxy request: %s",
myname, req);
if (hostaddr_to_sockaddr(smtp_client_addr.buf, smtp_client_port.buf, 0,
&src_res) != 0)
msg_fatal("%s: unable to convert source address %s port %s",
myname, smtp_client_addr.buf, smtp_client_port.buf);
if (hostaddr_to_sockaddr(smtp_server_addr.buf, smtp_server_port.buf, 0,
&dst_res) != 0)
msg_fatal("%s: unable to convert destination address %s port %s",
myname, smtp_server_addr.buf, smtp_server_port.buf);
if (src_res->ai_family != dst_res->ai_family)
msg_fatal("%s: mixed source/destination address families", myname);
#ifdef AF_INET6
if (src_res->ai_family == PF_INET6) {
hdr_v2->fam = PP2_FAM_INET6 | PP2_TRANS_STREAM;
hdr_v2->len = htons(PP2_ADDR_LEN_INET6);
memcpy(hdr_v2->addr.ip6.src_addr,
&SOCK_ADDR_IN6_ADDR(src_res->ai_addr),
sizeof(hdr_v2->addr.ip6.src_addr));
hdr_v2->addr.ip6.src_port = SOCK_ADDR_IN6_PORT(src_res->ai_addr);
memcpy(hdr_v2->addr.ip6.dst_addr,
&SOCK_ADDR_IN6_ADDR(dst_res->ai_addr),
sizeof(hdr_v2->addr.ip6.dst_addr));
hdr_v2->addr.ip6.dst_port = SOCK_ADDR_IN6_PORT(dst_res->ai_addr);
} else
#endif
if (src_res->ai_family == PF_INET) {
hdr_v2->fam = PP2_FAM_INET | PP2_TRANS_STREAM;
hdr_v2->len = htons(PP2_ADDR_LEN_INET);
hdr_v2->addr.ip4.src_addr = SOCK_ADDR_IN_ADDR(src_res->ai_addr).s_addr;
hdr_v2->addr.ip4.src_port = SOCK_ADDR_IN_PORT(src_res->ai_addr);
hdr_v2->addr.ip4.dst_addr = SOCK_ADDR_IN_ADDR(dst_res->ai_addr).s_addr;
hdr_v2->addr.ip4.dst_port = SOCK_ADDR_IN_PORT(dst_res->ai_addr);
} else {
msg_panic("unknown address family 0x%x", src_res->ai_family);
}
vstring_set_payload_size(buf, PP2_SIGNATURE_LEN + ntohs(hdr_v2->len));
freeaddrinfo(src_res);
freeaddrinfo(dst_res);
}
int main(int argc, char **argv)
{
VSTRING *test_label;
TEST_CASE *v1_test_case;
TEST_CASE v2_test_case;
TEST_CASE mutated_test_case;
VSTRING *v2_request_buf;
VSTRING *mutated_request_buf;
msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR);
/* Findings. */
int tests_failed = 0;
int tests_passed = 0;
test_label = vstring_alloc(100);
v2_request_buf = vstring_alloc(100);
mutated_request_buf = vstring_alloc(100);
/*
* Evaluate each case in the test case list. If the test input is
* well-formed, also test a number of mutations based on that test,
* before proceeding with the next test case in the list.
*/
for (tests_failed = 0, tests_passed = 0, v1_test_case = v1_test_cases;
v1_test_case->haproxy_request != 0; v1_test_case++) {
/*
* Fill in missing string length info in v1 test data.
*/
if (v1_test_case->haproxy_req_len == 0)
v1_test_case->haproxy_req_len =
strlen(v1_test_case->haproxy_request);
if (v1_test_case->exp_req_len == 0)
v1_test_case->exp_req_len = v1_test_case->haproxy_req_len;
/*
* Evaluate each v1 test case.
*/
vstring_sprintf(test_label, "%d (%s%.5s input)",
(int) (v1_test_case - v1_test_cases),
v1_test_case->exp_return ? "malformed" : "well-formed",
v1_test_case->exp_return ? "" : v1_test_case->haproxy_request + 5);
msg_info("RUN %s", STR(test_label));
if (evaluate_test_case(STR(test_label), v1_test_case,
NO_SOCKADDR_OUTPUT)) {
msg_info("FAIL %s", STR(test_label));
tests_failed += 1;
} else {
msg_info("PASS %s", STR(test_label));
tests_passed += 1;
}
/*
* If the v1 test input is malformed, skip the mutation tests.
*/
if (v1_test_case->exp_return != 0)
continue;
/*
* Mutation test: a well-formed v1 test case should also pass with
* output to sockaddr arguments.
*/
vstring_sprintf(test_label, "%d (with sockaddr output)",
(int) (v1_test_case - v1_test_cases));
msg_info("RUN %s", STR(test_label));
if (evaluate_test_case(STR(test_label), v1_test_case,
DO_SOCKADDR_OUTPUT)) {
msg_info("FAIL %s", STR(test_label));
tests_failed += 1;
} else {
msg_info("PASS %s", STR(test_label));
tests_passed += 1;
}
/*
* Mutation test: a well-formed v1 test case should still pass after
* appending a byte, and should return the actual parsed header
* length. The test uses the implicit VSTRING null safety byte.
*/
vstring_sprintf(test_label, "%d (one byte appended)",
(int) (v1_test_case - v1_test_cases));
mutated_test_case = *v1_test_case;
mutated_test_case.haproxy_req_len += 1;
msg_info("RUN %s", STR(test_label));
/* reuse v1_test_case->exp_req_len */
if (evaluate_test_case(STR(test_label), &mutated_test_case,
NO_SOCKADDR_OUTPUT)) {
msg_info("FAIL %s", STR(test_label));
tests_failed += 1;
} else {
msg_info("PASS %s", STR(test_label));
tests_passed += 1;
}
/*
* Mutation test: a well-formed v1 test case should fail after
* stripping the terminator.
*/
vstring_sprintf(test_label, "%d (last byte stripped)",
(int) (v1_test_case - v1_test_cases));
mutated_test_case = *v1_test_case;
mutated_test_case.exp_return = "missing protocol header terminator";
mutated_test_case.haproxy_req_len -= 1;
mutated_test_case.exp_req_len = mutated_test_case.haproxy_req_len;
msg_info("RUN %s", STR(test_label));
if (evaluate_test_case(STR(test_label), &mutated_test_case,
NO_SOCKADDR_OUTPUT)) {
msg_info("FAIL %s", STR(test_label));
tests_failed += 1;
} else {
msg_info("PASS %s", STR(test_label));
tests_passed += 1;
}
/*
* A 'well-formed' v1 test case should pass after conversion to v2.
*/
vstring_sprintf(test_label, "%d (converted to v2)",
(int) (v1_test_case - v1_test_cases));
v2_test_case = *v1_test_case;
convert_v1_proxy_req_to_v2(v2_request_buf,
v1_test_case->haproxy_request,
v1_test_case->haproxy_req_len);
v2_test_case.haproxy_request = STR(v2_request_buf);
v2_test_case.haproxy_req_len = PP2_HEADER_LEN
+ ntohs(((struct proxy_hdr_v2 *) STR(v2_request_buf))->len);
v2_test_case.exp_req_len = v2_test_case.haproxy_req_len;
msg_info("RUN %s", STR(test_label));
if (evaluate_test_case(STR(test_label), &v2_test_case,
NO_SOCKADDR_OUTPUT)) {
msg_info("FAIL %s", STR(test_label));
tests_failed += 1;
} else {
msg_info("PASS %s", STR(test_label));
tests_passed += 1;
}
/*
* Mutation test: a well-formed v2 test case should also pass with
* output to sockaddr arguments.
*/
vstring_sprintf(test_label,
"%d (converted to v2, with sockaddr output)",
(int) (v1_test_case - v1_test_cases));
msg_info("RUN %s", STR(test_label));
if (evaluate_test_case(STR(test_label), &v2_test_case,
DO_SOCKADDR_OUTPUT)) {
msg_info("FAIL %s", STR(test_label));
tests_failed += 1;
} else {
msg_info("PASS %s", STR(test_label));
tests_passed += 1;
}
/*
* Mutation test: a well-formed v2 test case should still pass after
* appending a byte, and should return the actual parsed header
* length. The test uses the implicit VSTRING null safety byte.
*/
vstring_sprintf(test_label, "%d (converted to v2, one byte appended)",
(int) (v1_test_case - v1_test_cases));
mutated_test_case = v2_test_case;
mutated_test_case.haproxy_req_len += 1;
/* reuse v2_test_case->exp_req_len */
msg_info("RUN %s", STR(test_label));
if (evaluate_test_case(STR(test_label), &mutated_test_case,
NO_SOCKADDR_OUTPUT)) {
msg_info("FAIL %s", STR(test_label));
tests_failed += 1;
} else {
msg_info("PASS %s", STR(test_label));
tests_passed += 1;
}
/*
* Mutation test: a well-formed v2 test case should fail after
* stripping one byte
*/
vstring_sprintf(test_label, "%d (converted to v2, last byte stripped)",
(int) (v1_test_case - v1_test_cases));
mutated_test_case = v2_test_case;
mutated_test_case.haproxy_req_len -= 1;
mutated_test_case.exp_req_len = mutated_test_case.haproxy_req_len;
mutated_test_case.exp_return = "short version 2 protocol header";
msg_info("RUN %s", STR(test_label));
if (evaluate_test_case(STR(test_label), &mutated_test_case,
NO_SOCKADDR_OUTPUT)) {
msg_info("FAIL %s", STR(test_label));
tests_failed += 1;
} else {
msg_info("PASS %s", STR(test_label));
tests_passed += 1;
}
}
/*
* Additional V2-only tests.
*/
vstring_strcpy(test_label, "v2 non-proxy request");
msg_info("RUN %s", STR(test_label));
if (evaluate_test_case("v2 non-proxy request", &v2_non_proxy_test,
NO_SOCKADDR_OUTPUT)) {
msg_info("FAIL %s", STR(test_label));
tests_failed += 1;
} else {
msg_info("PASS %s", STR(test_label));
tests_passed += 1;
}
/*
* Clean up.
*/
vstring_free(v2_request_buf);
vstring_free(mutated_request_buf);
vstring_free(test_label);
msg_info("PASS=%d FAIL=%d", tests_passed, tests_failed);
exit(tests_failed != 0);
}

View File

@ -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 "20250323"
#define MAIL_RELEASE_DATE "20250409"
#define MAIL_VERSION_NUMBER "3.11"
#ifdef SNAPSHOT

View File

@ -116,7 +116,7 @@ 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);
SOCKADDR_SIZE addr_storage_len;
int status;
MAI_HOSTADDR_STR smtp_client_addr;
MAI_SERVPORT_STR smtp_client_port;
@ -124,10 +124,13 @@ void psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
MAI_SERVPORT_STR smtp_server_port;
int aierr;
#define RESET_ADDR_STORAGE_LEN() (addr_storage_len = sizeof(addr_storage))
/*
* Look up the remote SMTP client address and port.
*/
if (getpeername(vstream_fileno(smtp_client_stream), (struct sockaddr *)
if (RESET_ADDR_STORAGE_LEN(),
getpeername(vstream_fileno(smtp_client_stream), (struct sockaddr *)
&addr_storage, &addr_storage_len) < 0) {
msg_warn("getpeername: %m -- dropping this connection");
status = -1;
@ -135,11 +138,12 @@ void psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
/*
* Convert the remote SMTP client address and port to printable form for
* logging and access control.
* logging and access control. Note: this may change addr_storage and
* addr_storage_len.
*/
else if ((aierr = sane_sockaddr_to_hostaddr(
(struct sockaddr *) &addr_storage,
addr_storage_len, &smtp_client_addr,
&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",
@ -148,9 +152,11 @@ void psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
}
/*
* Look up the local SMTP server address and port.
* Look up the local SMTP server address and port. Be sure to reset the
* addr_storage_len value.
*/
else if (getsockname(vstream_fileno(smtp_client_stream),
else if (RESET_ADDR_STORAGE_LEN(),
getsockname(vstream_fileno(smtp_client_stream),
(struct sockaddr *) &addr_storage,
&addr_storage_len) < 0) {
msg_warn("getsockname: %m -- dropping this connection");
@ -159,11 +165,12 @@ void psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
/*
* Convert the local SMTP server address and port to printable form for
* logging.
* logging. This may also change addr_storage and addr_storage_len, but
* those variables are dead.
*/
else if ((aierr = sane_sockaddr_to_hostaddr(
(struct sockaddr *) &addr_storage,
addr_storage_len, &smtp_server_addr,
&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",

View File

@ -372,7 +372,7 @@ void smtp_tlsrpt_set_tcp_connection(SMTP_STATE *state)
client_addr.buf[0] = 0;
} else if ((aierr = sane_sockaddr_to_hostaddr(
(struct sockaddr *) &addr_storage,
addr_storage_len, &client_addr,
&addr_storage_len, &client_addr,
(MAI_SERVPORT_STR *) 0,
SOCK_STREAM)) != 0) {
msg_warn("%s: cannot convert IP address to string (%s)"

View File

@ -58,6 +58,10 @@
/* .IP dest_port
/* Server port, available as Milter {daemon_port} macro, and
/* as server_port policy delegation attribute.
/* .IP sockaddr_len
/* .IP dest_sockaddr_len
/* These are initialized to zero, to indicate that the corresponding
/* sockaddr_storage members are not set.
/* .IP name_status
/* The name_status result field specifies how the name
/* information should be interpreted:
@ -117,6 +121,9 @@
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
/*
/* Wietse Venema
/* porcupine.org
/*--*/
/* System library. */
@ -617,6 +624,7 @@ void smtpd_peer_init(SMTPD_STATE *state)
state->anvil_range = 0;
state->dest_addr = 0;
state->dest_port = 0;
state->dest_sockaddr_len = 0;
/*
* Determine the remote SMTP client address and port.

View File

@ -47,7 +47,7 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
mkmap_fail.c mkmap_lmdb.c mkmap_open.c mkmap_sdbm.c inet_prefix_top.c \
inet_addr_sizes.c quote_for_json.c mystrerror.c \
sane_sockaddr_to_hostaddr.c normalize_ws.c valid_uri_scheme.c \
clean_ascii_cntrl_space.c
clean_ascii_cntrl_space.c normalize_v4mapped_addr.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 \
@ -95,7 +95,8 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
sane_strtol.o hash_fnv.o ldseed.o mkmap_db.o mkmap_dbm.o \
mkmap_fail.o mkmap_open.o inet_prefix_top.o inet_addr_sizes.o \
quote_for_json.o mystrerror.o sane_sockaddr_to_hostaddr.o \
normalize_ws.o valid_uri_scheme.o clean_ascii_cntrl_space.o
normalize_ws.o valid_uri_scheme.o clean_ascii_cntrl_space.o \
normalize_v4mapped_addr.o
# MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
# When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
# otherwise it sets the PLUGIN_* macros.
@ -128,7 +129,7 @@ HDRS = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \
check_arg.h argv_attr.h msg_logger.h logwriter.h byte_mask.h \
known_tcp_ports.h sane_strtol.h hash_fnv.h ldseed.h mkmap.h \
inet_prefix_top.h inet_addr_sizes.h valid_uri_scheme.h \
clean_ascii_cntrl_space.h
clean_ascii_cntrl_space.h normalize_v4mapped_addr.h
TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
stream_test.c dup2_pass_on_exec.c
DEFS = -I. -D$(SYSTYPE)
@ -151,7 +152,8 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \
vbuf_print split_qnameval vstream msg_logger byte_mask \
known_tcp_ports dict_stream find_inet binhash hash_fnv argv \
clean_env inet_prefix_top printable readlline quote_for_json \
normalize_ws valid_uri_scheme clean_ascii_cntrl_space
normalize_ws valid_uri_scheme clean_ascii_cntrl_space \
normalize_v4mapped_addr_test
PLUGIN_MAP_SO = $(LIB_PREFIX)pcre$(LIB_SUFFIX) $(LIB_PREFIX)lmdb$(LIB_SUFFIX) \
$(LIB_PREFIX)cdb$(LIB_SUFFIX) $(LIB_PREFIX)sdbm$(LIB_SUFFIX)
HTABLE_FIX = NORANDOMIZE=1
@ -630,6 +632,9 @@ clean_ascii_cntrl_space: $(LIB)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
normalize_v4mapped_addr_test: normalize_v4mapped_addr_test.c $(LIB)
$(CC) $(CFLAGS) -o $@ normalize_v4mapped_addr_test.c $(LIB) $(SYSLIBS)
tests: all 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 host_port_test dict_tests \
@ -641,7 +646,8 @@ tests: all valid_hostname_test mac_expand_test dict_test unescape_test \
vstream_test byte_mask_tests mystrtok_test known_tcp_ports_test \
binhash_test argv_test inet_prefix_top_test printable_test \
valid_utf8_string_test readlline_test quote_for_json_test \
normalize_ws_test valid_uri_scheme_test clean_ascii_cntrl_space_test
normalize_ws_test valid_uri_scheme_test clean_ascii_cntrl_space_test \
test_normalize_v4mapped_addr
dict_tests: all dict_test \
dict_pcre_tests dict_cidr_test dict_thash_test dict_static_test \
@ -1127,6 +1133,9 @@ valid_uri_scheme_test: valid_uri_scheme
clean_ascii_cntrl_space_test: clean_ascii_cntrl_space
$(SHLIB_ENV) ${VALGRIND} ./clean_ascii_cntrl_space
test_normalize_v4mapped_addr: update normalize_v4mapped_addr_test
$(SHLIB_ENV) ${VALGRIND} ./normalize_v4mapped_addr_test
depend: $(MAKES)
(sed '1,/^# do not edit/!d' Makefile.in; \
set -e; for i in [a-z][a-z0-9]*.c; do \
@ -2532,6 +2541,24 @@ non_blocking.o: iostuff.h
non_blocking.o: msg.h
non_blocking.o: non_blocking.c
non_blocking.o: sys_defs.h
normalize_v4mapped_addr.o: inet_proto.h
normalize_v4mapped_addr.o: myaddrinfo.h
normalize_v4mapped_addr.o: normalize_v4mapped_addr.c
normalize_v4mapped_addr.o: normalize_v4mapped_addr.h
normalize_v4mapped_addr.o: sock_addr.h
normalize_v4mapped_addr.o: sys_defs.h
normalize_v4mapped_addr_test.o: check_arg.h
normalize_v4mapped_addr_test.o: inet_proto.h
normalize_v4mapped_addr_test.o: msg.h
normalize_v4mapped_addr_test.o: msg_vstream.h
normalize_v4mapped_addr_test.o: myaddrinfo.h
normalize_v4mapped_addr_test.o: normalize_v4mapped_addr.h
normalize_v4mapped_addr_test.o: normalize_v4mapped_addr_test.c
normalize_v4mapped_addr_test.o: stringops.h
normalize_v4mapped_addr_test.o: sys_defs.h
normalize_v4mapped_addr_test.o: vbuf.h
normalize_v4mapped_addr_test.o: vstream.h
normalize_v4mapped_addr_test.o: vstring.h
normalize_ws.o: check_arg.h
normalize_ws.o: msg.h
normalize_ws.o: msg_vstream.h
@ -2673,8 +2700,8 @@ sane_rename.o: sane_fsops.h
sane_rename.o: sane_rename.c
sane_rename.o: sys_defs.h
sane_rename.o: warn_stat.h
sane_sockaddr_to_hostaddr.o: inet_proto.h
sane_sockaddr_to_hostaddr.o: myaddrinfo.h
sane_sockaddr_to_hostaddr.o: normalize_v4mapped_addr.h
sane_sockaddr_to_hostaddr.o: sane_sockaddr_to_hostaddr.c
sane_sockaddr_to_hostaddr.o: sys_defs.h
sane_socketpair.o: msg.h

View File

@ -15,7 +15,9 @@
/* .in -4
/* } INET_PROTO_INFO;
/*
/* const INET_PROTO_INFO *inet_proto_init(context, protocols)
/* const INET_PROTO_INFO *inet_proto_init(
/* const char *context,
/* const char *protocols)
/*
/* const INET_PROTO_INFO *inet_proto_info()
/* DESCRIPTION

View File

@ -833,8 +833,11 @@ int main(int argc, char **argv)
struct addrinfo **resv;
MAI_HOSTNAME_STR host;
MAI_HOSTADDR_STR addr;
MAI_SERVNAME_STR serv;
MAI_SERVPORT_STR port;
size_t len, n;
int err;
char *aport;
msg_vstream_init(argv[0], VSTREAM_ERR);
@ -845,9 +848,13 @@ int main(int argc, char **argv)
msg_info("=== hostname %s ===", argv[2]);
if ((err = hostname_to_sockaddr(argv[2], (char *) 0, 0, &info)) != 0) {
msg_info("hostname_to_sockaddr(%s): %s",
argv[2], err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
#define STR_OR_NULL(s) ((s) ? (s) : "(null)")
aport = split_at(argv[2], ':');
if ((err = hostname_to_sockaddr(argv[2], aport, 0, &info)) != 0) {
msg_warn("hostname_to_sockaddr(%s:%s): %s",
argv[2], STR_OR_NULL(aport), err == EAI_SYSTEM ?
strerror(errno) : gai_strerror(err));
} else {
for (len = 0, ip = info; ip != 0; ip = ip->ai_next)
len += 1;
@ -858,20 +865,21 @@ int main(int argc, char **argv)
for (n = 0; n < len; n++) {
ip = resv[n];
if ((err = sockaddr_to_hostaddr(ip->ai_addr, ip->ai_addrlen, &addr,
(MAI_SERVPORT_STR *) 0, 0)) != 0) {
msg_info("sockaddr_to_hostaddr: %s",
&port, 0)) != 0) {
msg_warn("sockaddr_to_hostaddr: %s",
err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
continue;
}
msg_info("%s -> family=%d sock=%d proto=%d %s", argv[2],
ip->ai_family, ip->ai_socktype, ip->ai_protocol, addr.buf);
msg_info("%s:%s -> family=%d sock=%d proto=%d %s:%s",
argv[2], STR_OR_NULL(aport), ip->ai_family,
ip->ai_socktype, ip->ai_protocol, addr.buf, port.buf);
if ((err = sockaddr_to_hostname(ip->ai_addr, ip->ai_addrlen, &host,
(MAI_SERVNAME_STR *) 0, 0)) != 0) {
msg_info("sockaddr_to_hostname: %s",
&serv, 0)) != 0) {
msg_warn("sockaddr_to_hostname: %s",
err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
continue;
}
msg_info("%s -> %s", addr.buf, host.buf);
msg_info("%s:%s -> %s:%s", addr.buf, port.buf, host.buf, serv.buf);
}
freeaddrinfo(info);
myfree((void *) resv);
@ -879,23 +887,25 @@ int main(int argc, char **argv)
msg_info("=== host address %s ===", argv[3]);
if ((err = hostaddr_to_sockaddr(argv[3], (char *) 0, 0, &ip)) != 0) {
msg_info("hostaddr_to_sockaddr(%s): %s",
argv[3], err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
aport = split_at(argv[3], ':');
if ((err = hostaddr_to_sockaddr(argv[3], aport, 0, &ip)) != 0) {
msg_warn("hostaddr_to_sockaddr(%s:%s): %s",
argv[3], STR_OR_NULL(aport), err == EAI_SYSTEM ?
strerror(errno) : gai_strerror(err));
} else {
if ((err = sockaddr_to_hostaddr(ip->ai_addr, ip->ai_addrlen, &addr,
(MAI_SERVPORT_STR *) 0, 0)) != 0) {
msg_info("sockaddr_to_hostaddr: %s",
&port, 0)) != 0) {
msg_warn("sockaddr_to_hostaddr: %s",
err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
} else {
msg_info("%s -> family=%d sock=%d proto=%d %s", argv[3],
ip->ai_family, ip->ai_socktype, ip->ai_protocol, addr.buf);
msg_info("%s:%s -> family=%d sock=%d proto=%d %s:%s", argv[3], STR_OR_NULL(aport),
ip->ai_family, ip->ai_socktype, ip->ai_protocol, addr.buf, port.buf);
if ((err = sockaddr_to_hostname(ip->ai_addr, ip->ai_addrlen, &host,
(MAI_SERVNAME_STR *) 0, 0)) != 0) {
msg_info("sockaddr_to_hostname: %s",
&serv, 0)) != 0) {
msg_warn("sockaddr_to_hostname: %s",
err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
} else
msg_info("%s -> %s", addr.buf, host.buf);
msg_info("%s:%s -> %s:%s", addr.buf, port.buf, host.buf, serv.buf);
freeaddrinfo(ip);
}
}

View File

@ -213,8 +213,8 @@ extern void myaddrinfo_control(int,...);
/*
* sane_sockaddr_to_hostaddr.c
*/
extern int WARN_UNUSED_RESULT sane_sockaddr_to_hostaddr(const struct sockaddr *,
SOCKADDR_SIZE, MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *, int);
extern int WARN_UNUSED_RESULT sane_sockaddr_to_hostaddr(struct sockaddr *,
SOCKADDR_SIZE *, MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *, int);
/* LICENSE
/* .ad

View File

@ -1,8 +1,8 @@
./myaddrinfo: === hostname belly.porcupine.org ===
./myaddrinfo: belly.porcupine.org -> family=2 sock=1 proto=6 168.100.3.6
./myaddrinfo: 168.100.3.6 -> belly.porcupine.org
./myaddrinfo: belly.porcupine.org -> family=28 sock=1 proto=6 2604:8d00:189::6
./myaddrinfo: 2604:8d00:189::6 -> belly.porcupine.org
./myaddrinfo: belly.porcupine.org:(null) -> family=2 sock=1 proto=6 168.100.3.6:0
./myaddrinfo: 168.100.3.6:0 -> belly.porcupine.org:0
./myaddrinfo: belly.porcupine.org:(null) -> family=28 sock=1 proto=6 2604:8d00:189::6:0
./myaddrinfo: 2604:8d00:189::6:0 -> belly.porcupine.org:0
./myaddrinfo: === host address 168.100.3.2 ===
./myaddrinfo: 168.100.3.2 -> family=2 sock=1 proto=6 168.100.3.2
./myaddrinfo: 168.100.3.2 -> spike.porcupine.org
./myaddrinfo: 168.100.3.2:(null) -> family=2 sock=1 proto=6 168.100.3.2:0
./myaddrinfo: 168.100.3.2:0 -> spike.porcupine.org:0

View File

@ -1,5 +1,5 @@
./myaddrinfo: === hostname null.porcupine.org ===
./myaddrinfo: hostname_to_sockaddr(null.porcupine.org): hostname nor servname provided, or not known
./myaddrinfo: warning: hostname_to_sockaddr(null.porcupine.org:(null)): hostname nor servname provided, or not known
./myaddrinfo: === host address 10.0.0.0 ===
./myaddrinfo: 10.0.0.0 -> family=2 sock=1 proto=6 10.0.0.0
./myaddrinfo: sockaddr_to_hostname: hostname nor servname provided, or not known
./myaddrinfo: 10.0.0.0:(null) -> family=2 sock=1 proto=6 10.0.0.0:0
./myaddrinfo: warning: sockaddr_to_hostname: hostname nor servname provided, or not known

View File

@ -1,6 +1,6 @@
./myaddrinfo4: === hostname belly.porcupine.org ===
./myaddrinfo4: belly.porcupine.org -> family=2 sock=1 proto=6 168.100.3.6
./myaddrinfo4: 168.100.3.6 -> belly.porcupine.org
./myaddrinfo4: belly.porcupine.org:(null) -> family=2 sock=1 proto=0 168.100.3.6:0
./myaddrinfo4: warning: sockaddr_to_hostname: hostname nor servname provided, or not known
./myaddrinfo4: === host address 168.100.3.2 ===
./myaddrinfo4: 168.100.3.2 -> family=2 sock=1 proto=6 168.100.3.2
./myaddrinfo4: 168.100.3.2 -> spike.porcupine.org
./myaddrinfo4: 168.100.3.2:(null) -> family=2 sock=1 proto=0 168.100.3.2:0
./myaddrinfo4: warning: sockaddr_to_hostname: hostname nor servname provided, or not known

View File

@ -1,5 +1,5 @@
./myaddrinfo4: === hostname null.porcupine.org ===
./myaddrinfo4: hostname2sockaddr(null.porcupine.org): No address associated with hostname
./myaddrinfo4: warning: hostname_to_sockaddr(null.porcupine.org:(null)): No address associated with hostname
./myaddrinfo4: === host address 10.0.0.0 ===
./myaddrinfo4: 10.0.0.0 -> family=2 sock=1 proto=6 10.0.0.0
./myaddrinfo4: sockaddr2hostname: hostname nor servname provided, or not known
./myaddrinfo4: 10.0.0.0:(null) -> family=2 sock=1 proto=0 10.0.0.0:0
./myaddrinfo4: warning: sockaddr_to_hostname: hostname nor servname provided, or not known

View File

@ -0,0 +1,90 @@
/*++
/* NAME
/* normalize_v4mapped_addr 3
/* SUMMARY
/* normalize v4mapped IPv6 address
/* SYNOPSIS
/* #include <stringops.h>
/*
/* int normalize_v4mapped_sockaddr(
/* struct sockaddr *sa,
/* SOCKADDR_SIZE *sa_len)
/*
/* int normalize_v4mapped_hostaddr(
/* MAI_HOSTADDR_STR *addr)
/* DESCRIPTION
/* The functions in this module convert IPV6 addresses containing a
/* mapped IPv4 address to the IPv4 form, but only if IPv4 support
/* is enabled.
/*
/* normalize_v4mapped_sockaddr converts a V4mapped IPv6 sockaddr
/* structure (struct sockaddr_in6) to the IPv4 form (struct
/* sockaddr_in) and reduces *sa_len to (sizeof(struct sockaddr_in)).
/*
/* normalize_v4mapped_hostaddr converts a V4mapped IPv6 printable
/* address (:ffff:d.d.d.d) to the IPv4 form (d.d.d.d).
/*
/* Both functions return 0 (false) when no input was converted,
/* and return 1 (true) when input was converted.
/* BUGS
/* normalize_v4mapped_hostaddr() converts only canonical address
/* forms, not all equivalent forms.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* porcupine.org
/* New York, NY 10011, USA
/*--*/
/*
* System library.
*/
#include <sys_defs.h>
/*
* Utility library.
*/
#include <inet_proto.h>
#include <sock_addr.h>
#include <normalize_v4mapped_addr.h>
/* normalize_v4mapped_sockaddr - normalize V4mapped IPv6 sockaddr */
int normalize_v4mapped_sockaddr(struct sockaddr *sa, SOCKADDR_SIZE *sa_len)
{
#ifdef AF_INET6
struct sockaddr_in sin;
if (sa->sa_family == AF_INET6
&& IN6_IS_ADDR_V4MAPPED(&SOCK_ADDR_IN6_ADDR(sa))
&& strchr((char *) inet_proto_info()->sa_family_list, AF_INET) != 0) {
memset((void *) &sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = SOCK_ADDR_IN6_PORT(sa);
memcpy((void *) &sin.sin_addr, ((void *) &SOCK_ADDR_IN6_ADDR(sa)) + 12,
sizeof(sin.sin_addr));
*(struct sockaddr_in *) sa = sin;
*sa_len = sizeof(sin);
return (1);
}
#endif
return (0);
}
/* normalize_v4mapped_hostaddr - normalize V4mapped IPv6 printable address */
int normalize_v4mapped_hostaddr(MAI_HOSTADDR_STR *addr)
{
#ifdef AF_INET6
if (addr->buf[0] == ':'
&& strncasecmp("::ffff:", addr->buf, 7) == 0
&& strchr((char *) inet_proto_info()->sa_family_list, AF_INET) != 0) {
memmove(addr->buf, addr->buf + 7, strlen(addr->buf) + 1 - 7);
return (1);
}
#endif
return (0);
}

View File

@ -0,0 +1,40 @@
#ifndef _NORMALIZE_V4MAPPED_ADDR_H_INCLUDED_
#define _NORMALIZE_V4MAPPED_ADDR_H_INCLUDED_
/*++
/* NAME
/* normalize_v4mapped_addr 3h
/* SUMMARY
/* normalize v4mapped IPv6 address
/* SYNOPSIS
/* #include <normalize_v4mapped_addr.h>
/* DESCRIPTION
/* .nf
/*
* System library.
*/
#include <sys_defs.h>
#include <sys/socket.h>
/*
* Utility library.
*/
#include <myaddrinfo.h>
/*
* External interface.
*/
extern int normalize_v4mapped_sockaddr(struct sockaddr *, SOCKADDR_SIZE *);
extern int normalize_v4mapped_hostaddr(MAI_HOSTADDR_STR *);
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* porcupine.org
/*--*/
#endif

View File

@ -0,0 +1,189 @@
/*
* System library.
*/
#include <sys_defs.h>
#include <stdlib.h>
/*
* Utility library.
*/
#include <normalize_v4mapped_addr.h>
#include <msg.h>
#include <msg_vstream.h>
#include <inet_proto.h>
#include <stringops.h>
/*
* Test cases are used twice, first to test normalize_v4mapped_hostaddr(), and
* then normalize_v4mapped_sockaddr().
*/
typedef struct TEST_CASE {
const char *label;
const char *inet_protocols;
const char *in_hostaddr;
int want_return;
const char *want_hostaddr;
} TEST_CASE;
#define PASS 1
#define FAIL 2
static int test_normalize_v4mapped_hostaddr(const TEST_CASE *tp)
{
int got_return;
MAI_HOSTADDR_STR hostaddr;
/*
* Prepare inputs.
*/
if (strlen(tp->in_hostaddr) >= sizeof(hostaddr.buf)) {
msg_warn("test_normalize_v4mapped_hostaddr: input too large");
return (FAIL);
}
memcpy(hostaddr.buf, tp->in_hostaddr, strlen(tp->in_hostaddr) + 1);
inet_proto_init("test_normalize_v4mapped_hostaddr", tp->inet_protocols);
/*
* Exercise the function under test: normalize_v4mapped_hostaddr().
*/
got_return = normalize_v4mapped_hostaddr(&hostaddr);
/*
* Verify the results.
*/
if (got_return != tp->want_return) {
msg_warn("got return value %d, want %d", got_return, tp->want_return);
return (FAIL);
}
if (strcmp(hostaddr.buf, tp->want_hostaddr) != 0) {
msg_warn("got hostaddr '%s', want '%s'",
hostaddr.buf, tp->want_hostaddr);
return (FAIL);
}
return (PASS);
}
static int test_normalize_v4mapped_sockaddr(const TEST_CASE *tp)
{
int got_return;
MAI_HOSTADDR_STR hostaddr;
int err;
struct addrinfo *res = 0;
struct sockaddr_storage ss;
SOCKADDR_SIZE ss_len;
/*
* Prepare inputs.
*/
if (strlen(tp->in_hostaddr) >= sizeof(hostaddr.buf)) {
msg_warn("test_normalize_v4mapped_hostaddr: input too large");
return (FAIL);
}
memcpy(hostaddr.buf, tp->in_hostaddr, strlen(tp->in_hostaddr) + 1);
inet_proto_init("test_normalize_v4mapped_sockaddr", tp->inet_protocols);
/*
* Convert the input hostaddr to sockaddr.
*/
if ((err = hostaddr_to_sockaddr(tp->in_hostaddr, (char *) 0, 0, &res)) != 0) {
msg_warn("hostaddr_to_sockaddr(\"%s\"...): %s",
tp->in_hostaddr, MAI_STRERROR(err));
return (FAIL);
}
memcpy((void *) &ss, res->ai_addr, res->ai_addrlen);
ss_len = res->ai_addrlen;
freeaddrinfo(res);
/*
* Exercise the function under test: normalize_v4mapped_sockaddr().
*/
got_return = normalize_v4mapped_sockaddr((struct sockaddr *) &ss, &ss_len);
/*
* Convert the output sockaddr to hostaddr.
*/
if ((err = sockaddr_to_hostaddr((struct sockaddr *) &ss, ss_len,
&hostaddr, (MAI_SERVPORT_STR *) 0, 0)) != 0) {
msg_warn("cannot convert address to string: %s", MAI_STRERROR(err));
return (FAIL);
}
/*
* Verify the results.
*/
if (got_return != tp->want_return) {
msg_warn("got return value %d, want %d", got_return, tp->want_return);
return (FAIL);
}
if (strcmp(hostaddr.buf, tp->want_hostaddr) != 0) {
msg_warn("got hostaddr '%s', want '%s'",
hostaddr.buf, tp->want_hostaddr);
return (FAIL);
}
return (PASS);
}
static const TEST_CASE test_cases[] = {
{.label = "does not convert v4 address, ipv4 enabled",
.inet_protocols = "ipv6, ipv4",
.in_hostaddr = "192.168.1.1",
.want_return = 0,
.want_hostaddr = "192.168.1.1",
},
#if 0
/* Can't test IPv4 forms with ipv4 disabled. */
{.label = "does not convert v4 address, ipv4 disabled",
.inet_protocols = "ipv6",
.in_hostaddr = "192.168.1.1",
.want_return = 0,
.want_hostaddr = "192.168.1.1",
},
#endif
{.label = "does not convert v4inv6 address, ipv4 disabled",
.inet_protocols = "ipv6",
.in_hostaddr = "::ffff:192.168.1.1",
.want_return = 0,
.want_hostaddr = "::ffff:192.168.1.1",
},
{.label = "converts v4inv6 address, ipv4 enabled",
.inet_protocols = "ipv6, ipv4",
.in_hostaddr = "::ffff:192.168.1.1",
.want_return = 1,
.want_hostaddr = "192.168.1.1",
},
0,
};
int main(int argc, char **argv)
{
const TEST_CASE *tp;
int pass = 0;
int fail = 0;
struct test_action {
int (*act_fn) (const TEST_CASE *);
const char *act_name;
};
static struct test_action actions[] = {
test_normalize_v4mapped_hostaddr, "test_normalize_v4mapped_hostaddr",
test_normalize_v4mapped_sockaddr, "test_normalize_v4mapped_sockaddr",
0
};
struct test_action *ap;
msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR);
for (ap = actions; ap->act_fn; ap++) {
for (tp = test_cases; tp->label != 0; tp++) {
msg_info("RUN %s/%s", ap->act_name, tp->label);
if (ap->act_fn(tp) != PASS) {
fail++;
msg_info("FAIL %s/%s", ap->act_name, tp->label);
} else {
msg_info("PASS %s/%s", ap->act_name, tp->label);
pass++;
}
}
}
msg_info("PASS=%d FAIL=%d", pass, fail);
exit(fail != 0);
}

View File

@ -13,14 +13,13 @@
/* MAI_SERVPORT_STR *port_buf,
/* int socktype)
/* DESCRIPTION
/* sane_sockaddr_to_hostaddr() wraps sockaddr_to_hostaddr() and
/* converts an IPv4 in IPv6 address to IPv4 form, but only if IPv4
/* support is available.
/* HISTORY
/* .ad
/* .fi
/* This implementation was taken from postscreen, and consolidates
/* multiple instances of similar code across the Postfix code base.
/* sane_sockaddr_to_hostaddr() converts a V4mapped IPv6 address to
/* IPv4 form, but only if IPv4 support is available. It then invokes
/* sockaddr_to_hostaddr() to convert the result to human-readable
/* form.
/*
/* NOTE: The V4mapped IPv6 conversion to IPv4 applies to both inputs
/* and output.
/* LICENSE
/* .ad
/* .fi
@ -49,28 +48,20 @@
* Utility library.
*/
#include <myaddrinfo.h>
#include <inet_proto.h>
static const INET_PROTO_INFO *proto_info;
#include <normalize_v4mapped_addr.h>
/* sane_sockaddr_to_hostaddr - sanitize IPV4 in IPV6 address */
int sane_sockaddr_to_hostaddr(const struct sockaddr *addr_storage,
SOCKADDR_SIZE addr_storage_len,
int sane_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 (proto_info == 0)
proto_info = inet_proto_info();
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);
#ifdef AF_INET6
if (addr_storage->sa_family == AF_INET6)
normalize_v4mapped_sockaddr(addr_storage, addr_storage_len);
#endif
return (sockaddr_to_hostaddr(addr_storage, *addr_storage_len,
addr_buf, port_buf, socktype));
}