summaryrefslogtreecommitdiffstats
path: root/src/dns
diff options
context:
space:
mode:
Diffstat (limited to '')
-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 5f53dbc..0d9c8a3 100644
--- a/src/dns/dns.h
+++ b/src/dns/dns.h
@@ -159,11 +159,17 @@ typedef struct DNS_RR {
unsigned int ttl; /* always */
unsigned int dnssec_valid; /* DNSSEC validated */
unsigned short pref; /* T_MX only */
+ /* 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[1]; /* actually a bunch of data */
} DNS_RR;
+#define DNS_RR_FLAG_TRUNCATED (1<<0)
+
+#define DNS_RR_IS_TRUNCATED(rr) ((rr)->flags & DNS_RR_FLAG_TRUNCATED)
+
/*
* dns_strerror.c
*/
@@ -197,6 +203,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 615902d..fa7fcb0 100644
--- a/src/dns/dns_lookup.c
+++ b/src/dns/dns_lookup.c
@@ -960,6 +960,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) {
CORRUPT(status); /* TODO: use better name */
} else if (not_found_status != DNS_RETRY)
@@ -1184,8 +1186,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;
@@ -1236,8 +1241,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 b550788..cf82f9f 100644
--- a/src/dns/dns_rr.c
+++ b/src/dns/dns_rr.c
@@ -49,6 +49,8 @@
/* DNS_RR *dns_rr_remove(list, record)
/* DNS_RR *list;
/* DNS_RR *record;
+/*
+/* int var_dns_rr_list_limit;
/* DESCRIPTION
/* The routines in this module maintain memory for DNS resource record
/* information, and maintain lists of DNS resource records.
@@ -65,9 +67,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
@@ -108,6 +118,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,
@@ -129,6 +149,7 @@ DNS_RR *dns_rr_create(const char *qname, const char *rname,
memcpy(rr->data, data, data_len);
rr->data_len = data_len;
rr->next = 0;
+ rr->flags = 0;
return (rr);
}
@@ -163,14 +184,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);