summaryrefslogtreecommitdiffstats
path: root/src/dns
diff options
context:
space:
mode:
Diffstat (limited to 'src/dns')
-rw-r--r--src/dns/dns.h7
-rw-r--r--src/dns/dns_lookup.c12
-rw-r--r--src/dns/dns_rr.c79
-rw-r--r--src/dns/test_dns_lookup.c4
4 files changed, 92 insertions, 10 deletions
diff --git a/src/dns/dns.h b/src/dns/dns.h
index 2b77015..987b988 100644
--- a/src/dns/dns.h
+++ b/src/dns/dns.h
@@ -161,12 +161,18 @@ typedef struct DNS_RR {
unsigned short pref; /* T_MX and T_SRV record related */
unsigned short weight; /* T_SRV related, defined in rfc2782 */
unsigned short port; /* T_SRV related, defined in rfc2782 */
+ /* Assume that flags lives in what was previously padding */
+ unsigned short flags; /* DNS_RR_FLAG_XX, see below */
struct DNS_RR *next; /* linkage */
size_t data_len; /* actual data size */
char *data; /* a bunch of data */
/* Add new fields at the end, for ABI forward compatibility. */
} DNS_RR;
+#define DNS_RR_FLAG_TRUNCATED (1<<0)
+
+#define DNS_RR_IS_TRUNCATED(rr) ((rr)->flags & DNS_RR_FLAG_TRUNCATED)
+
/*
* dns_strerror.c
*/
@@ -215,6 +221,7 @@ extern int dns_rr_compare_pref_any(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_remove(DNS_RR *, DNS_RR *);
+extern int var_dns_rr_list_limit;
/*
* dns_rr_to_pa.c
diff --git a/src/dns/dns_lookup.c b/src/dns/dns_lookup.c
index 06028c8..c21b619 100644
--- a/src/dns/dns_lookup.c
+++ b/src/dns/dns_lookup.c
@@ -978,6 +978,8 @@ static int dns_get_answer(const char *orig_name, DNS_REPLY *reply, int type,
resource_found++;
rr->dnssec_valid = *maybe_secure ? reply->dnssec_ad : 0;
*rrlist = dns_rr_append(*rrlist, rr);
+ if (DNS_RR_IS_TRUNCATED(*rrlist))
+ break;
} else if (status == DNS_NULLMX || status == DNS_NULLSRV) {
CORRUPT(status); /* TODO: use better name */
} else if (not_found_status != DNS_RETRY)
@@ -1208,8 +1210,11 @@ int dns_lookup_rl(const char *name, unsigned flags, DNS_RR **rrlist,
name, dns_strtype(type), dns_str_resflags(flags));
status = dns_lookup_x(name, type, flags, rrlist ? &rr : (DNS_RR **) 0,
fqdn, why, rcode, lflags);
- if (rrlist && rr)
+ if (rrlist && rr) {
*rrlist = dns_rr_append(*rrlist, rr);
+ if (DNS_RR_IS_TRUNCATED(*rrlist))
+ break;
+ }
if (status == DNS_OK) {
if (lflags & DNS_REQ_FLAG_STOP_OK)
break;
@@ -1260,8 +1265,11 @@ int dns_lookup_rv(const char *name, unsigned flags, DNS_RR **rrlist,
name, dns_strtype(type), dns_str_resflags(flags));
status = dns_lookup_x(name, type, flags, rrlist ? &rr : (DNS_RR **) 0,
fqdn, why, rcode, lflags);
- if (rrlist && rr)
+ if (rrlist && rr) {
*rrlist = dns_rr_append(*rrlist, rr);
+ if (DNS_RR_IS_TRUNCATED(*rrlist))
+ break;
+ }
if (status == DNS_OK) {
if (lflags & DNS_REQ_FLAG_STOP_OK)
break;
diff --git a/src/dns/dns_rr.c b/src/dns/dns_rr.c
index 3fde10e..882a42f 100644
--- a/src/dns/dns_rr.c
+++ b/src/dns/dns_rr.c
@@ -54,6 +54,8 @@
/*
/* DNS_RR *dns_srv_rr_sort(list)
/* DNS_RR *list;
+/*
+/* int var_dns_rr_list_limit;
/* AUXILIARY FUNCTIONS
/* DNS_RR *dns_rr_create_nopref(qname, rname, type, class, ttl,
/* data, data_len)
@@ -95,9 +97,17 @@
/*
/* dns_rr_copy() makes a copy of a resource record.
/*
-/* dns_rr_append() appends a resource record to a (list of) resource
-/* record(s).
-/* A null input list is explicitly allowed.
+/* dns_rr_append() appends an input resource record list to
+/* an output list. Null arguments are explicitly allowed.
+/* When the result would be longer than var_dns_rr_list_limit
+/* (default: 100), dns_rr_append() logs a warning, flags the
+/* output list as truncated, and discards the excess elements.
+/* 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
/* order according to a user-specified criterion. The result is the
@@ -150,6 +160,16 @@
#include "dns.h"
+ /*
+ * 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 = 100;
+
/* dns_rr_create - fill in resource record structure */
DNS_RR *dns_rr_create(const char *qname, const char *rname,
@@ -181,6 +201,7 @@ DNS_RR *dns_rr_create(const char *qname, const char *rname,
}
rr->data_len = data_len;
rr->next = 0;
+ rr->flags = 0;
return (rr);
}
@@ -218,14 +239,58 @@ DNS_RR *dns_rr_copy(DNS_RR *src)
return (dst);
}
-/* dns_rr_append - append resource record to list */
+/* dns_rr_append_with_limit - append resource record to limited list */
+
+static void dns_rr_append_with_limit(DNS_RR *list, DNS_RR *rr, int limit)
+{
+
+ /*
+ * Pre: list != 0, all lists are concatenated with dns_rr_append().
+ *
+ * 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 (limit <= 1) {
+ if (list->next || rr) {
+ msg_warn("DNS record count limit (%d) exceeded -- dropping"
+ " excess record(s) after qname=%s qtype=%s",
+ var_dns_rr_list_limit, list->qname,
+ dns_strtype(list->type));
+ list->flags |= DNS_RR_FLAG_TRUNCATED;
+ dns_rr_free(list->next);
+ dns_rr_free(rr);
+ list->next = 0;
+ }
+ } 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_append - append resource record(s) to list, or discard */
DNS_RR *dns_rr_append(DNS_RR *list, DNS_RR *rr)
{
- if (list == 0) {
- list = rr;
+
+ /*
+ * 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 {
- list->next = dns_rr_append(list->next, rr);
+ dns_rr_free(rr);
}
return (list);
}
diff --git a/src/dns/test_dns_lookup.c b/src/dns/test_dns_lookup.c
index e25f523..f07c3ef 100644
--- a/src/dns/test_dns_lookup.c
+++ b/src/dns/test_dns_lookup.c
@@ -121,9 +121,11 @@ int main(int argc, char **argv)
vstream_printf("%s: fqdn: %s\n", name, vstring_str(fqdn));
buf = vstring_alloc(100);
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);
vstring_free(buf);
- vstream_fflush(VSTREAM_OUT);
}
}
myfree((void *) types);