diff options
Diffstat (limited to 'src/dns')
-rw-r--r-- | src/dns/dns.h | 7 | ||||
-rw-r--r-- | src/dns/dns_lookup.c | 12 | ||||
-rw-r--r-- | src/dns/dns_rr.c | 79 | ||||
-rw-r--r-- | src/dns/test_dns_lookup.c | 4 |
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); |