mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 10:18:25 +00:00
Add --cache-rr to enable caching of arbitrary RR types.
This commit is contained in:
123
src/blockdata.c
123
src/blockdata.c
@@ -19,7 +19,7 @@
|
||||
static struct blockdata *keyblock_free;
|
||||
static unsigned int blockdata_count, blockdata_hwm, blockdata_alloced;
|
||||
|
||||
static void blockdata_expand(int n)
|
||||
static void add_blocks(int n)
|
||||
{
|
||||
struct blockdata *new = whine_malloc(n * sizeof(struct blockdata));
|
||||
|
||||
@@ -47,7 +47,7 @@ void blockdata_init(void)
|
||||
|
||||
/* Note that daemon->cachesize is enforced to have non-zero size if OPT_DNSSEC_VALID is set */
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
blockdata_expand(daemon->cachesize);
|
||||
add_blocks(daemon->cachesize);
|
||||
}
|
||||
|
||||
void blockdata_report(void)
|
||||
@@ -58,50 +58,61 @@ void blockdata_report(void)
|
||||
blockdata_alloced * sizeof(struct blockdata));
|
||||
}
|
||||
|
||||
static struct blockdata *new_block(void)
|
||||
{
|
||||
struct blockdata *block;
|
||||
|
||||
if (!keyblock_free)
|
||||
add_blocks(50);
|
||||
|
||||
if (keyblock_free)
|
||||
{
|
||||
block = keyblock_free;
|
||||
keyblock_free = block->next;
|
||||
blockdata_count++;
|
||||
if (blockdata_hwm < blockdata_count)
|
||||
blockdata_hwm = blockdata_count;
|
||||
block->next = NULL;
|
||||
return block;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct blockdata *blockdata_alloc_real(int fd, char *data, size_t len)
|
||||
{
|
||||
struct blockdata *block, *ret = NULL;
|
||||
struct blockdata **prev = &ret;
|
||||
size_t blen;
|
||||
|
||||
while (len > 0)
|
||||
do
|
||||
{
|
||||
if (!keyblock_free)
|
||||
blockdata_expand(50);
|
||||
|
||||
if (keyblock_free)
|
||||
{
|
||||
block = keyblock_free;
|
||||
keyblock_free = block->next;
|
||||
blockdata_count++;
|
||||
}
|
||||
else
|
||||
if (!(block = new_block()))
|
||||
{
|
||||
/* failed to alloc, free partial chain */
|
||||
blockdata_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (blockdata_hwm < blockdata_count)
|
||||
blockdata_hwm = blockdata_count;
|
||||
|
||||
if ((blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len) > 0)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
memcpy(block->key, data, blen);
|
||||
data += blen;
|
||||
}
|
||||
else if (!read_write(fd, block->key, blen, 1))
|
||||
{
|
||||
/* failed read free partial chain */
|
||||
blockdata_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len;
|
||||
if (data)
|
||||
{
|
||||
memcpy(block->key, data, blen);
|
||||
data += blen;
|
||||
}
|
||||
else if (!read_write(fd, block->key, blen, 1))
|
||||
{
|
||||
/* failed read free partial chain */
|
||||
blockdata_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
len -= blen;
|
||||
*prev = block;
|
||||
prev = &block->next;
|
||||
block->next = NULL;
|
||||
}
|
||||
} while (len != 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -111,6 +122,58 @@ struct blockdata *blockdata_alloc(char *data, size_t len)
|
||||
return blockdata_alloc_real(0, data, len);
|
||||
}
|
||||
|
||||
/* Add data to the end of the block.
|
||||
newlen is length of new data, NOT total new length.
|
||||
Use blockdata_alloc(NULL, 0) to make empty block to add to. */
|
||||
int blockdata_expand(struct blockdata *block, size_t oldlen, char *data, size_t newlen)
|
||||
{
|
||||
struct blockdata *b;
|
||||
|
||||
/* find size of current final block */
|
||||
for (b = block; oldlen > KEYBLOCK_LEN && b; b = b->next, oldlen -= KEYBLOCK_LEN);
|
||||
|
||||
/* chain to short for length, something is broken */
|
||||
if (oldlen > KEYBLOCK_LEN)
|
||||
{
|
||||
blockdata_free(block);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
struct blockdata *new;
|
||||
size_t blocksize = KEYBLOCK_LEN - oldlen;
|
||||
size_t size = (newlen <= blocksize) ? newlen : blocksize;
|
||||
|
||||
if (size != 0)
|
||||
{
|
||||
memcpy(&b->key[oldlen], data, size);
|
||||
data += size;
|
||||
newlen -= size;
|
||||
}
|
||||
|
||||
/* full blocks from now on. */
|
||||
oldlen = 0;
|
||||
|
||||
if (newlen == 0)
|
||||
break;
|
||||
|
||||
if ((new = new_block()))
|
||||
{
|
||||
b->next = new;
|
||||
b = new;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* failed to alloc, free partial chain */
|
||||
blockdata_free(block);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void blockdata_free(struct blockdata *blocks)
|
||||
{
|
||||
struct blockdata *tmp;
|
||||
|
||||
45
src/cache.c
45
src/cache.c
@@ -29,6 +29,7 @@ static void make_non_terminals(struct crec *source);
|
||||
static struct crec *really_insert(char *name, union all_addr *addr, unsigned short class,
|
||||
time_t now, unsigned long ttl, unsigned int flags);
|
||||
static void dump_cache_entry(struct crec *cache, time_t now);
|
||||
static char *querystr(char *desc, unsigned short type);
|
||||
|
||||
/* type->string mapping: this is also used by the name-hash function as a mixing table. */
|
||||
/* taken from https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml */
|
||||
@@ -133,6 +134,17 @@ static void cache_link(struct crec *crecp);
|
||||
static void rehash(int size);
|
||||
static void cache_hash(struct crec *crecp);
|
||||
|
||||
unsigned short rrtype(char *in)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
|
||||
if (strcasecmp(in, typestr[i].name) == 0)
|
||||
return typestr[i].type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void next_uid(struct crec *crecp)
|
||||
{
|
||||
static unsigned int uid = 0;
|
||||
@@ -265,6 +277,8 @@ static void cache_blockdata_free(struct crec *crecp)
|
||||
{
|
||||
if (crecp->flags & F_SRV)
|
||||
blockdata_free(crecp->addr.srv.target);
|
||||
else if (crecp->flags & F_RR)
|
||||
blockdata_free(crecp->addr.rr.rrdata);
|
||||
#ifdef HAVE_DNSSEC
|
||||
else if (crecp->flags & F_DNSKEY)
|
||||
blockdata_free(crecp->addr.key.keydata);
|
||||
@@ -459,7 +473,8 @@ static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned s
|
||||
{
|
||||
/* Don't delete DNSSEC in favour of a CNAME, they can co-exist */
|
||||
if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_SRV | F_NXDOMAIN)) ||
|
||||
(((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))))
|
||||
(((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))) ||
|
||||
((crecp->flags & flags & F_RR) && addr->rr.rrtype == crecp->addr.rr.rrtype))
|
||||
{
|
||||
if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
|
||||
return crecp;
|
||||
@@ -776,7 +791,7 @@ void cache_end_insert(void)
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->ttd, sizeof(new_chain->ttd), 0);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&flags, sizeof(flags), 0);
|
||||
|
||||
if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_SRV))
|
||||
if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_SRV | F_RR))
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), 0);
|
||||
if (flags & F_SRV)
|
||||
{
|
||||
@@ -784,6 +799,12 @@ void cache_end_insert(void)
|
||||
if (!(flags & F_NEG))
|
||||
blockdata_write(new_chain->addr.srv.target, new_chain->addr.srv.targetlen, daemon->pipe_to_parent);
|
||||
}
|
||||
if (flags & F_RR)
|
||||
{
|
||||
/* A negative RR entry is possible and has no data, obviously. */
|
||||
if (!(flags & F_NEG))
|
||||
blockdata_write(new_chain->addr.rr.rrdata, new_chain->addr.rr.datalen, daemon->pipe_to_parent);
|
||||
}
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (flags & F_DNSKEY)
|
||||
{
|
||||
@@ -848,16 +869,18 @@ int cache_recv_insert(time_t now, int fd)
|
||||
|
||||
ttl = difftime(ttd, now);
|
||||
|
||||
if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_SRV))
|
||||
if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_SRV | F_RR))
|
||||
{
|
||||
unsigned short class = C_IN;
|
||||
|
||||
|
||||
if (!read_write(fd, (unsigned char *)&addr, sizeof(addr), 1))
|
||||
return 0;
|
||||
|
||||
|
||||
if ((flags & F_SRV) && !(flags & F_NEG) && !(addr.srv.target = blockdata_read(fd, addr.srv.targetlen)))
|
||||
return 0;
|
||||
|
||||
|
||||
if ((flags & F_RR) && !(flags & F_NEG) && !(addr.rr.rrdata = blockdata_read(fd, addr.rr.datalen)))
|
||||
return 0;
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (flags & F_DNSKEY)
|
||||
{
|
||||
@@ -1587,7 +1610,7 @@ static void make_non_terminals(struct crec *source)
|
||||
if (!is_outdated_cname_pointer(crecp) &&
|
||||
(crecp->flags & F_FORWARD) &&
|
||||
(crecp->flags & type) &&
|
||||
!(crecp->flags & (F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_DNSKEY | F_DS)) &&
|
||||
!(crecp->flags & (F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_DNSKEY | F_DS | F_RR)) &&
|
||||
hostname_isequal(name, cache_get_name(crecp)))
|
||||
{
|
||||
*up = crecp->hash_next;
|
||||
@@ -1644,7 +1667,7 @@ static void make_non_terminals(struct crec *source)
|
||||
|
||||
if (crecp)
|
||||
{
|
||||
crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_DNSKEY | F_DS | F_REVERSE);
|
||||
crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_RR | F_DNSKEY | F_DS | F_REVERSE);
|
||||
if (!(crecp->flags & F_IMMORTAL))
|
||||
crecp->ttd = source->ttd;
|
||||
crecp->name.namep = name;
|
||||
@@ -1792,6 +1815,8 @@ static void dump_cache_entry(struct crec *cache, time_t now)
|
||||
blockdata_retrieve(cache->addr.srv.target, targetlen, a + len);
|
||||
a[len + targetlen] = 0;
|
||||
}
|
||||
else if (cache->flags & F_RR)
|
||||
sprintf(a, "%s", querystr(NULL, cache->addr.rr.rrtype));
|
||||
#ifdef HAVE_DNSSEC
|
||||
else if (cache->flags & F_DS)
|
||||
{
|
||||
@@ -1820,6 +1845,8 @@ static void dump_cache_entry(struct crec *cache, time_t now)
|
||||
t = "C";
|
||||
else if (cache->flags & F_SRV)
|
||||
t = "V";
|
||||
else if (cache->flags & F_RR)
|
||||
t = "T";
|
||||
#ifdef HAVE_DNSSEC
|
||||
else if (cache->flags & F_DS)
|
||||
t = "S";
|
||||
@@ -2078,6 +2105,8 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
|
||||
sprintf(portstring, "#%u", type);
|
||||
}
|
||||
}
|
||||
else if (flags & F_RR)
|
||||
dest = querystr(NULL, addr->rr.rrtype);
|
||||
else
|
||||
dest = arg;
|
||||
}
|
||||
|
||||
@@ -125,17 +125,11 @@ int main (int argc, char **argv)
|
||||
{
|
||||
/* Note that both /000 and '.' are allowed within labels. These get
|
||||
represented in presentation format using NAME_ESCAPE as an escape
|
||||
character when in DNSSEC mode.
|
||||
In theory, if all the characters in a name were /000 or
|
||||
character. In theory, if all the characters in a name were /000 or
|
||||
'.' or NAME_ESCAPE then all would have to be escaped, so the
|
||||
presentation format would be twice as long as the spec.
|
||||
|
||||
daemon->namebuff was previously allocated by the option-reading
|
||||
code before we knew if we're in DNSSEC mode, so reallocate here. */
|
||||
free(daemon->namebuff);
|
||||
daemon->namebuff = safe_malloc(MAXDNAME * 2);
|
||||
daemon->keyname = safe_malloc(MAXDNAME * 2);
|
||||
daemon->workspacename = safe_malloc(MAXDNAME * 2);
|
||||
presentation format would be twice as long as the spec. */
|
||||
daemon->keyname = safe_malloc((MAXDNAME * 2) + 1);
|
||||
daemon->workspacename = safe_malloc((MAXDNAME * 2) + 1);
|
||||
/* one char flag per possible RR in answer section (may get extended). */
|
||||
daemon->rr_status_sz = 64;
|
||||
daemon->rr_status = safe_malloc(sizeof(*daemon->rr_status) * daemon->rr_status_sz);
|
||||
|
||||
@@ -282,7 +282,8 @@ struct event_desc {
|
||||
#define OPT_STRIP_MAC 70
|
||||
#define OPT_NORR 71
|
||||
#define OPT_NO_IDENT 72
|
||||
#define OPT_LAST 73
|
||||
#define OPT_CACHE_RR 73
|
||||
#define OPT_LAST 74
|
||||
|
||||
#define OPTION_BITS (sizeof(unsigned int)*8)
|
||||
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
|
||||
@@ -337,7 +338,7 @@ union all_addr {
|
||||
/* for arbitrary RR record. */
|
||||
struct {
|
||||
struct blockdata *rrdata;
|
||||
u16 rrtype;
|
||||
unsigned short rrtype, datalen;
|
||||
} rr;
|
||||
};
|
||||
|
||||
@@ -663,6 +664,11 @@ struct iname {
|
||||
struct iname *next;
|
||||
};
|
||||
|
||||
struct rrlist {
|
||||
unsigned short rr;
|
||||
struct rrlist *next;
|
||||
};
|
||||
|
||||
/* subnet parameters from command line */
|
||||
struct mysubnet {
|
||||
union mysockaddr addr;
|
||||
@@ -1128,6 +1134,7 @@ extern struct daemon {
|
||||
struct naptr *naptr;
|
||||
struct txt_record *txt, *rr;
|
||||
struct ptr_record *ptr;
|
||||
struct rrlist *cache_rr, filter_rr;
|
||||
struct host_record *host_records, *host_records_tail;
|
||||
struct cname *cnames;
|
||||
struct auth_zone *auth_zones;
|
||||
@@ -1309,6 +1316,7 @@ struct server_details {
|
||||
|
||||
/* cache.c */
|
||||
void cache_init(void);
|
||||
unsigned short rrtype(char *in);
|
||||
void next_uid(struct crec *crecp);
|
||||
void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg, unsigned short type);
|
||||
char *record_source(unsigned int index);
|
||||
@@ -1342,6 +1350,8 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size,
|
||||
void blockdata_init(void);
|
||||
void blockdata_report(void);
|
||||
struct blockdata *blockdata_alloc(char *data, size_t len);
|
||||
int blockdata_expand(struct blockdata *block, size_t oldlen,
|
||||
char *data, size_t newlen);
|
||||
void *blockdata_retrieve(struct blockdata *block, size_t len, void *data);
|
||||
struct blockdata *blockdata_read(int fd, size_t len);
|
||||
void blockdata_write(struct blockdata *block, size_t len, int fd);
|
||||
@@ -1423,6 +1433,7 @@ void rand_init(void);
|
||||
unsigned short rand16(void);
|
||||
u32 rand32(void);
|
||||
u64 rand64(void);
|
||||
int rr_on_list(struct rrlist *list, unsigned short rr);
|
||||
int legal_hostname(char *name);
|
||||
char *canonicalise(char *in, int *nomem);
|
||||
unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit);
|
||||
@@ -1817,13 +1828,16 @@ int do_poll(int timeout);
|
||||
|
||||
/* rrfilter.c */
|
||||
size_t rrfilter(struct dns_header *header, size_t *plen, int mode);
|
||||
u16 *rrfilter_desc(int type);
|
||||
short *rrfilter_desc(int type);
|
||||
int expand_workspace(unsigned char ***wkspc, int *szp, int new);
|
||||
int to_wire(char *name);
|
||||
void from_wire(char *name);
|
||||
/* modes. */
|
||||
#define RRFILTER_EDNS0 0
|
||||
#define RRFILTER_DNSSEC 1
|
||||
#define RRFILTER_A 2
|
||||
#define RRFILTER_AAAA 3
|
||||
|
||||
/* edns0.c */
|
||||
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
|
||||
size_t *len, unsigned char **p, int *is_sign, int *is_last);
|
||||
|
||||
87
src/dnssec.c
87
src/dnssec.c
@@ -24,81 +24,6 @@
|
||||
#define SERIAL_LT -1
|
||||
#define SERIAL_GT 1
|
||||
|
||||
/* Convert from presentation format to wire format, in place.
|
||||
Also map UC -> LC.
|
||||
Note that using extract_name to get presentation format
|
||||
then calling to_wire() removes compression and maps case,
|
||||
thus generating names in canonical form.
|
||||
Calling to_wire followed by from_wire is almost an identity,
|
||||
except that the UC remains mapped to LC.
|
||||
|
||||
Note that both /000 and '.' are allowed within labels. These get
|
||||
represented in presentation format using NAME_ESCAPE as an escape
|
||||
character. In theory, if all the characters in a name were /000 or
|
||||
'.' or NAME_ESCAPE then all would have to be escaped, so the
|
||||
presentation format would be twice as long as the spec (1024).
|
||||
The buffers are all declared as 2049 (allowing for the trailing zero)
|
||||
for this reason.
|
||||
*/
|
||||
static int to_wire(char *name)
|
||||
{
|
||||
unsigned char *l, *p, *q, term;
|
||||
int len;
|
||||
|
||||
for (l = (unsigned char*)name; *l != 0; l = p)
|
||||
{
|
||||
for (p = l; *p != '.' && *p != 0; p++)
|
||||
if (*p >= 'A' && *p <= 'Z')
|
||||
*p = *p - 'A' + 'a';
|
||||
else if (*p == NAME_ESCAPE)
|
||||
{
|
||||
for (q = p; *q; q++)
|
||||
*q = *(q+1);
|
||||
(*p)--;
|
||||
}
|
||||
term = *p;
|
||||
|
||||
if ((len = p - l) != 0)
|
||||
memmove(l+1, l, len);
|
||||
*l = len;
|
||||
|
||||
p++;
|
||||
|
||||
if (term == 0)
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
return l + 1 - (unsigned char *)name;
|
||||
}
|
||||
|
||||
/* Note: no compression allowed in input. */
|
||||
static void from_wire(char *name)
|
||||
{
|
||||
unsigned char *l, *p, *last;
|
||||
int len;
|
||||
|
||||
for (last = (unsigned char *)name; *last != 0; last += *last+1);
|
||||
|
||||
for (l = (unsigned char *)name; *l != 0; l += len+1)
|
||||
{
|
||||
len = *l;
|
||||
memmove(l, l+1, len);
|
||||
for (p = l; p < l + len; p++)
|
||||
if (*p == '.' || *p == 0 || *p == NAME_ESCAPE)
|
||||
{
|
||||
memmove(p+1, p, 1 + last - p);
|
||||
len++;
|
||||
*p++ = NAME_ESCAPE;
|
||||
(*p)++;
|
||||
}
|
||||
|
||||
l[len] = '.';
|
||||
}
|
||||
|
||||
if ((char *)l != name)
|
||||
*(l-1) = 0;
|
||||
}
|
||||
|
||||
/* Input in presentation format */
|
||||
static int count_labels(char *name)
|
||||
{
|
||||
@@ -225,7 +150,7 @@ static int is_check_date(unsigned long curtime)
|
||||
On returning 0, the end has been reached.
|
||||
*/
|
||||
struct rdata_state {
|
||||
u16 *desc;
|
||||
short *desc;
|
||||
size_t c;
|
||||
unsigned char *end, *ip, *op;
|
||||
char *buff;
|
||||
@@ -246,7 +171,7 @@ static int get_rdata(struct dns_header *header, size_t plen, struct rdata_state
|
||||
{
|
||||
d = *(state->desc);
|
||||
|
||||
if (d == (u16)-1)
|
||||
if (d == -1)
|
||||
{
|
||||
/* all the bytes to the end. */
|
||||
if ((state->c = state->end - state->ip) != 0)
|
||||
@@ -294,7 +219,7 @@ static int get_rdata(struct dns_header *header, size_t plen, struct rdata_state
|
||||
|
||||
/* Bubble sort the RRset into the canonical order. */
|
||||
|
||||
static int sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int rrsetidx,
|
||||
static int sort_rrset(struct dns_header *header, size_t plen, short *rr_desc, int rrsetidx,
|
||||
unsigned char **rrset, char *buff1, char *buff2)
|
||||
{
|
||||
int swap, i, j;
|
||||
@@ -331,7 +256,7 @@ static int sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
|
||||
is the identity function and we can compare
|
||||
the RRs directly. If not we compare the
|
||||
canonicalised RRs one byte at a time. */
|
||||
if (*rr_desc == (u16)-1)
|
||||
if (*rr_desc == -1)
|
||||
{
|
||||
int rdmin = rdlen1 > rdlen2 ? rdlen2 : rdlen1;
|
||||
int cmp = memcmp(state1.ip, state2.ip, rdmin);
|
||||
@@ -524,7 +449,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
unsigned char *p;
|
||||
int rdlen, j, name_labels, algo, labels, key_tag;
|
||||
struct crec *crecp = NULL;
|
||||
u16 *rr_desc = rrfilter_desc(type);
|
||||
short *rr_desc = rrfilter_desc(type);
|
||||
u32 sig_expiration, sig_inception;
|
||||
int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP | DNSSEC_FAIL_NOKEYSUP;
|
||||
|
||||
@@ -671,7 +596,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
|
||||
If canonicalisation is not needed, a simple insertion into the hash works.
|
||||
*/
|
||||
if (*rr_desc == (u16)-1)
|
||||
if (*rr_desc == -1)
|
||||
{
|
||||
len = htons(rdlen);
|
||||
hash->update(ctx, 2, (unsigned char *)&len);
|
||||
|
||||
37
src/option.c
37
src/option.c
@@ -186,6 +186,7 @@ struct myoption {
|
||||
#define LOPT_STALE_CACHE 377
|
||||
#define LOPT_NORR 378
|
||||
#define LOPT_NO_IDENT 379
|
||||
#define LOPT_CACHE_RR 380
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
static const struct option opts[] =
|
||||
@@ -239,6 +240,7 @@ static const struct myoption opts[] =
|
||||
{ "local-ttl", 1, 0, 'T' },
|
||||
{ "no-negcache", 0, 0, 'N' },
|
||||
{ "no-round-robin", 0, 0, LOPT_NORR },
|
||||
{ "cache-rr", 1, 0, LOPT_CACHE_RR },
|
||||
{ "addn-hosts", 1, 0, 'H' },
|
||||
{ "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
|
||||
{ "query-port", 1, 0, 'Q' },
|
||||
@@ -566,13 +568,14 @@ static struct {
|
||||
{ LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL },
|
||||
{ LOPT_REPLY_DELAY, ARG_ONE, "<integer>", gettext_noop("Delay DHCP replies for at least number of seconds."), NULL },
|
||||
{ LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL },
|
||||
{ LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL },
|
||||
{ LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL },
|
||||
{ LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file."), NULL },
|
||||
{ LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump."), NULL },
|
||||
{ LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL },
|
||||
{ LOPT_UMBRELLA, ARG_ONE, "[=<optspec>]", gettext_noop("Send Cisco Umbrella identifiers including remote IP."), NULL },
|
||||
{ LOPT_QUIET_TFTP, OPT_QUIET_TFTP, NULL, gettext_noop("Do not log routine TFTP."), NULL },
|
||||
{ LOPT_NORR, OPT_NORR, NULL, gettext_noop("Suppress round-robin ordering of DNS records."), NULL },
|
||||
{ LOPT_NO_IDENT, OPT_NO_IDENT, NULL, gettext_noop("Do not add CHAOS TXT records."), NULL },
|
||||
{ LOPT_CACHE_RR, ARG_DUP, "RRtype", gettext_noop("Cache this DNS resource record type."), NULL },
|
||||
{ 0, 0, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
@@ -3465,6 +3468,27 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case LOPT_CACHE_RR:
|
||||
while (1) {
|
||||
int type;
|
||||
struct rrlist *new;
|
||||
|
||||
comma = split(arg);
|
||||
if (!atoi_check(arg, &type) && (type = rrtype(arg)) == 0)
|
||||
ret_err(_("bad RR type"));
|
||||
|
||||
new = opt_malloc(sizeof(struct rrlist));
|
||||
new->rr = type;
|
||||
|
||||
new->next = daemon->cache_rr;
|
||||
daemon->cache_rr = new;
|
||||
|
||||
if (!comma) break;
|
||||
arg = comma;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
case 'X': /* --dhcp-lease-max */
|
||||
@@ -5733,10 +5757,15 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
{
|
||||
size_t argbuf_size = MAXDNAME;
|
||||
char *argbuf = opt_malloc(argbuf_size);
|
||||
char *buff = opt_malloc(MAXDNAME);
|
||||
/* Note that both /000 and '.' are allowed within labels. These get
|
||||
represented in presentation format using NAME_ESCAPE as an escape
|
||||
character. In theory, if all the characters in a name were /000 or
|
||||
'.' or NAME_ESCAPE then all would have to be escaped, so the
|
||||
presentation format would be twice as long as the spec. */
|
||||
char *buff = opt_malloc((MAXDNAME * 2) + 1);
|
||||
int option, testmode = 0;
|
||||
char *arg, *conffile = NULL;
|
||||
|
||||
|
||||
opterr = 0;
|
||||
|
||||
daemon = opt_malloc(sizeof(struct daemon));
|
||||
|
||||
160
src/rfc1035.c
160
src/rfc1035.c
@@ -89,23 +89,14 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
if (isExtract)
|
||||
{
|
||||
unsigned char c = *p;
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
|
||||
if (c == 0 || c == '.' || c == NAME_ESCAPE)
|
||||
{
|
||||
if (c == 0 || c == '.' || c == NAME_ESCAPE)
|
||||
{
|
||||
*cp++ = NAME_ESCAPE;
|
||||
*cp++ = c+1;
|
||||
}
|
||||
else
|
||||
*cp++ = c;
|
||||
*cp++ = NAME_ESCAPE;
|
||||
*cp++ = c+1;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (c != 0 && c != '.')
|
||||
*cp++ = c;
|
||||
else
|
||||
return 0;
|
||||
*cp++ = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -118,10 +109,9 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
cp++;
|
||||
if (c1 >= 'A' && c1 <= 'Z')
|
||||
c1 += 'a' - 'A';
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && c1 == NAME_ESCAPE)
|
||||
|
||||
if (c1 == NAME_ESCAPE)
|
||||
c1 = (*cp++)-1;
|
||||
#endif
|
||||
|
||||
if (c2 >= 'A' && c2 <= 'Z')
|
||||
c2 += 'a' - 'A';
|
||||
@@ -502,12 +492,10 @@ static int find_soa(struct dns_header *header, size_t qlen, int *doctored)
|
||||
}
|
||||
|
||||
/* Print TXT reply to log */
|
||||
static int print_txt(struct dns_header *header, const size_t qlen, char *name,
|
||||
unsigned char *p, const int ardlen, int secflag)
|
||||
static int log_txt(char *name, unsigned char *p, const int ardlen, int secflag)
|
||||
{
|
||||
unsigned char *p1 = p;
|
||||
if (!CHECK_LEN(header, p1, qlen, ardlen))
|
||||
return 0;
|
||||
|
||||
/* Loop over TXT payload */
|
||||
while ((p1 - p) < ardlen)
|
||||
{
|
||||
@@ -526,7 +514,7 @@ static int print_txt(struct dns_header *header, const size_t qlen, char *name,
|
||||
}
|
||||
|
||||
*p3 = 0;
|
||||
log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, (char*)p1, 0);
|
||||
log_query(secflag | F_FORWARD, name, NULL, (char*)p1, 0);
|
||||
/* restore */
|
||||
memmove(p1 + 1, p1, i);
|
||||
*p1 = len;
|
||||
@@ -719,6 +707,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
}
|
||||
else if (qtype == T_SRV)
|
||||
flags |= F_SRV;
|
||||
else if (qtype != T_CNAME && rr_on_list(daemon->cache_rr, qtype))
|
||||
flags |= F_RR;
|
||||
else
|
||||
insert = 0; /* NOTE: do not cache data from CNAME queries. */
|
||||
|
||||
@@ -804,7 +794,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (!option_bool(OPT_DNSSEC_VALID) || aqtype != T_RRSIG)
|
||||
#endif
|
||||
log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, NULL, aqtype);
|
||||
log_query(secflag | F_FORWARD | F_UPSTREAM | F_RRNAME, name, NULL, NULL, aqtype);
|
||||
}
|
||||
else if (!(flags & F_NXDOMAIN))
|
||||
{
|
||||
@@ -829,6 +819,64 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
if (!extract_name(header, qlen, &tmp, name, 1, 0))
|
||||
return 2;
|
||||
}
|
||||
else if (flags & F_RR)
|
||||
{
|
||||
short desc, *rrdesc = rrfilter_desc(aqtype);
|
||||
unsigned char *tmp = namep;
|
||||
|
||||
if (!CHECK_LEN(header, p1, qlen, ardlen))
|
||||
return 2; /* bad packet */
|
||||
addr.rr.rrtype = aqtype;
|
||||
addr.rr.datalen = 0;
|
||||
|
||||
/* The RR data may include names, and those names may include
|
||||
compression, which will be rendered meaningless when
|
||||
copied into another packet.
|
||||
Here we go through a description of the packet type to
|
||||
find the names, and extract them to a c-string and then
|
||||
re-encode them to standalone DNS format without compression. */
|
||||
if (!(addr.rr.rrdata = blockdata_alloc(NULL, 0)))
|
||||
return 0;
|
||||
do
|
||||
{
|
||||
desc = *rrdesc++;
|
||||
|
||||
if (desc == -1)
|
||||
{
|
||||
/* Copy the rest of the RR and end. */
|
||||
if (!blockdata_expand(addr.rr.rrdata, addr.rr.datalen, (char *)p1, endrr - p1))
|
||||
return 0;
|
||||
addr.rr.datalen += endrr - p1;
|
||||
}
|
||||
else if (desc == 0)
|
||||
{
|
||||
/* Name, extract it then re-encode. */
|
||||
int len;
|
||||
|
||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||
return 2;
|
||||
|
||||
len = to_wire(name);
|
||||
if (!blockdata_expand(addr.rr.rrdata, addr.rr.datalen, name, len))
|
||||
return 0;
|
||||
addr.rr.datalen += len;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* desc is length of a block of data to be used as-is */
|
||||
if (desc > endrr - p1)
|
||||
desc = endrr - p1;
|
||||
if (!blockdata_expand(addr.rr.rrdata, addr.rr.datalen, (char *)p1, desc))
|
||||
return 0;
|
||||
addr.rr.datalen += desc;
|
||||
p1 += desc;
|
||||
}
|
||||
} while (desc != -1);
|
||||
|
||||
/* we overwrote the original name, so get it back here. */
|
||||
if (!extract_name(header, qlen, &tmp, name, 1, 0))
|
||||
return 2;
|
||||
}
|
||||
else if (flags & (F_IPV4 | F_IPV6))
|
||||
{
|
||||
/* copy address into aligned storage */
|
||||
@@ -876,8 +924,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
if (aqtype == T_TXT)
|
||||
{
|
||||
if (!print_txt(header, qlen, name, p1, ardlen, secflag))
|
||||
return 2;
|
||||
if (!CHECK_LEN(header, p1, qlen, ardlen))
|
||||
return 2;
|
||||
|
||||
log_txt(name, p1, ardlen, secflag | F_UPSTREAM);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -903,7 +953,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
{
|
||||
if (flags & F_NXDOMAIN)
|
||||
{
|
||||
flags &= ~(F_IPV4 | F_IPV6 | F_SRV);
|
||||
flags &= ~(F_IPV4 | F_IPV6 | F_SRV | F_RR);
|
||||
|
||||
/* Can store NXDOMAIN reply for any qtype. */
|
||||
insert = 1;
|
||||
@@ -924,7 +974,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
if (ttl == 0)
|
||||
ttl = cttl;
|
||||
|
||||
newc = cache_insert(name, NULL, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));
|
||||
if (flags & F_RR)
|
||||
addr.rr.rrtype = qtype;
|
||||
|
||||
newc = cache_insert(name, &addr, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));
|
||||
if (newc && cpp)
|
||||
{
|
||||
next_uid(newc);
|
||||
@@ -2044,7 +2097,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
if (!found)
|
||||
{
|
||||
if ((crecp = cache_find_by_name(NULL, name, now, F_SRV | F_NXDOMAIN | (dryrun ? F_NO_RR : 0))) &&
|
||||
rd_bit && (!do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))))
|
||||
rd_bit && (!do_bit || cache_validated(crecp)))
|
||||
do
|
||||
{
|
||||
int stale_flag = 0;
|
||||
@@ -2125,8 +2178,57 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
if (!dryrun)
|
||||
log_query(F_CONFIG | F_NEG, name, &addr, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ans && qtype != T_ANY)
|
||||
{
|
||||
if ((crecp = cache_find_by_name(NULL, name, now, F_RR | F_NXDOMAIN | (dryrun ? F_NO_RR : 0))) &&
|
||||
rd_bit && (!do_bit || cache_validated(crecp)))
|
||||
do
|
||||
{
|
||||
int stale_flag = 0;
|
||||
|
||||
if (crecp->addr.rr.rrtype == qtype)
|
||||
{
|
||||
if (crec_isstale(crecp, now))
|
||||
{
|
||||
if (stale)
|
||||
*stale = 1;
|
||||
|
||||
stale_flag = F_STALE;
|
||||
}
|
||||
|
||||
if (!(crecp->flags & F_DNSSECOK))
|
||||
sec_data = 0;
|
||||
|
||||
auth = 0;
|
||||
ans = 1;
|
||||
|
||||
if (!dryrun)
|
||||
{
|
||||
char *rrdata = NULL;
|
||||
|
||||
if (!(crecp->flags & F_NEG))
|
||||
{
|
||||
rrdata = blockdata_retrieve(crecp->addr.rr.rrdata, crecp->addr.rr.datalen, NULL);
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
crec_ttl(crecp, now), NULL, qtype, C_IN, "t",
|
||||
crecp->addr.rr.datalen, rrdata))
|
||||
anscount++;
|
||||
}
|
||||
|
||||
/* log after cache insertion as log_txt mangles rrdata */
|
||||
if (qtype == T_TXT && !(crecp->flags & F_NEG))
|
||||
log_txt(name, (unsigned char *)rrdata, crecp->addr.rr.datalen, crecp->flags & F_DNSSECOK);
|
||||
else
|
||||
log_query(stale_flag | crecp->flags, name, &crecp->addr, NULL, 0);
|
||||
}
|
||||
}
|
||||
} while ((crecp = cache_find_by_name(crecp, name, now, F_RR)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!ans)
|
||||
{
|
||||
/* We may know that the domain doesn't exist for any RRtype. */
|
||||
|
||||
@@ -136,9 +136,9 @@ static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, i
|
||||
|
||||
if (class == C_IN)
|
||||
{
|
||||
u16 *d;
|
||||
short *d;
|
||||
|
||||
for (pp = p, d = rrfilter_desc(type); *d != (u16)-1; d++)
|
||||
for (pp = p, d = rrfilter_desc(type); *d != -1; d++)
|
||||
{
|
||||
if (*d != 0)
|
||||
pp += *d;
|
||||
@@ -285,7 +285,7 @@ size_t rrfilter(struct dns_header *header, size_t *plen, int mode)
|
||||
}
|
||||
|
||||
/* This is used in the DNSSEC code too, hence it's exported */
|
||||
u16 *rrfilter_desc(int type)
|
||||
short *rrfilter_desc(int type)
|
||||
{
|
||||
/* List of RRtypes which include domains in the data.
|
||||
0 -> domain
|
||||
@@ -296,7 +296,7 @@ u16 *rrfilter_desc(int type)
|
||||
anything which needs no mangling.
|
||||
*/
|
||||
|
||||
static u16 rr_desc[] =
|
||||
static short rr_desc[] =
|
||||
{
|
||||
T_NS, 0, -1,
|
||||
T_MD, 0, -1,
|
||||
@@ -321,10 +321,10 @@ u16 *rrfilter_desc(int type)
|
||||
0, -1 /* wildcard/catchall */
|
||||
};
|
||||
|
||||
u16 *p = rr_desc;
|
||||
short *p = rr_desc;
|
||||
|
||||
while (*p != type && *p != 0)
|
||||
while (*p++ != (u16)-1);
|
||||
while (*p++ != -1);
|
||||
|
||||
return p+1;
|
||||
}
|
||||
@@ -352,3 +352,78 @@ int expand_workspace(unsigned char ***wkspc, int *szp, int new)
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Convert from presentation format to wire format, in place.
|
||||
Also map UC -> LC.
|
||||
Note that using extract_name to get presentation format
|
||||
then calling to_wire() removes compression and maps case,
|
||||
thus generating names in canonical form.
|
||||
Calling to_wire followed by from_wire is almost an identity,
|
||||
except that the UC remains mapped to LC.
|
||||
|
||||
Note that both /000 and '.' are allowed within labels. These get
|
||||
represented in presentation format using NAME_ESCAPE as an escape
|
||||
character. In theory, if all the characters in a name were /000 or
|
||||
'.' or NAME_ESCAPE then all would have to be escaped, so the
|
||||
presentation format would be twice as long as the spec (1024).
|
||||
The buffers are all declared as 2049 (allowing for the trailing zero)
|
||||
for this reason.
|
||||
*/
|
||||
int to_wire(char *name)
|
||||
{
|
||||
unsigned char *l, *p, *q, term;
|
||||
int len;
|
||||
|
||||
for (l = (unsigned char*)name; *l != 0; l = p)
|
||||
{
|
||||
for (p = l; *p != '.' && *p != 0; p++)
|
||||
if (*p >= 'A' && *p <= 'Z')
|
||||
*p = *p - 'A' + 'a';
|
||||
else if (*p == NAME_ESCAPE)
|
||||
{
|
||||
for (q = p; *q; q++)
|
||||
*q = *(q+1);
|
||||
(*p)--;
|
||||
}
|
||||
term = *p;
|
||||
|
||||
if ((len = p - l) != 0)
|
||||
memmove(l+1, l, len);
|
||||
*l = len;
|
||||
|
||||
p++;
|
||||
|
||||
if (term == 0)
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
return l + 1 - (unsigned char *)name;
|
||||
}
|
||||
|
||||
/* Note: no compression allowed in input. */
|
||||
void from_wire(char *name)
|
||||
{
|
||||
unsigned char *l, *p, *last;
|
||||
int len;
|
||||
|
||||
for (last = (unsigned char *)name; *last != 0; last += *last+1);
|
||||
|
||||
for (l = (unsigned char *)name; *l != 0; l += len+1)
|
||||
{
|
||||
len = *l;
|
||||
memmove(l, l+1, len);
|
||||
for (p = l; p < l + len; p++)
|
||||
if (*p == '.' || *p == 0 || *p == NAME_ESCAPE)
|
||||
{
|
||||
memmove(p+1, p, 1 + last - p);
|
||||
len++;
|
||||
*p++ = NAME_ESCAPE;
|
||||
(*p)++;
|
||||
}
|
||||
|
||||
l[len] = '.';
|
||||
}
|
||||
|
||||
if ((char *)l != name)
|
||||
*(l-1) = 0;
|
||||
}
|
||||
|
||||
17
src/util.c
17
src/util.c
@@ -115,6 +115,19 @@ u64 rand64(void)
|
||||
return (u64)out[outleft+1] + (((u64)out[outleft]) << 32);
|
||||
}
|
||||
|
||||
int rr_on_list(struct rrlist *list, unsigned short rr)
|
||||
{
|
||||
while (list)
|
||||
{
|
||||
if (list->rr == rr)
|
||||
return 1;
|
||||
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* returns 1 if name is OK and ascii printable
|
||||
* returns 2 if name should be processed by IDN */
|
||||
static int check_name(char *in)
|
||||
@@ -280,11 +293,9 @@ unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit)
|
||||
if (limit && p + 1 > (unsigned char*)limit)
|
||||
return NULL;
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && *sval == NAME_ESCAPE)
|
||||
if (*sval == NAME_ESCAPE)
|
||||
*p++ = (*(++sval))-1;
|
||||
else
|
||||
#endif
|
||||
*p++ = *sval;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user