* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) The Exim Maintainers 2020 - 2022 */
+/* Copyright (c) The Exim Maintainers 2020 - 2023 */
/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "../exim.h"
#include "lf_functions.h"
{
int rc;
int sep = 0;
-int defer_mode = PASS;
-int dnssec_mode = PASS;
-int save_retrans = dns_retrans;
-int save_retry = dns_retry;
+int defer_mode = PASS, dnssec_mode = PASS;
+int save_retrans = dns_retrans, save_retry = dns_retry;
int type;
int failrc = FAIL;
-const uschar *outsep = CUS"\n";
-const uschar *outsep2 = NULL;
-uschar *equals, *domain, *found;
+const uschar * outsep = CUS"\n", * outsep2 = NULL;
+uschar * equals, * domain, * found;
dns_answer * dnsa = store_get_dns_answer();
dns_scan dnss;
/* If the string starts with '>' we change the output separator.
If it's followed by ';' or ',' we set the TXT output separator. */
-while (isspace(*keystring)) keystring++;
-if (*keystring == '>')
+if (Uskip_whitespace(&keystring) == '>')
{
outsep = keystring + 1;
keystring += 2;
outsep2 = US"";
keystring++;
}
- while (isspace(*keystring)) keystring++;
+ Uskip_whitespace(&keystring);
}
/* Check for a modifier keyword. */
else
break;
- while (isspace(*keystring)) keystring++;
+ Uskip_whitespace(&keystring);
if (*keystring++ != ',')
{
*errmsg = US"dnsdb modifier syntax error";
rc = DEFER;
goto out;
}
- while (isspace(*keystring)) keystring++;
+ Uskip_whitespace(&keystring);
}
/* Figure out the "type" value if it is not T_TXT.
}
keystring = equals + 1;
- while (isspace(*keystring)) keystring++;
+ Uskip_whitespace(&keystring);
}
/* Initialize the resolver in case this is the first time it has been used. */
if (type == T_A || type == T_AAAA || type == T_ADDRESSES)
{
for (dns_address * da = dns_address_from_rr(dnsa, rr); da; da = da->next)
- {
- if (yield->ptr) yield = string_catn(yield, outsep, 1);
- yield = string_cat(yield, da->address);
- }
+ yield = string_append_listele(yield, *outsep, da->address);
continue;
}
/* Other kinds of record just have one piece of data each, but there may be
- several of them, of course. */
+ several of them, of course. TXT & SPF can have data in multiple chunks. */
if (yield->ptr) yield = string_catn(yield, outsep, 1);
if (type == T_TXT || type == T_SPF)
- {
- if (!outsep2) /* output only the first item of data */
+ for (unsigned data_offset = 0; data_offset + 1 < rr->size; )
{
- uschar n = (rr->data)[0];
- /* size byte + data bytes must not excced the RRs length */
- if (n + 1 <= rr->size)
- yield = string_catn(yield, US (rr->data+1), n);
+ uschar chunk_len = (rr->data)[data_offset];
+ int remain;
+
+ if (outsep2 && *outsep2 && data_offset != 0)
+ yield = string_catn(yield, outsep2, 1);
+
+ /* Apparently there are resolvers that do not check RRs before passing
+ them on, and glibc fails to do so. So every application must...
+ Check for chunk len exceeding RR */
+
+ remain = rr->size - ++data_offset;
+ if (chunk_len > remain)
+ chunk_len = remain;
+ yield = string_catn(yield, US ((rr->data) + data_offset), chunk_len);
+ data_offset += chunk_len;
+
+ if (!outsep2) break; /* output only the first chunk of the RR */
}
- else
- {
- /* output all items */
- int data_offset = 0;
- while (data_offset < rr->size)
- {
- uschar chunk_len = (rr->data)[data_offset];
- int remain = rr->size - data_offset;
-
- /* Apparently there are resolvers that do not check RRs before passing
- them on, and glibc fails to do so. So every application must...
- Check for chunk len exceeding RR */
-
- if (chunk_len > remain)
- chunk_len = remain;
-
- if (*outsep2 && data_offset != 0)
- yield = string_catn(yield, outsep2, 1);
- yield = string_catn(yield, US ((rr->data) + ++data_offset), --chunk_len);
- data_offset += chunk_len;
- }
- }
- }
else if (type == T_TLSA)
if (rr->size < 3)
continue;
switch (type)
{
case T_MXH:
- if (rr->size < sizeof(u_int16_t)) continue;
+ if (rr_bad_size(rr, sizeof(uint16_t))) continue;
/* mxh ignores the priority number and includes only the hostnames */
GETSHORT(priority, p);
break;
case T_MX:
- if (rr->size < sizeof(u_int16_t)) continue;
+ if (rr_bad_size(rr, sizeof(uint16_t))) continue;
GETSHORT(priority, p);
sprintf(CS s, "%d%c", priority, *outsep2);
yield = string_cat(yield, s);
break;
case T_SRV:
- if (rr->size < 3*sizeof(u_int16_t)) continue;
+ if (rr_bad_size(rr, 3*sizeof(uint16_t))) continue;
GETSHORT(priority, p);
GETSHORT(weight, p);
GETSHORT(port, p);
break;
case T_CSA:
- if (rr->size < 3*sizeof(u_int16_t)) continue;
+ if (rr_bad_size(rr, 3*sizeof(uint16_t))) continue;
/* See acl_verify_csa() for more comments about CSA. */
GETSHORT(priority, p);
GETSHORT(weight, p);
else yield = string_cat(yield, s);
p += rc;
- if (rr->size >= p - rr->data - 5*sizeof(u_int32_t))
+ if (!rr_bad_increment(rr, p, 5 * sizeof(u_int32_t)))
{
GETLONG(serial, p); GETLONG(refresh, p);
GETLONG(retry, p); GETLONG(expire, p); GETLONG(minimum, p);