mirror of
https://github.com/vdukhovni/postfix
synced 2025-08-22 09:57:34 +00:00
postfix-3.11-20250409
This commit is contained in:
parent
ba7dbdd88a
commit
1b9f8ac2fb
@ -29044,3 +29044,40 @@ Apologies for any names omitted.
|
||||
quotes. Oscar Bataille reported that the non-recommended
|
||||
form is not protected against SQL injection. Files:
|
||||
global/dict_sqlite.c, global/dict_sqlite_test.c.
|
||||
|
||||
20250326
|
||||
|
||||
Updated myaddrinfo tests to also cover service/port conversion.
|
||||
Files: util/myaddrinfo4.ref, util/myaddrinfo4.ref2,
|
||||
util/myaddrinfo.c, util/myaddrinfo.ref, util/myaddrinfo.ref2.
|
||||
|
||||
20260402
|
||||
|
||||
Documentation: updated guidance for using DNS-based reputation
|
||||
services. File: proto/postconf.proto.
|
||||
|
||||
20250404
|
||||
|
||||
Code health: simplified the conversions from IPv4 mapped
|
||||
IPv6 addresses (::ffff:d.d.d.d) to their IPv4 form (d.d.d.d),
|
||||
for both the binary form and human-readable form. Added
|
||||
unit tests to show that the conversions work as expected.
|
||||
Files: util/normalize_v4mapped_addr.[hc],
|
||||
util/normalize_v4mapped_addr_test.c.
|
||||
|
||||
20260406
|
||||
|
||||
Code health: overhauled the haproxy adapter to simplify
|
||||
code and to avoid unnecessary conversions between binary
|
||||
and human-readable forms. Added more unit tests for the v1
|
||||
and v2 proxy protocols. A separate update will overhaul
|
||||
the smtpd 'peer' lookups. Files: global/haproxy_srvr.c,
|
||||
global/haproxy_srvr_test.c.
|
||||
|
||||
20250408
|
||||
|
||||
Code health: replace explicit code with normalize_v4mapped_xxx()
|
||||
call. File: util/sane_sockaddr_to_hostaddr.c.
|
||||
|
||||
Bit rot: sane_sockaddr_to_hostaddr() may modify its inputs.
|
||||
smtp/smtp_tlsrpt.c, postscreen/postscreen_endpt.c
|
||||
|
@ -8878,6 +8878,10 @@ clients,
|
||||
and <a href="postscreen.8.html">postscreen(8)</a> will update an SMTP client's DNSBL score with
|
||||
each non-error reply as described below. </p>
|
||||
|
||||
<p> NOTE: Always respect the usage policies of reputation services.
|
||||
Avoid public or ISP resolvers, unless the queries use your unique
|
||||
API key. </p>
|
||||
|
||||
<p> Caution: when postscreen rejects mail, its SMTP response contains
|
||||
the DNSBL
|
||||
domain name. Use the <a href="postconf.5.html#postscreen_dnsbl_reply_map">postscreen_dnsbl_reply_map</a> feature to hide
|
||||
@ -15553,7 +15557,12 @@ The <a href="postconf.5.html#maps_rbl_reject_code">maps_rbl_reject_code</a> para
|
||||
rejected requests (default: 554), the <a href="postconf.5.html#default_rbl_reply">default_rbl_reply</a> parameter
|
||||
specifies the default server reply, and the <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> parameter
|
||||
specifies tables with server replies indexed by <i>rbl_domain</i>.
|
||||
This feature is available in Postfix 2.0 and later. </dd>
|
||||
<br>
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> for how to avoid leaking the key in SMTP server
|
||||
responses). <br>
|
||||
This feature is available in Postfix 2.0 and later. </dd>
|
||||
|
||||
<dt><b><a name="permit_dnswl_client">permit_dnswl_client <i>dnswl_domain=d.d.d.d</i></a></b></dt>
|
||||
|
||||
@ -15565,8 +15574,12 @@ If no "<i>=d.d.d.d</i>" is specified, accept the request when the
|
||||
reversed client network address is listed with any A record under
|
||||
<i>dnswl_domain</i>. <br> For safety, <a href="postconf.5.html#permit_dnswl_client">permit_dnswl_client</a> is silently
|
||||
ignored when it would override <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a>. The
|
||||
result is DEFER_IF_REJECT when allowlist lookup fails. This feature
|
||||
is available in Postfix 2.8 and later. </dd>
|
||||
result is DEFER_IF_REJECT when allowlist lookup fails. <br>
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> for how to avoid leaking an API key in SMTP
|
||||
server responses). <br>
|
||||
This feature is available in Postfix 2.8 and later. </dd>
|
||||
|
||||
<dt><b><a name="reject_rhsbl_client">reject_rhsbl_client <i>rbl_domain=d.d.d.d</i></a></b></dt>
|
||||
|
||||
@ -15579,9 +15592,14 @@ number..number ranges (Postfix version 2.8 and later). If no
|
||||
hostname is listed with
|
||||
any A record under <i>rbl_domain</i>. See the <a href="postconf.5.html#reject_rbl_client">reject_rbl_client</a>
|
||||
description above for additional RBL related configuration parameters.
|
||||
<br>
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> for how to avoid leaking an API key in SMTP
|
||||
server responses). <br>
|
||||
This feature is available in Postfix 2.0 and later; with Postfix
|
||||
version 2.8 and later, <a href="postconf.5.html#reject_rhsbl_reverse_client">reject_rhsbl_reverse_client</a> will usually
|
||||
produce better results. </dd>
|
||||
produce better results. </dd>
|
||||
|
||||
<dt><b><a name="permit_rhswl_client">permit_rhswl_client <i>rhswl_domain=d.d.d.d</i></a></b></dt>
|
||||
|
||||
@ -15597,8 +15615,12 @@ allowlisting should be used only to reduce false positives in e.g.
|
||||
DNS-based blocklists, and not for making access rule exceptions.
|
||||
<br> For safety, <a href="postconf.5.html#permit_rhswl_client">permit_rhswl_client</a> is silently ignored when it
|
||||
would override <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a>. The result is DEFER_IF_REJECT
|
||||
when allowlist lookup fails. This feature is available in Postfix
|
||||
2.8 and later. </dd>
|
||||
when allowlist lookup fails. <br>
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> for how to avoid leaking an API key in SMTP
|
||||
server responses). <br>
|
||||
This feature is available in Postfix 2.8 and later.</dd>
|
||||
|
||||
<dt><b><a name="reject_rhsbl_reverse_client">reject_rhsbl_reverse_client <i>rbl_domain=d.d.d.d</i></a></b></dt>
|
||||
|
||||
@ -15609,8 +15631,12 @@ one or more ";"-separated numbers or number..number ranges.
|
||||
If no "<i>=d.d.d.d</i>" is specified, reject the request when the
|
||||
unverified reverse client hostname is listed with any A record under
|
||||
<i>rbl_domain</i>. See the <a href="postconf.5.html#reject_rbl_client">reject_rbl_client</a> description above for
|
||||
additional RBL related configuration parameters. This feature is
|
||||
available in Postfix 2.8 and later. </dd>
|
||||
additional RBL related configuration parameters. <br>
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> for how to avoid leaking an API key in SMTP
|
||||
server responses). <br>
|
||||
This feature is available in Postfix 2.8 and later. </dd>
|
||||
|
||||
<dt><b><a name="reject_unknown_client_hostname">reject_unknown_client_hostname</a></b> (with Postfix < 2.3: reject_unknown_client)</dt>
|
||||
|
||||
@ -16512,8 +16538,12 @@ listed with any A record under <i>rbl_domain</i>. See the
|
||||
parameters. Note: specify "<a href="postconf.5.html#smtpd_helo_required">smtpd_helo_required</a> = yes" to fully
|
||||
enforce this restriction (without "<a href="postconf.5.html#smtpd_helo_required">smtpd_helo_required</a> = yes", a
|
||||
client can simply skip <a href="postconf.5.html#reject_rhsbl_helo">reject_rhsbl_helo</a> by not sending HELO or
|
||||
EHLO). This feature is available in Postfix 2.0
|
||||
and later. </dd>
|
||||
EHLO). <br>
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> for how to avoid leaking an API key in SMTP
|
||||
server responses). <br>
|
||||
This feature is available in Postfix 2.0 and later. </dd>
|
||||
|
||||
<dt><b><a name="reject_unknown_helo_hostname">reject_unknown_helo_hostname</a></b> (with Postfix < 2.3: reject_unknown_hostname)</dt>
|
||||
|
||||
@ -17231,8 +17261,12 @@ any A record under <i>rbl_domain</i>. <br> The <a href="postconf.5.html#maps_rbl
|
||||
parameter specifies the response code for rejected requests (default:
|
||||
554); the <a href="postconf.5.html#default_rbl_reply">default_rbl_reply</a> parameter specifies the default server
|
||||
reply; and the <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> parameter specifies tables with server
|
||||
replies indexed by <i>rbl_domain</i>. This feature is available
|
||||
in Postfix version 2.0 and later.</dd>
|
||||
replies indexed by <i>rbl_domain</i>. <br>
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> for how to avoid leaking an API key in SMTP
|
||||
server responses). <br>
|
||||
This feature is available in Postfix version 2.0 and later.</dd>
|
||||
|
||||
<dt><b><a name="reject_unauth_destination">reject_unauth_destination</a></b></dt>
|
||||
|
||||
@ -18082,6 +18116,11 @@ listed with any A record under <i>rbl_domain</i>. <br> The
|
||||
rejected requests (default: 554); the <a href="postconf.5.html#default_rbl_reply">default_rbl_reply</a> parameter
|
||||
specifies the default server reply; and the <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> parameter
|
||||
specifies tables with server replies indexed by <i>rbl_domain</i>.
|
||||
<br>
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> for how to avoid leaking an API key in SMTP
|
||||
server responses). <br>
|
||||
This feature is available in Postfix 2.0 and later.</dd>
|
||||
|
||||
<dt><b><a name="reject_sender_login_mismatch">reject_sender_login_mismatch</a></b></dt>
|
||||
|
@ -5505,6 +5505,10 @@ clients,
|
||||
and \fBpostscreen\fR(8) will update an SMTP client's DNSBL score with
|
||||
each non\-error reply as described below.
|
||||
.PP
|
||||
NOTE: Always respect the usage policies of reputation services.
|
||||
Avoid public or ISP resolvers, unless the queries use your unique
|
||||
API key.
|
||||
.PP
|
||||
Caution: when postscreen rejects mail, its SMTP response contains
|
||||
the DNSBL
|
||||
domain name. Use the postscreen_dnsbl_reply_map feature to hide
|
||||
@ -10381,6 +10385,12 @@ The maps_rbl_reject_code parameter specifies the response code for
|
||||
rejected requests (default: 554), the default_rbl_reply parameter
|
||||
specifies the default server reply, and the rbl_reply_maps parameter
|
||||
specifies tables with server replies indexed by \fIrbl_domain\fR.
|
||||
.br
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see rbl_reply_maps for how to avoid leaking the key in SMTP server
|
||||
responses).
|
||||
.br
|
||||
This feature is available in Postfix 2.0 and later.
|
||||
.br
|
||||
.IP "\fBpermit_dnswl_client \fIdnswl_domain=d.d.d.d\fR\fR"
|
||||
@ -10394,8 +10404,14 @@ reversed client network address is listed with any A record under
|
||||
.br
|
||||
For safety, permit_dnswl_client is silently
|
||||
ignored when it would override reject_unauth_destination. The
|
||||
result is DEFER_IF_REJECT when allowlist lookup fails. This feature
|
||||
is available in Postfix 2.8 and later.
|
||||
result is DEFER_IF_REJECT when allowlist lookup fails.
|
||||
.br
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
|
||||
server responses).
|
||||
.br
|
||||
This feature is available in Postfix 2.8 and later.
|
||||
.br
|
||||
.IP "\fBreject_rhsbl_client \fIrbl_domain=d.d.d.d\fR\fR"
|
||||
Reject the request when the client hostname is listed with the
|
||||
@ -10407,6 +10423,12 @@ number..number ranges (Postfix version 2.8 and later). If no
|
||||
hostname is listed with
|
||||
any A record under \fIrbl_domain\fR. See the reject_rbl_client
|
||||
description above for additional RBL related configuration parameters.
|
||||
.br
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
|
||||
server responses).
|
||||
.br
|
||||
This feature is available in Postfix 2.0 and later; with Postfix
|
||||
version 2.8 and later, reject_rhsbl_reverse_client will usually
|
||||
produce better results.
|
||||
@ -10426,8 +10448,14 @@ DNS\-based blocklists, and not for making access rule exceptions.
|
||||
.br
|
||||
For safety, permit_rhswl_client is silently ignored when it
|
||||
would override reject_unauth_destination. The result is DEFER_IF_REJECT
|
||||
when allowlist lookup fails. This feature is available in Postfix
|
||||
2.8 and later.
|
||||
when allowlist lookup fails.
|
||||
.br
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
|
||||
server responses).
|
||||
.br
|
||||
This feature is available in Postfix 2.8 and later.
|
||||
.br
|
||||
.IP "\fBreject_rhsbl_reverse_client \fIrbl_domain=d.d.d.d\fR\fR"
|
||||
Reject the request when the unverified reverse client hostname
|
||||
@ -10437,8 +10465,14 @@ one or more ";"\-separated numbers or number..number ranges.
|
||||
If no "\fI=d.d.d.d\fR" is specified, reject the request when the
|
||||
unverified reverse client hostname is listed with any A record under
|
||||
\fIrbl_domain\fR. See the reject_rbl_client description above for
|
||||
additional RBL related configuration parameters. This feature is
|
||||
available in Postfix 2.8 and later.
|
||||
additional RBL related configuration parameters.
|
||||
.br
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
|
||||
server responses).
|
||||
.br
|
||||
This feature is available in Postfix 2.8 and later.
|
||||
.br
|
||||
.IP "\fBreject_unknown_client_hostname\fR (with Postfix < 2.3: reject_unknown_client)"
|
||||
Reject the request when 1) the client IP address\->name mapping
|
||||
@ -11155,8 +11189,14 @@ reject_rbl_client description for additional RBL related configuration
|
||||
parameters. Note: specify "smtpd_helo_required = yes" to fully
|
||||
enforce this restriction (without "smtpd_helo_required = yes", a
|
||||
client can simply skip reject_rhsbl_helo by not sending HELO or
|
||||
EHLO). This feature is available in Postfix 2.0
|
||||
and later.
|
||||
EHLO).
|
||||
.br
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
|
||||
server responses).
|
||||
.br
|
||||
This feature is available in Postfix 2.0 and later.
|
||||
.br
|
||||
.IP "\fBreject_unknown_helo_hostname\fR (with Postfix < 2.3: reject_unknown_hostname)"
|
||||
Reject the request when the HELO or EHLO hostname has no DNS A
|
||||
@ -11653,8 +11693,14 @@ The maps_rbl_reject_code
|
||||
parameter specifies the response code for rejected requests (default:
|
||||
554); the default_rbl_reply parameter specifies the default server
|
||||
reply; and the rbl_reply_maps parameter specifies tables with server
|
||||
replies indexed by \fIrbl_domain\fR. This feature is available
|
||||
in Postfix version 2.0 and later.
|
||||
replies indexed by \fIrbl_domain\fR.
|
||||
.br
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
|
||||
server responses).
|
||||
.br
|
||||
This feature is available in Postfix version 2.0 and later.
|
||||
.br
|
||||
.IP "\fBreject_unauth_destination\fR"
|
||||
Reject the request unless one of the following is true:
|
||||
@ -12298,6 +12344,12 @@ maps_rbl_reject_code parameter specifies the response code for
|
||||
rejected requests (default: 554); the default_rbl_reply parameter
|
||||
specifies the default server reply; and the rbl_reply_maps parameter
|
||||
specifies tables with server replies indexed by \fIrbl_domain\fR.
|
||||
.br
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
|
||||
server responses).
|
||||
.br
|
||||
This feature is available in Postfix 2.0 and later.
|
||||
.br
|
||||
.IP "\fBreject_sender_login_mismatch\fR"
|
||||
|
@ -5471,7 +5471,12 @@ The maps_rbl_reject_code parameter specifies the response code for
|
||||
rejected requests (default: 554), the default_rbl_reply parameter
|
||||
specifies the default server reply, and the rbl_reply_maps parameter
|
||||
specifies tables with server replies indexed by <i>rbl_domain</i>.
|
||||
This feature is available in Postfix 2.0 and later. </dd>
|
||||
<br>
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see rbl_reply_maps for how to avoid leaking the key in SMTP server
|
||||
responses). <br>
|
||||
This feature is available in Postfix 2.0 and later. </dd>
|
||||
|
||||
<dt><b><a name="permit_dnswl_client">permit_dnswl_client <i>dnswl_domain=d.d.d.d</i></a></b></dt>
|
||||
|
||||
@ -5483,8 +5488,12 @@ If no "<i>=d.d.d.d</i>" is specified, accept the request when the
|
||||
reversed client network address is listed with any A record under
|
||||
<i>dnswl_domain</i>. <br> For safety, permit_dnswl_client is silently
|
||||
ignored when it would override reject_unauth_destination. The
|
||||
result is DEFER_IF_REJECT when allowlist lookup fails. This feature
|
||||
is available in Postfix 2.8 and later. </dd>
|
||||
result is DEFER_IF_REJECT when allowlist lookup fails. <br>
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
|
||||
server responses). <br>
|
||||
This feature is available in Postfix 2.8 and later. </dd>
|
||||
|
||||
<dt><b><a name="reject_rhsbl_client">reject_rhsbl_client <i>rbl_domain=d.d.d.d</i></a></b></dt>
|
||||
|
||||
@ -5497,9 +5506,14 @@ number..number ranges (Postfix version 2.8 and later). If no
|
||||
hostname is listed with
|
||||
any A record under <i>rbl_domain</i>. See the reject_rbl_client
|
||||
description above for additional RBL related configuration parameters.
|
||||
<br>
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
|
||||
server responses). <br>
|
||||
This feature is available in Postfix 2.0 and later; with Postfix
|
||||
version 2.8 and later, reject_rhsbl_reverse_client will usually
|
||||
produce better results. </dd>
|
||||
produce better results. </dd>
|
||||
|
||||
<dt><b><a name="permit_rhswl_client">permit_rhswl_client <i>rhswl_domain=d.d.d.d</i></a></b></dt>
|
||||
|
||||
@ -5515,8 +5529,12 @@ allowlisting should be used only to reduce false positives in e.g.
|
||||
DNS-based blocklists, and not for making access rule exceptions.
|
||||
<br> For safety, permit_rhswl_client is silently ignored when it
|
||||
would override reject_unauth_destination. The result is DEFER_IF_REJECT
|
||||
when allowlist lookup fails. This feature is available in Postfix
|
||||
2.8 and later. </dd>
|
||||
when allowlist lookup fails. <br>
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
|
||||
server responses). <br>
|
||||
This feature is available in Postfix 2.8 and later.</dd>
|
||||
|
||||
<dt><b><a name="reject_rhsbl_reverse_client">reject_rhsbl_reverse_client <i>rbl_domain=d.d.d.d</i></a></b></dt>
|
||||
|
||||
@ -5527,8 +5545,12 @@ one or more ";"-separated numbers or number..number ranges.
|
||||
If no "<i>=d.d.d.d</i>" is specified, reject the request when the
|
||||
unverified reverse client hostname is listed with any A record under
|
||||
<i>rbl_domain</i>. See the reject_rbl_client description above for
|
||||
additional RBL related configuration parameters. This feature is
|
||||
available in Postfix 2.8 and later. </dd>
|
||||
additional RBL related configuration parameters. <br>
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
|
||||
server responses). <br>
|
||||
This feature is available in Postfix 2.8 and later. </dd>
|
||||
|
||||
<dt><b><a name="reject_unknown_client_hostname">reject_unknown_client_hostname</a></b> (with Postfix < 2.3: reject_unknown_client)</dt>
|
||||
|
||||
@ -6034,8 +6056,12 @@ reject_rbl_client description for additional RBL related configuration
|
||||
parameters. Note: specify "smtpd_helo_required = yes" to fully
|
||||
enforce this restriction (without "smtpd_helo_required = yes", a
|
||||
client can simply skip reject_rhsbl_helo by not sending HELO or
|
||||
EHLO). This feature is available in Postfix 2.0
|
||||
and later. </dd>
|
||||
EHLO). <br>
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
|
||||
server responses). <br>
|
||||
This feature is available in Postfix 2.0 and later. </dd>
|
||||
|
||||
<dt><b><a name="reject_unknown_helo_hostname">reject_unknown_helo_hostname</a></b> (with Postfix < 2.3: reject_unknown_hostname)</dt>
|
||||
|
||||
@ -6343,8 +6369,12 @@ any A record under <i>rbl_domain</i>. <br> The maps_rbl_reject_code
|
||||
parameter specifies the response code for rejected requests (default:
|
||||
554); the default_rbl_reply parameter specifies the default server
|
||||
reply; and the rbl_reply_maps parameter specifies tables with server
|
||||
replies indexed by <i>rbl_domain</i>. This feature is available
|
||||
in Postfix version 2.0 and later.</dd>
|
||||
replies indexed by <i>rbl_domain</i>. <br>
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
|
||||
server responses). <br>
|
||||
This feature is available in Postfix version 2.0 and later.</dd>
|
||||
|
||||
<dt><b><a name="reject_unauth_destination">reject_unauth_destination</a></b></dt>
|
||||
|
||||
@ -6861,6 +6891,11 @@ maps_rbl_reject_code parameter specifies the response code for
|
||||
rejected requests (default: 554); the default_rbl_reply parameter
|
||||
specifies the default server reply; and the rbl_reply_maps parameter
|
||||
specifies tables with server replies indexed by <i>rbl_domain</i>.
|
||||
<br>
|
||||
NOTE: Always respect the usage policies of reputation services. Avoid
|
||||
public or ISP resolvers, unless the queries use your unique API key
|
||||
(see rbl_reply_maps for how to avoid leaking an API key in SMTP
|
||||
server responses). <br>
|
||||
This feature is available in Postfix 2.0 and later.</dd>
|
||||
|
||||
<dt><b><a name="reject_sender_login_mismatch">reject_sender_login_mismatch</a></b></dt>
|
||||
@ -14699,6 +14734,10 @@ clients,
|
||||
and postscreen(8) will update an SMTP client's DNSBL score with
|
||||
each non-error reply as described below. </p>
|
||||
|
||||
<p> NOTE: Always respect the usage policies of reputation services.
|
||||
Avoid public or ISP resolvers, unless the queries use your unique
|
||||
API key. </p>
|
||||
|
||||
<p> Caution: when postscreen rejects mail, its SMTP response contains
|
||||
the DNSBL
|
||||
domain name. Use the postscreen_dnsbl_reply_map feature to hide
|
||||
|
@ -1672,3 +1672,4 @@ URIs
|
||||
bugfix
|
||||
MLKEM
|
||||
cleartext
|
||||
redacted
|
||||
|
@ -1858,3 +1858,5 @@ TINYCDB
|
||||
getdata
|
||||
XXXSENDOPTS
|
||||
xtra
|
||||
HAPROXY
|
||||
SRVR
|
||||
|
@ -129,7 +129,7 @@ TESTPROG= domain_list dot_lockfile mail_addr_crunch mail_addr_find \
|
||||
data_redirect addr_match_list safe_ultostr verify_sender_addr \
|
||||
mail_version mail_dict server_acl uxtext mail_parm_split \
|
||||
fold_addr smtp_reply_footer mail_addr_map normalize_mailhost_addr \
|
||||
haproxy_srvr map_search delivered_hdr login_sender_match \
|
||||
haproxy_srvr_test map_search delivered_hdr login_sender_match \
|
||||
compat_level config_known_tcp_ports hfrom_format rfc2047_code \
|
||||
ascii_header_text sendopts_test dict_sqlite_test
|
||||
|
||||
@ -381,7 +381,7 @@ smtp_reply_footer: smtp_reply_footer.c $(LIB) $(LIBS)
|
||||
normalize_mailhost_addr: normalize_mailhost_addr.c $(LIB) $(LIBS)
|
||||
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
|
||||
|
||||
haproxy_srvr: haproxy_srvr.c $(LIB) $(LIBS)
|
||||
haproxy_srvr_test: haproxy_srvr_test.c $(LIB) $(LIBS)
|
||||
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
|
||||
|
||||
map_search: map_search.c $(LIB) $(LIBS)
|
||||
@ -422,7 +422,7 @@ tests: tok822_test mime_tests strip_addr_test tok822_limit_test \
|
||||
safe_ultostr_test mail_parm_split_test fold_addr_test \
|
||||
smtp_reply_footer_test off_cvt_test mail_addr_crunch_test \
|
||||
mail_addr_find_test mail_addr_map_test quote_822_local_test \
|
||||
normalize_mailhost_addr_test haproxy_srvr_test map_search_test \
|
||||
normalize_mailhost_addr_test test_haproxy_srvr map_search_test \
|
||||
delivered_hdr_test login_sender_match_test compat_level_test \
|
||||
config_known_tcp_ports_test hfrom_format_test rfc2047_code_test \
|
||||
ascii_header_text_test test_sendopts test_dict_sqlite
|
||||
@ -743,10 +743,8 @@ normalize_mailhost_addr_test: update normalize_mailhost_addr
|
||||
diff /dev/null normalize_mailhost_addr.tmp
|
||||
rm -f normalize_mailhost_addr.tmp
|
||||
|
||||
haproxy_srvr_test: update haproxy_srvr
|
||||
-$(SHLIB_ENV) $(VALGRIND) ./haproxy_srvr >haproxy_srvr.tmp 2>&1
|
||||
diff /dev/null haproxy_srvr.tmp
|
||||
rm -f haproxy_srvr.tmp
|
||||
test_haproxy_srvr: update haproxy_srvr_test
|
||||
$(SHLIB_ENV) $(VALGRIND) ./haproxy_srvr_test
|
||||
|
||||
map_search_test: update map_search map_search.ref
|
||||
-$(SHLIB_ENV) $(VALGRIND) ./map_search >map_search.tmp 2>&1
|
||||
@ -1532,6 +1530,7 @@ haproxy_srvr.o: ../../include/inet_proto.h
|
||||
haproxy_srvr.o: ../../include/msg.h
|
||||
haproxy_srvr.o: ../../include/myaddrinfo.h
|
||||
haproxy_srvr.o: ../../include/mymalloc.h
|
||||
haproxy_srvr.o: ../../include/normalize_v4mapped_addr.h
|
||||
haproxy_srvr.o: ../../include/sock_addr.h
|
||||
haproxy_srvr.o: ../../include/split_at.h
|
||||
haproxy_srvr.o: ../../include/stringops.h
|
||||
@ -1541,6 +1540,18 @@ haproxy_srvr.o: ../../include/vbuf.h
|
||||
haproxy_srvr.o: ../../include/vstring.h
|
||||
haproxy_srvr.o: haproxy_srvr.c
|
||||
haproxy_srvr.o: haproxy_srvr.h
|
||||
haproxy_srvr_test.o: ../../include/check_arg.h
|
||||
haproxy_srvr_test.o: ../../include/msg.h
|
||||
haproxy_srvr_test.o: ../../include/msg_vstream.h
|
||||
haproxy_srvr_test.o: ../../include/myaddrinfo.h
|
||||
haproxy_srvr_test.o: ../../include/sock_addr.h
|
||||
haproxy_srvr_test.o: ../../include/stringops.h
|
||||
haproxy_srvr_test.o: ../../include/sys_defs.h
|
||||
haproxy_srvr_test.o: ../../include/vbuf.h
|
||||
haproxy_srvr_test.o: ../../include/vstream.h
|
||||
haproxy_srvr_test.o: ../../include/vstring.h
|
||||
haproxy_srvr_test.o: haproxy_srvr.h
|
||||
haproxy_srvr_test.o: haproxy_srvr_test.c
|
||||
header_body_checks.o: ../../include/argv.h
|
||||
header_body_checks.o: ../../include/check_arg.h
|
||||
header_body_checks.o: ../../include/dict.h
|
||||
|
@ -6,6 +6,31 @@
|
||||
/* SYNOPSIS
|
||||
/* #include <haproxy_srvr.h>
|
||||
/*
|
||||
/* const char *haproxy_srvr_parse_sa(
|
||||
/* const char *str,
|
||||
/* ssize_t *str_len,
|
||||
/* int *non_proxy,
|
||||
/* MAI_HOSTADDR_STR *smtp_client_addr,
|
||||
/* MAI_SERVPORT_STR *smtp_client_port,
|
||||
/* MAI_HOSTADDR_STR *smtp_server_addr,
|
||||
/* MAI_SERVPORT_STR *smtp_server_port,
|
||||
/* struct sockaddr *client_sa,
|
||||
/* SOCKADDR_SIZE *client_sa_len,
|
||||
/* struct sockaddr *server_sa,
|
||||
/* SOCKADDR_SIZE *server_sa_len)
|
||||
/*
|
||||
/* const char *haproxy_srvr_receive_sa(
|
||||
/* int fd,
|
||||
/* int *non_proxy,
|
||||
/* MAI_HOSTADDR_STR *smtp_client_addr,
|
||||
/* MAI_SERVPORT_STR *smtp_client_port,
|
||||
/* MAI_HOSTADDR_STR *smtp_server_addr,
|
||||
/* MAI_SERVPORT_STR *smtp_server_port,
|
||||
/* struct sockaddr *client_sa,
|
||||
/* SOCKADDR_SIZE *client_sa_len,
|
||||
/* struct sockaddr *server_sa,
|
||||
/* SOCKADDR_SIZE *server_sa_len)
|
||||
/* ABI COMPATIBILITY
|
||||
/* const char *haproxy_srvr_parse(str, str_len, non_proxy,
|
||||
/* smtp_client_addr, smtp_client_port,
|
||||
/* smtp_server_addr, smtp_server_port)
|
||||
@ -27,7 +52,7 @@
|
||||
/* MAI_HOSTADDR_STR *smtp_server_addr,
|
||||
/* MAI_SERVPORT_STR *smtp_server_port;
|
||||
/* DESCRIPTION
|
||||
/* haproxy_srvr_parse() parses a haproxy v1 or v2 protocol
|
||||
/* haproxy_srvr_parse_sa() parses a haproxy v1 or v2 protocol
|
||||
/* message. The result is null in case of success, a pointer
|
||||
/* to text (with the error type) in case of error. If both
|
||||
/* IPv6 and IPv4 support are enabled, IPV4_IN_IPV6 address
|
||||
@ -36,7 +61,7 @@
|
||||
/* of bytes parsed, and the non_proxy argument is true or false
|
||||
/* if the haproxy message specifies a non-proxied connection.
|
||||
/*
|
||||
/* haproxy_srvr_receive() receives and parses a haproxy protocol
|
||||
/* haproxy_srvr_receive_sa() receives and parses a haproxy protocol
|
||||
/* handshake. This must be called before any I/O is done on
|
||||
/* the specified file descriptor. The result is 0 in case of
|
||||
/* success, -1 in case of error. All errors are logged.
|
||||
@ -45,6 +70,13 @@
|
||||
/* TCP over IPv6, and non-proxied connections. In the latter
|
||||
/* case, the caller is responsible for any local or remote
|
||||
/* address/port lookup.
|
||||
/*
|
||||
/* The client or server sockaddr and length storage are updated
|
||||
/* when their pointers are non-null.
|
||||
/*
|
||||
/* haproxy_srvr_parse() and haproxy_srvr_receive() provide ABI
|
||||
/* backwards compatibility, passing null pointers for the sockaddr
|
||||
/* and length storage arguments.
|
||||
/* LICENSE
|
||||
/* .ad
|
||||
/* .fi
|
||||
@ -59,6 +91,9 @@
|
||||
/* Google, Inc.
|
||||
/* 111 8th Avenue
|
||||
/* New York, NY 10011, USA
|
||||
/*
|
||||
/* Wietse Venema
|
||||
/* porcupine.org
|
||||
/*--*/
|
||||
|
||||
/* System library. */
|
||||
@ -82,89 +117,13 @@
|
||||
#include <inet_proto.h>
|
||||
#include <split_at.h>
|
||||
#include <sock_addr.h>
|
||||
#include <normalize_v4mapped_addr.h>
|
||||
|
||||
/* Global library. */
|
||||
|
||||
#define _HAPROXY_SRVR_INTERNAL_
|
||||
#include <haproxy_srvr.h>
|
||||
|
||||
/* Application-specific. */
|
||||
|
||||
/*
|
||||
* The haproxy protocol assumes that a haproxy header will normally not
|
||||
* exceed the default IPv4 TCP MSS, i.e. 576-40=536 bytes (the IPv6 default
|
||||
* is larger: 1280-60=1220). With a proxy header that contains IPv6
|
||||
* addresses, that leaves room for 536-52=484 bytes of TLVs. The Postfix
|
||||
* implementation does not support headers with UNIX-domain addresses.
|
||||
*/
|
||||
#define HAPROXY_HEADER_MAX_LEN 536
|
||||
|
||||
/*
|
||||
* Begin protocol v2 definitions from haproxy/include/types/connection.h.
|
||||
*/
|
||||
#define PP2_SIGNATURE "\r\n\r\n\0\r\nQUIT\n"
|
||||
#define PP2_SIGNATURE_LEN 12
|
||||
#define PP2_HEADER_LEN 16
|
||||
|
||||
/* ver_cmd byte */
|
||||
#define PP2_CMD_LOCAL 0x00
|
||||
#define PP2_CMD_PROXY 0x01
|
||||
#define PP2_CMD_MASK 0x0F
|
||||
|
||||
#define PP2_VERSION 0x20
|
||||
#define PP2_VERSION_MASK 0xF0
|
||||
|
||||
/* fam byte */
|
||||
#define PP2_TRANS_UNSPEC 0x00
|
||||
#define PP2_TRANS_STREAM 0x01
|
||||
#define PP2_TRANS_DGRAM 0x02
|
||||
#define PP2_TRANS_MASK 0x0F
|
||||
|
||||
#define PP2_FAM_UNSPEC 0x00
|
||||
#define PP2_FAM_INET 0x10
|
||||
#define PP2_FAM_INET6 0x20
|
||||
#define PP2_FAM_UNIX 0x30
|
||||
#define PP2_FAM_MASK 0xF0
|
||||
|
||||
/* len field (2 bytes) */
|
||||
#define PP2_ADDR_LEN_UNSPEC (0)
|
||||
#define PP2_ADDR_LEN_INET (4 + 4 + 2 + 2)
|
||||
#define PP2_ADDR_LEN_INET6 (16 + 16 + 2 + 2)
|
||||
#define PP2_ADDR_LEN_UNIX (108 + 108)
|
||||
|
||||
#define PP2_HDR_LEN_UNSPEC (PP2_HEADER_LEN + PP2_ADDR_LEN_UNSPEC)
|
||||
#define PP2_HDR_LEN_INET (PP2_HEADER_LEN + PP2_ADDR_LEN_INET)
|
||||
#define PP2_HDR_LEN_INET6 (PP2_HEADER_LEN + PP2_ADDR_LEN_INET6)
|
||||
#define PP2_HDR_LEN_UNIX (PP2_HEADER_LEN + PP2_ADDR_LEN_UNIX)
|
||||
|
||||
struct proxy_hdr_v2 {
|
||||
uint8_t sig[PP2_SIGNATURE_LEN]; /* PP2_SIGNATURE */
|
||||
uint8_t ver_cmd; /* protocol version | command */
|
||||
uint8_t fam; /* protocol family and transport */
|
||||
uint16_t len; /* length of remainder */
|
||||
union {
|
||||
struct { /* for TCP/UDP over IPv4, len = 12 */
|
||||
uint32_t src_addr;
|
||||
uint32_t dst_addr;
|
||||
uint16_t src_port;
|
||||
uint16_t dst_port;
|
||||
} ip4;
|
||||
struct { /* for TCP/UDP over IPv6, len = 36 */
|
||||
uint8_t src_addr[16];
|
||||
uint8_t dst_addr[16];
|
||||
uint16_t src_port;
|
||||
uint16_t dst_port;
|
||||
} ip6;
|
||||
struct { /* for AF_UNIX sockets, len = 216 */
|
||||
uint8_t src_addr[108];
|
||||
uint8_t dst_addr[108];
|
||||
} unx;
|
||||
} addr;
|
||||
};
|
||||
|
||||
/*
|
||||
* End protocol v2 definitions from haproxy/include/types/connection.h.
|
||||
*/
|
||||
|
||||
static const INET_PROTO_INFO *proto_info;
|
||||
|
||||
#define STR_OR_NULL(str) ((str) ? (str) : "(null)")
|
||||
@ -223,10 +182,14 @@ static int haproxy_srvr_parse_proto(const char *str, int *addr_family)
|
||||
/* haproxy_srvr_parse_addr - extract and validate IP address */
|
||||
|
||||
static int haproxy_srvr_parse_addr(const char *str, MAI_HOSTADDR_STR *addr,
|
||||
int addr_family)
|
||||
int addr_family,
|
||||
struct sockaddr *sa,
|
||||
SOCKADDR_SIZE *sa_len)
|
||||
{
|
||||
struct addrinfo *res = 0;
|
||||
struct addrinfo *res;
|
||||
int err;
|
||||
struct sockaddr_storage ss;
|
||||
SOCKADDR_SIZE ss_len;
|
||||
|
||||
if (msg_verbose)
|
||||
msg_info("haproxy_srvr_parse: addr=%s proto=%d",
|
||||
@ -238,30 +201,60 @@ static int haproxy_srvr_parse_addr(const char *str, MAI_HOSTADDR_STR *addr,
|
||||
switch (addr_family) {
|
||||
#ifdef AF_INET6
|
||||
case AF_INET6:
|
||||
err = !valid_ipv6_hostaddr(str, DONT_GRIPE);
|
||||
if (!valid_ipv6_hostaddr(str, DONT_GRIPE))
|
||||
return (-1);
|
||||
break;
|
||||
#endif
|
||||
case AF_INET:
|
||||
err = !valid_ipv4_hostaddr(str, DONT_GRIPE);
|
||||
if (!valid_ipv4_hostaddr(str, DONT_GRIPE))
|
||||
return (-1);
|
||||
break;
|
||||
default:
|
||||
msg_panic("haproxy_srvr_parse: unexpected address family: %d",
|
||||
addr_family);
|
||||
}
|
||||
if (err == 0)
|
||||
err = (hostaddr_to_sockaddr(str, (char *) 0, 0, &res)
|
||||
|| sane_sockaddr_to_hostaddr(res->ai_addr, res->ai_addrlen,
|
||||
addr, (MAI_SERVPORT_STR *) 0, 0));
|
||||
if (res)
|
||||
freeaddrinfo(res);
|
||||
if (err)
|
||||
|
||||
/*
|
||||
* Convert the printable address to canonical form. Don't rely on the
|
||||
* proxy. This requires a conversion to binary form and back, even if a
|
||||
* caller such as postscreen does not need the binary form.
|
||||
*/
|
||||
if ((err = hostaddr_to_sockaddr(str, (char *) 0, 0, &res)) != 0) {
|
||||
msg_warn("haproxy_srvr_parse: hostaddr_to_sockaddr(\"%s\") failed: %s",
|
||||
str, MAI_STRERROR(err));
|
||||
return (-1);
|
||||
}
|
||||
if (sa == 0) {
|
||||
sa = (struct sockaddr *) &ss;
|
||||
ss_len = sizeof(ss);
|
||||
sa_len = &ss_len;
|
||||
} else {
|
||||
if (sa_len == 0)
|
||||
msg_panic("haproxy_srvr_parse: sockaddr length not specified");
|
||||
}
|
||||
if (*sa_len < res->ai_addrlen)
|
||||
msg_panic("haproxy_srvr_parse: sockaddr size %d too small",
|
||||
(int) *sa_len);
|
||||
*sa_len = res->ai_addrlen;
|
||||
memcpy((void *) sa, res->ai_addr, res->ai_addrlen);
|
||||
freeaddrinfo(res);
|
||||
#ifdef AF_INET6
|
||||
if (sa->sa_family == AF_INET6)
|
||||
normalize_v4mapped_sockaddr(sa, sa_len);
|
||||
#endif
|
||||
if ((err = sockaddr_to_hostaddr(sa, *sa_len,
|
||||
addr, (MAI_SERVPORT_STR *) 0, 0)) != 0) {
|
||||
msg_warn("haproxy_srvr_parse: sockaddr_to_hostaddr() failed: %s",
|
||||
MAI_STRERROR(err));
|
||||
return (-1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* haproxy_srvr_parse_port - extract and validate TCP port */
|
||||
|
||||
static int haproxy_srvr_parse_port(const char *str, MAI_SERVPORT_STR *port)
|
||||
static int haproxy_srvr_parse_port(const char *str, MAI_SERVPORT_STR *port,
|
||||
struct sockaddr *sa)
|
||||
{
|
||||
if (msg_verbose)
|
||||
msg_info("haproxy_srvr_parse: port=%s", STR_OR_NULL(str));
|
||||
@ -270,6 +263,21 @@ static int haproxy_srvr_parse_port(const char *str, MAI_SERVPORT_STR *port)
|
||||
return (-1);
|
||||
} else {
|
||||
memcpy(port->buf, str, strlen(str) + 1);
|
||||
if (sa != 0) {
|
||||
switch (sa->sa_family) {
|
||||
#ifdef AF_INET6
|
||||
case AF_INET6:
|
||||
SOCK_ADDR_IN6_PORT(sa) = htons(atoi(str));
|
||||
break;
|
||||
#endif
|
||||
case AF_INET:
|
||||
SOCK_ADDR_IN_PORT(sa) = htons(atoi(str));
|
||||
break;
|
||||
default:
|
||||
msg_panic("haproxy_srvr_parse: unexpected address family: %d",
|
||||
sa->sa_family);
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
@ -279,16 +287,33 @@ static int haproxy_srvr_parse_port(const char *str, MAI_SERVPORT_STR *port)
|
||||
static int haproxy_srvr_parse_v2_addr_v4(uint32_t sin_addr,
|
||||
unsigned sin_port,
|
||||
MAI_HOSTADDR_STR *addr,
|
||||
MAI_SERVPORT_STR *port)
|
||||
MAI_SERVPORT_STR *port,
|
||||
struct sockaddr *sa,
|
||||
SOCKADDR_SIZE *sa_len)
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
SOCKADDR_SIZE sin_len;
|
||||
|
||||
memset((void *) &sin, 0, sizeof(sin));
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr.s_addr = sin_addr;
|
||||
sin.sin_port = sin_port;
|
||||
if (sockaddr_to_hostaddr((struct sockaddr *) &sin, sizeof(sin),
|
||||
addr, port, 0) < 0)
|
||||
/*
|
||||
* Convert the binary address and port to printable form.
|
||||
*/
|
||||
if (sa == 0) {
|
||||
sa = (struct sockaddr *) &sin;
|
||||
sin_len = sizeof(sin);
|
||||
sa_len = &sin_len;
|
||||
} else {
|
||||
if (sa_len == 0)
|
||||
msg_panic("haproxy_srvr_parse: sockaddr length not specified");
|
||||
if (*sa_len < sizeof(sin))
|
||||
msg_panic("haproxy_srvr_parse: sockaddr size %d too small",
|
||||
(int) *sa_len);
|
||||
*sa_len = sizeof(sin);
|
||||
}
|
||||
memset((void *) sa, 0, *sa_len);
|
||||
SOCK_ADDR_IN_FAMILY(sa) = AF_INET;
|
||||
SOCK_ADDR_IN_ADDR(sa).s_addr = sin_addr;
|
||||
SOCK_ADDR_IN_PORT(sa) = sin_port;
|
||||
if (sockaddr_to_hostaddr(sa, *sa_len, addr, port, 0) < 0)
|
||||
return (-1);
|
||||
return (0);
|
||||
}
|
||||
@ -300,22 +325,35 @@ static int haproxy_srvr_parse_v2_addr_v4(uint32_t sin_addr,
|
||||
static int haproxy_srvr_parse_v2_addr_v6(uint8_t *sin6_addr,
|
||||
unsigned sin6_port,
|
||||
MAI_HOSTADDR_STR *addr,
|
||||
MAI_SERVPORT_STR *port)
|
||||
MAI_SERVPORT_STR *port,
|
||||
struct sockaddr *sa,
|
||||
SOCKADDR_SIZE *sa_len)
|
||||
{
|
||||
struct sockaddr_in6 sin6;
|
||||
SOCKADDR_SIZE sin6_len;
|
||||
|
||||
memset((void *) &sin6, 0, sizeof(sin6));
|
||||
sin6.sin6_family = AF_INET6;
|
||||
memcpy(&sin6.sin6_addr, sin6_addr, 16);
|
||||
sin6.sin6_port = sin6_port;
|
||||
if (sockaddr_to_hostaddr((struct sockaddr *) &sin6,
|
||||
sizeof(sin6), addr, port, 0) < 0)
|
||||
/*
|
||||
* Convert the binary address and port to printable form.
|
||||
*/
|
||||
if (sa == 0) {
|
||||
sa = (struct sockaddr *) &sin6;
|
||||
sin6_len = sizeof(sin6);
|
||||
sa_len = &sin6_len;
|
||||
} else {
|
||||
if (sa_len == 0)
|
||||
msg_panic("haproxy_srvr_parse: sockaddr length not specified");
|
||||
if (*sa_len < sizeof(sin6))
|
||||
msg_panic("haproxy_srvr_parse: sockaddr size %d too small",
|
||||
(int) *sa_len);
|
||||
*sa_len = sizeof(sin6);
|
||||
}
|
||||
memset((void *) sa, 0, *sa_len);
|
||||
SOCK_ADDR_IN6_FAMILY(sa) = AF_INET6;
|
||||
memcpy(&SOCK_ADDR_IN6_ADDR(sa), sin6_addr, sizeof(SOCK_ADDR_IN6_ADDR(sa)));
|
||||
SOCK_ADDR_IN6_PORT(sa) = sin6_port;
|
||||
normalize_v4mapped_sockaddr(sa, sa_len);
|
||||
if (sockaddr_to_hostaddr(sa, *sa_len, addr, port, 0) < 0)
|
||||
return (-1);
|
||||
if (addr->buf[0] == ':'
|
||||
&& strncasecmp("::ffff:", addr->buf, 7) == 0
|
||||
&& strchr((char *) proto_info->sa_family_list, AF_INET) != 0)
|
||||
memmove(addr->buf, addr->buf + 7,
|
||||
strlen(addr->buf) + 1 - 7);
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -328,7 +366,11 @@ static const char *haproxy_srvr_parse_v2_hdr(const char *str, ssize_t *str_len,
|
||||
MAI_HOSTADDR_STR *smtp_client_addr,
|
||||
MAI_SERVPORT_STR *smtp_client_port,
|
||||
MAI_HOSTADDR_STR *smtp_server_addr,
|
||||
MAI_SERVPORT_STR *smtp_server_port)
|
||||
MAI_SERVPORT_STR *smtp_server_port,
|
||||
struct sockaddr *client_sa,
|
||||
SOCKADDR_SIZE *client_sa_len,
|
||||
struct sockaddr *server_sa,
|
||||
SOCKADDR_SIZE *server_sa_len)
|
||||
{
|
||||
const char myname[] = "haproxy_srvr_parse_v2_hdr";
|
||||
struct proxy_hdr_v2 *hdr_v2;
|
||||
@ -357,14 +399,18 @@ static const char *haproxy_srvr_parse_v2_hdr(const char *str, ssize_t *str_len,
|
||||
return ("short address field");
|
||||
if (haproxy_srvr_parse_v2_addr_v4(hdr_v2->addr.ip4.src_addr,
|
||||
hdr_v2->addr.ip4.src_port,
|
||||
smtp_client_addr, smtp_client_port) < 0)
|
||||
smtp_client_addr, smtp_client_port,
|
||||
client_sa, client_sa_len) < 0)
|
||||
return ("client network address conversion error");
|
||||
if (msg_verbose)
|
||||
msg_info("%s: smtp_client_addr=%s smtp_client_port=%s",
|
||||
myname, smtp_client_addr->buf, smtp_client_port->buf);
|
||||
if (haproxy_srvr_parse_v2_addr_v4(hdr_v2->addr.ip4.dst_addr,
|
||||
hdr_v2->addr.ip4.dst_port,
|
||||
smtp_server_addr, smtp_server_port) < 0)
|
||||
smtp_server_addr,
|
||||
smtp_server_port,
|
||||
server_sa,
|
||||
server_sa_len) < 0)
|
||||
return ("server network address conversion error");
|
||||
if (msg_verbose)
|
||||
msg_info("%s: smtp_server_addr=%s smtp_server_port=%s",
|
||||
@ -380,7 +426,9 @@ static const char *haproxy_srvr_parse_v2_hdr(const char *str, ssize_t *str_len,
|
||||
if (haproxy_srvr_parse_v2_addr_v6(hdr_v2->addr.ip6.src_addr,
|
||||
hdr_v2->addr.ip6.src_port,
|
||||
smtp_client_addr,
|
||||
smtp_client_port) < 0)
|
||||
smtp_client_port,
|
||||
client_sa,
|
||||
client_sa_len) < 0)
|
||||
return ("client network address conversion error");
|
||||
if (msg_verbose)
|
||||
msg_info("%s: smtp_client_addr=%s smtp_client_port=%s",
|
||||
@ -388,7 +436,9 @@ static const char *haproxy_srvr_parse_v2_hdr(const char *str, ssize_t *str_len,
|
||||
if (haproxy_srvr_parse_v2_addr_v6(hdr_v2->addr.ip6.dst_addr,
|
||||
hdr_v2->addr.ip6.dst_port,
|
||||
smtp_server_addr,
|
||||
smtp_server_port) < 0)
|
||||
smtp_server_port,
|
||||
server_sa,
|
||||
server_sa_len) < 0)
|
||||
return ("server network address conversion error");
|
||||
if (msg_verbose)
|
||||
msg_info("%s: smtp_server_addr=%s smtp_server_port=%s",
|
||||
@ -418,14 +468,18 @@ static const char *haproxy_srvr_parse_v2_hdr(const char *str, ssize_t *str_len,
|
||||
}
|
||||
}
|
||||
|
||||
/* haproxy_srvr_parse - parse haproxy line */
|
||||
/* haproxy_srvr_parse_sa - parse haproxy line */
|
||||
|
||||
const char *haproxy_srvr_parse(const char *str, ssize_t *str_len,
|
||||
int *non_proxy,
|
||||
MAI_HOSTADDR_STR *smtp_client_addr,
|
||||
MAI_SERVPORT_STR *smtp_client_port,
|
||||
MAI_HOSTADDR_STR *smtp_server_addr,
|
||||
MAI_SERVPORT_STR *smtp_server_port)
|
||||
const char *haproxy_srvr_parse_sa(const char *str, ssize_t *str_len,
|
||||
int *non_proxy,
|
||||
MAI_HOSTADDR_STR *smtp_client_addr,
|
||||
MAI_SERVPORT_STR *smtp_client_port,
|
||||
MAI_HOSTADDR_STR *smtp_server_addr,
|
||||
MAI_SERVPORT_STR *smtp_server_port,
|
||||
struct sockaddr *client_sa,
|
||||
SOCKADDR_SIZE *client_sa_len,
|
||||
struct sockaddr *server_sa,
|
||||
SOCKADDR_SIZE *server_sa_len)
|
||||
{
|
||||
const char *err;
|
||||
|
||||
@ -456,14 +510,18 @@ const char *haproxy_srvr_parse(const char *str, ssize_t *str_len,
|
||||
else if (haproxy_srvr_parse_proto(NEXT_TOKEN, &addr_family) < 0)
|
||||
err = "bad or missing protocol type";
|
||||
else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_client_addr,
|
||||
addr_family) < 0)
|
||||
addr_family, client_sa,
|
||||
client_sa_len) < 0)
|
||||
err = "bad or missing client address";
|
||||
else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_server_addr,
|
||||
addr_family) < 0)
|
||||
addr_family, server_sa,
|
||||
server_sa_len) < 0)
|
||||
err = "bad or missing server address";
|
||||
else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_client_port) < 0)
|
||||
else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_client_port,
|
||||
client_sa) < 0)
|
||||
err = "bad or missing client port";
|
||||
else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_server_port) < 0)
|
||||
else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_server_port,
|
||||
server_sa) < 0)
|
||||
err = "bad or missing server port";
|
||||
else {
|
||||
err = 0;
|
||||
@ -480,17 +538,48 @@ const char *haproxy_srvr_parse(const char *str, ssize_t *str_len,
|
||||
else {
|
||||
return (haproxy_srvr_parse_v2_hdr(str, str_len, non_proxy,
|
||||
smtp_client_addr, smtp_client_port,
|
||||
smtp_server_addr, smtp_server_port));
|
||||
smtp_server_addr, smtp_server_port,
|
||||
client_sa, client_sa_len,
|
||||
server_sa, server_sa_len));
|
||||
}
|
||||
}
|
||||
|
||||
/* haproxy_srvr_receive - receive and parse haproxy protocol handshake */
|
||||
/* haproxy_srvr_parse - ABI compatibility */
|
||||
|
||||
int haproxy_srvr_receive(int fd, int *non_proxy,
|
||||
MAI_HOSTADDR_STR *smtp_client_addr,
|
||||
MAI_SERVPORT_STR *smtp_client_port,
|
||||
MAI_HOSTADDR_STR *smtp_server_addr,
|
||||
MAI_SERVPORT_STR *smtp_server_port)
|
||||
#undef haproxy_srvr_parse
|
||||
|
||||
const char *haproxy_srvr_parse(const char *str, ssize_t *str_len,
|
||||
int *non_proxy,
|
||||
MAI_HOSTADDR_STR *smtp_client_addr,
|
||||
MAI_SERVPORT_STR *smtp_client_port,
|
||||
MAI_HOSTADDR_STR *smtp_server_addr,
|
||||
MAI_SERVPORT_STR *smtp_server_port);
|
||||
|
||||
const char *haproxy_srvr_parse(const char *str, ssize_t *str_len,
|
||||
int *non_proxy,
|
||||
MAI_HOSTADDR_STR *smtp_client_addr,
|
||||
MAI_SERVPORT_STR *smtp_client_port,
|
||||
MAI_HOSTADDR_STR *smtp_server_addr,
|
||||
MAI_SERVPORT_STR *smtp_server_port)
|
||||
{
|
||||
return (haproxy_srvr_parse_sa(str, str_len, non_proxy,
|
||||
smtp_client_addr, smtp_client_port,
|
||||
smtp_server_addr, smtp_server_port,
|
||||
(struct sockaddr *) 0, (SOCKADDR_SIZE *) 0,
|
||||
(struct sockaddr *) 0, (SOCKADDR_SIZE *) 0));
|
||||
}
|
||||
|
||||
/* haproxy_srvr_receive_sa - receive and parse haproxy protocol handshake */
|
||||
|
||||
int haproxy_srvr_receive_sa(int fd, int *non_proxy,
|
||||
MAI_HOSTADDR_STR *smtp_client_addr,
|
||||
MAI_SERVPORT_STR *smtp_client_port,
|
||||
MAI_HOSTADDR_STR *smtp_server_addr,
|
||||
MAI_SERVPORT_STR *smtp_server_port,
|
||||
struct sockaddr *client_sa,
|
||||
SOCKADDR_SIZE *client_sa_len,
|
||||
struct sockaddr *server_sa,
|
||||
SOCKADDR_SIZE *server_sa_len)
|
||||
{
|
||||
const char *err;
|
||||
VSTRING *escape_buf;
|
||||
@ -513,9 +602,11 @@ int haproxy_srvr_receive(int fd, int *non_proxy,
|
||||
*/
|
||||
read_buf[read_len] = 0;
|
||||
|
||||
if ((err = haproxy_srvr_parse(read_buf, &read_len, non_proxy,
|
||||
smtp_client_addr, smtp_client_port,
|
||||
smtp_server_addr, smtp_server_port)) != 0) {
|
||||
if ((err = haproxy_srvr_parse_sa(read_buf, &read_len, non_proxy,
|
||||
smtp_client_addr, smtp_client_port,
|
||||
smtp_server_addr, smtp_server_port,
|
||||
client_sa, client_sa_len,
|
||||
server_sa, server_sa_len)) != 0) {
|
||||
escape_buf = vstring_alloc(read_len * 2);
|
||||
escape(escape_buf, read_buf, read_len);
|
||||
msg_warn("haproxy read: %s: %s", err, vstring_str(escape_buf));
|
||||
@ -533,356 +624,27 @@ int haproxy_srvr_receive(int fd, int *non_proxy,
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test program.
|
||||
*/
|
||||
#ifdef TEST
|
||||
/* haproxy_srvr_receive - ABI compatibility */
|
||||
|
||||
/*
|
||||
* Test cases with inputs and expected outputs. A request may contain
|
||||
* trailing garbage, and it may be too short. A v1 request may also contain
|
||||
* malformed address or port information.
|
||||
*/
|
||||
typedef struct TEST_CASE {
|
||||
const char *haproxy_request; /* v1 or v2 request including thrash */
|
||||
ssize_t haproxy_req_len; /* request length including thrash */
|
||||
ssize_t exp_req_len; /* parsed request length */
|
||||
int exp_non_proxy; /* request is not proxied */
|
||||
const char *exp_return; /* expected error string */
|
||||
const char *exp_client_addr; /* expected client address string */
|
||||
const char *exp_server_addr; /* expected client port string */
|
||||
const char *exp_client_port; /* expected client address string */
|
||||
const char *exp_server_port; /* expected server port string */
|
||||
} TEST_CASE;
|
||||
static TEST_CASE v1_test_cases[] = {
|
||||
/* IPv6. */
|
||||
{"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1 123 321\n", 0, 0, 0, 0, "fc::1:2:3:4", "fc::4:3:2:1", "123", "321"},
|
||||
{"PROXY TCP6 FC:00:00:00:1:2:3:4 FC:00:00:00:4:3:2:1 123 321\n", 0, 0, 0, 0, "fc::1:2:3:4", "fc::4:3:2:1", "123", "321"},
|
||||
{"PROXY TCP6 1.2.3.4 4.3.2.1 123 321\n", 0, 0, 0, "bad or missing client address"},
|
||||
{"PROXY TCP6 fc:00:00:00:1:2:3:4 4.3.2.1 123 321\n", 0, 0, 0, "bad or missing server address"},
|
||||
/* IPv4 in IPv6. */
|
||||
{"PROXY TCP6 ::ffff:1.2.3.4 ::ffff:4.3.2.1 123 321\n", 0, 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"},
|
||||
{"PROXY TCP6 ::FFFF:1.2.3.4 ::FFFF:4.3.2.1 123 321\n", 0, 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"},
|
||||
{"PROXY TCP4 ::ffff:1.2.3.4 ::ffff:4.3.2.1 123 321\n", 0, 0, 0, "bad or missing client address"},
|
||||
{"PROXY TCP4 1.2.3.4 ::ffff:4.3.2.1 123 321\n", 0, 0, 0, "bad or missing server address"},
|
||||
/* IPv4. */
|
||||
{"PROXY TCP4 1.2.3.4 4.3.2.1 123 321\n", 0, 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"},
|
||||
{"PROXY TCP4 01.02.03.04 04.03.02.01 123 321\n", 0, 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"},
|
||||
{"PROXY TCP4 1.2.3.4 4.3.2.1 123456 321\n", 0, 0, 0, "bad or missing client port"},
|
||||
{"PROXY TCP4 1.2.3.4 4.3.2.1 123 654321\n", 0, 0, 0, "bad or missing server port"},
|
||||
{"PROXY TCP4 1.2.3.4 4.3.2.1 0123 321\n", 0, 0, 0, "bad or missing client port"},
|
||||
{"PROXY TCP4 1.2.3.4 4.3.2.1 123 0321\n", 0, 0, 0, "bad or missing server port"},
|
||||
/* Missing fields. */
|
||||
{"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1 123\n", 0, 0, 0, "bad or missing server port"},
|
||||
{"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1\n", 0, 0, 0, "bad or missing client port"},
|
||||
{"PROXY TCP6 fc:00:00:00:1:2:3:4\n", 0, 0, 0, "bad or missing server address"},
|
||||
{"PROXY TCP6\n", 0, 0, 0, "bad or missing client address"},
|
||||
{"PROXY TCP4 1.2.3.4 4.3.2.1 123\n", 0, 0, 0, "bad or missing server port"},
|
||||
{"PROXY TCP4 1.2.3.4 4.3.2.1\n", 0, 0, 0, "bad or missing client port"},
|
||||
{"PROXY TCP4 1.2.3.4\n", 0, 0, 0, "bad or missing server address"},
|
||||
{"PROXY TCP4\n", 0, 0, 0, "bad or missing client address"},
|
||||
/* Other. */
|
||||
{"PROXY BLAH\n", 0, 0, 0, "bad or missing protocol type"},
|
||||
{"PROXY\n", 0, 0, 0, "short protocol header"},
|
||||
{"BLAH\n", 0, 0, 0, "short protocol header"},
|
||||
{"\n", 0, 0, 0, "short protocol header"},
|
||||
{"", 0, 0, 0, "short protocol header"},
|
||||
0,
|
||||
};
|
||||
#undef haproxy_srvr_receive
|
||||
|
||||
static struct proxy_hdr_v2 v2_local_request = {
|
||||
PP2_SIGNATURE, PP2_VERSION | PP2_CMD_LOCAL,
|
||||
};
|
||||
static TEST_CASE v2_non_proxy_test = {
|
||||
(char *) &v2_local_request, PP2_HEADER_LEN, PP2_HEADER_LEN, 1,
|
||||
};
|
||||
int haproxy_srvr_receive(int fd, int *non_proxy,
|
||||
MAI_HOSTADDR_STR *smtp_client_addr,
|
||||
MAI_SERVPORT_STR *smtp_client_port,
|
||||
MAI_HOSTADDR_STR *smtp_server_addr,
|
||||
MAI_SERVPORT_STR *smtp_server_port);
|
||||
|
||||
#define STR(x) vstring_str(x)
|
||||
#define LEN(x) VSTRING_LEN(x)
|
||||
|
||||
/* evaluate_test_case - evaluate one test case */
|
||||
|
||||
static int evaluate_test_case(const char *test_label,
|
||||
const TEST_CASE *test_case)
|
||||
int haproxy_srvr_receive(int fd, int *non_proxy,
|
||||
MAI_HOSTADDR_STR *smtp_client_addr,
|
||||
MAI_SERVPORT_STR *smtp_client_port,
|
||||
MAI_HOSTADDR_STR *smtp_server_addr,
|
||||
MAI_SERVPORT_STR *smtp_server_port)
|
||||
{
|
||||
/* Actual results. */
|
||||
const char *act_return;
|
||||
ssize_t act_req_len;
|
||||
int act_non_proxy;
|
||||
MAI_HOSTADDR_STR act_smtp_client_addr;
|
||||
MAI_HOSTADDR_STR act_smtp_server_addr;
|
||||
MAI_SERVPORT_STR act_smtp_client_port;
|
||||
MAI_SERVPORT_STR act_smtp_server_port;
|
||||
int test_failed;
|
||||
|
||||
if (msg_verbose)
|
||||
msg_info("test case=%s exp_client_addr=%s exp_server_addr=%s "
|
||||
"exp_client_port=%s exp_server_port=%s",
|
||||
test_label, STR_OR_NULL(test_case->exp_client_addr),
|
||||
STR_OR_NULL(test_case->exp_server_addr),
|
||||
STR_OR_NULL(test_case->exp_client_port),
|
||||
STR_OR_NULL(test_case->exp_server_port));
|
||||
|
||||
/*
|
||||
* Start the test.
|
||||
*/
|
||||
test_failed = 0;
|
||||
act_req_len = test_case->haproxy_req_len;
|
||||
act_return =
|
||||
haproxy_srvr_parse(test_case->haproxy_request, &act_req_len,
|
||||
&act_non_proxy,
|
||||
&act_smtp_client_addr, &act_smtp_client_port,
|
||||
&act_smtp_server_addr, &act_smtp_server_port);
|
||||
if (act_return != test_case->exp_return) {
|
||||
msg_warn("test case %s return expected=%s actual=%s",
|
||||
test_label, STR_OR_NULL(test_case->exp_return),
|
||||
STR_OR_NULL(act_return));
|
||||
test_failed = 1;
|
||||
return (test_failed);
|
||||
}
|
||||
if (act_req_len != test_case->exp_req_len) {
|
||||
msg_warn("test case %s str_len expected=%ld actual=%ld",
|
||||
test_label,
|
||||
(long) test_case->exp_req_len, (long) act_req_len);
|
||||
test_failed = 1;
|
||||
return (test_failed);
|
||||
}
|
||||
if (act_non_proxy != test_case->exp_non_proxy) {
|
||||
msg_warn("test case %s non_proxy expected=%d actual=%d",
|
||||
test_label,
|
||||
test_case->exp_non_proxy, act_non_proxy);
|
||||
test_failed = 1;
|
||||
return (test_failed);
|
||||
}
|
||||
if (test_case->exp_non_proxy || test_case->exp_return != 0)
|
||||
/* No expected address/port results. */
|
||||
return (test_failed);
|
||||
|
||||
/*
|
||||
* Compare address/port results against expected results.
|
||||
*/
|
||||
if (strcmp(test_case->exp_client_addr, act_smtp_client_addr.buf)) {
|
||||
msg_warn("test case %s client_addr expected=%s actual=%s",
|
||||
test_label,
|
||||
test_case->exp_client_addr, act_smtp_client_addr.buf);
|
||||
test_failed = 1;
|
||||
}
|
||||
if (strcmp(test_case->exp_server_addr, act_smtp_server_addr.buf)) {
|
||||
msg_warn("test case %s server_addr expected=%s actual=%s",
|
||||
test_label,
|
||||
test_case->exp_server_addr, act_smtp_server_addr.buf);
|
||||
test_failed = 1;
|
||||
}
|
||||
if (strcmp(test_case->exp_client_port, act_smtp_client_port.buf)) {
|
||||
msg_warn("test case %s client_port expected=%s actual=%s",
|
||||
test_label,
|
||||
test_case->exp_client_port, act_smtp_client_port.buf);
|
||||
test_failed = 1;
|
||||
}
|
||||
if (strcmp(test_case->exp_server_port, act_smtp_server_port.buf)) {
|
||||
msg_warn("test case %s server_port expected=%s actual=%s",
|
||||
test_label,
|
||||
test_case->exp_server_port, act_smtp_server_port.buf);
|
||||
test_failed = 1;
|
||||
}
|
||||
return (test_failed);
|
||||
return (haproxy_srvr_receive_sa(fd, non_proxy,
|
||||
smtp_client_addr, smtp_client_port,
|
||||
smtp_server_addr, smtp_server_port,
|
||||
(struct sockaddr *) 0,
|
||||
(SOCKADDR_SIZE *) 0,
|
||||
(struct sockaddr *) 0,
|
||||
(SOCKADDR_SIZE *) 0));
|
||||
}
|
||||
|
||||
/* convert_v1_proxy_req_to_v2 - convert well-formed v1 proxy request to v2 */
|
||||
|
||||
static void convert_v1_proxy_req_to_v2(VSTRING *buf, const char *req,
|
||||
ssize_t req_len)
|
||||
{
|
||||
const char myname[] = "convert_v1_proxy_req_to_v2";
|
||||
const char *err;
|
||||
int non_proxy;
|
||||
MAI_HOSTADDR_STR smtp_client_addr;
|
||||
MAI_SERVPORT_STR smtp_client_port;
|
||||
MAI_HOSTADDR_STR smtp_server_addr;
|
||||
MAI_SERVPORT_STR smtp_server_port;
|
||||
struct proxy_hdr_v2 *hdr_v2;
|
||||
struct addrinfo *src_res;
|
||||
struct addrinfo *dst_res;
|
||||
|
||||
/*
|
||||
* Allocate buffer space for the largest possible protocol header, so we
|
||||
* don't have to worry about hidden realloc() calls.
|
||||
*/
|
||||
VSTRING_RESET(buf);
|
||||
VSTRING_SPACE(buf, sizeof(struct proxy_hdr_v2));
|
||||
hdr_v2 = (struct proxy_hdr_v2 *) STR(buf);
|
||||
|
||||
/*
|
||||
* Fill in the header,
|
||||
*/
|
||||
memcpy(hdr_v2->sig, PP2_SIGNATURE, PP2_SIGNATURE_LEN);
|
||||
hdr_v2->ver_cmd = PP2_VERSION | PP2_CMD_PROXY;
|
||||
if ((err = haproxy_srvr_parse(req, &req_len, &non_proxy, &smtp_client_addr,
|
||||
&smtp_client_port, &smtp_server_addr,
|
||||
&smtp_server_port)) != 0 || non_proxy)
|
||||
msg_fatal("%s: malformed or non-proxy request: %s",
|
||||
myname, req);
|
||||
|
||||
if (hostaddr_to_sockaddr(smtp_client_addr.buf, smtp_client_port.buf, 0,
|
||||
&src_res) != 0)
|
||||
msg_fatal("%s: unable to convert source address %s port %s",
|
||||
myname, smtp_client_addr.buf, smtp_client_port.buf);
|
||||
if (hostaddr_to_sockaddr(smtp_server_addr.buf, smtp_server_port.buf, 0,
|
||||
&dst_res) != 0)
|
||||
msg_fatal("%s: unable to convert destination address %s port %s",
|
||||
myname, smtp_server_addr.buf, smtp_server_port.buf);
|
||||
if (src_res->ai_family != dst_res->ai_family)
|
||||
msg_fatal("%s: mixed source/destination address families", myname);
|
||||
#ifdef AF_INET6
|
||||
if (src_res->ai_family == PF_INET6) {
|
||||
hdr_v2->fam = PP2_FAM_INET6 | PP2_TRANS_STREAM;
|
||||
hdr_v2->len = htons(PP2_ADDR_LEN_INET6);
|
||||
memcpy(hdr_v2->addr.ip6.src_addr,
|
||||
&SOCK_ADDR_IN6_ADDR(src_res->ai_addr),
|
||||
sizeof(hdr_v2->addr.ip6.src_addr));
|
||||
hdr_v2->addr.ip6.src_port = SOCK_ADDR_IN6_PORT(src_res->ai_addr);
|
||||
memcpy(hdr_v2->addr.ip6.dst_addr,
|
||||
&SOCK_ADDR_IN6_ADDR(dst_res->ai_addr),
|
||||
sizeof(hdr_v2->addr.ip6.dst_addr));
|
||||
hdr_v2->addr.ip6.dst_port = SOCK_ADDR_IN6_PORT(dst_res->ai_addr);
|
||||
} else
|
||||
#endif
|
||||
if (src_res->ai_family == PF_INET) {
|
||||
hdr_v2->fam = PP2_FAM_INET | PP2_TRANS_STREAM;
|
||||
hdr_v2->len = htons(PP2_ADDR_LEN_INET);
|
||||
hdr_v2->addr.ip4.src_addr = SOCK_ADDR_IN_ADDR(src_res->ai_addr).s_addr;
|
||||
hdr_v2->addr.ip4.src_port = SOCK_ADDR_IN_PORT(src_res->ai_addr);
|
||||
hdr_v2->addr.ip4.dst_addr = SOCK_ADDR_IN_ADDR(dst_res->ai_addr).s_addr;
|
||||
hdr_v2->addr.ip4.dst_port = SOCK_ADDR_IN_PORT(dst_res->ai_addr);
|
||||
} else {
|
||||
msg_panic("unknown address family 0x%x", src_res->ai_family);
|
||||
}
|
||||
vstring_set_payload_size(buf, PP2_SIGNATURE_LEN + ntohs(hdr_v2->len));
|
||||
freeaddrinfo(src_res);
|
||||
freeaddrinfo(dst_res);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
VSTRING *test_label;
|
||||
TEST_CASE *v1_test_case;
|
||||
TEST_CASE v2_test_case;
|
||||
TEST_CASE mutated_test_case;
|
||||
VSTRING *v2_request_buf;
|
||||
VSTRING *mutated_request_buf;
|
||||
|
||||
/* Findings. */
|
||||
int tests_failed = 0;
|
||||
int test_failed;
|
||||
|
||||
test_label = vstring_alloc(100);
|
||||
v2_request_buf = vstring_alloc(100);
|
||||
mutated_request_buf = vstring_alloc(100);
|
||||
|
||||
for (tests_failed = 0, v1_test_case = v1_test_cases;
|
||||
v1_test_case->haproxy_request != 0;
|
||||
tests_failed += test_failed, v1_test_case++) {
|
||||
|
||||
/*
|
||||
* Fill in missing string length info in v1 test data.
|
||||
*/
|
||||
if (v1_test_case->haproxy_req_len == 0)
|
||||
v1_test_case->haproxy_req_len =
|
||||
strlen(v1_test_case->haproxy_request);
|
||||
if (v1_test_case->exp_req_len == 0)
|
||||
v1_test_case->exp_req_len = v1_test_case->haproxy_req_len;
|
||||
|
||||
/*
|
||||
* Evaluate each v1 test case.
|
||||
*/
|
||||
vstring_sprintf(test_label, "%d", (int) (v1_test_case - v1_test_cases));
|
||||
test_failed = evaluate_test_case(STR(test_label), v1_test_case);
|
||||
|
||||
/*
|
||||
* If the v1 test input is malformed, skip the mutation tests.
|
||||
*/
|
||||
if (v1_test_case->exp_return != 0)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Mutation test: a well-formed v1 test case should still pass after
|
||||
* appending a byte, and should return the actual parsed header
|
||||
* length. The test uses the implicit VSTRING null safety byte.
|
||||
*/
|
||||
vstring_sprintf(test_label, "%d (one byte appended)",
|
||||
(int) (v1_test_case - v1_test_cases));
|
||||
mutated_test_case = *v1_test_case;
|
||||
mutated_test_case.haproxy_req_len += 1;
|
||||
/* reuse v1_test_case->exp_req_len */
|
||||
test_failed += evaluate_test_case(STR(test_label), &mutated_test_case);
|
||||
|
||||
/*
|
||||
* Mutation test: a well-formed v1 test case should fail after
|
||||
* stripping the terminator.
|
||||
*/
|
||||
vstring_sprintf(test_label, "%d (last byte stripped)",
|
||||
(int) (v1_test_case - v1_test_cases));
|
||||
mutated_test_case = *v1_test_case;
|
||||
mutated_test_case.exp_return = "missing protocol header terminator";
|
||||
mutated_test_case.haproxy_req_len -= 1;
|
||||
mutated_test_case.exp_req_len = mutated_test_case.haproxy_req_len;
|
||||
test_failed += evaluate_test_case(STR(test_label), &mutated_test_case);
|
||||
|
||||
/*
|
||||
* A 'well-formed' v1 test case should pass after conversion to v2.
|
||||
*/
|
||||
vstring_sprintf(test_label, "%d (converted to v2)",
|
||||
(int) (v1_test_case - v1_test_cases));
|
||||
v2_test_case = *v1_test_case;
|
||||
convert_v1_proxy_req_to_v2(v2_request_buf,
|
||||
v1_test_case->haproxy_request,
|
||||
v1_test_case->haproxy_req_len);
|
||||
v2_test_case.haproxy_request = STR(v2_request_buf);
|
||||
v2_test_case.haproxy_req_len = PP2_HEADER_LEN
|
||||
+ ntohs(((struct proxy_hdr_v2 *) STR(v2_request_buf))->len);
|
||||
v2_test_case.exp_req_len = v2_test_case.haproxy_req_len;
|
||||
test_failed += evaluate_test_case(STR(test_label), &v2_test_case);
|
||||
|
||||
/*
|
||||
* Mutation test: a well-formed v2 test case should still pass after
|
||||
* appending a byte, and should return the actual parsed header
|
||||
* length. The test uses the implicit VSTRING null safety byte.
|
||||
*/
|
||||
vstring_sprintf(test_label, "%d (converted to v2, one byte appended)",
|
||||
(int) (v1_test_case - v1_test_cases));
|
||||
mutated_test_case = v2_test_case;
|
||||
mutated_test_case.haproxy_req_len += 1;
|
||||
/* reuse v2_test_case->exp_req_len */
|
||||
test_failed += evaluate_test_case(STR(test_label), &mutated_test_case);
|
||||
|
||||
/*
|
||||
* Mutation test: a well-formed v2 test case should fail after
|
||||
* stripping one byte
|
||||
*/
|
||||
vstring_sprintf(test_label, "%d (converted to v2, last byte stripped)",
|
||||
(int) (v1_test_case - v1_test_cases));
|
||||
mutated_test_case = v2_test_case;
|
||||
mutated_test_case.haproxy_req_len -= 1;
|
||||
mutated_test_case.exp_req_len = mutated_test_case.haproxy_req_len;
|
||||
mutated_test_case.exp_return = "short version 2 protocol header";
|
||||
test_failed += evaluate_test_case(STR(test_label), &mutated_test_case);
|
||||
}
|
||||
|
||||
/*
|
||||
* Additional V2-only tests.
|
||||
*/
|
||||
test_failed +=
|
||||
evaluate_test_case("v2 non-proxy request", &v2_non_proxy_test);
|
||||
|
||||
/*
|
||||
* Clean up.
|
||||
*/
|
||||
vstring_free(v2_request_buf);
|
||||
vstring_free(mutated_request_buf);
|
||||
vstring_free(test_label);
|
||||
if (tests_failed)
|
||||
msg_info("tests failed: %d", tests_failed);
|
||||
exit(tests_failed != 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -19,12 +19,32 @@
|
||||
/*
|
||||
* External interface.
|
||||
*/
|
||||
extern const char *haproxy_srvr_parse(const char *, ssize_t *, int *,
|
||||
extern const char *haproxy_srvr_parse_sa(const char *, ssize_t *, int *,
|
||||
MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *,
|
||||
MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *);
|
||||
extern int haproxy_srvr_receive(int, int *,
|
||||
MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *,
|
||||
MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *);
|
||||
struct sockaddr *, SOCKADDR_SIZE *,
|
||||
struct sockaddr *, SOCKADDR_SIZE *);
|
||||
|
||||
#define haproxy_srvr_parse(str, len, non_proxy, client_addr, client_port, \
|
||||
server_addr, server_port) \
|
||||
haproxy_srvr_parse_sa((str), (len), (non_proxy), \
|
||||
(client_addr), (client_port), \
|
||||
(server_addr), (server_port), \
|
||||
(struct sockaddr *) 0, (SOCKADDR_SIZE *) 0, \
|
||||
(struct sockaddr *) 0, (SOCKADDR_SIZE *) 0)
|
||||
extern int haproxy_srvr_receive_sa(int, int *,
|
||||
MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *,
|
||||
MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *,
|
||||
struct sockaddr *, SOCKADDR_SIZE *,
|
||||
struct sockaddr *, SOCKADDR_SIZE *);
|
||||
|
||||
#define haproxy_srvr_receive(fd, non_proxy, client_addr, client_port, \
|
||||
server_addr, server_port) \
|
||||
haproxy_srvr_receive_sa((fd), (non_proxy), \
|
||||
(client_addr), (client_port), \
|
||||
(server_addr), (server_port), \
|
||||
(struct sockaddr *) 0, (SOCKADDR_SIZE *) 0, \
|
||||
(struct sockaddr *) 0, (SOCKADDR_SIZE *) 0)
|
||||
|
||||
#define HAPROXY_PROTO_NAME "haproxy"
|
||||
|
||||
@ -33,6 +53,86 @@ extern int haproxy_srvr_receive(int, int *,
|
||||
#define DONT_GRIPE 0
|
||||
#endif
|
||||
|
||||
#ifdef _HAPROXY_SRVR_INTERNAL_
|
||||
|
||||
/*
|
||||
* The haproxy protocol assumes that a haproxy header will normally not
|
||||
* exceed the default IPv4 TCP MSS, i.e. 576-40=536 bytes (the IPv6 default
|
||||
* is larger: 1280-60=1220). With a proxy header that contains IPv6
|
||||
* addresses, that leaves room for 536-52=484 bytes of TLVs. The Postfix
|
||||
* implementation does not support headers with UNIX-domain addresses.
|
||||
*/
|
||||
#define HAPROXY_HEADER_MAX_LEN 536
|
||||
|
||||
/*
|
||||
* Begin protocol v2 definitions from haproxy/include/types/connection.h.
|
||||
*/
|
||||
#define PP2_SIGNATURE "\r\n\r\n\0\r\nQUIT\n"
|
||||
#define PP2_SIGNATURE_LEN 12
|
||||
#define PP2_HEADER_LEN 16
|
||||
|
||||
/* ver_cmd byte */
|
||||
#define PP2_CMD_LOCAL 0x00
|
||||
#define PP2_CMD_PROXY 0x01
|
||||
#define PP2_CMD_MASK 0x0F
|
||||
|
||||
#define PP2_VERSION 0x20
|
||||
#define PP2_VERSION_MASK 0xF0
|
||||
|
||||
/* fam byte */
|
||||
#define PP2_TRANS_UNSPEC 0x00
|
||||
#define PP2_TRANS_STREAM 0x01
|
||||
#define PP2_TRANS_DGRAM 0x02
|
||||
#define PP2_TRANS_MASK 0x0F
|
||||
|
||||
#define PP2_FAM_UNSPEC 0x00
|
||||
#define PP2_FAM_INET 0x10
|
||||
#define PP2_FAM_INET6 0x20
|
||||
#define PP2_FAM_UNIX 0x30
|
||||
#define PP2_FAM_MASK 0xF0
|
||||
|
||||
/* len field (2 bytes) */
|
||||
#define PP2_ADDR_LEN_UNSPEC (0)
|
||||
#define PP2_ADDR_LEN_INET (4 + 4 + 2 + 2)
|
||||
#define PP2_ADDR_LEN_INET6 (16 + 16 + 2 + 2)
|
||||
#define PP2_ADDR_LEN_UNIX (108 + 108)
|
||||
|
||||
#define PP2_HDR_LEN_UNSPEC (PP2_HEADER_LEN + PP2_ADDR_LEN_UNSPEC)
|
||||
#define PP2_HDR_LEN_INET (PP2_HEADER_LEN + PP2_ADDR_LEN_INET)
|
||||
#define PP2_HDR_LEN_INET6 (PP2_HEADER_LEN + PP2_ADDR_LEN_INET6)
|
||||
#define PP2_HDR_LEN_UNIX (PP2_HEADER_LEN + PP2_ADDR_LEN_UNIX)
|
||||
|
||||
struct proxy_hdr_v2 {
|
||||
uint8_t sig[PP2_SIGNATURE_LEN]; /* PP2_SIGNATURE */
|
||||
uint8_t ver_cmd; /* protocol version | command */
|
||||
uint8_t fam; /* protocol family and transport */
|
||||
uint16_t len; /* length of remainder */
|
||||
union {
|
||||
struct { /* for TCP/UDP over IPv4, len = 12 */
|
||||
uint32_t src_addr;
|
||||
uint32_t dst_addr;
|
||||
uint16_t src_port;
|
||||
uint16_t dst_port;
|
||||
} ip4;
|
||||
struct { /* for TCP/UDP over IPv6, len = 36 */
|
||||
uint8_t src_addr[16];
|
||||
uint8_t dst_addr[16];
|
||||
uint16_t src_port;
|
||||
uint16_t dst_port;
|
||||
} ip6;
|
||||
struct { /* for AF_UNIX sockets, len = 216 */
|
||||
uint8_t src_addr[108];
|
||||
uint8_t dst_addr[108];
|
||||
} unx;
|
||||
} addr;
|
||||
};
|
||||
|
||||
/*
|
||||
* End protocol v2 definitions from haproxy/include/types/connection.h.
|
||||
*/
|
||||
|
||||
#endif /* _HAPROXY_SRVR_INTERNAL_ */
|
||||
|
||||
/* LICENSE
|
||||
/* .ad
|
||||
/* .fi
|
||||
|
534
postfix/src/global/haproxy_srvr_test.c
Normal file
534
postfix/src/global/haproxy_srvr_test.c
Normal 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);
|
||||
}
|
@ -20,7 +20,7 @@
|
||||
* Patches change both the patchlevel and the release date. Snapshots have no
|
||||
* patchlevel; they change the release date only.
|
||||
*/
|
||||
#define MAIL_RELEASE_DATE "20250323"
|
||||
#define MAIL_RELEASE_DATE "20250409"
|
||||
#define MAIL_VERSION_NUMBER "3.11"
|
||||
|
||||
#ifdef SNAPSHOT
|
||||
|
@ -116,7 +116,7 @@ void psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
|
||||
PSC_ENDPT_LOOKUP_FN lookup_done)
|
||||
{
|
||||
struct sockaddr_storage addr_storage;
|
||||
SOCKADDR_SIZE addr_storage_len = sizeof(addr_storage);
|
||||
SOCKADDR_SIZE addr_storage_len;
|
||||
int status;
|
||||
MAI_HOSTADDR_STR smtp_client_addr;
|
||||
MAI_SERVPORT_STR smtp_client_port;
|
||||
@ -124,10 +124,13 @@ void psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
|
||||
MAI_SERVPORT_STR smtp_server_port;
|
||||
int aierr;
|
||||
|
||||
#define RESET_ADDR_STORAGE_LEN() (addr_storage_len = sizeof(addr_storage))
|
||||
|
||||
/*
|
||||
* Look up the remote SMTP client address and port.
|
||||
*/
|
||||
if (getpeername(vstream_fileno(smtp_client_stream), (struct sockaddr *)
|
||||
if (RESET_ADDR_STORAGE_LEN(),
|
||||
getpeername(vstream_fileno(smtp_client_stream), (struct sockaddr *)
|
||||
&addr_storage, &addr_storage_len) < 0) {
|
||||
msg_warn("getpeername: %m -- dropping this connection");
|
||||
status = -1;
|
||||
@ -135,11 +138,12 @@ void psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
|
||||
|
||||
/*
|
||||
* Convert the remote SMTP client address and port to printable form for
|
||||
* logging and access control.
|
||||
* logging and access control. Note: this may change addr_storage and
|
||||
* addr_storage_len.
|
||||
*/
|
||||
else if ((aierr = sane_sockaddr_to_hostaddr(
|
||||
(struct sockaddr *) &addr_storage,
|
||||
addr_storage_len, &smtp_client_addr,
|
||||
&addr_storage_len, &smtp_client_addr,
|
||||
&smtp_client_port, SOCK_STREAM)) != 0) {
|
||||
msg_warn("cannot convert client address/port to string: %s"
|
||||
" -- dropping this connection",
|
||||
@ -148,9 +152,11 @@ void psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up the local SMTP server address and port.
|
||||
* Look up the local SMTP server address and port. Be sure to reset the
|
||||
* addr_storage_len value.
|
||||
*/
|
||||
else if (getsockname(vstream_fileno(smtp_client_stream),
|
||||
else if (RESET_ADDR_STORAGE_LEN(),
|
||||
getsockname(vstream_fileno(smtp_client_stream),
|
||||
(struct sockaddr *) &addr_storage,
|
||||
&addr_storage_len) < 0) {
|
||||
msg_warn("getsockname: %m -- dropping this connection");
|
||||
@ -159,11 +165,12 @@ void psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
|
||||
|
||||
/*
|
||||
* Convert the local SMTP server address and port to printable form for
|
||||
* logging.
|
||||
* logging. This may also change addr_storage and addr_storage_len, but
|
||||
* those variables are dead.
|
||||
*/
|
||||
else if ((aierr = sane_sockaddr_to_hostaddr(
|
||||
(struct sockaddr *) &addr_storage,
|
||||
addr_storage_len, &smtp_server_addr,
|
||||
&addr_storage_len, &smtp_server_addr,
|
||||
&smtp_server_port, SOCK_STREAM)) != 0) {
|
||||
msg_warn("cannot convert server address/port to string: %s"
|
||||
" -- dropping this connection",
|
||||
|
@ -372,7 +372,7 @@ void smtp_tlsrpt_set_tcp_connection(SMTP_STATE *state)
|
||||
client_addr.buf[0] = 0;
|
||||
} else if ((aierr = sane_sockaddr_to_hostaddr(
|
||||
(struct sockaddr *) &addr_storage,
|
||||
addr_storage_len, &client_addr,
|
||||
&addr_storage_len, &client_addr,
|
||||
(MAI_SERVPORT_STR *) 0,
|
||||
SOCK_STREAM)) != 0) {
|
||||
msg_warn("%s: cannot convert IP address to string (%s)"
|
||||
|
@ -58,6 +58,10 @@
|
||||
/* .IP dest_port
|
||||
/* Server port, available as Milter {daemon_port} macro, and
|
||||
/* as server_port policy delegation attribute.
|
||||
/* .IP sockaddr_len
|
||||
/* .IP dest_sockaddr_len
|
||||
/* These are initialized to zero, to indicate that the corresponding
|
||||
/* sockaddr_storage members are not set.
|
||||
/* .IP name_status
|
||||
/* The name_status result field specifies how the name
|
||||
/* information should be interpreted:
|
||||
@ -117,6 +121,9 @@
|
||||
/* Google, Inc.
|
||||
/* 111 8th Avenue
|
||||
/* New York, NY 10011, USA
|
||||
/*
|
||||
/* Wietse Venema
|
||||
/* porcupine.org
|
||||
/*--*/
|
||||
|
||||
/* System library. */
|
||||
@ -617,6 +624,7 @@ void smtpd_peer_init(SMTPD_STATE *state)
|
||||
state->anvil_range = 0;
|
||||
state->dest_addr = 0;
|
||||
state->dest_port = 0;
|
||||
state->dest_sockaddr_len = 0;
|
||||
|
||||
/*
|
||||
* Determine the remote SMTP client address and port.
|
||||
|
@ -47,7 +47,7 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
|
||||
mkmap_fail.c mkmap_lmdb.c mkmap_open.c mkmap_sdbm.c inet_prefix_top.c \
|
||||
inet_addr_sizes.c quote_for_json.c mystrerror.c \
|
||||
sane_sockaddr_to_hostaddr.c normalize_ws.c valid_uri_scheme.c \
|
||||
clean_ascii_cntrl_space.c
|
||||
clean_ascii_cntrl_space.c normalize_v4mapped_addr.c
|
||||
OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
|
||||
attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
|
||||
attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
|
||||
@ -95,7 +95,8 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
|
||||
sane_strtol.o hash_fnv.o ldseed.o mkmap_db.o mkmap_dbm.o \
|
||||
mkmap_fail.o mkmap_open.o inet_prefix_top.o inet_addr_sizes.o \
|
||||
quote_for_json.o mystrerror.o sane_sockaddr_to_hostaddr.o \
|
||||
normalize_ws.o valid_uri_scheme.o clean_ascii_cntrl_space.o
|
||||
normalize_ws.o valid_uri_scheme.o clean_ascii_cntrl_space.o \
|
||||
normalize_v4mapped_addr.o
|
||||
# MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
|
||||
# When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
|
||||
# otherwise it sets the PLUGIN_* macros.
|
||||
@ -128,7 +129,7 @@ HDRS = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \
|
||||
check_arg.h argv_attr.h msg_logger.h logwriter.h byte_mask.h \
|
||||
known_tcp_ports.h sane_strtol.h hash_fnv.h ldseed.h mkmap.h \
|
||||
inet_prefix_top.h inet_addr_sizes.h valid_uri_scheme.h \
|
||||
clean_ascii_cntrl_space.h
|
||||
clean_ascii_cntrl_space.h normalize_v4mapped_addr.h
|
||||
TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
|
||||
stream_test.c dup2_pass_on_exec.c
|
||||
DEFS = -I. -D$(SYSTYPE)
|
||||
@ -151,7 +152,8 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \
|
||||
vbuf_print split_qnameval vstream msg_logger byte_mask \
|
||||
known_tcp_ports dict_stream find_inet binhash hash_fnv argv \
|
||||
clean_env inet_prefix_top printable readlline quote_for_json \
|
||||
normalize_ws valid_uri_scheme clean_ascii_cntrl_space
|
||||
normalize_ws valid_uri_scheme clean_ascii_cntrl_space \
|
||||
normalize_v4mapped_addr_test
|
||||
PLUGIN_MAP_SO = $(LIB_PREFIX)pcre$(LIB_SUFFIX) $(LIB_PREFIX)lmdb$(LIB_SUFFIX) \
|
||||
$(LIB_PREFIX)cdb$(LIB_SUFFIX) $(LIB_PREFIX)sdbm$(LIB_SUFFIX)
|
||||
HTABLE_FIX = NORANDOMIZE=1
|
||||
@ -630,6 +632,9 @@ clean_ascii_cntrl_space: $(LIB)
|
||||
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
|
||||
mv junk $@.o
|
||||
|
||||
normalize_v4mapped_addr_test: normalize_v4mapped_addr_test.c $(LIB)
|
||||
$(CC) $(CFLAGS) -o $@ normalize_v4mapped_addr_test.c $(LIB) $(SYSLIBS)
|
||||
|
||||
tests: all valid_hostname_test mac_expand_test dict_test unescape_test \
|
||||
hex_quote_test ctable_test inet_addr_list_test base64_code_test \
|
||||
attr_scan64_test attr_scan0_test host_port_test dict_tests \
|
||||
@ -641,7 +646,8 @@ tests: all valid_hostname_test mac_expand_test dict_test unescape_test \
|
||||
vstream_test byte_mask_tests mystrtok_test known_tcp_ports_test \
|
||||
binhash_test argv_test inet_prefix_top_test printable_test \
|
||||
valid_utf8_string_test readlline_test quote_for_json_test \
|
||||
normalize_ws_test valid_uri_scheme_test clean_ascii_cntrl_space_test
|
||||
normalize_ws_test valid_uri_scheme_test clean_ascii_cntrl_space_test \
|
||||
test_normalize_v4mapped_addr
|
||||
|
||||
dict_tests: all dict_test \
|
||||
dict_pcre_tests dict_cidr_test dict_thash_test dict_static_test \
|
||||
@ -1127,6 +1133,9 @@ valid_uri_scheme_test: valid_uri_scheme
|
||||
clean_ascii_cntrl_space_test: clean_ascii_cntrl_space
|
||||
$(SHLIB_ENV) ${VALGRIND} ./clean_ascii_cntrl_space
|
||||
|
||||
test_normalize_v4mapped_addr: update normalize_v4mapped_addr_test
|
||||
$(SHLIB_ENV) ${VALGRIND} ./normalize_v4mapped_addr_test
|
||||
|
||||
depend: $(MAKES)
|
||||
(sed '1,/^# do not edit/!d' Makefile.in; \
|
||||
set -e; for i in [a-z][a-z0-9]*.c; do \
|
||||
@ -2532,6 +2541,24 @@ non_blocking.o: iostuff.h
|
||||
non_blocking.o: msg.h
|
||||
non_blocking.o: non_blocking.c
|
||||
non_blocking.o: sys_defs.h
|
||||
normalize_v4mapped_addr.o: inet_proto.h
|
||||
normalize_v4mapped_addr.o: myaddrinfo.h
|
||||
normalize_v4mapped_addr.o: normalize_v4mapped_addr.c
|
||||
normalize_v4mapped_addr.o: normalize_v4mapped_addr.h
|
||||
normalize_v4mapped_addr.o: sock_addr.h
|
||||
normalize_v4mapped_addr.o: sys_defs.h
|
||||
normalize_v4mapped_addr_test.o: check_arg.h
|
||||
normalize_v4mapped_addr_test.o: inet_proto.h
|
||||
normalize_v4mapped_addr_test.o: msg.h
|
||||
normalize_v4mapped_addr_test.o: msg_vstream.h
|
||||
normalize_v4mapped_addr_test.o: myaddrinfo.h
|
||||
normalize_v4mapped_addr_test.o: normalize_v4mapped_addr.h
|
||||
normalize_v4mapped_addr_test.o: normalize_v4mapped_addr_test.c
|
||||
normalize_v4mapped_addr_test.o: stringops.h
|
||||
normalize_v4mapped_addr_test.o: sys_defs.h
|
||||
normalize_v4mapped_addr_test.o: vbuf.h
|
||||
normalize_v4mapped_addr_test.o: vstream.h
|
||||
normalize_v4mapped_addr_test.o: vstring.h
|
||||
normalize_ws.o: check_arg.h
|
||||
normalize_ws.o: msg.h
|
||||
normalize_ws.o: msg_vstream.h
|
||||
@ -2673,8 +2700,8 @@ sane_rename.o: sane_fsops.h
|
||||
sane_rename.o: sane_rename.c
|
||||
sane_rename.o: sys_defs.h
|
||||
sane_rename.o: warn_stat.h
|
||||
sane_sockaddr_to_hostaddr.o: inet_proto.h
|
||||
sane_sockaddr_to_hostaddr.o: myaddrinfo.h
|
||||
sane_sockaddr_to_hostaddr.o: normalize_v4mapped_addr.h
|
||||
sane_sockaddr_to_hostaddr.o: sane_sockaddr_to_hostaddr.c
|
||||
sane_sockaddr_to_hostaddr.o: sys_defs.h
|
||||
sane_socketpair.o: msg.h
|
||||
|
@ -15,7 +15,9 @@
|
||||
/* .in -4
|
||||
/* } INET_PROTO_INFO;
|
||||
/*
|
||||
/* const INET_PROTO_INFO *inet_proto_init(context, protocols)
|
||||
/* const INET_PROTO_INFO *inet_proto_init(
|
||||
/* const char *context,
|
||||
/* const char *protocols)
|
||||
/*
|
||||
/* const INET_PROTO_INFO *inet_proto_info()
|
||||
/* DESCRIPTION
|
||||
|
@ -833,8 +833,11 @@ int main(int argc, char **argv)
|
||||
struct addrinfo **resv;
|
||||
MAI_HOSTNAME_STR host;
|
||||
MAI_HOSTADDR_STR addr;
|
||||
MAI_SERVNAME_STR serv;
|
||||
MAI_SERVPORT_STR port;
|
||||
size_t len, n;
|
||||
int err;
|
||||
char *aport;
|
||||
|
||||
msg_vstream_init(argv[0], VSTREAM_ERR);
|
||||
|
||||
@ -845,9 +848,13 @@ int main(int argc, char **argv)
|
||||
|
||||
msg_info("=== hostname %s ===", argv[2]);
|
||||
|
||||
if ((err = hostname_to_sockaddr(argv[2], (char *) 0, 0, &info)) != 0) {
|
||||
msg_info("hostname_to_sockaddr(%s): %s",
|
||||
argv[2], err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
|
||||
#define STR_OR_NULL(s) ((s) ? (s) : "(null)")
|
||||
|
||||
aport = split_at(argv[2], ':');
|
||||
if ((err = hostname_to_sockaddr(argv[2], aport, 0, &info)) != 0) {
|
||||
msg_warn("hostname_to_sockaddr(%s:%s): %s",
|
||||
argv[2], STR_OR_NULL(aport), err == EAI_SYSTEM ?
|
||||
strerror(errno) : gai_strerror(err));
|
||||
} else {
|
||||
for (len = 0, ip = info; ip != 0; ip = ip->ai_next)
|
||||
len += 1;
|
||||
@ -858,20 +865,21 @@ int main(int argc, char **argv)
|
||||
for (n = 0; n < len; n++) {
|
||||
ip = resv[n];
|
||||
if ((err = sockaddr_to_hostaddr(ip->ai_addr, ip->ai_addrlen, &addr,
|
||||
(MAI_SERVPORT_STR *) 0, 0)) != 0) {
|
||||
msg_info("sockaddr_to_hostaddr: %s",
|
||||
&port, 0)) != 0) {
|
||||
msg_warn("sockaddr_to_hostaddr: %s",
|
||||
err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
|
||||
continue;
|
||||
}
|
||||
msg_info("%s -> family=%d sock=%d proto=%d %s", argv[2],
|
||||
ip->ai_family, ip->ai_socktype, ip->ai_protocol, addr.buf);
|
||||
msg_info("%s:%s -> family=%d sock=%d proto=%d %s:%s",
|
||||
argv[2], STR_OR_NULL(aport), ip->ai_family,
|
||||
ip->ai_socktype, ip->ai_protocol, addr.buf, port.buf);
|
||||
if ((err = sockaddr_to_hostname(ip->ai_addr, ip->ai_addrlen, &host,
|
||||
(MAI_SERVNAME_STR *) 0, 0)) != 0) {
|
||||
msg_info("sockaddr_to_hostname: %s",
|
||||
&serv, 0)) != 0) {
|
||||
msg_warn("sockaddr_to_hostname: %s",
|
||||
err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
|
||||
continue;
|
||||
}
|
||||
msg_info("%s -> %s", addr.buf, host.buf);
|
||||
msg_info("%s:%s -> %s:%s", addr.buf, port.buf, host.buf, serv.buf);
|
||||
}
|
||||
freeaddrinfo(info);
|
||||
myfree((void *) resv);
|
||||
@ -879,23 +887,25 @@ int main(int argc, char **argv)
|
||||
|
||||
msg_info("=== host address %s ===", argv[3]);
|
||||
|
||||
if ((err = hostaddr_to_sockaddr(argv[3], (char *) 0, 0, &ip)) != 0) {
|
||||
msg_info("hostaddr_to_sockaddr(%s): %s",
|
||||
argv[3], err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
|
||||
aport = split_at(argv[3], ':');
|
||||
if ((err = hostaddr_to_sockaddr(argv[3], aport, 0, &ip)) != 0) {
|
||||
msg_warn("hostaddr_to_sockaddr(%s:%s): %s",
|
||||
argv[3], STR_OR_NULL(aport), err == EAI_SYSTEM ?
|
||||
strerror(errno) : gai_strerror(err));
|
||||
} else {
|
||||
if ((err = sockaddr_to_hostaddr(ip->ai_addr, ip->ai_addrlen, &addr,
|
||||
(MAI_SERVPORT_STR *) 0, 0)) != 0) {
|
||||
msg_info("sockaddr_to_hostaddr: %s",
|
||||
&port, 0)) != 0) {
|
||||
msg_warn("sockaddr_to_hostaddr: %s",
|
||||
err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
|
||||
} else {
|
||||
msg_info("%s -> family=%d sock=%d proto=%d %s", argv[3],
|
||||
ip->ai_family, ip->ai_socktype, ip->ai_protocol, addr.buf);
|
||||
msg_info("%s:%s -> family=%d sock=%d proto=%d %s:%s", argv[3], STR_OR_NULL(aport),
|
||||
ip->ai_family, ip->ai_socktype, ip->ai_protocol, addr.buf, port.buf);
|
||||
if ((err = sockaddr_to_hostname(ip->ai_addr, ip->ai_addrlen, &host,
|
||||
(MAI_SERVNAME_STR *) 0, 0)) != 0) {
|
||||
msg_info("sockaddr_to_hostname: %s",
|
||||
&serv, 0)) != 0) {
|
||||
msg_warn("sockaddr_to_hostname: %s",
|
||||
err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
|
||||
} else
|
||||
msg_info("%s -> %s", addr.buf, host.buf);
|
||||
msg_info("%s:%s -> %s:%s", addr.buf, port.buf, host.buf, serv.buf);
|
||||
freeaddrinfo(ip);
|
||||
}
|
||||
}
|
||||
|
@ -213,8 +213,8 @@ extern void myaddrinfo_control(int,...);
|
||||
/*
|
||||
* sane_sockaddr_to_hostaddr.c
|
||||
*/
|
||||
extern int WARN_UNUSED_RESULT sane_sockaddr_to_hostaddr(const struct sockaddr *,
|
||||
SOCKADDR_SIZE, MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *, int);
|
||||
extern int WARN_UNUSED_RESULT sane_sockaddr_to_hostaddr(struct sockaddr *,
|
||||
SOCKADDR_SIZE *, MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *, int);
|
||||
|
||||
/* LICENSE
|
||||
/* .ad
|
||||
|
@ -1,8 +1,8 @@
|
||||
./myaddrinfo: === hostname belly.porcupine.org ===
|
||||
./myaddrinfo: belly.porcupine.org -> family=2 sock=1 proto=6 168.100.3.6
|
||||
./myaddrinfo: 168.100.3.6 -> belly.porcupine.org
|
||||
./myaddrinfo: belly.porcupine.org -> family=28 sock=1 proto=6 2604:8d00:189::6
|
||||
./myaddrinfo: 2604:8d00:189::6 -> belly.porcupine.org
|
||||
./myaddrinfo: belly.porcupine.org:(null) -> family=2 sock=1 proto=6 168.100.3.6:0
|
||||
./myaddrinfo: 168.100.3.6:0 -> belly.porcupine.org:0
|
||||
./myaddrinfo: belly.porcupine.org:(null) -> family=28 sock=1 proto=6 2604:8d00:189::6:0
|
||||
./myaddrinfo: 2604:8d00:189::6:0 -> belly.porcupine.org:0
|
||||
./myaddrinfo: === host address 168.100.3.2 ===
|
||||
./myaddrinfo: 168.100.3.2 -> family=2 sock=1 proto=6 168.100.3.2
|
||||
./myaddrinfo: 168.100.3.2 -> spike.porcupine.org
|
||||
./myaddrinfo: 168.100.3.2:(null) -> family=2 sock=1 proto=6 168.100.3.2:0
|
||||
./myaddrinfo: 168.100.3.2:0 -> spike.porcupine.org:0
|
||||
|
@ -1,5 +1,5 @@
|
||||
./myaddrinfo: === hostname null.porcupine.org ===
|
||||
./myaddrinfo: hostname_to_sockaddr(null.porcupine.org): hostname nor servname provided, or not known
|
||||
./myaddrinfo: warning: hostname_to_sockaddr(null.porcupine.org:(null)): hostname nor servname provided, or not known
|
||||
./myaddrinfo: === host address 10.0.0.0 ===
|
||||
./myaddrinfo: 10.0.0.0 -> family=2 sock=1 proto=6 10.0.0.0
|
||||
./myaddrinfo: sockaddr_to_hostname: hostname nor servname provided, or not known
|
||||
./myaddrinfo: 10.0.0.0:(null) -> family=2 sock=1 proto=6 10.0.0.0:0
|
||||
./myaddrinfo: warning: sockaddr_to_hostname: hostname nor servname provided, or not known
|
||||
|
@ -1,6 +1,6 @@
|
||||
./myaddrinfo4: === hostname belly.porcupine.org ===
|
||||
./myaddrinfo4: belly.porcupine.org -> family=2 sock=1 proto=6 168.100.3.6
|
||||
./myaddrinfo4: 168.100.3.6 -> belly.porcupine.org
|
||||
./myaddrinfo4: belly.porcupine.org:(null) -> family=2 sock=1 proto=0 168.100.3.6:0
|
||||
./myaddrinfo4: warning: sockaddr_to_hostname: hostname nor servname provided, or not known
|
||||
./myaddrinfo4: === host address 168.100.3.2 ===
|
||||
./myaddrinfo4: 168.100.3.2 -> family=2 sock=1 proto=6 168.100.3.2
|
||||
./myaddrinfo4: 168.100.3.2 -> spike.porcupine.org
|
||||
./myaddrinfo4: 168.100.3.2:(null) -> family=2 sock=1 proto=0 168.100.3.2:0
|
||||
./myaddrinfo4: warning: sockaddr_to_hostname: hostname nor servname provided, or not known
|
||||
|
@ -1,5 +1,5 @@
|
||||
./myaddrinfo4: === hostname null.porcupine.org ===
|
||||
./myaddrinfo4: hostname2sockaddr(null.porcupine.org): No address associated with hostname
|
||||
./myaddrinfo4: warning: hostname_to_sockaddr(null.porcupine.org:(null)): No address associated with hostname
|
||||
./myaddrinfo4: === host address 10.0.0.0 ===
|
||||
./myaddrinfo4: 10.0.0.0 -> family=2 sock=1 proto=6 10.0.0.0
|
||||
./myaddrinfo4: sockaddr2hostname: hostname nor servname provided, or not known
|
||||
./myaddrinfo4: 10.0.0.0:(null) -> family=2 sock=1 proto=0 10.0.0.0:0
|
||||
./myaddrinfo4: warning: sockaddr_to_hostname: hostname nor servname provided, or not known
|
||||
|
90
postfix/src/util/normalize_v4mapped_addr.c
Normal file
90
postfix/src/util/normalize_v4mapped_addr.c
Normal 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);
|
||||
}
|
40
postfix/src/util/normalize_v4mapped_addr.h
Normal file
40
postfix/src/util/normalize_v4mapped_addr.h
Normal 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
|
189
postfix/src/util/normalize_v4mapped_addr_test.c
Normal file
189
postfix/src/util/normalize_v4mapped_addr_test.c
Normal 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);
|
||||
}
|
@ -13,14 +13,13 @@
|
||||
/* MAI_SERVPORT_STR *port_buf,
|
||||
/* int socktype)
|
||||
/* DESCRIPTION
|
||||
/* sane_sockaddr_to_hostaddr() wraps sockaddr_to_hostaddr() and
|
||||
/* converts an IPv4 in IPv6 address to IPv4 form, but only if IPv4
|
||||
/* support is available.
|
||||
/* HISTORY
|
||||
/* .ad
|
||||
/* .fi
|
||||
/* This implementation was taken from postscreen, and consolidates
|
||||
/* multiple instances of similar code across the Postfix code base.
|
||||
/* sane_sockaddr_to_hostaddr() converts a V4mapped IPv6 address to
|
||||
/* IPv4 form, but only if IPv4 support is available. It then invokes
|
||||
/* sockaddr_to_hostaddr() to convert the result to human-readable
|
||||
/* form.
|
||||
/*
|
||||
/* NOTE: The V4mapped IPv6 conversion to IPv4 applies to both inputs
|
||||
/* and output.
|
||||
/* LICENSE
|
||||
/* .ad
|
||||
/* .fi
|
||||
@ -49,28 +48,20 @@
|
||||
* Utility library.
|
||||
*/
|
||||
#include <myaddrinfo.h>
|
||||
#include <inet_proto.h>
|
||||
|
||||
static const INET_PROTO_INFO *proto_info;
|
||||
#include <normalize_v4mapped_addr.h>
|
||||
|
||||
/* sane_sockaddr_to_hostaddr - sanitize IPV4 in IPV6 address */
|
||||
|
||||
int sane_sockaddr_to_hostaddr(const struct sockaddr *addr_storage,
|
||||
SOCKADDR_SIZE addr_storage_len,
|
||||
int sane_sockaddr_to_hostaddr(struct sockaddr *addr_storage,
|
||||
SOCKADDR_SIZE *addr_storage_len,
|
||||
MAI_HOSTADDR_STR *addr_buf,
|
||||
MAI_SERVPORT_STR *port_buf,
|
||||
int socktype)
|
||||
{
|
||||
int aierr;
|
||||
|
||||
if (proto_info == 0)
|
||||
proto_info = inet_proto_info();
|
||||
|
||||
if ((aierr = sockaddr_to_hostaddr(addr_storage, addr_storage_len,
|
||||
addr_buf, port_buf, socktype)) == 0
|
||||
&& strncasecmp("::ffff:", addr_buf->buf, 7) == 0
|
||||
&& strchr((char *) proto_info->sa_family_list, AF_INET) != 0)
|
||||
memmove(addr_buf->buf, addr_buf->buf + 7,
|
||||
sizeof(addr_buf->buf) - 7);
|
||||
return (aierr);
|
||||
#ifdef AF_INET6
|
||||
if (addr_storage->sa_family == AF_INET6)
|
||||
normalize_v4mapped_sockaddr(addr_storage, addr_storage_len);
|
||||
#endif
|
||||
return (sockaddr_to_hostaddr(addr_storage, *addr_storage_len,
|
||||
addr_buf, port_buf, socktype));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user