2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-28 12:48:01 +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 quotes. Oscar Bataille reported that the non-recommended
form is not protected against SQL injection. Files: form is not protected against SQL injection. Files:
global/dict_sqlite.c, global/dict_sqlite_test.c. 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 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> 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 <p> Caution: when postscreen rejects mail, its SMTP response contains
the DNSBL the DNSBL
domain name. Use the <a href="postconf.5.html#postscreen_dnsbl_reply_map">postscreen_dnsbl_reply_map</a> feature to hide 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 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 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>. 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> <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 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 <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 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 result is DEFER_IF_REJECT when allowlist lookup fails. <br>
is available in Postfix 2.8 and later. </dd> 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> <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 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> 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. 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 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 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> <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. 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 <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 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 when allowlist lookup fails. <br>
2.8 and later. </dd> 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> <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 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 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 <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 additional RBL related configuration parameters. <br>
available in Postfix 2.8 and later. </dd> 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> <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 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 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 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 EHLO). <br>
and later. </dd> 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> <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: 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 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 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 replies indexed by <i>rbl_domain</i>. <br>
in Postfix version 2.0 and later.</dd> 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> <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 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 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>. 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> 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> <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 and \fBpostscreen\fR(8) will update an SMTP client's DNSBL score with
each non\-error reply as described below. each non\-error reply as described below.
.PP .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 Caution: when postscreen rejects mail, its SMTP response contains
the DNSBL the DNSBL
domain name. Use the postscreen_dnsbl_reply_map feature to hide 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 rejected requests (default: 554), the default_rbl_reply parameter
specifies the default server reply, and the rbl_reply_maps parameter specifies the default server reply, and the rbl_reply_maps parameter
specifies tables with server replies indexed by \fIrbl_domain\fR. 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. This feature is available in Postfix 2.0 and later.
.br .br
.IP "\fBpermit_dnswl_client \fIdnswl_domain=d.d.d.d\fR\fR" .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 .br
For safety, permit_dnswl_client is silently For safety, permit_dnswl_client is silently
ignored when it would override reject_unauth_destination. The ignored when it would override reject_unauth_destination. The
result is DEFER_IF_REJECT when allowlist lookup fails. This feature result is DEFER_IF_REJECT when allowlist lookup fails.
is available in Postfix 2.8 and later. .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 .br
.IP "\fBreject_rhsbl_client \fIrbl_domain=d.d.d.d\fR\fR" .IP "\fBreject_rhsbl_client \fIrbl_domain=d.d.d.d\fR\fR"
Reject the request when the client hostname is listed with the 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 hostname is listed with
any A record under \fIrbl_domain\fR. See the reject_rbl_client any A record under \fIrbl_domain\fR. See the reject_rbl_client
description above for additional RBL related configuration parameters. 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 This feature is available in Postfix 2.0 and later; with Postfix
version 2.8 and later, reject_rhsbl_reverse_client will usually version 2.8 and later, reject_rhsbl_reverse_client will usually
produce better results. produce better results.
@ -10426,8 +10448,14 @@ DNS\-based blocklists, and not for making access rule exceptions.
.br .br
For safety, permit_rhswl_client is silently ignored when it For safety, permit_rhswl_client is silently ignored when it
would override reject_unauth_destination. The result is DEFER_IF_REJECT would override reject_unauth_destination. The result is DEFER_IF_REJECT
when allowlist lookup fails. This feature is available in Postfix when allowlist lookup fails.
2.8 and later. .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 .br
.IP "\fBreject_rhsbl_reverse_client \fIrbl_domain=d.d.d.d\fR\fR" .IP "\fBreject_rhsbl_reverse_client \fIrbl_domain=d.d.d.d\fR\fR"
Reject the request when the unverified reverse client hostname 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 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 unverified reverse client hostname is listed with any A record under
\fIrbl_domain\fR. See the reject_rbl_client description above for \fIrbl_domain\fR. See the reject_rbl_client description above for
additional RBL related configuration parameters. This feature is additional RBL related configuration parameters.
available in Postfix 2.8 and later. .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 .br
.IP "\fBreject_unknown_client_hostname\fR (with Postfix < 2.3: reject_unknown_client)" .IP "\fBreject_unknown_client_hostname\fR (with Postfix < 2.3: reject_unknown_client)"
Reject the request when 1) the client IP address\->name mapping 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 parameters. Note: specify "smtpd_helo_required = yes" to fully
enforce this restriction (without "smtpd_helo_required = yes", a enforce this restriction (without "smtpd_helo_required = yes", a
client can simply skip reject_rhsbl_helo by not sending HELO or client can simply skip reject_rhsbl_helo by not sending HELO or
EHLO). This feature is available in Postfix 2.0 EHLO).
and later. .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 .br
.IP "\fBreject_unknown_helo_hostname\fR (with Postfix < 2.3: reject_unknown_hostname)" .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 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: parameter specifies the response code for rejected requests (default:
554); the default_rbl_reply parameter specifies the default server 554); the default_rbl_reply parameter specifies the default server
reply; and the rbl_reply_maps parameter specifies tables with server reply; and the rbl_reply_maps parameter specifies tables with server
replies indexed by \fIrbl_domain\fR. This feature is available replies indexed by \fIrbl_domain\fR.
in Postfix version 2.0 and later. .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 .br
.IP "\fBreject_unauth_destination\fR" .IP "\fBreject_unauth_destination\fR"
Reject the request unless one of the following is true: 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 rejected requests (default: 554); the default_rbl_reply parameter
specifies the default server reply; and the rbl_reply_maps parameter specifies the default server reply; and the rbl_reply_maps parameter
specifies tables with server replies indexed by \fIrbl_domain\fR. 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. This feature is available in Postfix 2.0 and later.
.br .br
.IP "\fBreject_sender_login_mismatch\fR" .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 rejected requests (default: 554), the default_rbl_reply parameter
specifies the default server reply, and the rbl_reply_maps parameter specifies the default server reply, and the rbl_reply_maps parameter
specifies tables with server replies indexed by <i>rbl_domain</i>. 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> <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 reversed client network address is listed with any A record under
<i>dnswl_domain</i>. <br> For safety, permit_dnswl_client is silently <i>dnswl_domain</i>. <br> For safety, permit_dnswl_client is silently
ignored when it would override reject_unauth_destination. The ignored when it would override reject_unauth_destination. The
result is DEFER_IF_REJECT when allowlist lookup fails. This feature result is DEFER_IF_REJECT when allowlist lookup fails. <br>
is available in Postfix 2.8 and later. </dd> 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> <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 hostname is listed with
any A record under <i>rbl_domain</i>. See the reject_rbl_client any A record under <i>rbl_domain</i>. See the reject_rbl_client
description above for additional RBL related configuration parameters. 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 This feature is available in Postfix 2.0 and later; with Postfix
version 2.8 and later, reject_rhsbl_reverse_client will usually 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> <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. DNS-based blocklists, and not for making access rule exceptions.
<br> For safety, permit_rhswl_client is silently ignored when it <br> For safety, permit_rhswl_client is silently ignored when it
would override reject_unauth_destination. The result is DEFER_IF_REJECT would override reject_unauth_destination. The result is DEFER_IF_REJECT
when allowlist lookup fails. This feature is available in Postfix when allowlist lookup fails. <br>
2.8 and later. </dd> 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> <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 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 unverified reverse client hostname is listed with any A record under
<i>rbl_domain</i>. See the reject_rbl_client description above for <i>rbl_domain</i>. See the reject_rbl_client description above for
additional RBL related configuration parameters. This feature is additional RBL related configuration parameters. <br>
available in Postfix 2.8 and later. </dd> 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> <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 parameters. Note: specify "smtpd_helo_required = yes" to fully
enforce this restriction (without "smtpd_helo_required = yes", a enforce this restriction (without "smtpd_helo_required = yes", a
client can simply skip reject_rhsbl_helo by not sending HELO or client can simply skip reject_rhsbl_helo by not sending HELO or
EHLO). This feature is available in Postfix 2.0 EHLO). <br>
and later. </dd> 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> <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: parameter specifies the response code for rejected requests (default:
554); the default_rbl_reply parameter specifies the default server 554); the default_rbl_reply parameter specifies the default server
reply; and the rbl_reply_maps parameter specifies tables with server reply; and the rbl_reply_maps parameter specifies tables with server
replies indexed by <i>rbl_domain</i>. This feature is available replies indexed by <i>rbl_domain</i>. <br>
in Postfix version 2.0 and later.</dd> 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> <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 rejected requests (default: 554); the default_rbl_reply parameter
specifies the default server reply; and the rbl_reply_maps parameter specifies the default server reply; and the rbl_reply_maps parameter
specifies tables with server replies indexed by <i>rbl_domain</i>. 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> 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> <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 and postscreen(8) will update an SMTP client's DNSBL score with
each non-error reply as described below. </p> 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 <p> Caution: when postscreen rejects mail, its SMTP response contains
the DNSBL the DNSBL
domain name. Use the postscreen_dnsbl_reply_map feature to hide domain name. Use the postscreen_dnsbl_reply_map feature to hide

View File

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

View File

@ -1858,3 +1858,5 @@ TINYCDB
getdata getdata
XXXSENDOPTS XXXSENDOPTS
xtra 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 \ data_redirect addr_match_list safe_ultostr verify_sender_addr \
mail_version mail_dict server_acl uxtext mail_parm_split \ mail_version mail_dict server_acl uxtext mail_parm_split \
fold_addr smtp_reply_footer mail_addr_map normalize_mailhost_addr \ 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 \ compat_level config_known_tcp_ports hfrom_format rfc2047_code \
ascii_header_text sendopts_test dict_sqlite_test 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) normalize_mailhost_addr: normalize_mailhost_addr.c $(LIB) $(LIBS)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) $(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) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
map_search: map_search.c $(LIB) $(LIBS) 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 \ safe_ultostr_test mail_parm_split_test fold_addr_test \
smtp_reply_footer_test off_cvt_test mail_addr_crunch_test \ smtp_reply_footer_test off_cvt_test mail_addr_crunch_test \
mail_addr_find_test mail_addr_map_test quote_822_local_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 \ delivered_hdr_test login_sender_match_test compat_level_test \
config_known_tcp_ports_test hfrom_format_test rfc2047_code_test \ config_known_tcp_ports_test hfrom_format_test rfc2047_code_test \
ascii_header_text_test test_sendopts test_dict_sqlite 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 diff /dev/null normalize_mailhost_addr.tmp
rm -f normalize_mailhost_addr.tmp rm -f normalize_mailhost_addr.tmp
haproxy_srvr_test: update haproxy_srvr test_haproxy_srvr: update haproxy_srvr_test
-$(SHLIB_ENV) $(VALGRIND) ./haproxy_srvr >haproxy_srvr.tmp 2>&1 $(SHLIB_ENV) $(VALGRIND) ./haproxy_srvr_test
diff /dev/null haproxy_srvr.tmp
rm -f haproxy_srvr.tmp
map_search_test: update map_search map_search.ref map_search_test: update map_search map_search.ref
-$(SHLIB_ENV) $(VALGRIND) ./map_search >map_search.tmp 2>&1 -$(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/msg.h
haproxy_srvr.o: ../../include/myaddrinfo.h haproxy_srvr.o: ../../include/myaddrinfo.h
haproxy_srvr.o: ../../include/mymalloc.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/sock_addr.h
haproxy_srvr.o: ../../include/split_at.h haproxy_srvr.o: ../../include/split_at.h
haproxy_srvr.o: ../../include/stringops.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: ../../include/vstring.h
haproxy_srvr.o: haproxy_srvr.c haproxy_srvr.o: haproxy_srvr.c
haproxy_srvr.o: haproxy_srvr.h 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/argv.h
header_body_checks.o: ../../include/check_arg.h header_body_checks.o: ../../include/check_arg.h
header_body_checks.o: ../../include/dict.h header_body_checks.o: ../../include/dict.h

View File

@ -6,6 +6,31 @@
/* SYNOPSIS /* SYNOPSIS
/* #include <haproxy_srvr.h> /* #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, /* const char *haproxy_srvr_parse(str, str_len, non_proxy,
/* smtp_client_addr, smtp_client_port, /* smtp_client_addr, smtp_client_port,
/* smtp_server_addr, smtp_server_port) /* smtp_server_addr, smtp_server_port)
@ -27,7 +52,7 @@
/* MAI_HOSTADDR_STR *smtp_server_addr, /* MAI_HOSTADDR_STR *smtp_server_addr,
/* MAI_SERVPORT_STR *smtp_server_port; /* MAI_SERVPORT_STR *smtp_server_port;
/* DESCRIPTION /* 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 /* message. The result is null in case of success, a pointer
/* to text (with the error type) in case of error. If both /* to text (with the error type) in case of error. If both
/* IPv6 and IPv4 support are enabled, IPV4_IN_IPV6 address /* 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 /* of bytes parsed, and the non_proxy argument is true or false
/* if the haproxy message specifies a non-proxied connection. /* 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 /* handshake. This must be called before any I/O is done on
/* the specified file descriptor. The result is 0 in case of /* the specified file descriptor. The result is 0 in case of
/* success, -1 in case of error. All errors are logged. /* success, -1 in case of error. All errors are logged.
@ -45,6 +70,13 @@
/* TCP over IPv6, and non-proxied connections. In the latter /* TCP over IPv6, and non-proxied connections. In the latter
/* case, the caller is responsible for any local or remote /* case, the caller is responsible for any local or remote
/* address/port lookup. /* 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 /* LICENSE
/* .ad /* .ad
/* .fi /* .fi
@ -59,6 +91,9 @@
/* Google, Inc. /* Google, Inc.
/* 111 8th Avenue /* 111 8th Avenue
/* New York, NY 10011, USA /* New York, NY 10011, USA
/*
/* Wietse Venema
/* porcupine.org
/*--*/ /*--*/
/* System library. */ /* System library. */
@ -82,89 +117,13 @@
#include <inet_proto.h> #include <inet_proto.h>
#include <split_at.h> #include <split_at.h>
#include <sock_addr.h> #include <sock_addr.h>
#include <normalize_v4mapped_addr.h>
/* Global library. */ /* Global library. */
#define _HAPROXY_SRVR_INTERNAL_
#include <haproxy_srvr.h> #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; static const INET_PROTO_INFO *proto_info;
#define STR_OR_NULL(str) ((str) ? (str) : "(null)") #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 */ /* haproxy_srvr_parse_addr - extract and validate IP address */
static int haproxy_srvr_parse_addr(const char *str, MAI_HOSTADDR_STR *addr, 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; int err;
struct sockaddr_storage ss;
SOCKADDR_SIZE ss_len;
if (msg_verbose) if (msg_verbose)
msg_info("haproxy_srvr_parse: addr=%s proto=%d", 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) { switch (addr_family) {
#ifdef AF_INET6 #ifdef AF_INET6
case AF_INET6: case AF_INET6:
err = !valid_ipv6_hostaddr(str, DONT_GRIPE); if (!valid_ipv6_hostaddr(str, DONT_GRIPE))
return (-1);
break; break;
#endif #endif
case AF_INET: case AF_INET:
err = !valid_ipv4_hostaddr(str, DONT_GRIPE); if (!valid_ipv4_hostaddr(str, DONT_GRIPE))
return (-1);
break; break;
default: default:
msg_panic("haproxy_srvr_parse: unexpected address family: %d", msg_panic("haproxy_srvr_parse: unexpected address family: %d",
addr_family); addr_family);
} }
if (err == 0)
err = (hostaddr_to_sockaddr(str, (char *) 0, 0, &res) /*
|| sane_sockaddr_to_hostaddr(res->ai_addr, res->ai_addrlen, * Convert the printable address to canonical form. Don't rely on the
addr, (MAI_SERVPORT_STR *) 0, 0)); * proxy. This requires a conversion to binary form and back, even if a
if (res) * caller such as postscreen does not need the binary form.
freeaddrinfo(res); */
if (err) 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); 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); return (0);
} }
/* haproxy_srvr_parse_port - extract and validate TCP port */ /* 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) if (msg_verbose)
msg_info("haproxy_srvr_parse: port=%s", STR_OR_NULL(str)); 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); return (-1);
} else { } else {
memcpy(port->buf, str, strlen(str) + 1); 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); 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, static int haproxy_srvr_parse_v2_addr_v4(uint32_t sin_addr,
unsigned sin_port, unsigned sin_port,
MAI_HOSTADDR_STR *addr, MAI_HOSTADDR_STR *addr,
MAI_SERVPORT_STR *port) MAI_SERVPORT_STR *port,
struct sockaddr *sa,
SOCKADDR_SIZE *sa_len)
{ {
struct sockaddr_in sin; struct sockaddr_in sin;
SOCKADDR_SIZE sin_len;
memset((void *) &sin, 0, sizeof(sin)); /*
sin.sin_family = AF_INET; * Convert the binary address and port to printable form.
sin.sin_addr.s_addr = sin_addr; */
sin.sin_port = sin_port; if (sa == 0) {
if (sockaddr_to_hostaddr((struct sockaddr *) &sin, sizeof(sin), sa = (struct sockaddr *) &sin;
addr, port, 0) < 0) 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 (-1);
return (0); 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, static int haproxy_srvr_parse_v2_addr_v6(uint8_t *sin6_addr,
unsigned sin6_port, unsigned sin6_port,
MAI_HOSTADDR_STR *addr, MAI_HOSTADDR_STR *addr,
MAI_SERVPORT_STR *port) MAI_SERVPORT_STR *port,
struct sockaddr *sa,
SOCKADDR_SIZE *sa_len)
{ {
struct sockaddr_in6 sin6; struct sockaddr_in6 sin6;
SOCKADDR_SIZE sin6_len;
memset((void *) &sin6, 0, sizeof(sin6)); /*
sin6.sin6_family = AF_INET6; * Convert the binary address and port to printable form.
memcpy(&sin6.sin6_addr, sin6_addr, 16); */
sin6.sin6_port = sin6_port; if (sa == 0) {
if (sockaddr_to_hostaddr((struct sockaddr *) &sin6, sa = (struct sockaddr *) &sin6;
sizeof(sin6), addr, port, 0) < 0) 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); 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); 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_HOSTADDR_STR *smtp_client_addr,
MAI_SERVPORT_STR *smtp_client_port, MAI_SERVPORT_STR *smtp_client_port,
MAI_HOSTADDR_STR *smtp_server_addr, 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"; const char myname[] = "haproxy_srvr_parse_v2_hdr";
struct proxy_hdr_v2 *hdr_v2; 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"); return ("short address field");
if (haproxy_srvr_parse_v2_addr_v4(hdr_v2->addr.ip4.src_addr, if (haproxy_srvr_parse_v2_addr_v4(hdr_v2->addr.ip4.src_addr,
hdr_v2->addr.ip4.src_port, 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"); return ("client network address conversion error");
if (msg_verbose) if (msg_verbose)
msg_info("%s: smtp_client_addr=%s smtp_client_port=%s", msg_info("%s: smtp_client_addr=%s smtp_client_port=%s",
myname, smtp_client_addr->buf, smtp_client_port->buf); myname, smtp_client_addr->buf, smtp_client_port->buf);
if (haproxy_srvr_parse_v2_addr_v4(hdr_v2->addr.ip4.dst_addr, if (haproxy_srvr_parse_v2_addr_v4(hdr_v2->addr.ip4.dst_addr,
hdr_v2->addr.ip4.dst_port, 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"); return ("server network address conversion error");
if (msg_verbose) if (msg_verbose)
msg_info("%s: smtp_server_addr=%s smtp_server_port=%s", 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, if (haproxy_srvr_parse_v2_addr_v6(hdr_v2->addr.ip6.src_addr,
hdr_v2->addr.ip6.src_port, hdr_v2->addr.ip6.src_port,
smtp_client_addr, smtp_client_addr,
smtp_client_port) < 0) smtp_client_port,
client_sa,
client_sa_len) < 0)
return ("client network address conversion error"); return ("client network address conversion error");
if (msg_verbose) if (msg_verbose)
msg_info("%s: smtp_client_addr=%s smtp_client_port=%s", 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, if (haproxy_srvr_parse_v2_addr_v6(hdr_v2->addr.ip6.dst_addr,
hdr_v2->addr.ip6.dst_port, hdr_v2->addr.ip6.dst_port,
smtp_server_addr, smtp_server_addr,
smtp_server_port) < 0) smtp_server_port,
server_sa,
server_sa_len) < 0)
return ("server network address conversion error"); return ("server network address conversion error");
if (msg_verbose) if (msg_verbose)
msg_info("%s: smtp_server_addr=%s smtp_server_port=%s", 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, const char *haproxy_srvr_parse_sa(const char *str, ssize_t *str_len,
int *non_proxy, int *non_proxy,
MAI_HOSTADDR_STR *smtp_client_addr, MAI_HOSTADDR_STR *smtp_client_addr,
MAI_SERVPORT_STR *smtp_client_port, MAI_SERVPORT_STR *smtp_client_port,
MAI_HOSTADDR_STR *smtp_server_addr, 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 *err; 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) else if (haproxy_srvr_parse_proto(NEXT_TOKEN, &addr_family) < 0)
err = "bad or missing protocol type"; err = "bad or missing protocol type";
else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_client_addr, 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"; err = "bad or missing client address";
else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_server_addr, 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"; 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"; 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"; err = "bad or missing server port";
else { else {
err = 0; err = 0;
@ -480,17 +538,48 @@ const char *haproxy_srvr_parse(const char *str, ssize_t *str_len,
else { else {
return (haproxy_srvr_parse_v2_hdr(str, str_len, non_proxy, return (haproxy_srvr_parse_v2_hdr(str, str_len, non_proxy,
smtp_client_addr, smtp_client_port, 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, #undef haproxy_srvr_parse
MAI_HOSTADDR_STR *smtp_client_addr,
MAI_SERVPORT_STR *smtp_client_port, const char *haproxy_srvr_parse(const char *str, ssize_t *str_len,
MAI_HOSTADDR_STR *smtp_server_addr, int *non_proxy,
MAI_SERVPORT_STR *smtp_server_port) 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; const char *err;
VSTRING *escape_buf; VSTRING *escape_buf;
@ -513,9 +602,11 @@ int haproxy_srvr_receive(int fd, int *non_proxy,
*/ */
read_buf[read_len] = 0; read_buf[read_len] = 0;
if ((err = haproxy_srvr_parse(read_buf, &read_len, non_proxy, if ((err = haproxy_srvr_parse_sa(read_buf, &read_len, non_proxy,
smtp_client_addr, smtp_client_port, smtp_client_addr, smtp_client_port,
smtp_server_addr, smtp_server_port)) != 0) { 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_buf = vstring_alloc(read_len * 2);
escape(escape_buf, read_buf, read_len); escape(escape_buf, read_buf, read_len);
msg_warn("haproxy read: %s: %s", err, vstring_str(escape_buf)); 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); return (0);
} }
/* /* haproxy_srvr_receive - ABI compatibility */
* Test program.
*/
#ifdef TEST
/* #undef haproxy_srvr_receive
* 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,
};
static struct proxy_hdr_v2 v2_local_request = { int haproxy_srvr_receive(int fd, int *non_proxy,
PP2_SIGNATURE, PP2_VERSION | PP2_CMD_LOCAL, MAI_HOSTADDR_STR *smtp_client_addr,
}; MAI_SERVPORT_STR *smtp_client_port,
static TEST_CASE v2_non_proxy_test = { MAI_HOSTADDR_STR *smtp_server_addr,
(char *) &v2_local_request, PP2_HEADER_LEN, PP2_HEADER_LEN, 1, MAI_SERVPORT_STR *smtp_server_port);
};
#define STR(x) vstring_str(x) int haproxy_srvr_receive(int fd, int *non_proxy,
#define LEN(x) VSTRING_LEN(x) MAI_HOSTADDR_STR *smtp_client_addr,
MAI_SERVPORT_STR *smtp_client_port,
/* evaluate_test_case - evaluate one test case */ MAI_HOSTADDR_STR *smtp_server_addr,
MAI_SERVPORT_STR *smtp_server_port)
static int evaluate_test_case(const char *test_label,
const TEST_CASE *test_case)
{ {
/* Actual results. */ return (haproxy_srvr_receive_sa(fd, non_proxy,
const char *act_return; smtp_client_addr, smtp_client_port,
ssize_t act_req_len; smtp_server_addr, smtp_server_port,
int act_non_proxy; (struct sockaddr *) 0,
MAI_HOSTADDR_STR act_smtp_client_addr; (SOCKADDR_SIZE *) 0,
MAI_HOSTADDR_STR act_smtp_server_addr; (struct sockaddr *) 0,
MAI_SERVPORT_STR act_smtp_client_port; (SOCKADDR_SIZE *) 0));
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);
} }
/* 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. * 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 *,
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 *,
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" #define HAPROXY_PROTO_NAME "haproxy"
@ -33,6 +53,86 @@ extern int haproxy_srvr_receive(int, int *,
#define DONT_GRIPE 0 #define DONT_GRIPE 0
#endif #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 /* LICENSE
/* .ad /* .ad
/* .fi /* .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 * Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only. * patchlevel; they change the release date only.
*/ */
#define MAIL_RELEASE_DATE "20250323" #define MAIL_RELEASE_DATE "20250409"
#define MAIL_VERSION_NUMBER "3.11" #define MAIL_VERSION_NUMBER "3.11"
#ifdef SNAPSHOT #ifdef SNAPSHOT

View File

@ -116,7 +116,7 @@ void psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
PSC_ENDPT_LOOKUP_FN lookup_done) PSC_ENDPT_LOOKUP_FN lookup_done)
{ {
struct sockaddr_storage addr_storage; struct sockaddr_storage addr_storage;
SOCKADDR_SIZE addr_storage_len = sizeof(addr_storage); SOCKADDR_SIZE addr_storage_len;
int status; int status;
MAI_HOSTADDR_STR smtp_client_addr; MAI_HOSTADDR_STR smtp_client_addr;
MAI_SERVPORT_STR smtp_client_port; 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; MAI_SERVPORT_STR smtp_server_port;
int aierr; int aierr;
#define RESET_ADDR_STORAGE_LEN() (addr_storage_len = sizeof(addr_storage))
/* /*
* Look up the remote SMTP client address and port. * 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) { &addr_storage, &addr_storage_len) < 0) {
msg_warn("getpeername: %m -- dropping this connection"); msg_warn("getpeername: %m -- dropping this connection");
status = -1; 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 * 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( else if ((aierr = sane_sockaddr_to_hostaddr(
(struct sockaddr *) &addr_storage, (struct sockaddr *) &addr_storage,
addr_storage_len, &smtp_client_addr, &addr_storage_len, &smtp_client_addr,
&smtp_client_port, SOCK_STREAM)) != 0) { &smtp_client_port, SOCK_STREAM)) != 0) {
msg_warn("cannot convert client address/port to string: %s" msg_warn("cannot convert client address/port to string: %s"
" -- dropping this connection", " -- 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, (struct sockaddr *) &addr_storage,
&addr_storage_len) < 0) { &addr_storage_len) < 0) {
msg_warn("getsockname: %m -- dropping this connection"); 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 * 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( else if ((aierr = sane_sockaddr_to_hostaddr(
(struct sockaddr *) &addr_storage, (struct sockaddr *) &addr_storage,
addr_storage_len, &smtp_server_addr, &addr_storage_len, &smtp_server_addr,
&smtp_server_port, SOCK_STREAM)) != 0) { &smtp_server_port, SOCK_STREAM)) != 0) {
msg_warn("cannot convert server address/port to string: %s" msg_warn("cannot convert server address/port to string: %s"
" -- dropping this connection", " -- dropping this connection",

View File

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

View File

@ -58,6 +58,10 @@
/* .IP dest_port /* .IP dest_port
/* Server port, available as Milter {daemon_port} macro, and /* Server port, available as Milter {daemon_port} macro, and
/* as server_port policy delegation attribute. /* 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 /* .IP name_status
/* The name_status result field specifies how the name /* The name_status result field specifies how the name
/* information should be interpreted: /* information should be interpreted:
@ -117,6 +121,9 @@
/* Google, Inc. /* Google, Inc.
/* 111 8th Avenue /* 111 8th Avenue
/* New York, NY 10011, USA /* New York, NY 10011, USA
/*
/* Wietse Venema
/* porcupine.org
/*--*/ /*--*/
/* System library. */ /* System library. */
@ -617,6 +624,7 @@ void smtpd_peer_init(SMTPD_STATE *state)
state->anvil_range = 0; state->anvil_range = 0;
state->dest_addr = 0; state->dest_addr = 0;
state->dest_port = 0; state->dest_port = 0;
state->dest_sockaddr_len = 0;
/* /*
* Determine the remote SMTP client address and port. * 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 \ 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 \ inet_addr_sizes.c quote_for_json.c mystrerror.c \
sane_sockaddr_to_hostaddr.c normalize_ws.c valid_uri_scheme.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 \ 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_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 \ 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 \ 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 \ 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 \ 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. # 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), # When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
# otherwise it sets the PLUGIN_* macros. # 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 \ 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 \ 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 \ 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 \ TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
stream_test.c dup2_pass_on_exec.c stream_test.c dup2_pass_on_exec.c
DEFS = -I. -D$(SYSTYPE) 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 \ vbuf_print split_qnameval vstream msg_logger byte_mask \
known_tcp_ports dict_stream find_inet binhash hash_fnv argv \ known_tcp_ports dict_stream find_inet binhash hash_fnv argv \
clean_env inet_prefix_top printable readlline quote_for_json \ 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) \ PLUGIN_MAP_SO = $(LIB_PREFIX)pcre$(LIB_SUFFIX) $(LIB_PREFIX)lmdb$(LIB_SUFFIX) \
$(LIB_PREFIX)cdb$(LIB_SUFFIX) $(LIB_PREFIX)sdbm$(LIB_SUFFIX) $(LIB_PREFIX)cdb$(LIB_SUFFIX) $(LIB_PREFIX)sdbm$(LIB_SUFFIX)
HTABLE_FIX = NORANDOMIZE=1 HTABLE_FIX = NORANDOMIZE=1
@ -630,6 +632,9 @@ clean_ascii_cntrl_space: $(LIB)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o 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 \ tests: all valid_hostname_test mac_expand_test dict_test unescape_test \
hex_quote_test ctable_test inet_addr_list_test base64_code_test \ hex_quote_test ctable_test inet_addr_list_test base64_code_test \
attr_scan64_test attr_scan0_test host_port_test dict_tests \ 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 \ vstream_test byte_mask_tests mystrtok_test known_tcp_ports_test \
binhash_test argv_test inet_prefix_top_test printable_test \ binhash_test argv_test inet_prefix_top_test printable_test \
valid_utf8_string_test readlline_test quote_for_json_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_tests: all dict_test \
dict_pcre_tests dict_cidr_test dict_thash_test dict_static_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 clean_ascii_cntrl_space_test: clean_ascii_cntrl_space
$(SHLIB_ENV) ${VALGRIND} ./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) depend: $(MAKES)
(sed '1,/^# do not edit/!d' Makefile.in; \ (sed '1,/^# do not edit/!d' Makefile.in; \
set -e; for i in [a-z][a-z0-9]*.c; do \ set -e; for i in [a-z][a-z0-9]*.c; do \
@ -2532,6 +2541,24 @@ non_blocking.o: iostuff.h
non_blocking.o: msg.h non_blocking.o: msg.h
non_blocking.o: non_blocking.c non_blocking.o: non_blocking.c
non_blocking.o: sys_defs.h 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: check_arg.h
normalize_ws.o: msg.h normalize_ws.o: msg.h
normalize_ws.o: msg_vstream.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: sane_rename.c
sane_rename.o: sys_defs.h sane_rename.o: sys_defs.h
sane_rename.o: warn_stat.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: 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: sane_sockaddr_to_hostaddr.c
sane_sockaddr_to_hostaddr.o: sys_defs.h sane_sockaddr_to_hostaddr.o: sys_defs.h
sane_socketpair.o: msg.h sane_socketpair.o: msg.h

View File

@ -15,7 +15,9 @@
/* .in -4 /* .in -4
/* } INET_PROTO_INFO; /* } 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() /* const INET_PROTO_INFO *inet_proto_info()
/* DESCRIPTION /* DESCRIPTION

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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