mirror of
https://github.com/vdukhovni/postfix
synced 2025-08-22 09:57:34 +00:00
postfix-3.9-20240227
This commit is contained in:
parent
03382b275a
commit
f38f9d82f3
@ -27756,10 +27756,11 @@ Apologies for any names omitted.
|
|||||||
or access control limitations. Files: smtpd/smtpd.[hc],
|
or access control limitations. Files: smtpd/smtpd.[hc],
|
||||||
util/argv.[hc].
|
util/argv.[hc].
|
||||||
|
|
||||||
Workaround: some OS lies under load: it says that a socket
|
Workaround: tlsmgr logfile spam. Some OS lies under load:
|
||||||
is readable, then it says that the socket has unread data,
|
it says that a socket is readable, then it says that the
|
||||||
and then it says that read returns EOF, causing Postfix to
|
socket has unread data, and then it says that read returns
|
||||||
spam the log with a warning message. File: tlsmgr/tlsmgr.c.
|
EOF, causing Postfix to spam the log with a warning message.
|
||||||
|
File: tlsmgr/tlsmgr.c.
|
||||||
|
|
||||||
20240125
|
20240125
|
||||||
|
|
||||||
@ -27829,17 +27830,6 @@ Apologies for any names omitted.
|
|||||||
|
|
||||||
20240209
|
20240209
|
||||||
|
|
||||||
Safety: enforce a sane but generous upper bound (100) for
|
|
||||||
the number of DNS resource records that the Postfix DNS
|
|
||||||
client library will admit into a list. This is 20x the
|
|
||||||
default smtp_mx_address_limit value for the number of DNS
|
|
||||||
records that the Postfix SMTP client is willing to consider.
|
|
||||||
Log a warning "dropping records after qname=X qtype=Y" when
|
|
||||||
a list cannot accept more elements. This prevents a tail
|
|
||||||
recursion crash that degrades mail delivery performance.
|
|
||||||
Problem report by Toshifumi Sakaguchi. Files: dns/dns_rr.[hc],
|
|
||||||
global/mail_params.h.
|
|
||||||
|
|
||||||
Performance: eliminate worst-case behavior where the queue
|
Performance: eliminate worst-case behavior where the queue
|
||||||
manager deferred delivery to all destinations over a specific
|
manager deferred delivery to all destinations over a specific
|
||||||
delivery transport, after only a single delivery agent
|
delivery transport, after only a single delivery agent
|
||||||
@ -27920,3 +27910,27 @@ Apologies for any names omitted.
|
|||||||
status. Files: postconf/postconf.c, postconf/postconf_dbms.c,
|
status. Files: postconf/postconf.c, postconf/postconf_dbms.c,
|
||||||
postconf/postconf.h, conf/postfix-script, conf/post-install,
|
postconf/postconf.h, conf/postfix-script, conf/post-install,
|
||||||
postfix-install.
|
postfix-install.
|
||||||
|
|
||||||
|
20240221
|
||||||
|
|
||||||
|
Documentation: the text for TLS loglevel 2 was incomplete.
|
||||||
|
File: proto/postconf.proto.
|
||||||
|
|
||||||
|
20240226
|
||||||
|
|
||||||
|
Safety: drop and log over-size DNS responses resulting in
|
||||||
|
more than 100 records. This 20x larger than the number of
|
||||||
|
server addresses that the Postfix SMTP client is willing
|
||||||
|
to consider when delivering mail, and is well below the
|
||||||
|
number of records that could cause a tail recursion crash
|
||||||
|
in dns_rr_append() as reported by Toshifumi Sakaguchi. This
|
||||||
|
also limits the number of DNS requests from check_*_*_access
|
||||||
|
restrictions. Files: dns/dns.h, dns/dns_lookup.c, dns/dns_rr.c,
|
||||||
|
dns/test_dns_lookup.c, posttls-finger/posttls-finger.c,
|
||||||
|
smtp/smtp_addr.c, smtpd/smtpd_check.c.
|
||||||
|
|
||||||
|
20240227
|
||||||
|
|
||||||
|
Documentation: document the need to disable regular expression
|
||||||
|
special characters when using $name inside an inlined
|
||||||
|
pattern. Files: proto/pcre_table, proto/regexp_table.
|
||||||
|
@ -191,8 +191,12 @@ PCRE_TABLE(5) PCRE_TABLE(5)
|
|||||||
|
|
||||||
Postfix parses the result as if it is a file in /etc/postfix.
|
Postfix parses the result as if it is a file in /etc/postfix.
|
||||||
|
|
||||||
Note: if a rule contains <b>$</b>, specify <b>$$</b> to keep Postfix from trying to
|
Note: if an inlined rule contains <b>$</b>, specify <b>$$</b> to keep Postfix from
|
||||||
do <i>$name</i> expansion as it evaluates a parameter value.
|
trying to do <i>$name</i> expansion as it evaluates a parameter value.
|
||||||
|
|
||||||
|
Note: when using <i>$name</i> inside an inlined pattern, use \Q<i>$name</i>\E to dis-
|
||||||
|
able metacharacters such as '.' in the <i>$name</i> expansion. Otherwise, the
|
||||||
|
pattern may have unexpected matches.
|
||||||
|
|
||||||
<b>EXAMPLE SMTPD ACCESS MAP</b>
|
<b>EXAMPLE SMTPD ACCESS MAP</b>
|
||||||
# Protect your outgoing majordomo exploders
|
# Protect your outgoing majordomo exploders
|
||||||
|
@ -13597,7 +13597,9 @@ verification errors if server certificate verification is not required.
|
|||||||
With Postfix 2.8 and earlier, log the summary message and unconditionally
|
With Postfix 2.8 and earlier, log the summary message and unconditionally
|
||||||
log trust-chain verification errors. </dd>
|
log trust-chain verification errors. </dd>
|
||||||
|
|
||||||
<dt> </dt> <dd> 2 Also log levels during TLS negotiation. </dd>
|
<dt> </dt> <dd> 2 Also enable verbose logging in the Postfix TLS
|
||||||
|
library, log session cache operations, and enable OpenSSL logging
|
||||||
|
of the progress of the SSL handshake. </dd>
|
||||||
|
|
||||||
<dt> </dt> <dd> 3 Also log the hexadecimal and ASCII dump of the
|
<dt> </dt> <dd> 3 Also log the hexadecimal and ASCII dump of the
|
||||||
TLS negotiation process. </dd>
|
TLS negotiation process. </dd>
|
||||||
@ -18851,7 +18853,9 @@ if client certificate verification is not required. With Postfix 2.8 and
|
|||||||
earlier, log the summary message, peer certificate summary information
|
earlier, log the summary message, peer certificate summary information
|
||||||
and unconditionally log trust-chain verification errors. </dd>
|
and unconditionally log trust-chain verification errors. </dd>
|
||||||
|
|
||||||
<dt> </dt> <dd> 2 Also log levels during TLS negotiation. </dd>
|
<dt> </dt> <dd> 2 Also enable verbose logging in the Postfix TLS
|
||||||
|
library, log session cache operations, and enable OpenSSL logging
|
||||||
|
of the progress of the SSL handshake. </dd>
|
||||||
|
|
||||||
<dt> </dt> <dd> 3 Also log hexadecimal and ASCII dump of TLS negotiation
|
<dt> </dt> <dd> 3 Also log hexadecimal and ASCII dump of TLS negotiation
|
||||||
process. </dd>
|
process. </dd>
|
||||||
|
@ -148,8 +148,12 @@ REGEXP_TABLE(5) REGEXP_TABLE(5)
|
|||||||
|
|
||||||
Postfix parses the result as if it is a file in /etc/postfix.
|
Postfix parses the result as if it is a file in /etc/postfix.
|
||||||
|
|
||||||
Note: if a rule contains <b>$</b>, specify <b>$$</b> to keep Postfix from trying to
|
Note: if an inlined rule contains <b>$</b>, specify <b>$$</b> to keep Postfix from
|
||||||
do <i>$name</i> expansion as it evaluates a parameter value.
|
trying to do <i>$name</i> expansion as it evaluates a parameter value.
|
||||||
|
|
||||||
|
Note: when using <i>$name</i> inside an inlined pattern, this will not disable
|
||||||
|
metacharacters such as '.' in the <i>$name</i> expansion. To prevent unex-
|
||||||
|
pected matches, use a <a href="pcre_table.5.html">pcre</a>: table, and specify \Q<i>$name</i>\E.
|
||||||
|
|
||||||
<b>EXAMPLE SMTPD ACCESS MAP</b>
|
<b>EXAMPLE SMTPD ACCESS MAP</b>
|
||||||
# Disallow sender-specified routing. This is a must if you relay mail
|
# Disallow sender-specified routing. This is a must if you relay mail
|
||||||
|
@ -206,9 +206,14 @@ in\-memory file:
|
|||||||
|
|
||||||
Postfix parses the result as if it is a file in /etc/postfix.
|
Postfix parses the result as if it is a file in /etc/postfix.
|
||||||
|
|
||||||
Note: if a rule contains \fB$\fR, specify \fB$$\fR to keep
|
Note: if an inlined rule contains \fB$\fR, specify \fB$$\fR
|
||||||
Postfix from trying to do \fI$name\fR expansion as it
|
to keep Postfix from trying to do \fI$name\fR expansion as
|
||||||
evaluates a parameter value.
|
it evaluates a parameter value.
|
||||||
|
|
||||||
|
Note: when using \fI$name\fR inside an inlined pattern, use
|
||||||
|
\eQ\fI$name\fR\eE to disable metacharacters such as '.' in
|
||||||
|
the \fI$name\fR expansion. Otherwise, the pattern may have
|
||||||
|
unexpected matches.
|
||||||
.SH "EXAMPLE SMTPD ACCESS MAP"
|
.SH "EXAMPLE SMTPD ACCESS MAP"
|
||||||
.na
|
.na
|
||||||
.nf
|
.nf
|
||||||
|
@ -9025,7 +9025,9 @@ With Postfix 2.8 and earlier, log the summary message and unconditionally
|
|||||||
log trust\-chain verification errors.
|
log trust\-chain verification errors.
|
||||||
.br
|
.br
|
||||||
.IP ""
|
.IP ""
|
||||||
2 Also log levels during TLS negotiation.
|
2 Also enable verbose logging in the Postfix TLS
|
||||||
|
library, log session cache operations, and enable OpenSSL logging
|
||||||
|
of the progress of the SSL handshake.
|
||||||
.br
|
.br
|
||||||
.IP ""
|
.IP ""
|
||||||
3 Also log the hexadecimal and ASCII dump of the
|
3 Also log the hexadecimal and ASCII dump of the
|
||||||
@ -13412,7 +13414,9 @@ earlier, log the summary message, peer certificate summary information
|
|||||||
and unconditionally log trust\-chain verification errors.
|
and unconditionally log trust\-chain verification errors.
|
||||||
.br
|
.br
|
||||||
.IP ""
|
.IP ""
|
||||||
2 Also log levels during TLS negotiation.
|
2 Also enable verbose logging in the Postfix TLS
|
||||||
|
library, log session cache operations, and enable OpenSSL logging
|
||||||
|
of the progress of the SSL handshake.
|
||||||
.br
|
.br
|
||||||
.IP ""
|
.IP ""
|
||||||
3 Also log hexadecimal and ASCII dump of TLS negotiation
|
3 Also log hexadecimal and ASCII dump of TLS negotiation
|
||||||
|
@ -163,9 +163,14 @@ in\-memory file:
|
|||||||
|
|
||||||
Postfix parses the result as if it is a file in /etc/postfix.
|
Postfix parses the result as if it is a file in /etc/postfix.
|
||||||
|
|
||||||
Note: if a rule contains \fB$\fR, specify \fB$$\fR to keep
|
Note: if an inlined rule contains \fB$\fR, specify \fB$$\fR
|
||||||
Postfix from trying to do \fI$name\fR expansion as it
|
to keep Postfix from trying to do \fI$name\fR expansion as
|
||||||
evaluates a parameter value.
|
it evaluates a parameter value.
|
||||||
|
|
||||||
|
Note: when using \fI$name\fR inside an inlined pattern,
|
||||||
|
this will not disable metacharacters such as '.' in the
|
||||||
|
\fI$name\fR expansion. To prevent unexpected matches, use
|
||||||
|
a pcre: table, and specify \eQ\fI$name\fR\eE.
|
||||||
.SH "EXAMPLE SMTPD ACCESS MAP"
|
.SH "EXAMPLE SMTPD ACCESS MAP"
|
||||||
.na
|
.na
|
||||||
.nf
|
.nf
|
||||||
|
@ -190,9 +190,14 @@
|
|||||||
#
|
#
|
||||||
# Postfix parses the result as if it is a file in /etc/postfix.
|
# Postfix parses the result as if it is a file in /etc/postfix.
|
||||||
#
|
#
|
||||||
# Note: if a rule contains \fB$\fR, specify \fB$$\fR to keep
|
# Note: if an inlined rule contains \fB$\fR, specify \fB$$\fR
|
||||||
# Postfix from trying to do \fI$name\fR expansion as it
|
# to keep Postfix from trying to do \fI$name\fR expansion as
|
||||||
# evaluates a parameter value.
|
# it evaluates a parameter value.
|
||||||
|
#
|
||||||
|
# Note: when using \fI$name\fR inside an inlined pattern, use
|
||||||
|
# \eQ\fI$name\fR\eE to disable metacharacters such as '.' in
|
||||||
|
# the \fI$name\fR expansion. Otherwise, the pattern may have
|
||||||
|
# unexpected matches.
|
||||||
# EXAMPLE SMTPD ACCESS MAP
|
# EXAMPLE SMTPD ACCESS MAP
|
||||||
# # Protect your outgoing majordomo exploders
|
# # Protect your outgoing majordomo exploders
|
||||||
# /^(?!owner-)(.*)-outgoing@(.*)/ 550 Use ${1}@${2} instead
|
# /^(?!owner-)(.*)-outgoing@(.*)/ 550 Use ${1}@${2} instead
|
||||||
|
@ -9798,7 +9798,9 @@ if client certificate verification is not required. With Postfix 2.8 and
|
|||||||
earlier, log the summary message, peer certificate summary information
|
earlier, log the summary message, peer certificate summary information
|
||||||
and unconditionally log trust-chain verification errors. </dd>
|
and unconditionally log trust-chain verification errors. </dd>
|
||||||
|
|
||||||
<dt> </dt> <dd> 2 Also log levels during TLS negotiation. </dd>
|
<dt> </dt> <dd> 2 Also enable verbose logging in the Postfix TLS
|
||||||
|
library, log session cache operations, and enable OpenSSL logging
|
||||||
|
of the progress of the SSL handshake. </dd>
|
||||||
|
|
||||||
<dt> </dt> <dd> 3 Also log hexadecimal and ASCII dump of TLS negotiation
|
<dt> </dt> <dd> 3 Also log hexadecimal and ASCII dump of TLS negotiation
|
||||||
process. </dd>
|
process. </dd>
|
||||||
@ -10285,7 +10287,9 @@ verification errors if server certificate verification is not required.
|
|||||||
With Postfix 2.8 and earlier, log the summary message and unconditionally
|
With Postfix 2.8 and earlier, log the summary message and unconditionally
|
||||||
log trust-chain verification errors. </dd>
|
log trust-chain verification errors. </dd>
|
||||||
|
|
||||||
<dt> </dt> <dd> 2 Also log levels during TLS negotiation. </dd>
|
<dt> </dt> <dd> 2 Also enable verbose logging in the Postfix TLS
|
||||||
|
library, log session cache operations, and enable OpenSSL logging
|
||||||
|
of the progress of the SSL handshake. </dd>
|
||||||
|
|
||||||
<dt> </dt> <dd> 3 Also log the hexadecimal and ASCII dump of the
|
<dt> </dt> <dd> 3 Also log the hexadecimal and ASCII dump of the
|
||||||
TLS negotiation process. </dd>
|
TLS negotiation process. </dd>
|
||||||
|
@ -147,9 +147,14 @@
|
|||||||
#
|
#
|
||||||
# Postfix parses the result as if it is a file in /etc/postfix.
|
# Postfix parses the result as if it is a file in /etc/postfix.
|
||||||
#
|
#
|
||||||
# Note: if a rule contains \fB$\fR, specify \fB$$\fR to keep
|
# Note: if an inlined rule contains \fB$\fR, specify \fB$$\fR
|
||||||
# Postfix from trying to do \fI$name\fR expansion as it
|
# to keep Postfix from trying to do \fI$name\fR expansion as
|
||||||
# evaluates a parameter value.
|
# it evaluates a parameter value.
|
||||||
|
#
|
||||||
|
# Note: when using \fI$name\fR inside an inlined pattern,
|
||||||
|
# this will not disable metacharacters such as '.' in the
|
||||||
|
# \fI$name\fR expansion. To prevent unexpected matches, use
|
||||||
|
# a pcre: table, and specify \eQ\fI$name\fR\eE.
|
||||||
# EXAMPLE SMTPD ACCESS MAP
|
# EXAMPLE SMTPD ACCESS MAP
|
||||||
# # Disallow sender-specified routing. This is a must if you relay mail
|
# # Disallow sender-specified routing. This is a must if you relay mail
|
||||||
# # for other domains.
|
# # for other domains.
|
||||||
|
@ -1602,3 +1602,4 @@ GmbH
|
|||||||
Hamid
|
Hamid
|
||||||
LLC
|
LLC
|
||||||
Maadani
|
Maadani
|
||||||
|
GTEST
|
||||||
|
@ -116,3 +116,5 @@ proto proto aliases proto virtual proto ADDRESS_REWRITING_README html
|
|||||||
status Files postconf postconf c postconf postconf_dbms c
|
status Files postconf postconf c postconf postconf_dbms c
|
||||||
postconf postconf h conf postfix script conf post install
|
postconf postconf h conf postfix script conf post install
|
||||||
postconf postconf c postconf postconf_dbms c
|
postconf postconf c postconf postconf_dbms c
|
||||||
|
File tlsmgr tlsmgr c
|
||||||
|
restrictions Files dns dns h dns dns_lookup c dns dns_rr c
|
||||||
|
@ -357,3 +357,4 @@ the form of a domain name hostname hostname service hostname service
|
|||||||
expected to become a list of comma separated names br br This
|
expected to become a list of comma separated names br br This
|
||||||
Postfix Postfix can use MongoDB as a source for any of its lookups aliases 5 virtual 5 canonical 5 etc This allows you to keep information for your mail service in a replicated noSQL database with fine grained access controls By not storing it
|
Postfix Postfix can use MongoDB as a source for any of its lookups aliases 5 virtual 5 canonical 5 etc This allows you to keep information for your mail service in a replicated noSQL database with fine grained access controls By not storing it
|
||||||
CCARGS CCARGS DHAS_MONGODB I usr include libmongoc 1 0
|
CCARGS CCARGS DHAS_MONGODB I usr include libmongoc 1 0
|
||||||
|
dt dt dd 2 Also enable verbose logging in the Postfix TLS
|
||||||
|
@ -11,7 +11,8 @@ DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
|
|||||||
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
|
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
|
||||||
INCL =
|
INCL =
|
||||||
LIB = lib$(LIB_PREFIX)dns$(LIB_SUFFIX)
|
LIB = lib$(LIB_PREFIX)dns$(LIB_SUFFIX)
|
||||||
TESTPROG= test_dns_lookup dns_rr_to_pa dns_rr_to_sa dns_sa_to_rr dns_rr_eq_sa
|
TESTPROG= test_dns_lookup dns_rr_to_pa dns_rr_to_sa dns_sa_to_rr dns_rr_eq_sa \
|
||||||
|
dns_rr_test
|
||||||
LIBS = ../../lib/lib$(LIB_PREFIX)global$(LIB_SUFFIX) \
|
LIBS = ../../lib/lib$(LIB_PREFIX)global$(LIB_SUFFIX) \
|
||||||
../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX)
|
../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX)
|
||||||
LIB_DIR = ../../lib
|
LIB_DIR = ../../lib
|
||||||
@ -31,7 +32,7 @@ test: $(TESTPROG)
|
|||||||
tests: test dns_rr_to_pa_test dns_rr_to_sa_test dns_sa_to_rr_test \
|
tests: test dns_rr_to_pa_test dns_rr_to_sa_test dns_sa_to_rr_test \
|
||||||
dns_rr_eq_sa_test no-a-test no-aaaa-test no-mx-test \
|
dns_rr_eq_sa_test no-a-test no-aaaa-test no-mx-test \
|
||||||
error-filter-test nullmx_test nxdomain_test mxonly_test \
|
error-filter-test nullmx_test nxdomain_test mxonly_test \
|
||||||
dnsbl_tests
|
dnsbl_tests dns_rr_tests
|
||||||
|
|
||||||
dnsbl_tests: \
|
dnsbl_tests: \
|
||||||
dnsbl_ttl_127.0.0.2_bind_plain_test \
|
dnsbl_ttl_127.0.0.2_bind_plain_test \
|
||||||
@ -57,7 +58,7 @@ DNSBL_EXIST_REPLY_FIX = \
|
|||||||
-e 's/ [0-9]* [0-9]* [0-9]* [0-9]* [0-9]*/ D D D D D/' \
|
-e 's/ [0-9]* [0-9]* [0-9]* [0-9]* [0-9]*/ D D D D D/' \
|
||||||
-e 's/127.0.0.[0-9]*$$/127.0.0.D/' \
|
-e 's/127.0.0.[0-9]*$$/127.0.0.D/' \
|
||||||
| uniq
|
| uniq
|
||||||
|
|
||||||
root_tests:
|
root_tests:
|
||||||
|
|
||||||
$(LIB): $(OBJS)
|
$(LIB): $(OBJS)
|
||||||
@ -240,6 +241,12 @@ dnsbl_ttl_127.0.0.2_priv_ncache_test: test_dns_lookup dnsbl_ttl_127.0.0.2_bind_p
|
|||||||
diff dnsbl_ttl_127.0.0.2_bind_plain.ref dnsbl_ttl_127.0.0.2_priv_ncache.tmp
|
diff dnsbl_ttl_127.0.0.2_bind_plain.ref dnsbl_ttl_127.0.0.2_priv_ncache.tmp
|
||||||
rm -f dnsbl_ttl_127.0.0.2_priv_ncache.tmp
|
rm -f dnsbl_ttl_127.0.0.2_priv_ncache.tmp
|
||||||
|
|
||||||
|
dns_rr_tests: dns_rr_test
|
||||||
|
$(SHLIB_ENV) $(VALGRIND) ./dns_rr_test
|
||||||
|
|
||||||
|
dns_rr_test: dns_rr_test.o $(LIB) $(LIBS)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
|
||||||
|
|
||||||
printfck: $(OBJS) $(PROG)
|
printfck: $(OBJS) $(PROG)
|
||||||
rm -rf printfck
|
rm -rf printfck
|
||||||
mkdir printfck
|
mkdir printfck
|
||||||
@ -286,7 +293,6 @@ dns_lookup.o: ../../include/vstring.h
|
|||||||
dns_lookup.o: dns.h
|
dns_lookup.o: dns.h
|
||||||
dns_lookup.o: dns_lookup.c
|
dns_lookup.o: dns_lookup.c
|
||||||
dns_rr.o: ../../include/check_arg.h
|
dns_rr.o: ../../include/check_arg.h
|
||||||
dns_rr.o: ../../include/mail_params.h
|
|
||||||
dns_rr.o: ../../include/msg.h
|
dns_rr.o: ../../include/msg.h
|
||||||
dns_rr.o: ../../include/myaddrinfo.h
|
dns_rr.o: ../../include/myaddrinfo.h
|
||||||
dns_rr.o: ../../include/mymalloc.h
|
dns_rr.o: ../../include/mymalloc.h
|
||||||
@ -320,6 +326,19 @@ dns_rr_filter.o: ../../include/vstream.h
|
|||||||
dns_rr_filter.o: ../../include/vstring.h
|
dns_rr_filter.o: ../../include/vstring.h
|
||||||
dns_rr_filter.o: dns.h
|
dns_rr_filter.o: dns.h
|
||||||
dns_rr_filter.o: dns_rr_filter.c
|
dns_rr_filter.o: dns_rr_filter.c
|
||||||
|
dns_rr_test.o: ../../include/check_arg.h
|
||||||
|
dns_rr_test.o: ../../include/msg.h
|
||||||
|
dns_rr_test.o: ../../include/msg_vstream.h
|
||||||
|
dns_rr_test.o: ../../include/myaddrinfo.h
|
||||||
|
dns_rr_test.o: ../../include/mymalloc.h
|
||||||
|
dns_rr_test.o: ../../include/sock_addr.h
|
||||||
|
dns_rr_test.o: ../../include/stringops.h
|
||||||
|
dns_rr_test.o: ../../include/sys_defs.h
|
||||||
|
dns_rr_test.o: ../../include/vbuf.h
|
||||||
|
dns_rr_test.o: ../../include/vstream.h
|
||||||
|
dns_rr_test.o: ../../include/vstring.h
|
||||||
|
dns_rr_test.o: dns.h
|
||||||
|
dns_rr_test.o: dns_rr_test.c
|
||||||
dns_rr_to_pa.o: ../../include/check_arg.h
|
dns_rr_to_pa.o: ../../include/check_arg.h
|
||||||
dns_rr_to_pa.o: ../../include/msg.h
|
dns_rr_to_pa.o: ../../include/msg.h
|
||||||
dns_rr_to_pa.o: ../../include/myaddrinfo.h
|
dns_rr_to_pa.o: ../../include/myaddrinfo.h
|
||||||
|
@ -164,9 +164,14 @@ typedef struct DNS_RR {
|
|||||||
struct DNS_RR *next; /* linkage */
|
struct DNS_RR *next; /* linkage */
|
||||||
size_t data_len; /* actual data size */
|
size_t data_len; /* actual data size */
|
||||||
char *data; /* a bunch of data */
|
char *data; /* a bunch of data */
|
||||||
|
int flags; /* DNS_RR_FLAG_XX, see below */
|
||||||
/* Add new fields at the end, for ABI forward compatibility. */
|
/* Add new fields at the end, for ABI forward compatibility. */
|
||||||
} DNS_RR;
|
} DNS_RR;
|
||||||
|
|
||||||
|
#define DNS_RR_FLAG_TRUNCATED (1<<0)
|
||||||
|
|
||||||
|
#define DNS_RR_IS_TRUNCATED(rr) ((rr)->flags & DNS_RR_FLAG_TRUNCATED)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* dns_strerror.c
|
* dns_strerror.c
|
||||||
*/
|
*/
|
||||||
@ -215,6 +220,7 @@ extern int dns_rr_compare_pref_any(DNS_RR *, DNS_RR *);
|
|||||||
extern int dns_rr_compare_pref(DNS_RR *, DNS_RR *);
|
extern int dns_rr_compare_pref(DNS_RR *, DNS_RR *);
|
||||||
extern DNS_RR *dns_rr_shuffle(DNS_RR *);
|
extern DNS_RR *dns_rr_shuffle(DNS_RR *);
|
||||||
extern DNS_RR *dns_rr_remove(DNS_RR *, DNS_RR *);
|
extern DNS_RR *dns_rr_remove(DNS_RR *, DNS_RR *);
|
||||||
|
extern int var_dns_rr_list_limit;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* dns_rr_to_pa.c
|
* dns_rr_to_pa.c
|
||||||
|
@ -984,6 +984,8 @@ static int dns_get_answer(const char *orig_name, DNS_REPLY *reply, int type,
|
|||||||
resource_found++;
|
resource_found++;
|
||||||
rr->dnssec_valid = *maybe_secure ? reply->dnssec_ad : 0;
|
rr->dnssec_valid = *maybe_secure ? reply->dnssec_ad : 0;
|
||||||
*rrlist = dns_rr_append(*rrlist, rr);
|
*rrlist = dns_rr_append(*rrlist, rr);
|
||||||
|
if (DNS_RR_IS_TRUNCATED(*rrlist))
|
||||||
|
break;
|
||||||
} else if (status == DNS_NULLMX || status == DNS_NULLSRV) {
|
} else if (status == DNS_NULLMX || status == DNS_NULLSRV) {
|
||||||
CORRUPT(status); /* TODO: use better name */
|
CORRUPT(status); /* TODO: use better name */
|
||||||
} else if (not_found_status != DNS_RETRY)
|
} else if (not_found_status != DNS_RETRY)
|
||||||
@ -1214,8 +1216,11 @@ int dns_lookup_rl(const char *name, unsigned flags, DNS_RR **rrlist,
|
|||||||
name, dns_strtype(type), dns_str_resflags(flags));
|
name, dns_strtype(type), dns_str_resflags(flags));
|
||||||
status = dns_lookup_x(name, type, flags, rrlist ? &rr : (DNS_RR **) 0,
|
status = dns_lookup_x(name, type, flags, rrlist ? &rr : (DNS_RR **) 0,
|
||||||
fqdn, why, rcode, lflags);
|
fqdn, why, rcode, lflags);
|
||||||
if (rrlist && rr)
|
if (rrlist && rr) {
|
||||||
*rrlist = dns_rr_append(*rrlist, rr);
|
*rrlist = dns_rr_append(*rrlist, rr);
|
||||||
|
if (DNS_RR_IS_TRUNCATED(*rrlist))
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (status == DNS_OK) {
|
if (status == DNS_OK) {
|
||||||
if (lflags & DNS_REQ_FLAG_STOP_OK)
|
if (lflags & DNS_REQ_FLAG_STOP_OK)
|
||||||
break;
|
break;
|
||||||
@ -1266,8 +1271,11 @@ int dns_lookup_rv(const char *name, unsigned flags, DNS_RR **rrlist,
|
|||||||
name, dns_strtype(type), dns_str_resflags(flags));
|
name, dns_strtype(type), dns_str_resflags(flags));
|
||||||
status = dns_lookup_x(name, type, flags, rrlist ? &rr : (DNS_RR **) 0,
|
status = dns_lookup_x(name, type, flags, rrlist ? &rr : (DNS_RR **) 0,
|
||||||
fqdn, why, rcode, lflags);
|
fqdn, why, rcode, lflags);
|
||||||
if (rrlist && rr)
|
if (rrlist && rr) {
|
||||||
*rrlist = dns_rr_append(*rrlist, rr);
|
*rrlist = dns_rr_append(*rrlist, rr);
|
||||||
|
if (DNS_RR_IS_TRUNCATED(*rrlist))
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (status == DNS_OK) {
|
if (status == DNS_OK) {
|
||||||
if (lflags & DNS_REQ_FLAG_STOP_OK)
|
if (lflags & DNS_REQ_FLAG_STOP_OK)
|
||||||
break;
|
break;
|
||||||
|
@ -97,12 +97,17 @@
|
|||||||
/*
|
/*
|
||||||
/* dns_rr_copy() makes a copy of a resource record.
|
/* dns_rr_copy() makes a copy of a resource record.
|
||||||
/*
|
/*
|
||||||
/* dns_rr_append() appends a resource record to a (list of) resource
|
/* dns_rr_append() appends an input resource record list to
|
||||||
/* record(s).
|
/* an output list. Null arguments are explicitly allowed.
|
||||||
/* A null input list is explicitly allowed.
|
/* When the result would be longer than var_dns_rr_list_limit
|
||||||
/* This function will log a warning and will discard the
|
/* (default: 100), dns_rr_append() logs a warning, flags the
|
||||||
/* resource record, when a list already contains var_dns_rr_list_limit
|
/* output list as truncated, and discards the excess elements.
|
||||||
/* elements (default: 100).
|
/* Once an output list is flagged as truncated (test with
|
||||||
|
/* DNS_RR_IS_TRUNCATED()), the caller is expected to stop
|
||||||
|
/* trying to append records to that list. Note: the 'truncated'
|
||||||
|
/* flag is transitive, i.e. when appending a input list that
|
||||||
|
/* was flagged as truncated to an output list, the output list
|
||||||
|
/* will also be flagged as truncated.
|
||||||
/*
|
/*
|
||||||
/* dns_rr_sort() sorts a list of resource records into ascending
|
/* dns_rr_sort() sorts a list of resource records into ascending
|
||||||
/* order according to a user-specified criterion. The result is the
|
/* order according to a user-specified criterion. The result is the
|
||||||
@ -151,18 +156,19 @@
|
|||||||
#include <mymalloc.h>
|
#include <mymalloc.h>
|
||||||
#include <myrand.h>
|
#include <myrand.h>
|
||||||
|
|
||||||
/* Global library. */
|
|
||||||
|
|
||||||
#include <mail_params.h>
|
|
||||||
|
|
||||||
/* DNS library. */
|
/* DNS library. */
|
||||||
|
|
||||||
#include "dns.h"
|
#include "dns.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Global, to make code testable.
|
* A generous safety limit for the number of DNS resource records that the
|
||||||
|
* Postfix DNS client library will admit into a list. The default value 100
|
||||||
|
* is 20x the default limit on the number address records that the Postfix
|
||||||
|
* SMTP client is willing to consider.
|
||||||
|
*
|
||||||
|
* Mutable, to make code testable.
|
||||||
*/
|
*/
|
||||||
int var_dns_rr_list_limit = DEF_DNS_RR_LIST_LIMIT;
|
int var_dns_rr_list_limit = 100;
|
||||||
|
|
||||||
/* dns_rr_create - fill in resource record structure */
|
/* dns_rr_create - fill in resource record structure */
|
||||||
|
|
||||||
@ -195,6 +201,7 @@ DNS_RR *dns_rr_create(const char *qname, const char *rname,
|
|||||||
}
|
}
|
||||||
rr->data_len = data_len;
|
rr->data_len = data_len;
|
||||||
rr->next = 0;
|
rr->next = 0;
|
||||||
|
rr->flags = 0;
|
||||||
return (rr);
|
return (rr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,36 +241,58 @@ DNS_RR *dns_rr_copy(DNS_RR *src)
|
|||||||
|
|
||||||
/* dns_rr_append_with_limit - append resource record to limited list */
|
/* dns_rr_append_with_limit - append resource record to limited list */
|
||||||
|
|
||||||
static DNS_RR *dns_rr_append_with_limit(DNS_RR *list, DNS_RR *rr, int limit)
|
static void dns_rr_append_with_limit(DNS_RR *list, DNS_RR *rr, int limit)
|
||||||
{
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* To avoid log spam, remember a limited amount of information about a
|
* Pre: list != 0, all lists are concatenated with dns_rr_append().
|
||||||
* past warning. When anomalies happen frequently, then it is OK that
|
*
|
||||||
* some anomaly will not be logged, as long as the limit is enforced.
|
* Post: all elements have the DNS_RR_FLAG_TRUNCATED flag value set, or all
|
||||||
|
* elements have it cleared, so that there is no need to update code in
|
||||||
|
* legacy stable releases that deletes or reorders elements.
|
||||||
*/
|
*/
|
||||||
if (list == 0) {
|
if (limit <= 1) {
|
||||||
list = rr;
|
if (list->next || rr) {
|
||||||
} else if (limit > 1) {
|
msg_warn("DNS record count limit (%d) exceeded -- dropping"
|
||||||
list->next = dns_rr_append_with_limit(list->next, rr, limit - 1);
|
" excess record(s) after qname=%s qtype=%s",
|
||||||
} else {
|
var_dns_rr_list_limit, list->qname,
|
||||||
static DNS_RR *logged_node;
|
dns_strtype(list->type));
|
||||||
|
list->flags |= DNS_RR_FLAG_TRUNCATED;
|
||||||
if (logged_node != list) {
|
dns_rr_free(list->next);
|
||||||
logged_node = list;
|
dns_rr_free(rr);
|
||||||
msg_warn("dns_rr_append: dropping records after qname=%s qtype=%s",
|
list->next = 0;
|
||||||
list->qname, dns_strtype(list->type));
|
}
|
||||||
|
} else {
|
||||||
|
if (list->next == 0 && rr) {
|
||||||
|
list->next = rr;
|
||||||
|
rr = 0;
|
||||||
|
}
|
||||||
|
if (list->next) {
|
||||||
|
dns_rr_append_with_limit(list->next, rr, limit - 1);
|
||||||
|
list->flags |= list->next->flags;
|
||||||
}
|
}
|
||||||
dns_rr_free(rr);
|
|
||||||
}
|
}
|
||||||
return (list);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* dns_rr_append - append resource record to list */
|
/* dns_rr_append - append resource record(s) to list, or discard */
|
||||||
|
|
||||||
DNS_RR *dns_rr_append(DNS_RR *list, DNS_RR *rr)
|
DNS_RR *dns_rr_append(DNS_RR *list, DNS_RR *rr)
|
||||||
{
|
{
|
||||||
return (dns_rr_append_with_limit(list, rr, var_dns_rr_list_limit));
|
|
||||||
|
/*
|
||||||
|
* Note: rr is not length checked; when multiple lists are concatenated,
|
||||||
|
* the output length may be a small multiple of var_dns_rr_list_limit.
|
||||||
|
*/
|
||||||
|
if (rr == 0)
|
||||||
|
return (list);
|
||||||
|
if (list == 0)
|
||||||
|
return (rr);
|
||||||
|
if (!DNS_RR_IS_TRUNCATED(list)) {
|
||||||
|
dns_rr_append_with_limit(list, rr, var_dns_rr_list_limit);
|
||||||
|
} else {
|
||||||
|
dns_rr_free(rr);
|
||||||
|
}
|
||||||
|
return (list);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* dns_rr_compare_pref_ipv6 - compare records by preference, ipv6 preferred */
|
/* dns_rr_compare_pref_ipv6 - compare records by preference, ipv6 preferred */
|
||||||
|
433
postfix/src/dns/dns_rr_test.c
Normal file
433
postfix/src/dns/dns_rr_test.c
Normal file
@ -0,0 +1,433 @@
|
|||||||
|
/*
|
||||||
|
* System library.
|
||||||
|
*/
|
||||||
|
#include <sys_defs.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Utility library.
|
||||||
|
*/
|
||||||
|
#include <msg.h>
|
||||||
|
#include <msg_vstream.h>
|
||||||
|
#include <mymalloc.h>
|
||||||
|
#include <stringops.h>
|
||||||
|
#include <vstring.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DNS library.
|
||||||
|
*/
|
||||||
|
#include <dns.h>
|
||||||
|
|
||||||
|
#define STR(x) vstring_str(x)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test helpers. TODO: move eq_dns_rr() to testing/dns_rr_testers.c; need to
|
||||||
|
* verify that the expected difference is reported, or use a GTEST matcher.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* print_dns_rr - format as { qname, reply, flags } */
|
||||||
|
|
||||||
|
static char *print_dns_rr(VSTRING *buf, DNS_RR *rr)
|
||||||
|
{
|
||||||
|
static VSTRING *tmp;
|
||||||
|
|
||||||
|
if (tmp == 0)
|
||||||
|
tmp = vstring_alloc(100);
|
||||||
|
vstring_sprintf(buf, "{qname=%s, reply='%s', flags=0x%x}",
|
||||||
|
rr->qname, dns_strrecord(tmp, rr), rr->flags);
|
||||||
|
return (STR(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* eq_dns_rr - predicate that two lists are equivalent */
|
||||||
|
|
||||||
|
static int eq_dns_rr(DNS_RR *got, DNS_RR *want)
|
||||||
|
{
|
||||||
|
VSTRING *got_buf = 0;
|
||||||
|
VSTRING *want_buf = 0;
|
||||||
|
|
||||||
|
#define EQ_DNS_RR_RETURN(val) do { \
|
||||||
|
if (got_buf) \
|
||||||
|
vstring_free(got_buf); \
|
||||||
|
if (want_buf) \
|
||||||
|
vstring_free(want_buf); \
|
||||||
|
return (val); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Same length. */
|
||||||
|
if (got == 0 && want == 0)
|
||||||
|
EQ_DNS_RR_RETURN(1);
|
||||||
|
if (want == 0) {
|
||||||
|
msg_warn("got %s, want null",
|
||||||
|
print_dns_rr(got_buf = vstring_alloc(100), got));
|
||||||
|
}
|
||||||
|
if (got == 0) {
|
||||||
|
msg_warn("got null, want %s",
|
||||||
|
print_dns_rr(want_buf = vstring_alloc(100), want));
|
||||||
|
EQ_DNS_RR_RETURN(0);
|
||||||
|
}
|
||||||
|
/* Same query name, resource record, flags. */
|
||||||
|
if (strcmp(print_dns_rr(got_buf = vstring_alloc(100), got),
|
||||||
|
print_dns_rr(want_buf = vstring_alloc(100), want)) != 0) {
|
||||||
|
msg_warn("got %s, want %s", STR(want_buf), STR(got_buf));
|
||||||
|
EQ_DNS_RR_RETURN(0);
|
||||||
|
}
|
||||||
|
/* Same children. */
|
||||||
|
EQ_DNS_RR_RETURN(eq_dns_rr(got->next, want->next));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int eq_dns_rr_free(DNS_RR *got, DNS_RR *want)
|
||||||
|
{
|
||||||
|
int res = eq_dns_rr(got, want);
|
||||||
|
|
||||||
|
dns_rr_free(got);
|
||||||
|
dns_rr_free(want);
|
||||||
|
return (res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tests and test cases.
|
||||||
|
*/
|
||||||
|
typedef struct TEST_CASE {
|
||||||
|
const char *label; /* identifies test case */
|
||||||
|
int (*fn) (void);
|
||||||
|
} TEST_CASE;
|
||||||
|
|
||||||
|
#define PASS (0)
|
||||||
|
#define FAIL (1)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Begin helper tests. TODO: move these to testing/dns_rr_testers_test.c.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int eq_dns_rr_qname_differ(void)
|
||||||
|
{
|
||||||
|
DNS_RR *got = dns_rr_create("qa", "ra", T_SRV, C_IN, 3600, 1, 25, 1, "mxa", 3);
|
||||||
|
DNS_RR *want = dns_rr_copy(got);
|
||||||
|
|
||||||
|
myfree(want->qname);
|
||||||
|
want->qname = mystrdup("qb");
|
||||||
|
return (!eq_dns_rr_free(got, want));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int eq_dns_rr_reply_differ(void)
|
||||||
|
{
|
||||||
|
DNS_RR *got = dns_rr_create("qa", "ra", T_SRV, C_IN, 3600, 1, 25, 1, "mxa", 3);
|
||||||
|
DNS_RR *want = dns_rr_copy(got);
|
||||||
|
|
||||||
|
want->port += 1;
|
||||||
|
return (!eq_dns_rr_free(got, want));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* End helper tests.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Begin DNS_RR tests.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int eq_dns_rr_flags_differ(void)
|
||||||
|
{
|
||||||
|
DNS_RR *got = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
|
||||||
|
DNS_RR *want = dns_rr_copy(got);
|
||||||
|
|
||||||
|
want->flags |= DNS_RR_FLAG_TRUNCATED;
|
||||||
|
return (!eq_dns_rr_free(got, want));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int append_to_null_from_null(void)
|
||||||
|
{
|
||||||
|
DNS_RR *got = dns_rr_append((DNS_RR *) 0, (DNS_RR *) 0);
|
||||||
|
DNS_RR *want = 0;
|
||||||
|
|
||||||
|
return (eq_dns_rr_free(got, want));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int append_to_elem_from_null(void)
|
||||||
|
{
|
||||||
|
DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
|
||||||
|
DNS_RR *got, *want;
|
||||||
|
|
||||||
|
got = dns_rr_append(dns_rr_copy(a), (DNS_RR *) 0);
|
||||||
|
|
||||||
|
want = a;
|
||||||
|
|
||||||
|
return (eq_dns_rr_free(got, want));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int appent_to_null_from_elem(void)
|
||||||
|
{
|
||||||
|
DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
|
||||||
|
DNS_RR *got, *want;
|
||||||
|
|
||||||
|
got = dns_rr_append((DNS_RR *) 0, dns_rr_copy(a));
|
||||||
|
|
||||||
|
want = a;
|
||||||
|
|
||||||
|
return (eq_dns_rr_free(got, want));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int append_to_elem_from_elem(void)
|
||||||
|
{
|
||||||
|
DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
|
||||||
|
DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
|
||||||
|
DNS_RR *got, *want;
|
||||||
|
|
||||||
|
got = dns_rr_append(dns_rr_copy(a), dns_rr_copy(b));
|
||||||
|
|
||||||
|
(want = a)->next = b;
|
||||||
|
|
||||||
|
return (eq_dns_rr_free(got, want));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int append_to_elem_from_list(void)
|
||||||
|
{
|
||||||
|
DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
|
||||||
|
DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
|
||||||
|
DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 3);
|
||||||
|
DNS_RR *got, *want;
|
||||||
|
|
||||||
|
got = dns_rr_append(dns_rr_copy(a),
|
||||||
|
dns_rr_append(dns_rr_copy(b),
|
||||||
|
dns_rr_copy(c)));
|
||||||
|
|
||||||
|
((want = a)->next = b)->next = c;
|
||||||
|
|
||||||
|
return (eq_dns_rr_free(got, want));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int append_to_list_from_elem(void)
|
||||||
|
{
|
||||||
|
DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
|
||||||
|
DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
|
||||||
|
DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 3);
|
||||||
|
DNS_RR *got, *want;
|
||||||
|
|
||||||
|
got = dns_rr_append(dns_rr_append(dns_rr_copy(a),
|
||||||
|
dns_rr_copy(b)),
|
||||||
|
dns_rr_copy(c));
|
||||||
|
|
||||||
|
((want = a)->next = b)->next = c;
|
||||||
|
|
||||||
|
return (eq_dns_rr_free(got, want));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int append_to_list_from_list(void)
|
||||||
|
{
|
||||||
|
DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
|
||||||
|
DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
|
||||||
|
DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 3);
|
||||||
|
DNS_RR *d = dns_rr_create_noport("qd", "rd", T_MX, C_IN, 3600, 1, "mxd", 3);
|
||||||
|
DNS_RR *got, *want;
|
||||||
|
|
||||||
|
got = dns_rr_append(dns_rr_append(dns_rr_copy(a),
|
||||||
|
dns_rr_copy(b)),
|
||||||
|
dns_rr_append(dns_rr_copy(c),
|
||||||
|
dns_rr_copy(d)));
|
||||||
|
|
||||||
|
(((want = a)->next = b)->next = c)->next = d;
|
||||||
|
|
||||||
|
return (eq_dns_rr_free(got, want));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int append_propagates_flags(void)
|
||||||
|
{
|
||||||
|
DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
|
||||||
|
DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
|
||||||
|
DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 3);
|
||||||
|
DNS_RR *d = dns_rr_create_noport("qd", "rd", T_MX, C_IN, 3600, 1, "mxd", 3);
|
||||||
|
DNS_RR *left = dns_rr_append(dns_rr_copy(a), dns_rr_copy(b));
|
||||||
|
DNS_RR *rite = dns_rr_append(dns_rr_copy(c), dns_rr_copy(d));
|
||||||
|
DNS_RR *got, *want, *rr;
|
||||||
|
|
||||||
|
for (rr = rite; rr; rr = rr->next)
|
||||||
|
rr->flags |= DNS_RR_FLAG_TRUNCATED;
|
||||||
|
|
||||||
|
got = dns_rr_append(left, rite);
|
||||||
|
|
||||||
|
(((want = a)->next = b)->next = c)->next = d;
|
||||||
|
for (rr = want; rr; rr = rr->next)
|
||||||
|
rr->flags |= DNS_RR_FLAG_TRUNCATED;
|
||||||
|
|
||||||
|
return (eq_dns_rr_free(got, want));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int append_to_list_from_list_truncate(void)
|
||||||
|
{
|
||||||
|
DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
|
||||||
|
DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
|
||||||
|
DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 3);
|
||||||
|
DNS_RR *d = dns_rr_create_noport("qd", "rd", T_MX, C_IN, 3600, 1, "mxd", 3);
|
||||||
|
DNS_RR *got, *want, *rr;
|
||||||
|
|
||||||
|
var_dns_rr_list_limit = 3;
|
||||||
|
|
||||||
|
((want = dns_rr_copy(a))->next = dns_rr_copy(b))->next = dns_rr_copy(c);
|
||||||
|
for (rr = want; rr; rr = rr->next)
|
||||||
|
rr->flags |= DNS_RR_FLAG_TRUNCATED;
|
||||||
|
|
||||||
|
got = dns_rr_append(dns_rr_append(a, b),
|
||||||
|
dns_rr_append(c, d));
|
||||||
|
|
||||||
|
return (eq_dns_rr_free(got, want));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int append_to_list_from_elem_elem_truncate(void)
|
||||||
|
{
|
||||||
|
DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
|
||||||
|
DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
|
||||||
|
DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 3);
|
||||||
|
DNS_RR *d = dns_rr_create_noport("qd", "rd", T_MX, C_IN, 3600, 1, "mxd", 3);
|
||||||
|
DNS_RR *got, *want, *rr;
|
||||||
|
|
||||||
|
var_dns_rr_list_limit = 2;
|
||||||
|
|
||||||
|
(want = dns_rr_copy(a))->next = dns_rr_copy(b);
|
||||||
|
for (rr = want; rr; rr = rr->next)
|
||||||
|
rr->flags |= DNS_RR_FLAG_TRUNCATED;
|
||||||
|
|
||||||
|
got = dns_rr_append(a, b);
|
||||||
|
got = dns_rr_append(got, c); /* should be logged */
|
||||||
|
got = dns_rr_append(got, d); /* should be silent */
|
||||||
|
|
||||||
|
return (eq_dns_rr_free(got, want));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int append_to_list_from_elem_truncate(void)
|
||||||
|
{
|
||||||
|
DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
|
||||||
|
DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
|
||||||
|
DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 3);
|
||||||
|
DNS_RR *got, *want, *rr;
|
||||||
|
|
||||||
|
var_dns_rr_list_limit = 2;
|
||||||
|
|
||||||
|
(want = dns_rr_copy(a))->next = dns_rr_copy(b);
|
||||||
|
for (rr = want; rr; rr = rr->next)
|
||||||
|
rr->flags |= DNS_RR_FLAG_TRUNCATED;
|
||||||
|
|
||||||
|
got = dns_rr_append(dns_rr_append(a, b), c);
|
||||||
|
|
||||||
|
return (eq_dns_rr_free(got, want));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int append_to_elem_from_list_truncate(void)
|
||||||
|
{
|
||||||
|
DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
|
||||||
|
DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
|
||||||
|
DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 3);
|
||||||
|
DNS_RR *got, *want, *rr;
|
||||||
|
|
||||||
|
var_dns_rr_list_limit = 2;
|
||||||
|
|
||||||
|
(want = dns_rr_copy(a))->next = dns_rr_copy(b);
|
||||||
|
for (rr = want; rr; rr = rr->next)
|
||||||
|
rr->flags |= DNS_RR_FLAG_TRUNCATED;
|
||||||
|
|
||||||
|
got = dns_rr_append(a, dns_rr_append(b, c));
|
||||||
|
|
||||||
|
return (eq_dns_rr_free(got, want));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int append_to_list_from_elem_exact_fit(void)
|
||||||
|
{
|
||||||
|
DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
|
||||||
|
DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
|
||||||
|
DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 3);
|
||||||
|
DNS_RR *got, *want;
|
||||||
|
|
||||||
|
var_dns_rr_list_limit = 3;
|
||||||
|
|
||||||
|
((want = dns_rr_copy(a))->next = dns_rr_copy(b))->next = dns_rr_copy(c);
|
||||||
|
|
||||||
|
got = dns_rr_append(dns_rr_append(a, b), c);
|
||||||
|
|
||||||
|
return (eq_dns_rr_free(got, want));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int append_to_elem_from_list_exact_fit(void)
|
||||||
|
{
|
||||||
|
DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
|
||||||
|
DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
|
||||||
|
DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 3);
|
||||||
|
DNS_RR *got, *want;
|
||||||
|
|
||||||
|
var_dns_rr_list_limit = 3;
|
||||||
|
|
||||||
|
((want = dns_rr_copy(a))->next = dns_rr_copy(b))->next = dns_rr_copy(c);
|
||||||
|
|
||||||
|
got = dns_rr_append(a, dns_rr_append(b, c));
|
||||||
|
|
||||||
|
return (eq_dns_rr_free(got, want));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The test cases.
|
||||||
|
*/
|
||||||
|
static const TEST_CASE test_cases[] = {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test eq_dns_rr; TODO: move to testing/dns_rr_testers_test.c
|
||||||
|
*/
|
||||||
|
"eq_dns_rr qname differ", eq_dns_rr_qname_differ,
|
||||||
|
"eq_dns_rr reply differ", eq_dns_rr_reply_differ,
|
||||||
|
"eq_dns_rr flags differ", eq_dns_rr_flags_differ,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test dns_rr_append() without truncation.
|
||||||
|
*/
|
||||||
|
"append to null from null", append_to_null_from_null,
|
||||||
|
"append to null from element", appent_to_null_from_elem,
|
||||||
|
"append to element from null", append_to_elem_from_null,
|
||||||
|
"append to element from element", append_to_elem_from_elem,
|
||||||
|
"append to element from list", append_to_elem_from_list,
|
||||||
|
"append to list from element", append_to_list_from_elem,
|
||||||
|
"append to list from list", append_to_list_from_list,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test dns_rr_append() flag propagation.
|
||||||
|
*/
|
||||||
|
"append propagates flags", append_propagates_flags,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test dns_rr_append() with truncation.
|
||||||
|
*/
|
||||||
|
"append to list from list truncate", append_to_list_from_list_truncate,
|
||||||
|
"append to list from element element truncate", append_to_list_from_elem_elem_truncate,
|
||||||
|
"append to list from element truncate", append_to_list_from_elem_truncate,
|
||||||
|
"append to element from list truncate", append_to_elem_from_list_truncate,
|
||||||
|
"append to list from element exact fit", append_to_list_from_elem_exact_fit,
|
||||||
|
"append to element from list exact fit", append_to_elem_from_list_exact_fit,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: tests dns_rr_sort(), dns_rr_srv_sort(), dns_rr_remove(),
|
||||||
|
* dns_rr_shuffle(), etc.
|
||||||
|
*/
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
const TEST_CASE *tp;
|
||||||
|
int pass = 0;
|
||||||
|
int fail = 0;
|
||||||
|
VSTRING *res_buf = vstring_alloc(100);
|
||||||
|
int saved_limit = var_dns_rr_list_limit;
|
||||||
|
|
||||||
|
msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR);
|
||||||
|
|
||||||
|
for (tp = test_cases; tp->label != 0; tp++) {
|
||||||
|
msg_info("RUN %s", tp->label);
|
||||||
|
if (tp->fn() == 0) {
|
||||||
|
fail++;
|
||||||
|
msg_info("FAIL %s", tp->label);
|
||||||
|
} else {
|
||||||
|
msg_info("PASS %s", tp->label);
|
||||||
|
pass++;
|
||||||
|
}
|
||||||
|
var_dns_rr_list_limit = saved_limit;
|
||||||
|
}
|
||||||
|
msg_info("PASS=%d FAIL=%d", pass, fail);
|
||||||
|
vstring_free(res_buf);
|
||||||
|
exit(fail != 0);
|
||||||
|
}
|
@ -78,11 +78,12 @@ char *dns_strrecord(VSTRING *buf, DNS_RR *rr)
|
|||||||
vstring_sprintf_append(buf, "%s", rr->data);
|
vstring_sprintf_append(buf, "%s", rr->data);
|
||||||
break;
|
break;
|
||||||
case T_MX:
|
case T_MX:
|
||||||
vstring_sprintf_append(buf, "%u %s.", rr->pref, rr->data);
|
vstring_sprintf_append(buf, "%u %.*s.", rr->pref,
|
||||||
|
(int) rr->data_len, rr->data);
|
||||||
break;
|
break;
|
||||||
case T_SRV:
|
case T_SRV:
|
||||||
vstring_sprintf_append(buf, "%u %u %u %s.", rr->pref, rr->weight,
|
vstring_sprintf_append(buf, "%u %u %u %.*s.", rr->pref, rr->weight,
|
||||||
rr->port, rr->data);
|
rr->port, (int) rr->data_len, rr->data);
|
||||||
break;
|
break;
|
||||||
case T_TLSA:
|
case T_TLSA:
|
||||||
if (rr->data_len >= 3) {
|
if (rr->data_len >= 3) {
|
||||||
|
@ -124,9 +124,11 @@ int main(int argc, char **argv)
|
|||||||
vstream_printf("%s: fqdn: %s\n", name, vstring_str(fqdn));
|
vstream_printf("%s: fqdn: %s\n", name, vstring_str(fqdn));
|
||||||
buf = vstring_alloc(100);
|
buf = vstring_alloc(100);
|
||||||
print_rr(buf, rr);
|
print_rr(buf, rr);
|
||||||
|
vstream_fflush(VSTREAM_OUT);
|
||||||
|
if (DNS_RR_IS_TRUNCATED(rr))
|
||||||
|
msg_warn("one or more excess DNS_RR records were dropped");
|
||||||
dns_rr_free(rr);
|
dns_rr_free(rr);
|
||||||
vstring_free(buf);
|
vstring_free(buf);
|
||||||
vstream_fflush(VSTREAM_OUT);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
myfree((void *) types);
|
myfree((void *) types);
|
||||||
|
@ -4384,16 +4384,6 @@ extern int var_idna2003_compat;
|
|||||||
#define DEF_DNS_NCACHE_TTL_FIX 0
|
#define DEF_DNS_NCACHE_TTL_FIX 0
|
||||||
extern bool var_dns_ncache_ttl_fix;
|
extern bool var_dns_ncache_ttl_fix;
|
||||||
|
|
||||||
/*
|
|
||||||
* A generous safety limit for the number of DNS resource records that the
|
|
||||||
* Postfix DNS client library will admit into a list. The default is 20x the
|
|
||||||
* default limit on the number address records that the Postfix SMTP client
|
|
||||||
* is willing to consider.
|
|
||||||
*/
|
|
||||||
#define VAR_DNS_RR_LIST_LIMIT "dns_resource_list_limit"
|
|
||||||
#define DEF_DNS_RR_LIST_LIMIT 100
|
|
||||||
extern int var_dns_rr_list_limit;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Logging. As systems evolve over time, logging becomes more challenging.
|
* Logging. As systems evolve over time, logging becomes more challenging.
|
||||||
*/
|
*/
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
* Patches change both the patchlevel and the release date. Snapshots have no
|
* Patches change both the patchlevel and the release date. Snapshots have no
|
||||||
* patchlevel; they change the release date only.
|
* patchlevel; they change the release date only.
|
||||||
*/
|
*/
|
||||||
#define MAIL_RELEASE_DATE "20240218"
|
#define MAIL_RELEASE_DATE "20240227"
|
||||||
#define MAIL_VERSION_NUMBER "3.9"
|
#define MAIL_VERSION_NUMBER "3.9"
|
||||||
|
|
||||||
#ifdef SNAPSHOT
|
#ifdef SNAPSHOT
|
||||||
|
@ -1280,6 +1280,8 @@ static DNS_RR *addr_one(STATE *state, DNS_RR *addr_list, const char *host,
|
|||||||
msg_fatal("host %s: conversion error for address family %d: %m",
|
msg_fatal("host %s: conversion error for address family %d: %m",
|
||||||
host, ((struct sockaddr *) (res0->ai_addr))->sa_family);
|
host, ((struct sockaddr *) (res0->ai_addr))->sa_family);
|
||||||
addr_list = dns_rr_append(addr_list, addr);
|
addr_list = dns_rr_append(addr_list, addr);
|
||||||
|
if (DNS_RR_IS_TRUNCATED(addr_list))
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
freeaddrinfo(res0);
|
freeaddrinfo(res0);
|
||||||
if (found == 0) {
|
if (found == 0) {
|
||||||
@ -1317,6 +1319,8 @@ static DNS_RR *mx_addr_list(STATE *state, DNS_RR *mx_names)
|
|||||||
msg_panic("%s: bad resource type: %d", myname, rr->type);
|
msg_panic("%s: bad resource type: %d", myname, rr->type);
|
||||||
addr_list = addr_one(state, addr_list, (char *) rr->data, res_opt,
|
addr_list = addr_one(state, addr_list, (char *) rr->data, res_opt,
|
||||||
rr->pref, rr->port);
|
rr->pref, rr->port);
|
||||||
|
if (addr_list && DNS_RR_IS_TRUNCATED(addr_list))
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return (addr_list);
|
return (addr_list);
|
||||||
}
|
}
|
||||||
|
@ -179,10 +179,10 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, const char *host, int res_opt,
|
|||||||
if ((addr = dns_sa_to_rr(host, pref, res0->ai_addr)) == 0)
|
if ((addr = dns_sa_to_rr(host, pref, res0->ai_addr)) == 0)
|
||||||
msg_fatal("host %s: conversion error for address family "
|
msg_fatal("host %s: conversion error for address family "
|
||||||
"%d: %m", host, res0->ai_addr->sa_family);
|
"%d: %m", host, res0->ai_addr->sa_family);
|
||||||
addr_list = dns_rr_append(addr_list, addr);
|
|
||||||
addr->pref = pref;
|
addr->pref = pref;
|
||||||
addr->port = port;
|
addr->port = port;
|
||||||
if (msg_verbose)
|
addr_list = dns_rr_append(addr_list, addr);
|
||||||
|
if (msg_verbose && !DNS_RR_IS_TRUNCATED(addr_list))
|
||||||
msg_info("%s: using numerical host %s", myname, host);
|
msg_info("%s: using numerical host %s", myname, host);
|
||||||
freeaddrinfo(res0);
|
freeaddrinfo(res0);
|
||||||
return (addr_list);
|
return (addr_list);
|
||||||
@ -262,6 +262,8 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, const char *host, int res_opt,
|
|||||||
msg_fatal("host %s: conversion error for address family "
|
msg_fatal("host %s: conversion error for address family "
|
||||||
"%d: %m", host, res0->ai_addr->sa_family);
|
"%d: %m", host, res0->ai_addr->sa_family);
|
||||||
addr_list = dns_rr_append(addr_list, addr);
|
addr_list = dns_rr_append(addr_list, addr);
|
||||||
|
if (DNS_RR_IS_TRUNCATED(addr_list))
|
||||||
|
break;
|
||||||
if (msg_verbose) {
|
if (msg_verbose) {
|
||||||
MAI_HOSTADDR_STR hostaddr_str;
|
MAI_HOSTADDR_STR hostaddr_str;
|
||||||
|
|
||||||
@ -327,6 +329,8 @@ static DNS_RR *smtp_addr_list(DNS_RR *mx_names, DSN_BUF *why)
|
|||||||
msg_panic("smtp_addr_list: bad resource type: %d", rr->type);
|
msg_panic("smtp_addr_list: bad resource type: %d", rr->type);
|
||||||
addr_list = smtp_addr_one(addr_list, (char *) rr->data, res_opt,
|
addr_list = smtp_addr_one(addr_list, (char *) rr->data, res_opt,
|
||||||
rr->pref, rr->port, why);
|
rr->pref, rr->port, why);
|
||||||
|
if (addr_list && DNS_RR_IS_TRUNCATED(addr_list))
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return (addr_list);
|
return (addr_list);
|
||||||
}
|
}
|
||||||
@ -420,6 +424,13 @@ static DNS_RR *smtp_balance_inet_proto(DNS_RR *addr_list, int misc_flags,
|
|||||||
* relative list order is unchanged, but some elements are removed.
|
* relative list order is unchanged, but some elements are removed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure that dns_rr_append() won't interfere with the protocol
|
||||||
|
* balancing goals.
|
||||||
|
*/
|
||||||
|
if (addr_limit > var_dns_rr_list_limit)
|
||||||
|
addr_limit = var_dns_rr_list_limit;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Count the number of IPv6 and IPv4 addresses.
|
* Count the number of IPv6 and IPv4 addresses.
|
||||||
*/
|
*/
|
||||||
|
@ -3021,6 +3021,7 @@ static int check_server_access(SMTPD_STATE *state, const char *table,
|
|||||||
struct addrinfo *res;
|
struct addrinfo *res;
|
||||||
int status;
|
int status;
|
||||||
const INET_PROTO_INFO *proto_info;
|
const INET_PROTO_INFO *proto_info;
|
||||||
|
int server_addr_count = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sanity check.
|
* Sanity check.
|
||||||
@ -3172,6 +3173,15 @@ static int check_server_access(SMTPD_STATE *state, const char *table,
|
|||||||
msg_info("%s: %s host address check: %s",
|
msg_info("%s: %s host address check: %s",
|
||||||
myname, dns_strtype(type), (char *) server->data);
|
myname, dns_strtype(type), (char *) server->data);
|
||||||
for (res = res0; res != 0; res = res->ai_next) {
|
for (res = res0; res != 0; res = res->ai_next) {
|
||||||
|
server_addr_count += 1;
|
||||||
|
if (server_addr_count > var_dns_rr_list_limit) {
|
||||||
|
msg_warn("%s: %s server address count limit (%d) exceeded"
|
||||||
|
" for %s %s -- ignoring the remainder", myname,
|
||||||
|
dns_strtype(type), var_dns_rr_list_limit,
|
||||||
|
reply_class, reply_name);
|
||||||
|
freeaddrinfo(res0);
|
||||||
|
CHECK_SERVER_RETURN(SMTPD_CHECK_DUNNO);
|
||||||
|
}
|
||||||
if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
|
if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
|
||||||
if (msg_verbose)
|
if (msg_verbose)
|
||||||
msg_info("skipping address family %d for host %s",
|
msg_info("skipping address family %d for host %s",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user