mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 18:28:25 +00:00
Merge branch 'master' of ssh://central/var/cache/git/dnsmasq
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
119
src/blockdata.c
Normal file
119
src/blockdata.c
Normal file
@@ -0,0 +1,119 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991, or
|
||||
(at your option) version 3 dated 29 June, 2007.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
|
||||
static struct blockdata *keyblock_free = NULL;
|
||||
static unsigned int blockdata_count = 0, blockdata_hwm = 0;
|
||||
|
||||
void blockdata_report(void)
|
||||
{
|
||||
my_syslog(LOG_INFO, _("DNSSEC memory in use %u, max %u"),
|
||||
blockdata_count * sizeof(struct blockdata), blockdata_hwm * sizeof(struct blockdata));
|
||||
}
|
||||
|
||||
struct blockdata *blockdata_alloc(char *data, size_t len)
|
||||
{
|
||||
struct blockdata *block, *ret = NULL;
|
||||
struct blockdata **prev = &ret;
|
||||
size_t blen;
|
||||
|
||||
while (len > 0)
|
||||
{
|
||||
if (keyblock_free)
|
||||
{
|
||||
block = keyblock_free;
|
||||
keyblock_free = block->next;
|
||||
blockdata_count++;
|
||||
}
|
||||
else if ((block = whine_malloc(sizeof(struct blockdata))))
|
||||
{
|
||||
blockdata_count++;
|
||||
if (blockdata_hwm < blockdata_count)
|
||||
blockdata_hwm = blockdata_count;
|
||||
}
|
||||
|
||||
if (!block)
|
||||
{
|
||||
/* failed to alloc, free partial chain */
|
||||
blockdata_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len;
|
||||
memcpy(block->key, data, blen);
|
||||
data += blen;
|
||||
len -= blen;
|
||||
*prev = block;
|
||||
prev = &block->next;
|
||||
block->next = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t blockdata_walk(struct blockdata **key, unsigned char **p, size_t cnt)
|
||||
{
|
||||
if (*p == NULL)
|
||||
*p = (*key)->key;
|
||||
else if (*p == (*key)->key + KEYBLOCK_LEN)
|
||||
{
|
||||
*key = (*key)->next;
|
||||
if (*key == NULL)
|
||||
return 0;
|
||||
*p = (*key)->key;
|
||||
}
|
||||
|
||||
return MIN(cnt, (size_t)((*key)->key + KEYBLOCK_LEN - (*p)));
|
||||
}
|
||||
|
||||
void blockdata_free(struct blockdata *blocks)
|
||||
{
|
||||
struct blockdata *tmp;
|
||||
|
||||
if (blocks)
|
||||
{
|
||||
for (tmp = blocks; tmp->next; tmp = tmp->next)
|
||||
blockdata_count--;
|
||||
tmp->next = keyblock_free;
|
||||
keyblock_free = blocks;
|
||||
blockdata_count--;
|
||||
}
|
||||
}
|
||||
|
||||
/* copy blocks into data[], return 1 if data[] unchanged by so doing */
|
||||
int blockdata_retrieve(struct blockdata *block, size_t len, void *data)
|
||||
{
|
||||
size_t blen;
|
||||
struct blockdata *b;
|
||||
int match = 1;
|
||||
|
||||
for (b = block; len > 0 && b; b = b->next)
|
||||
{
|
||||
blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len;
|
||||
if (memcmp(data, b->key, blen) != 0)
|
||||
match = 0;
|
||||
memcpy(data, b->key, blen);
|
||||
data += blen;
|
||||
len -= blen;
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
140
src/cache.c
140
src/cache.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -25,9 +25,6 @@ static int cache_inserted = 0, cache_live_freed = 0, insert_error;
|
||||
static union bigname *big_free = NULL;
|
||||
static int bignames_left, hash_size;
|
||||
static int uid = 1;
|
||||
#ifdef HAVE_DNSSEC
|
||||
static struct keydata *keyblock_free = NULL;
|
||||
#endif
|
||||
|
||||
/* type->string mapping: this is also used by the name-hash function as a mixing table. */
|
||||
static const struct {
|
||||
@@ -56,6 +53,8 @@ static const struct {
|
||||
{ 38, "A6" },
|
||||
{ 39, "DNAME" },
|
||||
{ 41, "OPT" },
|
||||
{ 43, "DS" },
|
||||
{ 46, "RRSIG" },
|
||||
{ 48, "DNSKEY" },
|
||||
{ 249, "TKEY" },
|
||||
{ 250, "TSIG" },
|
||||
@@ -198,7 +197,7 @@ static void cache_free(struct crec *crecp)
|
||||
}
|
||||
#ifdef HAVE_DNSSEC
|
||||
else if (crecp->flags & (F_DNSKEY | F_DS))
|
||||
keydata_free(crecp->addr.key.keydata);
|
||||
blockdata_free(crecp->addr.key.keydata);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -693,7 +692,7 @@ static void add_hosts_cname(struct crec *target)
|
||||
if (hostname_isequal(cache_get_name(target), a->target) &&
|
||||
(crec = whine_malloc(sizeof(struct crec))))
|
||||
{
|
||||
crec->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_CONFIG | F_CNAME;
|
||||
crec->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_CONFIG | F_CNAME | F_DNSSECOK;
|
||||
crec->name.namep = a->alias;
|
||||
crec->addr.cname.target.cache = target;
|
||||
crec->addr.cname.uid = target->uid;
|
||||
@@ -830,14 +829,14 @@ static int read_hostsfile(char *filename, int index, int cache_size, struct crec
|
||||
|
||||
if (inet_pton(AF_INET, token, &addr) > 0)
|
||||
{
|
||||
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
|
||||
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_DNSSECOK;
|
||||
addrlen = INADDRSZ;
|
||||
domain_suffix = get_domain(addr.addr.addr4);
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
else if (inet_pton(AF_INET6, token, &addr) > 0)
|
||||
{
|
||||
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
|
||||
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_DNSSECOK;
|
||||
addrlen = IN6ADDRSZ;
|
||||
domain_suffix = get_domain6(&addr.addr.addr6);
|
||||
}
|
||||
@@ -916,12 +915,19 @@ void cache_reload(void)
|
||||
struct name_list *nl;
|
||||
struct cname *a;
|
||||
struct interface_name *intr;
|
||||
#ifdef HAVE_DNSSEC
|
||||
struct dnskey *key;
|
||||
#endif
|
||||
|
||||
cache_inserted = cache_live_freed = 0;
|
||||
|
||||
for (i=0; i<hash_size; i++)
|
||||
for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp)
|
||||
{
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (cache->flags & (F_DNSKEY | F_DS))
|
||||
blockdata_free(cache->addr.key.keydata);
|
||||
#endif
|
||||
tmp = cache->hash_next;
|
||||
if (cache->flags & (F_HOSTS | F_CONFIG))
|
||||
{
|
||||
@@ -948,13 +954,27 @@ void cache_reload(void)
|
||||
if (hostname_isequal(a->target, intr->name) &&
|
||||
((cache = whine_malloc(sizeof(struct crec)))))
|
||||
{
|
||||
cache->flags = F_FORWARD | F_NAMEP | F_CNAME | F_IMMORTAL | F_CONFIG;
|
||||
cache->flags = F_FORWARD | F_NAMEP | F_CNAME | F_IMMORTAL | F_CONFIG | F_DNSSECOK;
|
||||
cache->name.namep = a->alias;
|
||||
cache->addr.cname.target.int_name = intr;
|
||||
cache->addr.cname.uid = -1;
|
||||
cache_hash(cache);
|
||||
add_hosts_cname(cache); /* handle chains */
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
for (key = daemon->dnskeys; key; key = key->next)
|
||||
if ((cache = whine_malloc(sizeof(struct crec))) &&
|
||||
(cache->addr.key.keydata = blockdata_alloc(key->key, key->keylen)))
|
||||
{
|
||||
cache->flags = F_FORWARD | F_IMMORTAL | F_DNSKEY | F_CONFIG | F_NAMEP;
|
||||
cache->name.namep = key->name;
|
||||
cache->uid = key->keylen;
|
||||
cache->addr.key.algo = key->algo;
|
||||
cache->addr.key.keytag = dnskey_keytag(key->algo, key->flags, (unsigned char *)key->key, key->keylen);
|
||||
cache_hash(cache);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* borrow the packet buffer for a temporary by-address hash */
|
||||
memset(daemon->packet, 0, daemon->packet_buff_sz);
|
||||
@@ -970,7 +990,7 @@ void cache_reload(void)
|
||||
(cache = whine_malloc(sizeof(struct crec))))
|
||||
{
|
||||
cache->name.namep = nl->name;
|
||||
cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_NAMEP | F_CONFIG;
|
||||
cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_NAMEP | F_CONFIG | F_DNSSECOK;
|
||||
add_hosts_entry(cache, (struct all_addr *)&hr->addr, INADDRSZ, 0, (struct crec **)daemon->packet, revhashsz);
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
@@ -978,7 +998,7 @@ void cache_reload(void)
|
||||
(cache = whine_malloc(sizeof(struct crec))))
|
||||
{
|
||||
cache->name.namep = nl->name;
|
||||
cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_NAMEP | F_CONFIG;
|
||||
cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_NAMEP | F_CONFIG | F_DNSSECOK;
|
||||
add_hosts_entry(cache, (struct all_addr *)&hr->addr6, IN6ADDRSZ, 0, (struct crec **)daemon->packet, revhashsz);
|
||||
}
|
||||
#endif
|
||||
@@ -1048,7 +1068,7 @@ static void add_dhcp_cname(struct crec *target, time_t ttd)
|
||||
|
||||
if (aliasc)
|
||||
{
|
||||
aliasc->flags = F_FORWARD | F_NAMEP | F_DHCP | F_CNAME | F_CONFIG;
|
||||
aliasc->flags = F_FORWARD | F_NAMEP | F_DHCP | F_CNAME | F_CONFIG | F_DNSSECOK;
|
||||
if (ttd == 0)
|
||||
aliasc->flags |= F_IMMORTAL;
|
||||
else
|
||||
@@ -1136,7 +1156,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
|
||||
|
||||
if (crec) /* malloc may fail */
|
||||
{
|
||||
crec->flags = flags | F_NAMEP | F_DHCP | F_FORWARD;
|
||||
crec->flags = flags | F_NAMEP | F_DHCP | F_FORWARD | F_DNSSECOK;
|
||||
if (ttd == 0)
|
||||
crec->flags |= F_IMMORTAL;
|
||||
else
|
||||
@@ -1164,6 +1184,9 @@ void dump_cache(time_t now)
|
||||
#ifdef HAVE_AUTH
|
||||
my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->auth_answer);
|
||||
#endif
|
||||
#ifdef HAVE_DNSSEC
|
||||
blockdata_report();
|
||||
#endif
|
||||
|
||||
/* sum counts from different records for same server */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
@@ -1197,16 +1220,13 @@ void dump_cache(time_t now)
|
||||
for (i=0; i<hash_size; i++)
|
||||
for (cache = hash_table[i]; cache; cache = cache->hash_next)
|
||||
{
|
||||
char *a, *p = daemon->namebuff;
|
||||
p += sprintf(p, "%-40.40s ", cache_get_name(cache));
|
||||
if ((cache->flags & F_NEG) && (cache->flags & F_FORWARD))
|
||||
a = "";
|
||||
else if (cache->flags & F_CNAME)
|
||||
{
|
||||
a = "";
|
||||
if (!is_outdated_cname_pointer(cache))
|
||||
a = cache_get_cname_target(cache);
|
||||
}
|
||||
char *a = daemon->addrbuff, *p = daemon->namebuff, *n = cache_get_name(cache);
|
||||
*a = 0;
|
||||
if (strlen(n) == 0)
|
||||
n = "<Root>";
|
||||
p += sprintf(p, "%-40.40s ", n);
|
||||
if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))
|
||||
a = cache_get_cname_target(cache);
|
||||
#ifdef HAVE_DNSSEC
|
||||
else if (cache->flags & F_DNSKEY)
|
||||
{
|
||||
@@ -1216,11 +1236,11 @@ void dump_cache(time_t now)
|
||||
else if (cache->flags & F_DS)
|
||||
{
|
||||
a = daemon->addrbuff;
|
||||
sprintf(a, "%5u %3u %3u %u", cache->addr.key.flags_or_keyid,
|
||||
cache->addr.key.algo, cache->addr.key.digest, cache->uid);
|
||||
sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
|
||||
cache->addr.key.algo, cache->addr.key.digest);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD))
|
||||
{
|
||||
a = daemon->addrbuff;
|
||||
if (cache->flags & F_IPV4)
|
||||
@@ -1291,13 +1311,20 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
|
||||
|
||||
if (addr)
|
||||
{
|
||||
if (flags & F_KEYTAG)
|
||||
sprintf(daemon->addrbuff, arg, addr->addr.keytag);
|
||||
else
|
||||
{
|
||||
#ifdef HAVE_IPV6
|
||||
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
|
||||
addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
|
||||
addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
#else
|
||||
strncpy(daemon->addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);
|
||||
strncpy(daemon->addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
dest = arg;
|
||||
|
||||
if (flags & F_REVERSE)
|
||||
{
|
||||
@@ -1339,6 +1366,8 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
|
||||
source = arg;
|
||||
else if (flags & F_UPSTREAM)
|
||||
source = "reply";
|
||||
else if (flags & F_SECSTAT)
|
||||
source = "validation";
|
||||
else if (flags & F_AUTH)
|
||||
source = "auth";
|
||||
else if (flags & F_SERVER)
|
||||
@@ -1351,6 +1380,11 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
|
||||
source = arg;
|
||||
verb = "from";
|
||||
}
|
||||
else if (flags & F_DNSSEC)
|
||||
{
|
||||
source = arg;
|
||||
verb = "to";
|
||||
}
|
||||
else
|
||||
source = "cached";
|
||||
|
||||
@@ -1360,50 +1394,4 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
|
||||
my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
struct keydata *keydata_alloc(char *data, size_t len)
|
||||
{
|
||||
struct keydata *block, *ret = NULL;
|
||||
struct keydata **prev = &ret;
|
||||
while (len > 0)
|
||||
{
|
||||
if (keyblock_free)
|
||||
{
|
||||
block = keyblock_free;
|
||||
keyblock_free = block->next;
|
||||
}
|
||||
else
|
||||
block = whine_malloc(sizeof(struct keydata));
|
||||
|
||||
if (!block)
|
||||
{
|
||||
/* failed to alloc, free partial chain */
|
||||
keydata_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(block->key, data, len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len);
|
||||
data += KEYBLOCK_LEN;
|
||||
len -= KEYBLOCK_LEN;
|
||||
*prev = block;
|
||||
prev = &block->next;
|
||||
block->next = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void keydata_free(struct keydata *blocks)
|
||||
{
|
||||
struct keydata *tmp;
|
||||
|
||||
if (blocks)
|
||||
{
|
||||
for (tmp = blocks; tmp->next; tmp = tmp->next);
|
||||
tmp->next = keyblock_free;
|
||||
keyblock_free = blocks;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
12
src/config.h
12
src/config.h
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -18,7 +18,7 @@
|
||||
#define MAX_PROCS 20 /* max no children for TCP requests */
|
||||
#define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */
|
||||
#define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */
|
||||
#define KEYBLOCK_LEN 140 /* choose to mininise fragmentation when storing DNSSEC keys */
|
||||
#define KEYBLOCK_LEN 35 /* choose to mininise fragmentation when storing DNSSEC keys */
|
||||
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
|
||||
#define FORWARD_TEST 50 /* try all servers every 50 queries */
|
||||
#define FORWARD_TIME 20 /* or 20 seconds */
|
||||
@@ -139,6 +139,7 @@ RESOLVFILE
|
||||
/* #define HAVE_DBUS */
|
||||
/* #define HAVE_IDN */
|
||||
/* #define HAVE_CONNTRACK */
|
||||
/* #define HAVE_DNSSEC */
|
||||
|
||||
|
||||
/* Default locations for important system files. */
|
||||
@@ -384,7 +385,12 @@ static char *compile_opts =
|
||||
#ifndef HAVE_AUTH
|
||||
"no-"
|
||||
#endif
|
||||
"auth";
|
||||
"auth "
|
||||
#ifndef HAVE_DNSSEC
|
||||
"no-"
|
||||
#endif
|
||||
"DNSSEC";
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
13
src/dhcp6.c
13
src/dhcp6.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -23,7 +23,7 @@
|
||||
struct iface_param {
|
||||
struct dhcp_context *current;
|
||||
struct dhcp_relay *relay;
|
||||
struct in6_addr fallback, relay_local;
|
||||
struct in6_addr fallback, relay_local, ll_addr, ula_addr;
|
||||
int ind, addr_match;
|
||||
};
|
||||
|
||||
@@ -158,6 +158,8 @@ void dhcp6_packet(time_t now)
|
||||
parm.ind = if_index;
|
||||
parm.addr_match = 0;
|
||||
memset(&parm.fallback, 0, IN6ADDRSZ);
|
||||
memset(&parm.ll_addr, 0, IN6ADDRSZ);
|
||||
memset(&parm.ula_addr, 0, IN6ADDRSZ);
|
||||
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
|
||||
@@ -210,7 +212,7 @@ void dhcp6_packet(time_t now)
|
||||
lease_prune(NULL, now); /* lose any expired leases */
|
||||
|
||||
port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
|
||||
sz, &from.sin6_addr, now);
|
||||
&parm.ll_addr, &parm.ula_addr, sz, &from.sin6_addr, now);
|
||||
|
||||
lease_update_file(now);
|
||||
lease_update_dns(0);
|
||||
@@ -309,6 +311,11 @@ static int complete_context6(struct in6_addr *local, int prefix,
|
||||
|
||||
if (if_index == param->ind)
|
||||
{
|
||||
if (IN6_IS_ADDR_LINKLOCAL(local))
|
||||
param->ll_addr = *local;
|
||||
else if (IN6_IS_ADDR_ULA(local))
|
||||
param->ula_addr = *local;
|
||||
|
||||
if (!IN6_IS_ADDR_LOOPBACK(local) &&
|
||||
!IN6_IS_ADDR_LINKLOCAL(local) &&
|
||||
!IN6_IS_ADDR_MULTICAST(local))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -39,17 +39,34 @@
|
||||
#define C_ANY 255 /* wildcard match */
|
||||
|
||||
#define T_A 1
|
||||
#define T_NS 2
|
||||
#define T_NS 2
|
||||
#define T_MD 3
|
||||
#define T_MF 4
|
||||
#define T_CNAME 5
|
||||
#define T_SOA 6
|
||||
#define T_MB 7
|
||||
#define T_MG 8
|
||||
#define T_MR 9
|
||||
#define T_PTR 12
|
||||
#define T_MINFO 14
|
||||
#define T_MX 15
|
||||
#define T_TXT 16
|
||||
#define T_RP 17
|
||||
#define T_AFSDB 18
|
||||
#define T_RT 21
|
||||
#define T_SIG 24
|
||||
#define T_PX 26
|
||||
#define T_AAAA 28
|
||||
#define T_NXT 30
|
||||
#define T_SRV 33
|
||||
#define T_NAPTR 35
|
||||
#define T_KX 36
|
||||
#define T_DNAME 39
|
||||
#define T_OPT 41
|
||||
#define T_DS 43
|
||||
#define T_RRSIG 46
|
||||
#define T_NSEC 47
|
||||
#define T_DNSKEY 48
|
||||
#define T_TKEY 249
|
||||
#define T_TSIG 250
|
||||
#define T_AXFR 252
|
||||
@@ -78,6 +95,8 @@ struct dns_header {
|
||||
#define HB4_RCODE 0x0f
|
||||
|
||||
#define OPCODE(x) (((x)->hb3 & HB3_OPCODE) >> 3)
|
||||
#define SET_OPCODE(x, code) (x)->hb3 = ((x)->hb3 & ~HB3_OPCODE) | code
|
||||
|
||||
#define RCODE(x) ((x)->hb4 & HB4_RCODE)
|
||||
#define SET_RCODE(x, code) (x)->hb4 = ((x)->hb4 & ~HB4_RCODE) | code
|
||||
|
||||
@@ -117,3 +136,26 @@ struct dns_header {
|
||||
(cp) += 4; \
|
||||
}
|
||||
|
||||
#define CHECKED_GETCHAR(var, ptr, len) do { \
|
||||
if ((len) < 1) return 0; \
|
||||
var = *ptr++; \
|
||||
(len) -= 1; \
|
||||
} while (0)
|
||||
|
||||
#define CHECKED_GETSHORT(var, ptr, len) do { \
|
||||
if ((len) < 2) return 0; \
|
||||
GETSHORT(var, ptr); \
|
||||
(len) -= 2; \
|
||||
} while (0)
|
||||
|
||||
#define CHECKED_GETLONG(var, ptr, len) do { \
|
||||
if ((len) < 4) return 0; \
|
||||
GETLONG(var, ptr); \
|
||||
(len) -= 4; \
|
||||
} while (0)
|
||||
|
||||
#define CHECK_LEN(header, pp, plen, len) \
|
||||
((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))
|
||||
|
||||
#define ADD_RDLEN(header, pp, plen, len) \
|
||||
(!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -81,15 +81,25 @@ int main (int argc, char **argv)
|
||||
umask(022); /* known umask, create leases and pid files as 0644 */
|
||||
|
||||
read_opts(argc, argv, compile_opts);
|
||||
|
||||
|
||||
if (daemon->edns_pktsz < PACKETSZ)
|
||||
daemon->edns_pktsz = PACKETSZ;
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* Enforce min packet big enough for DNSSEC */
|
||||
if (option_bool(OPT_DNSSEC_VALID) && daemon->edns_pktsz < EDNS_PKTSZ)
|
||||
daemon->edns_pktsz = EDNS_PKTSZ;
|
||||
#endif
|
||||
|
||||
daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ?
|
||||
daemon->edns_pktsz : DNSMASQ_PACKETSZ;
|
||||
daemon->packet = safe_malloc(daemon->packet_buff_sz);
|
||||
|
||||
|
||||
daemon->addrbuff = safe_malloc(ADDRSTRLEN);
|
||||
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
daemon->keyname = safe_malloc(MAXDNAME);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
if (!daemon->lease_file)
|
||||
@@ -131,6 +141,11 @@ int main (int argc, char **argv)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (daemon->cachesize <CACHESIZ && option_bool(OPT_DNSSEC_VALID))
|
||||
die(_("Cannot reduce cache size from default when DNSSEC enabled"), NULL, EC_BADCONF);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_TFTP
|
||||
if (option_bool(OPT_TFTP))
|
||||
die(_("TFTP server not available: set HAVE_TFTP in src/config.h"), NULL, EC_BADCONF);
|
||||
@@ -1312,7 +1327,7 @@ static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
|
||||
|
||||
/* will we be able to get memory? */
|
||||
if (daemon->port != 0)
|
||||
get_new_frec(now, &wait);
|
||||
get_new_frec(now, &wait, 0);
|
||||
|
||||
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -14,7 +14,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define COPYRIGHT "Copyright (c) 2000-2013 Simon Kelley"
|
||||
#define COPYRIGHT "Copyright (c) 2000-2014 Simon Kelley"
|
||||
|
||||
#ifndef NO_LARGEFILE
|
||||
/* Ensure we can use files >2GB (log files may grow this big) */
|
||||
@@ -50,12 +50,16 @@
|
||||
#include <getopt.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "ip6addr.h"
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
|
||||
#define countof(x) (long)(sizeof(x) / sizeof(x[0]))
|
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
#include "dns-protocol.h"
|
||||
#include "dhcp-protocol.h"
|
||||
#ifdef HAVE_DHCP6
|
||||
@@ -213,7 +217,7 @@ struct event_desc {
|
||||
#define OPT_NO_OVERRIDE 30
|
||||
#define OPT_NO_REBIND 31
|
||||
#define OPT_ADD_MAC 32
|
||||
#define OPT_DNSSEC 33
|
||||
#define OPT_DNSSEC_PROXY 33
|
||||
#define OPT_CONSEC_ADDR 34
|
||||
#define OPT_CONNTRACK 35
|
||||
#define OPT_FQDN_UPDATE 36
|
||||
@@ -225,7 +229,8 @@ struct event_desc {
|
||||
#define OPT_QUIET_DHCP 42
|
||||
#define OPT_QUIET_DHCP6 43
|
||||
#define OPT_QUIET_RA 44
|
||||
#define OPT_LAST 45
|
||||
#define OPT_DNSSEC_VALID 45
|
||||
#define OPT_LAST 46
|
||||
|
||||
/* extra flags for my_syslog, we use a couple of facilities since they are known
|
||||
not to occupy the same bits as priorities, no matter how syslog.h is set up. */
|
||||
@@ -238,6 +243,7 @@ struct all_addr {
|
||||
#ifdef HAVE_IPV6
|
||||
struct in6_addr addr6;
|
||||
#endif
|
||||
unsigned int keytag;
|
||||
} addr;
|
||||
};
|
||||
|
||||
@@ -282,6 +288,12 @@ struct cname {
|
||||
struct cname *next;
|
||||
};
|
||||
|
||||
struct dnskey {
|
||||
char *name, *key;
|
||||
int keylen, algo, flags;
|
||||
struct dnskey *next;
|
||||
};
|
||||
|
||||
#define ADDRLIST_LITERAL 1
|
||||
#define ADDRLIST_IPV6 2
|
||||
|
||||
@@ -331,8 +343,8 @@ union bigname {
|
||||
union bigname *next; /* freelist */
|
||||
};
|
||||
|
||||
struct keydata {
|
||||
struct keydata *next;
|
||||
struct blockdata {
|
||||
struct blockdata *next;
|
||||
unsigned char key[KEYBLOCK_LEN];
|
||||
};
|
||||
|
||||
@@ -349,14 +361,14 @@ struct crec {
|
||||
int uid; /* -1 if union is interface-name */
|
||||
} cname;
|
||||
struct {
|
||||
struct keydata *keydata;
|
||||
struct blockdata *keydata;
|
||||
unsigned char algo;
|
||||
unsigned char digest; /* DS only */
|
||||
unsigned short flags_or_keyid; /* flags for DNSKEY, keyid for DS */
|
||||
unsigned short keytag;
|
||||
} key;
|
||||
} addr;
|
||||
time_t ttd; /* time to die */
|
||||
/* used as keylen if F_DS or F_DNSKEY, index to source for F_HOSTS */
|
||||
/* used as keylen ifF_DNSKEY, index to source for F_HOSTS */
|
||||
int uid;
|
||||
unsigned short flags;
|
||||
union {
|
||||
@@ -391,6 +403,9 @@ struct crec {
|
||||
#define F_QUERY (1u<<19)
|
||||
#define F_NOERR (1u<<20)
|
||||
#define F_AUTH (1u<<21)
|
||||
#define F_DNSSEC (1u<<22)
|
||||
#define F_KEYTAG (1u<<23)
|
||||
#define F_SECSTAT (1u<<24)
|
||||
|
||||
/* composites */
|
||||
#define F_TYPE (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS) /* Only one may be set */
|
||||
@@ -496,9 +511,19 @@ struct hostsfile {
|
||||
int index; /* matches to cache entries for logging */
|
||||
};
|
||||
|
||||
|
||||
/* DNSSEC status values. */
|
||||
#define STAT_SECURE 1
|
||||
#define STAT_INSECURE 2
|
||||
#define STAT_BOGUS 3
|
||||
#define STAT_NEED_DS 4
|
||||
#define STAT_NEED_KEY 5
|
||||
|
||||
#define FREC_NOREBIND 1
|
||||
#define FREC_CHECKING_DISABLED 2
|
||||
#define FREC_HAS_SUBNET 4
|
||||
#define FREC_DNSKEY_QUERY 8
|
||||
#define FREC_DS_QUERY 16
|
||||
|
||||
struct frec {
|
||||
union mysockaddr source;
|
||||
@@ -513,6 +538,13 @@ struct frec {
|
||||
int fd, forwardall, flags;
|
||||
unsigned int crc;
|
||||
time_t time;
|
||||
#ifdef HAVE_DNSSEC
|
||||
int class;
|
||||
struct blockdata *stash; /* Saved reply, whilst we validate */
|
||||
size_t stash_len;
|
||||
struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */
|
||||
struct frec *blocking_query; /* Query which is blocking us. */
|
||||
#endif
|
||||
struct frec *next;
|
||||
};
|
||||
|
||||
@@ -875,11 +907,17 @@ extern struct daemon {
|
||||
#ifdef OPTION6_PREFIX_CLASS
|
||||
struct prefix_class *prefix_classes;
|
||||
#endif
|
||||
#ifdef HAVE_DNSSEC
|
||||
struct dnskey *dnskeys;
|
||||
#endif
|
||||
|
||||
/* globally used stuff for DNS */
|
||||
char *packet; /* packet buffer */
|
||||
int packet_buff_sz; /* size of above */
|
||||
char *namebuff; /* MAXDNAME size buffer */
|
||||
#ifdef HAVE_DNSSEC
|
||||
char *keyname; /* MAXDNAME size buffer */
|
||||
#endif
|
||||
unsigned int local_answer, queries_forwarded, auth_answer;
|
||||
struct frec *frec_list;
|
||||
struct serverfd *sfds;
|
||||
@@ -950,9 +988,14 @@ void dump_cache(time_t now);
|
||||
char *cache_get_name(struct crec *crecp);
|
||||
char *cache_get_cname_target(struct crec *crecp);
|
||||
struct crec *cache_enumerate(int init);
|
||||
|
||||
/* blockdata.c */
|
||||
#ifdef HAVE_DNSSEC
|
||||
struct keydata *keydata_alloc(char *data, size_t len);
|
||||
void keydata_free(struct keydata *blocks);
|
||||
void blockdata_report(void);
|
||||
struct blockdata *blockdata_alloc(char *data, size_t len);
|
||||
size_t blockdata_walk(struct blockdata **key, unsigned char **p, size_t cnt);
|
||||
int blockdata_retrieve(struct blockdata *block, size_t len, void *data);
|
||||
void blockdata_free(struct blockdata *blocks);
|
||||
#endif
|
||||
|
||||
/* domain.c */
|
||||
@@ -964,6 +1007,10 @@ int is_name_synthetic(int flags, char *name, struct all_addr *addr);
|
||||
int is_rev_synth(int flag, struct all_addr *addr, char *name);
|
||||
|
||||
/* rfc1035.c */
|
||||
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
char *name, int isExtract, int extrabytes);
|
||||
unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes);
|
||||
unsigned char *skip_questions(struct dns_header *header, size_t plen);
|
||||
unsigned int extract_request(struct dns_header *header, size_t qlen,
|
||||
char *name, unsigned short *typep);
|
||||
size_t setup_reply(struct dns_header *header, size_t qlen,
|
||||
@@ -971,7 +1018,7 @@ size_t setup_reply(struct dns_header *header, size_t qlen,
|
||||
unsigned long local_ttl);
|
||||
int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff,
|
||||
time_t now, char **ipsets, int is_sign, int checkrebind,
|
||||
int checking_disabled);
|
||||
int no_cache, int secure);
|
||||
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
struct in_addr local_addr, struct in_addr local_netmask, time_t now);
|
||||
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
|
||||
@@ -984,6 +1031,9 @@ size_t resize_packet(struct dns_header *header, size_t plen,
|
||||
unsigned char *pheader, size_t hlen);
|
||||
size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3);
|
||||
size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source);
|
||||
#ifdef HAVE_DNSSEC
|
||||
size_t add_do_bit(struct dns_header *header, size_t plen, char *limit);
|
||||
#endif
|
||||
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer);
|
||||
int add_resource_record(struct dns_header *header, char *limit, int *truncp,
|
||||
int nameoffset, unsigned char **pp, unsigned long ttl,
|
||||
@@ -1001,6 +1051,13 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen,
|
||||
int in_zone(struct auth_zone *zone, char *name, char **cut);
|
||||
#endif
|
||||
|
||||
/* dnssec.c */
|
||||
size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr);
|
||||
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char *name, char *keyname, int class);
|
||||
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
|
||||
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class);
|
||||
int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
|
||||
|
||||
/* util.c */
|
||||
void rand_init(void);
|
||||
unsigned short rand16(void);
|
||||
@@ -1026,6 +1083,9 @@ void prettyprint_time(char *buf, unsigned int t);
|
||||
int prettyprint_addr(union mysockaddr *addr, char *buf);
|
||||
int parse_hex(char *in, unsigned char *out, int maxlen,
|
||||
unsigned int *wildcard_mask, int *mac_type);
|
||||
#ifdef HAVE_DNSSEC
|
||||
int parse_base64(char *in, char *out);
|
||||
#endif
|
||||
int memcmp_masked(unsigned char *a, unsigned char *b, int len,
|
||||
unsigned int mask);
|
||||
int expand_buf(struct iovec *iov, size_t size);
|
||||
@@ -1061,7 +1121,7 @@ void receive_query(struct listener *listen, time_t now);
|
||||
unsigned char *tcp_request(int confd, time_t now,
|
||||
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
|
||||
void server_gone(struct server *server);
|
||||
struct frec *get_new_frec(time_t now, int *wait);
|
||||
struct frec *get_new_frec(time_t now, int *wait, int force);
|
||||
int send_from(int fd, int nowild, char *packet, size_t len,
|
||||
union mysockaddr *to, struct all_addr *source,
|
||||
unsigned int iface);
|
||||
@@ -1255,7 +1315,8 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac,
|
||||
/* rfc3315.c */
|
||||
#ifdef HAVE_DHCP6
|
||||
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
|
||||
struct in6_addr *fallback, size_t sz, struct in6_addr *client_addr, time_t now);
|
||||
struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
|
||||
size_t sz, struct in6_addr *client_addr, time_t now);
|
||||
void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id);
|
||||
|
||||
unsigned short relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface);
|
||||
|
||||
83
src/dnssec-crypto.h
Normal file
83
src/dnssec-crypto.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/* dnssec-crypto.h is Copyright (c) 2012 Giovanni Bajo <rasky@develer.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991, or
|
||||
(at your option) version 3 dated 29 June, 2007.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef DNSSEC_CRYPTO_H
|
||||
#define DNSSEC_CRYPTO_H
|
||||
|
||||
struct blockdata;
|
||||
|
||||
/*
|
||||
* vtable for a signature verification algorithm.
|
||||
*
|
||||
* Each algorithm verifies that a certain signature over a (possibly non-contigous)
|
||||
* array of data has been made with the specified key.
|
||||
*
|
||||
* Sample of usage:
|
||||
*
|
||||
* // First, set the signature we need to check. Notice: data is not copied
|
||||
* // nor consumed, so the pointer must stay valid.
|
||||
* alg->set_signature(sig, 16);
|
||||
*
|
||||
* // Second, get push the data through the corresponding digest algorithm;
|
||||
* // data is consumed immediately, so the buffers can be freed or modified.
|
||||
* digestalg_begin(alg->get_digestalgo());
|
||||
* digestalg_add_data(buf1, 123);
|
||||
* digestalg_add_data(buf2, 45);
|
||||
* digestalg_add_data(buf3, 678);
|
||||
* alg->set_digest(digestalg_final());
|
||||
*
|
||||
* // Third, verify if we got the correct key for this signature.
|
||||
* alg->verify(key1, 16);
|
||||
* alg->verify(key2, 16);
|
||||
*/
|
||||
|
||||
typedef struct VerifyAlgCtx VerifyAlgCtx;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int digest_algo;
|
||||
int (*verify)(VerifyAlgCtx *ctx, struct blockdata *key, unsigned key_len);
|
||||
} VerifyAlg;
|
||||
|
||||
struct VerifyAlgCtx
|
||||
{
|
||||
const VerifyAlg *vtbl;
|
||||
unsigned char *sig;
|
||||
size_t siglen;
|
||||
unsigned char digest[64]; /* TODO: if memory problems, use VLA */
|
||||
};
|
||||
|
||||
int verifyalg_supported(int algo);
|
||||
VerifyAlgCtx* verifyalg_alloc(int algo);
|
||||
void verifyalg_free(VerifyAlgCtx *a);
|
||||
int verifyalg_algonum(VerifyAlgCtx *a);
|
||||
|
||||
/* Functions to calculate the digest of a key */
|
||||
|
||||
/* RFC4034 digest algorithms */
|
||||
#define DIGESTALG_SHA1 1
|
||||
#define DIGESTALG_SHA256 2
|
||||
#define DIGESTALG_MD5 256
|
||||
#define DIGESTALG_SHA512 257
|
||||
|
||||
int digestalg_supported(int algo);
|
||||
void digestalg_begin(int algo);
|
||||
void digestalg_add_data(void *data, unsigned len);
|
||||
void digestalg_add_keydata(struct blockdata *key, size_t len);
|
||||
unsigned char *digestalg_final(void);
|
||||
int digestalg_len(void);
|
||||
|
||||
#endif /* DNSSEC_CRYPTO_H */
|
||||
316
src/dnssec-openssl.c
Normal file
316
src/dnssec-openssl.c
Normal file
@@ -0,0 +1,316 @@
|
||||
/* dnssec-openssl.c is Copyright (c) 2012 Giovanni Bajo <rasky@develer.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991, or
|
||||
(at your option) version 3 dated 29 June, 2007.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
|
||||
#include "dnssec-crypto.h"
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/dsa.h>
|
||||
#include <openssl/err.h>
|
||||
#include <string.h>
|
||||
|
||||
#define POOL_SIZE 1
|
||||
static union _Pool
|
||||
{
|
||||
VerifyAlgCtx ctx;
|
||||
} Pool[POOL_SIZE];
|
||||
static char pool_used = 0;
|
||||
|
||||
static void print_hex(unsigned char *data, unsigned len)
|
||||
{
|
||||
while (len > 0)
|
||||
{
|
||||
printf("%02x", *data++);
|
||||
--len;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static int keydata_to_bn(BIGNUM *ret, struct blockdata **key_data, unsigned char **p, unsigned len)
|
||||
{
|
||||
size_t cnt;
|
||||
BIGNUM temp;
|
||||
|
||||
BN_init(ret);
|
||||
|
||||
cnt = blockdata_walk(key_data, p, len);
|
||||
BN_bin2bn(*p, cnt, ret);
|
||||
len -= cnt;
|
||||
*p += cnt;
|
||||
while (len > 0)
|
||||
{
|
||||
if (!(cnt = blockdata_walk(key_data, p, len)))
|
||||
return 0;
|
||||
BN_lshift(ret, ret, cnt*8);
|
||||
BN_init(&temp);
|
||||
BN_bin2bn(*p, cnt, &temp);
|
||||
BN_add(ret, ret, &temp);
|
||||
len -= cnt;
|
||||
*p += cnt;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int rsasha1_parse_key(BIGNUM *exp, BIGNUM *mod, struct blockdata *key_data, unsigned key_len)
|
||||
{
|
||||
unsigned char *p = key_data->key;
|
||||
size_t exp_len, mod_len;
|
||||
|
||||
CHECKED_GETCHAR(exp_len, p, key_len);
|
||||
if (exp_len == 0)
|
||||
CHECKED_GETSHORT(exp_len, p, key_len);
|
||||
if (exp_len >= key_len)
|
||||
return 0;
|
||||
mod_len = key_len - exp_len;
|
||||
|
||||
return keydata_to_bn(exp, &key_data, &p, exp_len) &&
|
||||
keydata_to_bn(mod, &key_data, &p, mod_len);
|
||||
}
|
||||
|
||||
static int dsasha1_parse_key(BIGNUM *Q, BIGNUM *P, BIGNUM *G, BIGNUM *Y, struct blockdata *key_data, unsigned key_len)
|
||||
{
|
||||
unsigned char *p = key_data->key;
|
||||
int T;
|
||||
|
||||
CHECKED_GETCHAR(T, p, key_len);
|
||||
return
|
||||
keydata_to_bn(Q, &key_data, &p, 20) &&
|
||||
keydata_to_bn(P, &key_data, &p, 64+T*8) &&
|
||||
keydata_to_bn(G, &key_data, &p, 64+T*8) &&
|
||||
keydata_to_bn(Y, &key_data, &p, 64+T*8);
|
||||
}
|
||||
|
||||
static int rsa_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len, int nid, int dlen)
|
||||
{
|
||||
int validated = 0;
|
||||
|
||||
RSA *rsa = RSA_new();
|
||||
rsa->e = BN_new();
|
||||
rsa->n = BN_new();
|
||||
if (rsasha1_parse_key(rsa->e, rsa->n, key_data, key_len)
|
||||
&& RSA_verify(nid, ctx->digest, dlen, ctx->sig, ctx->siglen, rsa))
|
||||
validated = 1;
|
||||
|
||||
RSA_free(rsa);
|
||||
return validated;
|
||||
}
|
||||
|
||||
static int rsamd5_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len)
|
||||
{
|
||||
return rsa_verify(ctx, key_data, key_len, NID_md5, 16);
|
||||
}
|
||||
|
||||
static int rsasha1_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len)
|
||||
{
|
||||
return rsa_verify(ctx, key_data, key_len, NID_sha1, 20);
|
||||
}
|
||||
|
||||
static int rsasha256_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len)
|
||||
{
|
||||
return rsa_verify(ctx, key_data, key_len, NID_sha256, 32);
|
||||
}
|
||||
|
||||
static int rsasha512_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len)
|
||||
{
|
||||
return rsa_verify(ctx, key_data, key_len, NID_sha512, 64);
|
||||
}
|
||||
|
||||
static int dsasha1_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len)
|
||||
{
|
||||
static unsigned char asn1_signature[] =
|
||||
{
|
||||
0x30, 0x2E, // sequence
|
||||
0x02, 21, // large integer (21 bytes)
|
||||
0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // R
|
||||
0x02, 21, // large integer (21 bytes)
|
||||
0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // S
|
||||
};
|
||||
int validated = 0;
|
||||
|
||||
/* A DSA signature is made of 2 bignums (R & S). We could parse them manually with BN_bin2bn(),
|
||||
but OpenSSL does not have an API to verify a DSA signature given R and S, and insists
|
||||
in having a ASN.1 BER sequence (as per RFC3279).
|
||||
We prepare a hard-coded ASN.1 sequence, and just fill in the R&S numbers in it. */
|
||||
memcpy(asn1_signature+5, ctx->sig+1, 20);
|
||||
memcpy(asn1_signature+28, ctx->sig+21, 20);
|
||||
|
||||
DSA *dsa = DSA_new();
|
||||
dsa->q = BN_new();
|
||||
dsa->p = BN_new();
|
||||
dsa->g = BN_new();
|
||||
dsa->pub_key = BN_new();
|
||||
|
||||
if (dsasha1_parse_key(dsa->q, dsa->p, dsa->g, dsa->pub_key, key_data, key_len)
|
||||
&& DSA_verify(0, ctx->digest, 20, asn1_signature, countof(asn1_signature), dsa) > 0)
|
||||
validated = 1;
|
||||
|
||||
DSA_free(dsa);
|
||||
return validated;
|
||||
}
|
||||
|
||||
#define VALG_UNSUPPORTED() { \
|
||||
0,0 \
|
||||
} /**/
|
||||
|
||||
#define VALG_VTABLE(alg, digest) { \
|
||||
digest, \
|
||||
alg ## _verify \
|
||||
} /**/
|
||||
|
||||
/* Updated registry that merges various RFCs:
|
||||
https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xml */
|
||||
static const VerifyAlg valgs[] =
|
||||
{
|
||||
VALG_UNSUPPORTED(), /* 0: reserved */
|
||||
VALG_VTABLE(rsamd5, DIGESTALG_MD5), /* 1: RSAMD5 */
|
||||
VALG_UNSUPPORTED(), /* 2: DH */
|
||||
VALG_VTABLE(dsasha1, DIGESTALG_SHA1), /* 3: DSA */
|
||||
VALG_UNSUPPORTED(), /* 4: ECC */
|
||||
VALG_VTABLE(rsasha1, DIGESTALG_SHA1), /* 5: RSASHA1 */
|
||||
VALG_VTABLE(dsasha1, DIGESTALG_SHA1), /* 6: DSA-NSEC3-SHA1 */
|
||||
VALG_VTABLE(rsasha1, DIGESTALG_SHA1), /* 7: RSASHA1-NSEC3-SHA1 */
|
||||
VALG_VTABLE(rsasha256, DIGESTALG_SHA256), /* 8: RSASHA256 */
|
||||
VALG_UNSUPPORTED(), /* 9: unassigned */
|
||||
VALG_VTABLE(rsasha512, DIGESTALG_SHA512), /* 10: RSASHA512 */
|
||||
VALG_UNSUPPORTED(), /* 11: unassigned */
|
||||
VALG_UNSUPPORTED(), /* 12: ECC-GOST */
|
||||
VALG_UNSUPPORTED(), /* 13: ECDSAP256SHA256 */
|
||||
VALG_UNSUPPORTED(), /* 14: ECDSAP384SHA384 */
|
||||
};
|
||||
|
||||
/* TODO: remove if we don't need this anymore
|
||||
(to be rechecked if we ever remove OpenSSL) */
|
||||
static const int valgctx_size[] =
|
||||
{
|
||||
0, /* 0: reserved */
|
||||
sizeof(VerifyAlgCtx), /* 1: RSAMD5 */
|
||||
0, /* 2: DH */
|
||||
sizeof(VerifyAlgCtx), /* 3: DSA */
|
||||
0, /* 4: ECC */
|
||||
sizeof(VerifyAlgCtx), /* 5: RSASHA1 */
|
||||
sizeof(VerifyAlgCtx), /* 6: DSA-NSEC3-SHA1 */
|
||||
sizeof(VerifyAlgCtx), /* 7: RSASHA1-NSEC3-SHA1 */
|
||||
sizeof(VerifyAlgCtx), /* 8: RSASHA256 */
|
||||
0, /* 9: unassigned */
|
||||
sizeof(VerifyAlgCtx), /* 10: RSASHA512 */
|
||||
0, /* 11: unassigned */
|
||||
0, /* 12: ECC-GOST */
|
||||
0, /* 13: ECDSAP256SHA256 */
|
||||
0, /* 14: ECDSAP384SHA384 */
|
||||
};
|
||||
|
||||
int verifyalg_supported(int algo)
|
||||
{
|
||||
return (algo < countof(valgctx_size) && valgctx_size[algo] != 0);
|
||||
}
|
||||
|
||||
VerifyAlgCtx* verifyalg_alloc(int algo)
|
||||
{
|
||||
int i;
|
||||
VerifyAlgCtx *ret = 0;
|
||||
|
||||
if (pool_used == (1<<POOL_SIZE)-1)
|
||||
ret = whine_malloc(valgctx_size[algo]);
|
||||
else
|
||||
for (i = 0; i < POOL_SIZE; ++i)
|
||||
if (!(pool_used & (1 << i)))
|
||||
{
|
||||
ret = (VerifyAlgCtx*)&Pool[i];
|
||||
pool_used |= 1 << i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
ret->vtbl = &valgs[algo];
|
||||
return ret;
|
||||
}
|
||||
|
||||
void verifyalg_free(VerifyAlgCtx *a)
|
||||
{
|
||||
int pool_idx = ((char*)a - (char*)&Pool[0]) / sizeof(Pool[0]);
|
||||
if (pool_idx < 0 || pool_idx >= POOL_SIZE)
|
||||
{
|
||||
free(a);
|
||||
return;
|
||||
}
|
||||
|
||||
pool_used &= ~(1 << pool_idx);
|
||||
}
|
||||
|
||||
int verifyalg_algonum(VerifyAlgCtx *a)
|
||||
{
|
||||
int num = a->vtbl - valgs;
|
||||
if (num < 0 || num >= countof(valgs))
|
||||
return -1;
|
||||
return num;
|
||||
}
|
||||
|
||||
static EVP_MD_CTX digctx;
|
||||
|
||||
int digestalg_supported(int algo)
|
||||
{
|
||||
return (algo == DIGESTALG_SHA1 ||
|
||||
algo == DIGESTALG_SHA256 ||
|
||||
algo == DIGESTALG_MD5 ||
|
||||
algo == DIGESTALG_SHA512);
|
||||
}
|
||||
|
||||
void digestalg_begin(int algo)
|
||||
{
|
||||
EVP_MD_CTX_init(&digctx);
|
||||
if (algo == DIGESTALG_SHA1)
|
||||
EVP_DigestInit_ex(&digctx, EVP_sha1(), NULL);
|
||||
else if (algo == DIGESTALG_SHA256)
|
||||
EVP_DigestInit_ex(&digctx, EVP_sha256(), NULL);
|
||||
else if (algo == DIGESTALG_SHA512)
|
||||
EVP_DigestInit_ex(&digctx, EVP_sha512(), NULL);
|
||||
else if (algo == DIGESTALG_MD5)
|
||||
EVP_DigestInit_ex(&digctx, EVP_md5(), NULL);
|
||||
}
|
||||
|
||||
int digestalg_len()
|
||||
{
|
||||
return EVP_MD_CTX_size(&digctx);
|
||||
}
|
||||
|
||||
void digestalg_add_data(void *data, unsigned len)
|
||||
{
|
||||
EVP_DigestUpdate(&digctx, data, len);
|
||||
}
|
||||
|
||||
void digestalg_add_keydata(struct blockdata *key, size_t len)
|
||||
{
|
||||
size_t cnt; unsigned char *p = NULL;
|
||||
while (len)
|
||||
{
|
||||
cnt = blockdata_walk(&key, &p, len);
|
||||
EVP_DigestUpdate(&digctx, p, cnt);
|
||||
p += cnt;
|
||||
len -= cnt;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char* digestalg_final(void)
|
||||
{
|
||||
static unsigned char digest[32];
|
||||
EVP_DigestFinal(&digctx, digest, NULL);
|
||||
return digest;
|
||||
}
|
||||
|
||||
#endif /* HAVE_DNSSEC */
|
||||
854
src/dnssec.c
Normal file
854
src/dnssec.c
Normal file
@@ -0,0 +1,854 @@
|
||||
/* dnssec.c is Copyright (c) 2012 Giovanni Bajo <rasky@develer.com>
|
||||
and Copyright (c) 2012-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991, or
|
||||
(at your option) version 3 dated 29 June, 2007.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
|
||||
#include "dnssec-crypto.h"
|
||||
|
||||
#define SERIAL_UNDEF -100
|
||||
#define SERIAL_EQ 0
|
||||
#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.
|
||||
*/
|
||||
static int to_wire(char *name)
|
||||
{
|
||||
unsigned char *l, *p, 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';
|
||||
|
||||
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;
|
||||
int len;
|
||||
|
||||
for (l = (unsigned char *)name; *l != 0; l += len+1)
|
||||
{
|
||||
len = *l;
|
||||
memmove(l, l+1, len);
|
||||
l[len] = '.';
|
||||
}
|
||||
|
||||
*(l-1) = 0;
|
||||
}
|
||||
|
||||
/* Input in presentation format */
|
||||
static int count_labels(char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (*name == 0)
|
||||
return 0;
|
||||
|
||||
for (i = 0; *name; name++)
|
||||
if (*name == '.')
|
||||
i++;
|
||||
|
||||
return i+1;
|
||||
}
|
||||
|
||||
/* Implement RFC1982 wrapped compare for 32-bit numbers */
|
||||
static int serial_compare_32(unsigned long s1, unsigned long s2)
|
||||
{
|
||||
if (s1 == s2)
|
||||
return SERIAL_EQ;
|
||||
|
||||
if ((s1 < s2 && (s2 - s1) < (1UL<<31)) ||
|
||||
(s1 > s2 && (s1 - s2) > (1UL<<31)))
|
||||
return SERIAL_LT;
|
||||
if ((s1 < s2 && (s2 - s1) > (1UL<<31)) ||
|
||||
(s1 > s2 && (s1 - s2) < (1UL<<31)))
|
||||
return SERIAL_GT;
|
||||
return SERIAL_UNDEF;
|
||||
}
|
||||
|
||||
/* Check whether today/now is between date_start and date_end */
|
||||
static int check_date_range(unsigned long date_start, unsigned long date_end)
|
||||
{
|
||||
unsigned long curtime = time(0);
|
||||
|
||||
/* We must explicitly check against wanted values, because of SERIAL_UNDEF */
|
||||
return serial_compare_32(curtime, date_start) == SERIAL_GT
|
||||
&& serial_compare_32(curtime, date_end) == SERIAL_LT;
|
||||
}
|
||||
|
||||
static u16 *get_desc(int type)
|
||||
{
|
||||
/* List of RRtypes which include domains in the data.
|
||||
0 -> domain
|
||||
integer -> no of plain bytes
|
||||
-1 -> end
|
||||
|
||||
zero is not a valid RRtype, so the final entry is returned for
|
||||
anything which needs no mangling.
|
||||
*/
|
||||
|
||||
static u16 rr_desc[] =
|
||||
{
|
||||
T_NS, 0, -1,
|
||||
T_MD, 0, -1,
|
||||
T_MF, 0, -1,
|
||||
T_CNAME, 0, -1,
|
||||
T_SOA, 0, 0, -1,
|
||||
T_MB, 0, -1,
|
||||
T_MG, 0, -1,
|
||||
T_MR, 0, -1,
|
||||
T_PTR, 0, -1,
|
||||
T_MINFO, 0, 0, -1,
|
||||
T_MX, 2, 0, -1,
|
||||
T_RP, 0, 0, -1,
|
||||
T_AFSDB, 2, 0, -1,
|
||||
T_RT, 2, 0, -1,
|
||||
T_SIG, 18, 0, -1,
|
||||
T_PX, 2, 0, 0, -1,
|
||||
T_NXT, 0, -1,
|
||||
T_KX, 2, 0, -1,
|
||||
T_SRV, 6, 0, -1,
|
||||
T_DNAME, 0, -1,
|
||||
T_RRSIG, 18, 0, -1,
|
||||
T_NSEC, 0, -1,
|
||||
0, -1 /* wildcard/catchall */
|
||||
};
|
||||
|
||||
u16 *p = rr_desc;
|
||||
|
||||
while (*p != type && *p != 0)
|
||||
while (*p++ != (u16)-1);
|
||||
|
||||
return p+1;
|
||||
}
|
||||
|
||||
/* Return bytes of canonicalised rdata, when the return value is zero, the remaining
|
||||
data, pointed to by *p, should be used raw. */
|
||||
static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end, char *buff,
|
||||
unsigned char **p, u16 **desc)
|
||||
{
|
||||
int d = **desc;
|
||||
|
||||
(*desc)++;
|
||||
|
||||
/* No more data needs mangling */
|
||||
if (d == (u16)-1)
|
||||
return 0;
|
||||
|
||||
if (d == 0 && extract_name(header, plen, p, buff, 1, 0))
|
||||
/* domain-name, canonicalise */
|
||||
return to_wire(buff);
|
||||
else
|
||||
{
|
||||
/* plain data preceding a domain-name, don't run off the end of the data */
|
||||
if ((end - *p) < d)
|
||||
d = end - *p;
|
||||
|
||||
if (d != 0)
|
||||
{
|
||||
memcpy(buff, *p, d);
|
||||
*p += d;
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
/* Bubble sort the RRset into the canonical order.
|
||||
Note that the byte-streams from two RRs may get unsynced: consider
|
||||
RRs which have two domain-names at the start and then other data.
|
||||
The domain-names may have different lengths in each RR, but sort equal
|
||||
|
||||
------------
|
||||
|abcde|fghi|
|
||||
------------
|
||||
|abcd|efghi|
|
||||
------------
|
||||
|
||||
leaving the following bytes as deciding the order. Hence the nasty left1 and left2 variables.
|
||||
*/
|
||||
|
||||
static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int rrsetidx,
|
||||
unsigned char **rrset, char *buff1, char *buff2)
|
||||
{
|
||||
int swap, quit, i;
|
||||
|
||||
do
|
||||
{
|
||||
for (swap = 0, i = 0; i < rrsetidx-1; i++)
|
||||
{
|
||||
int rdlen1, rdlen2, left1, left2, len1, len2, len, rc;
|
||||
u16 *dp1, *dp2;
|
||||
unsigned char *end1, *end2;
|
||||
unsigned char *p1 = skip_name(rrset[i], header, plen, 10);
|
||||
unsigned char *p2 = skip_name(rrset[i+1], header, plen, 10);
|
||||
|
||||
p1 += 8; /* skip class, type, ttl */
|
||||
GETSHORT(rdlen1, p1);
|
||||
end1 = p1 + rdlen1;
|
||||
|
||||
p2 += 8; /* skip class, type, ttl */
|
||||
GETSHORT(rdlen2, p2);
|
||||
end2 = p2 + rdlen2;
|
||||
|
||||
dp1 = dp2 = rr_desc;
|
||||
|
||||
for (quit = 0, left1 = 0, left2 = 0, len1 = 0, len2 = 0; !quit;)
|
||||
{
|
||||
if (left1 != 0)
|
||||
memmove(buff1, buff1 + len1 - left1, left1);
|
||||
|
||||
if ((len1 = get_rdata(header, plen, end1, buff1 + left1, &p1, &dp1)) == 0)
|
||||
{
|
||||
quit = 1;
|
||||
len1 = end1 - p1;
|
||||
memcpy(buff1 + left1, p1, len1);
|
||||
}
|
||||
len1 += left1;
|
||||
|
||||
if (left2 != 0)
|
||||
memmove(buff2, buff2 + len2 - left2, left2);
|
||||
|
||||
if ((len2 = get_rdata(header, plen, end2, buff2 + left2, &p2, &dp2)) == 0)
|
||||
{
|
||||
quit = 1;
|
||||
len2 = end2 - p2;
|
||||
memcpy(buff2 + left2, p2, len2);
|
||||
}
|
||||
len2 += left2;
|
||||
|
||||
if (len1 > len2)
|
||||
left1 = len1 - len2, left2 = 0, len = len2;
|
||||
else
|
||||
left2 = len2 - len1, left1 = 0, len = len1;
|
||||
|
||||
rc = memcmp(buff1, buff2, len);
|
||||
|
||||
if (rc == 1 || (rc == 0 && quit && len1 > len2))
|
||||
{
|
||||
unsigned char *tmp = rrset[i+1];
|
||||
rrset[i+1] = rrset[i];
|
||||
rrset[i] = tmp;
|
||||
swap = quit = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (swap);
|
||||
}
|
||||
|
||||
/* Validate a single RRset (class, type, name) in the supplied DNS reply
|
||||
Return code:
|
||||
STAT_SECURE if it validates.
|
||||
STAT_INSECURE can't validate (no RRSIG, bad packet).
|
||||
STAT_BOGUS signature is wrong.
|
||||
STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname)
|
||||
|
||||
if key is non-NULL, use that key, which has the algo and tag given in the params of those names,
|
||||
otherwise find the key in the cache.
|
||||
*/
|
||||
static int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class,
|
||||
int type, char *name, char *keyname, struct blockdata *key, int keylen, int algo_in, int keytag_in)
|
||||
{
|
||||
static unsigned char **rrset = NULL, **sigs = NULL;
|
||||
static int rrset_sz = 0, sig_sz = 0;
|
||||
|
||||
unsigned char *p;
|
||||
int rrsetidx, sigidx, res, rdlen, j, name_labels;
|
||||
struct crec *crecp = NULL;
|
||||
int type_covered, algo, labels, orig_ttl, sig_expiration, sig_inception, key_tag;
|
||||
u16 *rr_desc = get_desc(type);
|
||||
|
||||
if (!(p = skip_questions(header, plen)))
|
||||
return STAT_INSECURE;
|
||||
|
||||
name_labels = count_labels(name); /* For 4035 5.3.2 check */
|
||||
|
||||
/* look for RRSIGs for this RRset and get pointers to each RR in the set. */
|
||||
for (rrsetidx = 0, sigidx = 0, j = ntohs(header->ancount) + ntohs(header->nscount);
|
||||
j != 0; j--)
|
||||
{
|
||||
unsigned char *pstart, *pdata;
|
||||
int stype, sclass;
|
||||
|
||||
pstart = p;
|
||||
|
||||
if (!(res = extract_name(header, plen, &p, name, 0, 10)))
|
||||
return STAT_INSECURE; /* bad packet */
|
||||
|
||||
GETSHORT(stype, p);
|
||||
GETSHORT(sclass, p);
|
||||
p += 4; /* TTL */
|
||||
|
||||
pdata = p;
|
||||
|
||||
GETSHORT(rdlen, p);
|
||||
|
||||
if (res == 1 && sclass == class)
|
||||
{
|
||||
if (stype == type)
|
||||
{
|
||||
if (rrsetidx == rrset_sz)
|
||||
{
|
||||
unsigned char **new;
|
||||
|
||||
/* expand */
|
||||
if (!(new = whine_malloc((rrset_sz + 5) * sizeof(unsigned char **))))
|
||||
return STAT_INSECURE;
|
||||
|
||||
if (rrset)
|
||||
{
|
||||
memcpy(new, rrset, rrset_sz * sizeof(unsigned char **));
|
||||
free(rrset);
|
||||
}
|
||||
|
||||
rrset = new;
|
||||
rrset_sz += 5;
|
||||
}
|
||||
rrset[rrsetidx++] = pstart;
|
||||
}
|
||||
|
||||
if (stype == T_RRSIG)
|
||||
{
|
||||
if (rdlen < 18)
|
||||
return STAT_INSECURE; /* bad packet */
|
||||
|
||||
GETSHORT(type_covered, p);
|
||||
algo = *p++;
|
||||
labels = *p++;
|
||||
p += 4; /* orig_ttl */
|
||||
GETLONG(sig_expiration, p);
|
||||
GETLONG(sig_inception, p);
|
||||
p = pdata + 2; /* restore for ADD_RDLEN */
|
||||
|
||||
if (type_covered == type &&
|
||||
check_date_range(sig_inception, sig_expiration) &&
|
||||
verifyalg_supported(algo) &&
|
||||
labels <= name_labels)
|
||||
{
|
||||
if (sigidx == sig_sz)
|
||||
{
|
||||
unsigned char **new;
|
||||
|
||||
/* expand */
|
||||
if (!(new = whine_malloc((sig_sz + 5) * sizeof(unsigned char **))))
|
||||
return STAT_INSECURE;
|
||||
|
||||
if (sigs)
|
||||
{
|
||||
memcpy(new, sigs, sig_sz * sizeof(unsigned char **));
|
||||
free(sigs);
|
||||
}
|
||||
|
||||
sigs = new;
|
||||
sig_sz += 5;
|
||||
}
|
||||
|
||||
sigs[sigidx++] = pdata;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ADD_RDLEN(header, p, plen, rdlen))
|
||||
return STAT_INSECURE;
|
||||
}
|
||||
|
||||
/* RRset empty, no RRSIGs */
|
||||
if (rrsetidx == 0 || sigidx == 0)
|
||||
return STAT_INSECURE;
|
||||
|
||||
/* Sort RRset records into canonical order.
|
||||
Note that at this point keyname and name buffs are
|
||||
unused, and used as workspace by the sort. */
|
||||
sort_rrset(header, plen, rr_desc, rrsetidx, rrset, name, keyname);
|
||||
|
||||
/* Now try all the sigs to try and find one which validates */
|
||||
for (j = 0; j <sigidx; j++)
|
||||
{
|
||||
unsigned char *psav;
|
||||
int i, wire_len;
|
||||
VerifyAlgCtx *alg;
|
||||
u32 nsigttl;
|
||||
|
||||
p = sigs[j];
|
||||
GETSHORT(rdlen, p); /* rdlen >= 18 checked previously */
|
||||
psav = p;
|
||||
|
||||
p += 2; /* type_covered - already checked */
|
||||
algo = *p++;
|
||||
labels = *p++;
|
||||
GETLONG(orig_ttl, p);
|
||||
p += 8; /* sig_expiration and sig_inception */
|
||||
GETSHORT(key_tag, p);
|
||||
|
||||
if (!extract_name(header, plen, &p, keyname, 1, 0))
|
||||
return STAT_INSECURE;
|
||||
|
||||
/* OK, we have the signature record, see if the relevant DNSKEY is in the cache. */
|
||||
if (!key && !(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY)))
|
||||
return STAT_NEED_KEY;
|
||||
|
||||
alg = verifyalg_alloc(algo);
|
||||
alg->sig = p;
|
||||
alg->siglen = rdlen - (p - psav);
|
||||
|
||||
nsigttl = htonl(orig_ttl);
|
||||
|
||||
digestalg_begin(alg->vtbl->digest_algo);
|
||||
digestalg_add_data(psav, 18);
|
||||
wire_len = to_wire(keyname);
|
||||
digestalg_add_data(keyname, wire_len);
|
||||
from_wire(keyname);
|
||||
|
||||
for (i = 0; i < rrsetidx; ++i)
|
||||
{
|
||||
int seg;
|
||||
unsigned char *end, *cp;
|
||||
char *name_start = name;
|
||||
u16 len, *dp;
|
||||
|
||||
p = rrset[i];
|
||||
if (!extract_name(header, plen, &p, name, 1, 10))
|
||||
return STAT_INSECURE;
|
||||
|
||||
/* if more labels than in RRsig name, hash *.<no labels in rrsig labels field> 4035 5.3.2 */
|
||||
if (labels < name_labels)
|
||||
{
|
||||
int k;
|
||||
for (k = name_labels - labels; k != 0; k--)
|
||||
while (*name_start != '.' && *name_start != 0)
|
||||
name_start++;
|
||||
name_start--;
|
||||
*name_start = '*';
|
||||
}
|
||||
|
||||
wire_len = to_wire(name_start);
|
||||
digestalg_add_data(name_start, wire_len);
|
||||
digestalg_add_data(p, 4); /* class and type */
|
||||
digestalg_add_data(&nsigttl, 4);
|
||||
|
||||
p += 8; /* skip class, type, ttl */
|
||||
GETSHORT(rdlen, p);
|
||||
if (!CHECK_LEN(header, p, plen, rdlen))
|
||||
return STAT_INSECURE;
|
||||
|
||||
end = p + rdlen;
|
||||
|
||||
/* canonicalise rdata and calculate length of same, use name buffer as workspace */
|
||||
cp = p;
|
||||
dp = rr_desc;
|
||||
for (len = 0; (seg = get_rdata(header, plen, end, name, &cp, &dp)) != 0; len += seg);
|
||||
len += end - cp;
|
||||
len = htons(len);
|
||||
digestalg_add_data(&len, 2);
|
||||
|
||||
/* Now canonicalise again and digest. */
|
||||
cp = p;
|
||||
dp = rr_desc;
|
||||
while ((seg = get_rdata(header, plen, end, name, &cp, &dp)))
|
||||
digestalg_add_data(name, seg);
|
||||
if (cp != end)
|
||||
digestalg_add_data(cp, end - cp);
|
||||
}
|
||||
|
||||
/* namebuff used for workspace above, restore to leave unchanged on exit */
|
||||
p = (unsigned char*)(rrset[0]);
|
||||
extract_name(header, plen, &p, name, 1, 0);
|
||||
|
||||
memcpy(alg->digest, digestalg_final(), digestalg_len());
|
||||
|
||||
if (key)
|
||||
{
|
||||
if (algo_in == algo && keytag_in == key_tag &&
|
||||
alg->vtbl->verify(alg, key, keylen))
|
||||
return STAT_SECURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* iterate through all possible keys 4035 5.3.1 */
|
||||
for (; crecp; crecp = cache_find_by_name(crecp, keyname, now, F_DNSKEY))
|
||||
if (crecp->addr.key.algo == algo && crecp->addr.key.keytag == key_tag &&
|
||||
alg->vtbl->verify(alg, crecp->addr.key.keydata, crecp->uid))
|
||||
return STAT_SECURE;
|
||||
}
|
||||
}
|
||||
|
||||
return STAT_BOGUS;
|
||||
}
|
||||
|
||||
/* The DNS packet is expected to contain the answer to a DNSKEY query.
|
||||
Leave name of query in name.
|
||||
Put all DNSKEYs in the answer which are valid into the cache.
|
||||
return codes:
|
||||
STAT_INSECURE bad packet, no DNSKEYs in reply.
|
||||
STAT_SECURE At least one valid DNSKEY found and in cache.
|
||||
STAT_BOGUS No DNSKEYs found, which can be validated with DS,
|
||||
or self-sign for DNSKEY RRset is not valid.
|
||||
STAT_NEED_DS DS records to validate a key not found, name in keyname
|
||||
*/
|
||||
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
|
||||
{
|
||||
unsigned char *psave, *p = (unsigned char *)(header+1);
|
||||
struct crec *crecp, *recp1;
|
||||
int rc, j, qtype, qclass, ttl, rdlen, flags, algo, valid, keytag;
|
||||
struct blockdata *key;
|
||||
|
||||
if (ntohs(header->qdcount) != 1 ||
|
||||
!extract_name(header, plen, &p, name, 1, 4))
|
||||
{
|
||||
strcpy(name, "<none>");
|
||||
return STAT_INSECURE;
|
||||
}
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
GETSHORT(qclass, p);
|
||||
|
||||
if (qtype != T_DNSKEY || qclass != class || ntohs(header->ancount) == 0)
|
||||
return STAT_INSECURE;
|
||||
|
||||
/* See if we have cached a DS record which validates this key */
|
||||
if (!(crecp = cache_find_by_name(NULL, name, now, F_DS)))
|
||||
{
|
||||
strcpy(keyname, name);
|
||||
return STAT_NEED_DS;
|
||||
}
|
||||
|
||||
cache_start_insert();
|
||||
|
||||
/* NOTE, we need to find ONE DNSKEY which matches the DS */
|
||||
for (valid = 0, j = ntohs(header->ancount); j != 0; j--)
|
||||
{
|
||||
/* Ensure we have type, class TTL and length */
|
||||
if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
|
||||
return STAT_INSECURE; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
GETSHORT(qclass, p);
|
||||
GETLONG(ttl, p);
|
||||
GETSHORT(rdlen, p);
|
||||
|
||||
if (qclass != class || qtype != T_DNSKEY || rc == 2)
|
||||
{
|
||||
if (ADD_RDLEN(header, p, plen, rdlen))
|
||||
continue;
|
||||
|
||||
return STAT_INSECURE; /* bad packet */
|
||||
}
|
||||
|
||||
if (!CHECK_LEN(header, p, plen, rdlen) || rdlen < 4)
|
||||
return STAT_INSECURE; /* bad packet */
|
||||
|
||||
psave = p;
|
||||
|
||||
/* length at least covers flags, protocol and algo now. */
|
||||
GETSHORT(flags, p);
|
||||
if (*p++ != 3)
|
||||
return STAT_INSECURE;
|
||||
algo = *p++;
|
||||
keytag = dnskey_keytag(algo, flags, p, rdlen - 4);
|
||||
|
||||
/* Put the key into the cache. Note that if the validation fails, we won't
|
||||
call cache_end_insert() and this will never be committed. */
|
||||
if ((key = blockdata_alloc((char*)p, rdlen - 4)) &&
|
||||
(recp1 = cache_insert(name, NULL, now, ttl, F_FORWARD | F_DNSKEY)))
|
||||
{
|
||||
recp1->uid = rdlen - 4;
|
||||
recp1->addr.key.keydata = key;
|
||||
recp1->addr.key.algo = algo;
|
||||
recp1->addr.key.keytag = keytag;
|
||||
}
|
||||
|
||||
p = psave;
|
||||
if (!ADD_RDLEN(header, p, plen, rdlen))
|
||||
return STAT_INSECURE; /* bad packet */
|
||||
|
||||
/* Already determined that message is OK. Just loop stuffing cache */
|
||||
if (valid || !key)
|
||||
continue;
|
||||
|
||||
for (recp1 = crecp; recp1; recp1 = cache_find_by_name(recp1, name, now, F_DS))
|
||||
if (recp1->addr.key.algo == algo &&
|
||||
recp1->addr.key.keytag == keytag &&
|
||||
(flags & 0x100) && /* zone key flag */
|
||||
digestalg_supported(recp1->addr.key.digest))
|
||||
{
|
||||
int wire_len = to_wire(name);
|
||||
|
||||
digestalg_begin(recp1->addr.key.digest);
|
||||
digestalg_add_data(name, wire_len);
|
||||
digestalg_add_data((char *)psave, rdlen);
|
||||
|
||||
from_wire(name);
|
||||
|
||||
if (recp1->uid == digestalg_len() &&
|
||||
blockdata_retrieve(recp1->addr.key.keydata, recp1->uid, digestalg_final()) &&
|
||||
validate_rrset(now, header, plen, class, T_DNSKEY, name, keyname, key, rdlen - 4, algo, keytag))
|
||||
{
|
||||
struct all_addr a;
|
||||
valid = 1;
|
||||
a.addr.keytag = keytag;
|
||||
log_query(F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (valid)
|
||||
{
|
||||
/* commit cache insert. */
|
||||
cache_end_insert();
|
||||
return STAT_SECURE;
|
||||
}
|
||||
|
||||
log_query(F_UPSTREAM, name, NULL, "BOGUS DNSKEY");
|
||||
return STAT_BOGUS;
|
||||
}
|
||||
|
||||
/* The DNS packet is expected to contain the answer to a DS query
|
||||
Leave name of DS query in name.
|
||||
Put all DSs in the answer which are valid into the cache.
|
||||
return codes:
|
||||
STAT_INSECURE bad packet, no DS in reply.
|
||||
STAT_SECURE At least one valid DS found and in cache.
|
||||
STAT_BOGUS At least one DS found, which fails validation.
|
||||
STAT_NEED_DNSKEY DNSKEY records to validate a DS not found, name in keyname
|
||||
*/
|
||||
|
||||
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
|
||||
{
|
||||
unsigned char *psave, *p = (unsigned char *)(header+1);
|
||||
struct crec *crecp;
|
||||
int qtype, qclass, val, j, gotone;
|
||||
struct blockdata *key;
|
||||
|
||||
if (ntohs(header->qdcount) != 1 ||
|
||||
!extract_name(header, plen, &p, name, 1, 4))
|
||||
{
|
||||
strcpy(name, "<none>");
|
||||
return STAT_INSECURE;
|
||||
}
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
GETSHORT(qclass, p);
|
||||
|
||||
if (qtype != T_DS || qclass != class || ntohs(header->ancount) == 0)
|
||||
return STAT_INSECURE;
|
||||
|
||||
val = validate_rrset(now, header, plen, class, T_DS, name, keyname, NULL, 0, 0, 0);
|
||||
|
||||
if (val == STAT_BOGUS)
|
||||
log_query(F_UPSTREAM, name, NULL, "BOGUS DS");
|
||||
|
||||
/* failed to validate or missing key. */
|
||||
if (val != STAT_SECURE)
|
||||
return val;
|
||||
|
||||
cache_start_insert();
|
||||
|
||||
for (gotone = 0, j = ntohs(header->ancount); j != 0; j--)
|
||||
{
|
||||
int ttl, rdlen, rc, algo, digest, keytag;
|
||||
|
||||
/* Ensure we have type, class TTL and length */
|
||||
if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
|
||||
return STAT_INSECURE; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
GETSHORT(qclass, p);
|
||||
GETLONG(ttl, p);
|
||||
GETSHORT(rdlen, p);
|
||||
|
||||
/* check type, class and name, skip if not in DS rrset */
|
||||
if (qclass == class && qtype == T_DS && rc == 1)
|
||||
{
|
||||
if (!CHECK_LEN(header, p, plen, rdlen) || rdlen < 4)
|
||||
return STAT_INSECURE; /* bad packet */
|
||||
|
||||
psave = p;
|
||||
GETSHORT(keytag, p);
|
||||
algo = *p++;
|
||||
digest = *p++;
|
||||
|
||||
/* We've proved that the DS is OK, store it in the cache */
|
||||
if ((key = blockdata_alloc((char*)p, rdlen - 4)) &&
|
||||
(crecp = cache_insert(name, NULL, now, ttl, F_FORWARD | F_DS)))
|
||||
{
|
||||
struct all_addr a;
|
||||
a.addr.keytag = keytag;
|
||||
log_query(F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u");
|
||||
crecp->addr.key.digest = digest;
|
||||
crecp->addr.key.keydata = key;
|
||||
crecp->addr.key.algo = algo;
|
||||
crecp->addr.key.keytag = keytag;
|
||||
crecp->uid = rdlen - 4;
|
||||
}
|
||||
else
|
||||
return STAT_INSECURE; /* cache problem */
|
||||
|
||||
p = psave;
|
||||
}
|
||||
|
||||
if (!ADD_RDLEN(header, p, plen, rdlen))
|
||||
return STAT_INSECURE; /* bad packet */
|
||||
|
||||
}
|
||||
|
||||
cache_end_insert();
|
||||
|
||||
return STAT_SECURE;
|
||||
}
|
||||
|
||||
/* Validate all the RRsets in the answer and authority sections of the reply (4035:3.2.3) */
|
||||
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class)
|
||||
{
|
||||
unsigned char *ans_start, *p1, *p2;
|
||||
int type1, class1, rdlen1, type2, class2, rdlen2;
|
||||
int i, j, rc;
|
||||
|
||||
if (!(ans_start = skip_questions(header, plen)))
|
||||
return STAT_INSECURE;
|
||||
|
||||
for (p1 = ans_start, i = 0; i < ntohs(header->ancount) + ntohs(header->nscount); i++)
|
||||
{
|
||||
if (!extract_name(header, plen, &p1, name, 1, 10))
|
||||
return STAT_INSECURE; /* bad packet */
|
||||
|
||||
GETSHORT(type1, p1);
|
||||
GETSHORT(class1, p1);
|
||||
p1 += 4; /* TTL */
|
||||
GETSHORT(rdlen1, p1);
|
||||
|
||||
/* Don't try and validate RRSIGs! */
|
||||
if (type1 != T_RRSIG)
|
||||
{
|
||||
/* Check if we've done this RRset already */
|
||||
for (p2 = ans_start, j = 0; j < i; j++)
|
||||
{
|
||||
if (!(rc = extract_name(header, plen, &p2, name, 0, 10)))
|
||||
return STAT_INSECURE; /* bad packet */
|
||||
|
||||
GETSHORT(type2, p2);
|
||||
GETSHORT(class2, p2);
|
||||
p2 += 4; /* TTL */
|
||||
GETSHORT(rdlen2, p2);
|
||||
|
||||
if (type2 == type1 && class2 == class1 && rc == 1)
|
||||
break; /* Done it before: name, type, class all match. */
|
||||
|
||||
if (!ADD_RDLEN(header, p2, plen, rdlen2))
|
||||
return STAT_INSECURE;
|
||||
}
|
||||
|
||||
/* Not done, validate now */
|
||||
if (j == i && (rc = validate_rrset(now, header, plen, class1, type1, name, keyname, NULL, 0, 0, 0)) != STAT_SECURE)
|
||||
{
|
||||
*class = class1; /* Class for DS or DNSKEY */
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ADD_RDLEN(header, p1, plen, rdlen1))
|
||||
return STAT_INSECURE;
|
||||
}
|
||||
|
||||
return STAT_SECURE;
|
||||
}
|
||||
|
||||
|
||||
/* Compute keytag (checksum to quickly index a key). See RFC4034 */
|
||||
int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen)
|
||||
{
|
||||
if (alg == 1)
|
||||
{
|
||||
/* Algorithm 1 (RSAMD5) has a different (older) keytag calculation algorithm.
|
||||
See RFC4034, Appendix B.1 */
|
||||
return key[keylen-4] * 256 + key[keylen-3];
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned long ac;
|
||||
int i;
|
||||
|
||||
ac = ((htons(flags) >> 8) | ((htons(flags) << 8) & 0xff00)) + 0x300 + alg;
|
||||
for (i = 0; i < keylen; ++i)
|
||||
ac += (i & 1) ? key[i] : key[i] << 8;
|
||||
ac += (ac >> 16) & 0xffff;
|
||||
return ac & 0xffff;
|
||||
}
|
||||
}
|
||||
|
||||
size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr)
|
||||
{
|
||||
unsigned char *p;
|
||||
char types[20];
|
||||
|
||||
querystr("dnssec", types, type);
|
||||
|
||||
if (addr->sa.sa_family == AF_INET)
|
||||
log_query(F_DNSSEC | F_IPV4, name, (struct all_addr *)&addr->in.sin_addr, types);
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
log_query(F_DNSSEC | F_IPV6, name, (struct all_addr *)&addr->in6.sin6_addr, types);
|
||||
#endif
|
||||
|
||||
header->qdcount = htons(1);
|
||||
header->ancount = htons(0);
|
||||
header->nscount = htons(0);
|
||||
header->arcount = htons(0);
|
||||
|
||||
header->hb3 = HB3_RD;
|
||||
SET_OPCODE(header, QUERY);
|
||||
header->hb4 = HB4_CD;
|
||||
|
||||
/* ID filled in later */
|
||||
|
||||
p = (unsigned char *)(header+1);
|
||||
|
||||
p = do_rfc1035_name(p, name);
|
||||
*p++ = 0;
|
||||
PUTSHORT(type, p);
|
||||
PUTSHORT(class, p);
|
||||
|
||||
return add_do_bit(header, p - (unsigned char *)header, end);
|
||||
}
|
||||
|
||||
#endif /* HAVE_DNSSEC */
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
363
src/forward.c
363
src/forward.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -270,7 +270,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
if (gotname)
|
||||
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
|
||||
|
||||
if (!flags && !(forward = get_new_frec(now, NULL)))
|
||||
if (!flags && !(forward = get_new_frec(now, NULL, 0)))
|
||||
/* table full - server failure. */
|
||||
flags = F_NEG;
|
||||
|
||||
@@ -330,11 +330,11 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
int forwarded = 0;
|
||||
|
||||
if (option_bool(OPT_ADD_MAC))
|
||||
plen = add_mac(header, plen, ((char *) header) + PACKETSZ, &forward->source);
|
||||
plen = add_mac(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source);
|
||||
|
||||
if (option_bool(OPT_CLIENT_SUBNET))
|
||||
{
|
||||
size_t new = add_source_addr(header, plen, ((char *) header) + PACKETSZ, &forward->source);
|
||||
size_t new = add_source_addr(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source);
|
||||
if (new != plen)
|
||||
{
|
||||
plen = new;
|
||||
@@ -342,6 +342,14 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
plen = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz);
|
||||
header->hb4 |= HB4_CD;
|
||||
}
|
||||
#endif
|
||||
|
||||
while (1)
|
||||
{
|
||||
/* only send to servers dealing with our domain.
|
||||
@@ -447,7 +455,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
}
|
||||
|
||||
static size_t process_reply(struct dns_header *header, time_t now, struct server *server, size_t n, int check_rebind,
|
||||
int checking_disabled, int check_subnet, union mysockaddr *query_source)
|
||||
int no_cache, int cache_secure, int check_subnet, union mysockaddr *query_source)
|
||||
{
|
||||
unsigned char *pheader, *sizep;
|
||||
char **sets = 0;
|
||||
@@ -495,11 +503,18 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* RFC 4035 sect 4.6 para 3 */
|
||||
if (!is_sign && !option_bool(OPT_DNSSEC))
|
||||
if (!is_sign && !option_bool(OPT_DNSSEC_PROXY))
|
||||
header->hb4 &= ~HB4_AD;
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
header->hb4 &= ~HB4_AD;
|
||||
|
||||
|
||||
if (cache_secure)
|
||||
header->hb4 |= HB4_AD;
|
||||
#endif
|
||||
|
||||
if (OPCODE(header) != QUERY || (RCODE(header) != NOERROR && RCODE(header) != NXDOMAIN))
|
||||
return n;
|
||||
|
||||
@@ -512,7 +527,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
if (!option_bool(OPT_LOG))
|
||||
server->flags |= SERV_WARNED_RECURSIVE;
|
||||
}
|
||||
|
||||
|
||||
if (daemon->bogus_addr && RCODE(header) != NXDOMAIN &&
|
||||
check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))
|
||||
{
|
||||
@@ -534,7 +549,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
SET_RCODE(header, NOERROR);
|
||||
}
|
||||
|
||||
if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, checking_disabled))
|
||||
if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, no_cache, cache_secure))
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
|
||||
munged = 1;
|
||||
@@ -566,7 +581,7 @@ void reply_query(int fd, int family, time_t now)
|
||||
union mysockaddr serveraddr;
|
||||
struct frec *forward;
|
||||
socklen_t addrlen = sizeof(serveraddr);
|
||||
ssize_t n = recvfrom(fd, daemon->packet, daemon->edns_pktsz, 0, &serveraddr.sa, &addrlen);
|
||||
ssize_t n = recvfrom(fd, daemon->packet, daemon->packet_buff_sz, 0, &serveraddr.sa, &addrlen);
|
||||
size_t nn;
|
||||
struct server *server;
|
||||
|
||||
@@ -592,9 +607,7 @@ void reply_query(int fd, int family, time_t now)
|
||||
n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR) ||
|
||||
!(forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff))))
|
||||
return;
|
||||
|
||||
server = forward->sentto;
|
||||
|
||||
|
||||
if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) &&
|
||||
!option_bool(OPT_ORDER) &&
|
||||
forward->forwardall == 0)
|
||||
@@ -619,6 +632,8 @@ void reply_query(int fd, int family, time_t now)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
server = forward->sentto;
|
||||
|
||||
if ((forward->sentto->flags & SERV_TYPE) == 0)
|
||||
{
|
||||
@@ -640,7 +655,7 @@ void reply_query(int fd, int family, time_t now)
|
||||
if (!option_bool(OPT_ALL_SERVERS))
|
||||
daemon->last_server = server;
|
||||
}
|
||||
|
||||
|
||||
/* If the answer is an error, keep the forward record in place in case
|
||||
we get a good reply from another server. Kill it when we've
|
||||
had replies from all to avoid filling the forwarding table when
|
||||
@@ -648,12 +663,180 @@ void reply_query(int fd, int family, time_t now)
|
||||
if (forward->forwardall == 0 || --forward->forwardall == 1 ||
|
||||
(RCODE(header) != REFUSED && RCODE(header) != SERVFAIL))
|
||||
{
|
||||
int check_rebind = !(forward->flags & FREC_NOREBIND);
|
||||
int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0;
|
||||
|
||||
if (!option_bool(OPT_NO_REBIND))
|
||||
check_rebind = 0;
|
||||
if (option_bool(OPT_NO_REBIND))
|
||||
check_rebind = !(forward->flags & FREC_NOREBIND);
|
||||
|
||||
if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, forward->flags & FREC_CHECKING_DISABLED,
|
||||
/* Don't cache replies where DNSSEC validation was turned off, either
|
||||
the upstream server told us so, or the original query specified it. */
|
||||
if ((header->hb4 & HB4_CD) || (forward->flags & FREC_CHECKING_DISABLED))
|
||||
no_cache_dnssec = 1;
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && !(forward->flags & FREC_CHECKING_DISABLED))
|
||||
{
|
||||
int status;
|
||||
|
||||
/* We've had a reply already, which we're validating. Ignore this duplicate */
|
||||
if (forward->stash)
|
||||
return;
|
||||
|
||||
if (header->hb3 & HB3_TC)
|
||||
{
|
||||
/* Truncated answer can't be validated.
|
||||
The client will retry over TCP, but if this is an answer to a
|
||||
DNSSEC-generated query, we have a problem. Should really re-send
|
||||
over TCP. No-one with any sense will make a DNSKEY or DS RRset
|
||||
exceed 4096, so this may not be a real problem. Just log
|
||||
for now. */
|
||||
if (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY))
|
||||
my_syslog(LOG_ERR, _("Reply to DNSSEC query truncated - validation fails."));
|
||||
status = STAT_INSECURE;
|
||||
}
|
||||
else if (forward->flags & FREC_DNSKEY_QUERY)
|
||||
status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
||||
else if (forward->flags & FREC_DS_QUERY)
|
||||
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
||||
else
|
||||
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class);
|
||||
|
||||
/* Can't validate, as we're missing key data. Put this
|
||||
answer aside, whilst we get that. */
|
||||
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
|
||||
{
|
||||
struct frec *new;
|
||||
|
||||
if ((new = get_new_frec(now, NULL, 1)))
|
||||
{
|
||||
struct frec *next = new->next;
|
||||
*new = *forward; /* copy everything, then overwrite */
|
||||
new->next = next;
|
||||
new->stash = NULL;
|
||||
new->blocking_query = NULL;
|
||||
new->rfd4 = NULL;
|
||||
#ifdef HAVE_IPV6
|
||||
new->rfd6 = NULL;
|
||||
#endif
|
||||
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY);
|
||||
|
||||
if ((forward->stash = blockdata_alloc((char *)header, n)))
|
||||
{
|
||||
int fd;
|
||||
|
||||
forward->stash_len = n;
|
||||
|
||||
new->dependent = forward; /* to find query awaiting new one. */
|
||||
forward->blocking_query = new; /* for garbage cleaning */
|
||||
/* validate routines leave name of required record in daemon->keyname */
|
||||
if (status == STAT_NEED_KEY)
|
||||
{
|
||||
new->flags |= FREC_DNSKEY_QUERY;
|
||||
nn = dnssec_generate_query(header, ((char *) header) + daemon->packet_buff_sz,
|
||||
daemon->keyname, forward->class, T_DNSKEY, &server->addr);
|
||||
}
|
||||
else if (status == STAT_NEED_DS)
|
||||
{
|
||||
new->flags |= FREC_DS_QUERY;
|
||||
nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz,
|
||||
daemon->keyname, forward->class, T_DS, &server->addr);
|
||||
}
|
||||
new->crc = questions_crc(header, nn, daemon->namebuff);
|
||||
new->new_id = get_id(new->crc);
|
||||
header->id = htons(new->new_id);
|
||||
|
||||
/* Don't resend this. */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
if (server->sfd)
|
||||
fd = server->sfd->fd;
|
||||
else
|
||||
{
|
||||
fd = -1;
|
||||
#ifdef HAVE_IPV6
|
||||
if (server->addr.sa.sa_family == AF_INET6)
|
||||
{
|
||||
if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6)))
|
||||
fd = new->rfd6->fd;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET)))
|
||||
fd = new->rfd4->fd;
|
||||
}
|
||||
}
|
||||
|
||||
if (fd != -1)
|
||||
{
|
||||
while (sendto(fd, (char *)header, nn, 0, &server->addr.sa, sa_len(&server->addr)) == -1 && retry_send());
|
||||
server->queries++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ok, we reached far enough up the chain-of-trust that we can validate something.
|
||||
Now wind back down, pulling back answers which wouldn't previously validate
|
||||
and validate them with the new data. Failure to find needed data here is an internal error.
|
||||
Once we get to the original answer (FREC_DNSSEC_QUERY not set) and it validates,
|
||||
return it to the original requestor. */
|
||||
if (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY))
|
||||
{
|
||||
while (forward->dependent)
|
||||
{
|
||||
struct frec *prev;
|
||||
|
||||
if (status == STAT_SECURE)
|
||||
{
|
||||
if (forward->flags & FREC_DNSKEY_QUERY)
|
||||
status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
||||
else if (forward->flags & FREC_DS_QUERY)
|
||||
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
||||
}
|
||||
|
||||
prev = forward->dependent;
|
||||
free_frec(forward);
|
||||
forward = prev;
|
||||
forward->blocking_query = NULL; /* already gone */
|
||||
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
|
||||
n = forward->stash_len;
|
||||
}
|
||||
|
||||
/* All DNSKEY and DS records done and in cache, now finally validate original
|
||||
answer, provided last DNSKEY is OK. */
|
||||
if (status == STAT_SECURE)
|
||||
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class);
|
||||
|
||||
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
|
||||
{
|
||||
my_syslog(LOG_ERR, _("Unexpected missing data for DNSSEC validation"));
|
||||
status = STAT_INSECURE;
|
||||
}
|
||||
}
|
||||
|
||||
log_query(F_KEYTAG | F_SECSTAT, "result", NULL,
|
||||
status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
|
||||
|
||||
no_cache_dnssec = 0;
|
||||
|
||||
if (status == STAT_SECURE)
|
||||
cache_secure = 1;
|
||||
/* TODO return SERVFAIL here */
|
||||
else if (status == STAT_BOGUS)
|
||||
no_cache_dnssec = 1;
|
||||
|
||||
/* restore CD bit to the value in the query */
|
||||
if (forward->flags & FREC_CHECKING_DISABLED)
|
||||
header->hb4 |= HB4_CD;
|
||||
else
|
||||
header->hb4 &= ~HB4_CD;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, no_cache_dnssec, cache_secure,
|
||||
forward->flags & FREC_HAS_SUBNET, &forward->source)))
|
||||
{
|
||||
header->id = htons(forward->orig_id);
|
||||
@@ -887,7 +1070,7 @@ void receive_query(struct listener *listen, time_t now)
|
||||
#ifdef HAVE_AUTH
|
||||
if (auth_dns)
|
||||
{
|
||||
m = answer_auth(header, ((char *) header) + PACKETSZ, (size_t)n, now, &source_addr, local_auth);
|
||||
m = answer_auth(header, ((char *) header) + daemon->packet_buff_sz, (size_t)n, now, &source_addr, local_auth);
|
||||
if (m >= 1)
|
||||
{
|
||||
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
|
||||
@@ -898,7 +1081,7 @@ void receive_query(struct listener *listen, time_t now)
|
||||
else
|
||||
#endif
|
||||
{
|
||||
m = answer_request(header, ((char *) header) + PACKETSZ, (size_t)n,
|
||||
m = answer_request(header, ((char *) header) + daemon->packet_buff_sz, (size_t)n,
|
||||
dst_addr_4, netmask, now);
|
||||
|
||||
if (m >= 1)
|
||||
@@ -915,6 +1098,66 @@ void receive_query(struct listener *listen, time_t now)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
static int tcp_key_recurse(time_t now, int status, int class, char *keyname, struct server *server)
|
||||
{
|
||||
/* Recurse up the key heirarchy */
|
||||
size_t n;
|
||||
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
|
||||
unsigned char *payload = &packet[2];
|
||||
struct dns_header *header = (struct dns_header *)payload;
|
||||
u16 *length = (u16 *)packet;
|
||||
int new_status;
|
||||
unsigned char c1, c2;
|
||||
|
||||
n = dnssec_generate_query(header, ((char *) header) + 65536, keyname, class,
|
||||
status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr);
|
||||
|
||||
*length = htons(n);
|
||||
|
||||
if (!read_write(server->tcpfd, packet, n + sizeof(u16), 0) ||
|
||||
!read_write(server->tcpfd, &c1, 1, 1) ||
|
||||
!read_write(server->tcpfd, &c2, 1, 1) ||
|
||||
!read_write(server->tcpfd, payload, (c1 << 8) | c2, 1))
|
||||
{
|
||||
close(server->tcpfd);
|
||||
server->tcpfd = -1;
|
||||
new_status = STAT_INSECURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = (c1 << 8) | c2;
|
||||
|
||||
if (status == STAT_NEED_KEY)
|
||||
new_status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, class);
|
||||
else
|
||||
new_status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, class);
|
||||
|
||||
if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
|
||||
{
|
||||
if ((new_status = tcp_key_recurse(now, new_status, class, daemon->keyname, server) == STAT_SECURE))
|
||||
{
|
||||
if (status == STAT_NEED_KEY)
|
||||
new_status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, class);
|
||||
else
|
||||
new_status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, class);
|
||||
|
||||
if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
|
||||
{
|
||||
my_syslog(LOG_ERR, _("Unexpected missing data for DNSSEC validation"));
|
||||
status = STAT_INSECURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(packet);
|
||||
|
||||
return new_status;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* The daemon forks before calling this: it should deal with one connection,
|
||||
blocking as neccessary, and then return. Note, need to be a bit careful
|
||||
about resources for debug mode, when the fork is suppressed: that's
|
||||
@@ -927,7 +1170,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
#ifdef HAVE_AUTH
|
||||
int local_auth = 0;
|
||||
#endif
|
||||
int checking_disabled, check_subnet;
|
||||
int checking_disabled, check_subnet, no_cache_dnssec = 0, cache_secure = 0;
|
||||
size_t m;
|
||||
unsigned short qtype;
|
||||
unsigned int gotname;
|
||||
@@ -960,7 +1203,8 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
check_subnet = 0;
|
||||
|
||||
/* save state of "cd" flag in query */
|
||||
checking_disabled = header->hb4 & HB4_CD;
|
||||
if ((checking_disabled = header->hb4 & HB4_CD))
|
||||
no_cache_dnssec = 1;
|
||||
|
||||
/* RFC 4035: sect 4.6 para 2 */
|
||||
header->hb4 &= ~HB4_AD;
|
||||
@@ -1080,6 +1324,14 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
size = add_do_bit(header, size, ((char *) header) + 65536);
|
||||
header->hb4 |= HB4_CD;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
/* Copy connection mark of incoming query to outgoing connection. */
|
||||
if (option_bool(OPT_CONNTRACK))
|
||||
@@ -1103,7 +1355,8 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
|
||||
if (!read_write(last_server->tcpfd, packet, size + sizeof(u16), 0) ||
|
||||
!read_write(last_server->tcpfd, &c1, 1, 1) ||
|
||||
!read_write(last_server->tcpfd, &c2, 1, 1))
|
||||
!read_write(last_server->tcpfd, &c2, 1, 1) ||
|
||||
!read_write(last_server->tcpfd, payload, (c1 << 8) | c2, 1))
|
||||
{
|
||||
close(last_server->tcpfd);
|
||||
last_server->tcpfd = -1;
|
||||
@@ -1111,8 +1364,6 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
}
|
||||
|
||||
m = (c1 << 8) | c2;
|
||||
if (!read_write(last_server->tcpfd, payload, m, 1))
|
||||
return packet;
|
||||
|
||||
if (!gotname)
|
||||
strcpy(daemon->namebuff, "query");
|
||||
@@ -1124,6 +1375,36 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
|
||||
(struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled)
|
||||
{
|
||||
int class, status;
|
||||
|
||||
status = dnssec_validate_reply(now, header, m, daemon->namebuff, daemon->keyname, &class);
|
||||
|
||||
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
|
||||
{
|
||||
if ((status = tcp_key_recurse(now, status, class, daemon->keyname, last_server)) == STAT_SECURE)
|
||||
status = dnssec_validate_reply(now, header, m, daemon->namebuff, daemon->keyname, &class);
|
||||
}
|
||||
|
||||
log_query(F_KEYTAG | F_SECSTAT, "result", NULL,
|
||||
status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
|
||||
|
||||
if (status == STAT_BOGUS)
|
||||
no_cache_dnssec = 1;
|
||||
|
||||
if (status == STAT_SECURE)
|
||||
cache_secure = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* restore CD bit to the value in the query */
|
||||
if (checking_disabled)
|
||||
header->hb4 |= HB4_CD;
|
||||
else
|
||||
header->hb4 &= ~HB4_CD;
|
||||
|
||||
/* There's no point in updating the cache, since this process will exit and
|
||||
lose the information after a few queries. We make this call for the alias and
|
||||
@@ -1133,8 +1414,8 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
sending replies containing questions and bogus answers. */
|
||||
if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
|
||||
m = process_reply(header, now, last_server, (unsigned int)m,
|
||||
option_bool(OPT_NO_REBIND) && !norebind, checking_disabled,
|
||||
check_subnet, &peer_addr);
|
||||
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec,
|
||||
cache_secure, check_subnet, &peer_addr);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -1168,6 +1449,9 @@ static struct frec *allocate_frec(time_t now)
|
||||
f->flags = 0;
|
||||
#ifdef HAVE_IPV6
|
||||
f->rfd6 = NULL;
|
||||
#endif
|
||||
#ifdef HAVE_DNSSEC
|
||||
f->blocking_query = NULL;
|
||||
#endif
|
||||
daemon->frec_list = f;
|
||||
}
|
||||
@@ -1210,7 +1494,6 @@ static struct randfd *allocate_rfd(int family)
|
||||
|
||||
return NULL; /* doom */
|
||||
}
|
||||
|
||||
static void free_frec(struct frec *f)
|
||||
{
|
||||
if (f->rfd4 && --(f->rfd4->refcount) == 0)
|
||||
@@ -1226,13 +1509,29 @@ static void free_frec(struct frec *f)
|
||||
|
||||
f->rfd6 = NULL;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (f->stash)
|
||||
{
|
||||
blockdata_free(f->stash);
|
||||
f->stash = NULL;
|
||||
}
|
||||
|
||||
/* Anything we're waiting on is pointless now, too */
|
||||
if (f->blocking_query)
|
||||
free_frec(f->blocking_query);
|
||||
f->blocking_query = NULL;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
/* if wait==NULL return a free or older than TIMEOUT record.
|
||||
else return *wait zero if one available, or *wait is delay to
|
||||
when the oldest in-use record will expire. Impose an absolute
|
||||
limit of 4*TIMEOUT before we wipe things (for random sockets) */
|
||||
struct frec *get_new_frec(time_t now, int *wait)
|
||||
limit of 4*TIMEOUT before we wipe things (for random sockets).
|
||||
If force is set, always return a result, even if we have
|
||||
to allocate above the limit. */
|
||||
struct frec *get_new_frec(time_t now, int *wait, int force)
|
||||
{
|
||||
struct frec *f, *oldest, *target;
|
||||
int count;
|
||||
@@ -1281,7 +1580,7 @@ struct frec *get_new_frec(time_t now, int *wait)
|
||||
}
|
||||
|
||||
/* none available, calculate time 'till oldest record expires */
|
||||
if (count > daemon->ftabsize)
|
||||
if (!force && count > daemon->ftabsize)
|
||||
{
|
||||
static time_t last_log = 0;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
34
src/ip6addr.h
Normal file
34
src/ip6addr.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991, or
|
||||
(at your option) version 3 dated 29 June, 2007.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#define IN6_IS_ADDR_ULA(a) \
|
||||
((((__const uint32_t *) (a))[0] & htonl (0xff000000)) \
|
||||
== htonl (0xfd000000))
|
||||
|
||||
#define IN6_IS_ADDR_ULA_ZERO(a) \
|
||||
(((__const uint32_t *) (a))[0] == htonl (0xfd000000) \
|
||||
&& ((__const uint32_t *) (a))[1] == 0 \
|
||||
&& ((__const uint32_t *) (a))[2] == 0 \
|
||||
&& ((__const uint32_t *) (a))[3] == 0)
|
||||
|
||||
#define IN6_IS_ADDR_LINK_LOCAL_ZERO(a) \
|
||||
(((__const uint32_t *) (a))[0] == htonl (0xfe800000) \
|
||||
&& ((__const uint32_t *) (a))[1] == 0 \
|
||||
&& ((__const uint32_t *) (a))[2] == 0 \
|
||||
&& ((__const uint32_t *) (a))[3] == 0)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
41
src/option.c
41
src/option.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -138,6 +138,8 @@ struct myoption {
|
||||
#define LOPT_QUIET_DHCP 326
|
||||
#define LOPT_QUIET_DHCP6 327
|
||||
#define LOPT_QUIET_RA 328
|
||||
#define LOPT_SEC_VALID 329
|
||||
#define LOPT_DNSKEY 330
|
||||
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
@@ -274,6 +276,8 @@ static const struct myoption opts[] =
|
||||
{ "auth-peer", 1, 0, LOPT_AUTHPEER },
|
||||
{ "ipset", 1, 0, LOPT_IPSET },
|
||||
{ "synth-domain", 1, 0, LOPT_SYNTH },
|
||||
{ "dnssec", 0, 0, LOPT_SEC_VALID },
|
||||
{ "dnskey", 1, 0, LOPT_DNSKEY },
|
||||
#ifdef OPTION6_PREFIX_CLASS
|
||||
{ "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
|
||||
#endif
|
||||
@@ -407,7 +411,7 @@ static struct {
|
||||
{ LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
|
||||
{ LOPT_ADD_MAC, OPT_ADD_MAC, NULL, gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
|
||||
{ LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add requestor's IP subnet to forwarded DNS queries."), NULL },
|
||||
{ LOPT_DNSSEC, OPT_DNSSEC, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
|
||||
{ LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
|
||||
{ LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
|
||||
{ LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
|
||||
{ LOPT_FQDN, OPT_FQDN_UPDATE, NULL, gettext_noop("Allow DHCP clients to do their own DDNS updates."), NULL },
|
||||
@@ -424,6 +428,10 @@ static struct {
|
||||
{ LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
|
||||
{ LOPT_IPSET, ARG_DUP, "/<domain>/<ipset>[,<ipset>...]", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
|
||||
{ LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL },
|
||||
#ifdef HAVE_DNSSEC
|
||||
{ LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
|
||||
{ LOPT_DNSKEY, ARG_DUP, "<domain>,<algo>,<key>", gettext_noop("Specify trust anchor DNSKEY"), NULL },
|
||||
#endif
|
||||
#ifdef OPTION6_PREFIX_CLASS
|
||||
{ LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
|
||||
#endif
|
||||
@@ -3665,9 +3673,34 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
daemon->host_records_tail = new;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
case LOPT_DNSKEY:
|
||||
{
|
||||
struct dnskey *new = opt_malloc(sizeof(struct dnskey));
|
||||
char *key64, *algo;
|
||||
|
||||
if (!(comma = split(arg)) || !(algo = split(comma)) || !(key64 = split(algo)) ||
|
||||
!atoi_check16(comma, &new->flags) || !atoi_check16(algo, &new->algo) ||
|
||||
!(new->name = canonicalise_opt(arg)))
|
||||
ret_err(_("bad DNSKEY"));
|
||||
|
||||
|
||||
/* Upper bound on length */
|
||||
new->key = opt_malloc((3*strlen(key64)/4));
|
||||
unhide_metas(key64);
|
||||
if ((new->keylen = parse_base64(key64, new->key)) == -1)
|
||||
ret_err(_("bad base64 in DNSKEY"));
|
||||
|
||||
new->next = daemon->dnskeys;
|
||||
daemon->dnskeys = new;
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)"));
|
||||
ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
110
src/radv.c
110
src/radv.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -31,8 +31,8 @@ struct ra_param {
|
||||
int ind, managed, other, found_context, first;
|
||||
char *if_name;
|
||||
struct dhcp_netid *tags;
|
||||
struct in6_addr link_local, link_global;
|
||||
unsigned int pref_time, adv_interval;
|
||||
struct in6_addr link_local, link_global, ula;
|
||||
unsigned int glob_pref_time, link_pref_time, ula_pref_time, adv_interval;
|
||||
};
|
||||
|
||||
struct search_param {
|
||||
@@ -206,6 +206,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
|
||||
struct dhcp_opt *opt_cfg;
|
||||
struct ra_interface *ra_param = find_iface_param(iface_name);
|
||||
int done_dns = 0, old_prefix = 0;
|
||||
unsigned int min_pref_time;
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
FILE *f;
|
||||
#endif
|
||||
@@ -228,7 +229,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
|
||||
parm.if_name = iface_name;
|
||||
parm.first = 1;
|
||||
parm.now = now;
|
||||
parm.pref_time = 0;
|
||||
parm.glob_pref_time = parm.link_pref_time = parm.ula_pref_time = 0;
|
||||
parm.adv_interval = calc_interval(ra_param);
|
||||
|
||||
/* set tag with name == interface */
|
||||
@@ -245,6 +246,18 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
|
||||
if (!iface_enumerate(AF_INET6, &parm, add_prefixes))
|
||||
return;
|
||||
|
||||
/* Find smallest preferred time within address classes,
|
||||
to use as lifetime for options. This is a rather arbitrary choice. */
|
||||
min_pref_time = 0xffffffff;
|
||||
if (parm.glob_pref_time != 0 && parm.glob_pref_time < min_pref_time)
|
||||
min_pref_time = parm.glob_pref_time;
|
||||
|
||||
if (parm.ula_pref_time != 0 && parm.ula_pref_time < min_pref_time)
|
||||
min_pref_time = parm.ula_pref_time;
|
||||
|
||||
if (parm.link_pref_time != 0 && parm.link_pref_time < min_pref_time)
|
||||
min_pref_time = parm.link_pref_time;
|
||||
|
||||
/* Look for constructed contexts associated with addresses which have gone,
|
||||
and advertise them with preferred_time == 0 RFC 6204 4.3 L-13 */
|
||||
for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
|
||||
@@ -358,22 +371,48 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
|
||||
|
||||
if (opt_cfg->opt == OPTION6_DNS_SERVER)
|
||||
{
|
||||
struct in6_addr *a = (struct in6_addr *)opt_cfg->val;
|
||||
|
||||
struct in6_addr *a;
|
||||
int len;
|
||||
|
||||
done_dns = 1;
|
||||
if (opt_cfg->len == 0 || (IN6_IS_ADDR_UNSPECIFIED(a) && parm.pref_time != 0))
|
||||
|
||||
if (opt_cfg->len == 0)
|
||||
continue;
|
||||
|
||||
put_opt6_char(ICMP6_OPT_RDNSS);
|
||||
put_opt6_char((opt_cfg->len/8) + 1);
|
||||
put_opt6_short(0);
|
||||
put_opt6_long(parm.pref_time);
|
||||
/* zero means "self" */
|
||||
for (i = 0; i < opt_cfg->len; i += IN6ADDRSZ, a++)
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(a))
|
||||
put_opt6(&parm.link_global, IN6ADDRSZ);
|
||||
else
|
||||
put_opt6(a, IN6ADDRSZ);
|
||||
/* reduce len for any addresses we can't substitute */
|
||||
for (a = (struct in6_addr *)opt_cfg->val, len = opt_cfg->len, i = 0;
|
||||
i < opt_cfg->len; i += IN6ADDRSZ, a++)
|
||||
if ((IN6_IS_ADDR_UNSPECIFIED(a) && parm.glob_pref_time == 0) ||
|
||||
(IN6_IS_ADDR_ULA_ZERO(a) && parm.ula_pref_time == 0) ||
|
||||
(IN6_IS_ADDR_LINK_LOCAL_ZERO(a) && parm.link_pref_time == 0))
|
||||
len -= IN6ADDRSZ;
|
||||
|
||||
if (len != 0)
|
||||
{
|
||||
put_opt6_char(ICMP6_OPT_RDNSS);
|
||||
put_opt6_char((len/8) + 1);
|
||||
put_opt6_short(0);
|
||||
put_opt6_long(min_pref_time);
|
||||
|
||||
for (a = (struct in6_addr *)opt_cfg->val, i = 0; i < opt_cfg->len; i += IN6ADDRSZ, a++)
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(a))
|
||||
{
|
||||
if (parm.glob_pref_time != 0)
|
||||
put_opt6(&parm.link_global, IN6ADDRSZ);
|
||||
}
|
||||
else if (IN6_IS_ADDR_ULA_ZERO(a))
|
||||
{
|
||||
if (parm.ula_pref_time != 0)
|
||||
put_opt6(&parm.ula, IN6ADDRSZ);
|
||||
}
|
||||
else if (IN6_IS_ADDR_LINK_LOCAL_ZERO(a))
|
||||
{
|
||||
if (parm.link_pref_time != 0)
|
||||
put_opt6(&parm.link_local, IN6ADDRSZ);
|
||||
}
|
||||
else
|
||||
put_opt6(a, IN6ADDRSZ);
|
||||
}
|
||||
}
|
||||
|
||||
if (opt_cfg->opt == OPTION6_DOMAIN_SEARCH && opt_cfg->len != 0)
|
||||
@@ -383,7 +422,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
|
||||
put_opt6_char(ICMP6_OPT_DNSSL);
|
||||
put_opt6_char(len + 1);
|
||||
put_opt6_short(0);
|
||||
put_opt6_long(parm.pref_time);
|
||||
put_opt6_long(min_pref_time);
|
||||
put_opt6(opt_cfg->val, opt_cfg->len);
|
||||
|
||||
/* pad */
|
||||
@@ -392,13 +431,13 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
|
||||
}
|
||||
}
|
||||
|
||||
if (daemon->port == NAMESERVER_PORT && !done_dns && parm.pref_time != 0)
|
||||
if (daemon->port == NAMESERVER_PORT && !done_dns && parm.link_pref_time != 0)
|
||||
{
|
||||
/* default == us, as long as we are supplying DNS service. */
|
||||
put_opt6_char(ICMP6_OPT_RDNSS);
|
||||
put_opt6_char(3);
|
||||
put_opt6_short(0);
|
||||
put_opt6_long(parm.pref_time);
|
||||
put_opt6_long(min_pref_time);
|
||||
put_opt6(&parm.link_local, IN6ADDRSZ);
|
||||
}
|
||||
|
||||
@@ -444,7 +483,16 @@ static int add_prefixes(struct in6_addr *local, int prefix,
|
||||
if (if_index == param->ind)
|
||||
{
|
||||
if (IN6_IS_ADDR_LINKLOCAL(local))
|
||||
param->link_local = *local;
|
||||
{
|
||||
/* Can there be more than one LL address?
|
||||
Select the one with the longest preferred time
|
||||
if there is. */
|
||||
if (preferred > param->link_pref_time)
|
||||
{
|
||||
param->link_pref_time = preferred;
|
||||
param->link_local = *local;
|
||||
}
|
||||
}
|
||||
else if (!IN6_IS_ADDR_LOOPBACK(local) &&
|
||||
!IN6_IS_ADDR_MULTICAST(local))
|
||||
{
|
||||
@@ -534,11 +582,22 @@ static int add_prefixes(struct in6_addr *local, int prefix,
|
||||
/* configured time is ceiling */
|
||||
if (!constructed || preferred > time)
|
||||
preferred = time;
|
||||
|
||||
if (preferred > param->pref_time)
|
||||
|
||||
if (IN6_IS_ADDR_ULA(local))
|
||||
{
|
||||
param->pref_time = preferred;
|
||||
param->link_global = *local;
|
||||
if (preferred > param->ula_pref_time)
|
||||
{
|
||||
param->ula_pref_time = preferred;
|
||||
param->ula = *local;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (preferred > param->glob_pref_time)
|
||||
{
|
||||
param->glob_pref_time = preferred;
|
||||
param->link_global = *local;
|
||||
}
|
||||
}
|
||||
|
||||
if (real_prefix != 0)
|
||||
@@ -564,7 +623,6 @@ static int add_prefixes(struct in6_addr *local, int prefix,
|
||||
if (!option_bool(OPT_QUIET_RA))
|
||||
my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s", param->if_name, daemon->addrbuff);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,13 +16,6 @@
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
|
||||
#define CHECK_LEN(header, pp, plen, len) \
|
||||
((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))
|
||||
|
||||
#define ADD_RDLEN(header, pp, plen, len) \
|
||||
(!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1))
|
||||
|
||||
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
char *name, int isExtract, int extrabytes)
|
||||
{
|
||||
@@ -274,7 +267,7 @@ int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes)
|
||||
unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes)
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
@@ -500,7 +493,7 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t
|
||||
else if (is_sign &&
|
||||
i == arcount - 1 &&
|
||||
class == C_ANY &&
|
||||
(type == T_SIG || type == T_TSIG))
|
||||
type == T_TSIG)
|
||||
*is_sign = 1;
|
||||
}
|
||||
|
||||
@@ -515,7 +508,7 @@ struct macparm {
|
||||
};
|
||||
|
||||
static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
int optno, unsigned char *opt, size_t optlen)
|
||||
int optno, unsigned char *opt, size_t optlen, int set_do)
|
||||
{
|
||||
unsigned char *lenp, *datap, *p;
|
||||
int rdlen;
|
||||
@@ -531,7 +524,8 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned
|
||||
*p++ = 0; /* empty name */
|
||||
PUTSHORT(T_OPT, p);
|
||||
PUTSHORT(daemon->edns_pktsz, p); /* max packet length */
|
||||
PUTLONG(0, p); /* extended RCODE */
|
||||
PUTSHORT(0, p); /* extended RCODE and version */
|
||||
PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */
|
||||
lenp = p;
|
||||
PUTSHORT(0, p); /* RDLEN */
|
||||
rdlen = 0;
|
||||
@@ -543,7 +537,7 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned
|
||||
else
|
||||
{
|
||||
int i, is_sign;
|
||||
unsigned short code, len;
|
||||
unsigned short code, len, flags;
|
||||
|
||||
if (ntohs(header->arcount) != 1 ||
|
||||
!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign)) ||
|
||||
@@ -551,14 +545,24 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned
|
||||
(!(p = skip_name(p, header, plen, 10))))
|
||||
return plen;
|
||||
|
||||
p += 8; /* skip UDP length and RCODE */
|
||||
|
||||
p += 6; /* skip UDP length and RCODE */
|
||||
GETSHORT(flags, p);
|
||||
if (set_do)
|
||||
{
|
||||
p -=2;
|
||||
PUTSHORT(flags | 0x8000, p);
|
||||
}
|
||||
|
||||
lenp = p;
|
||||
GETSHORT(rdlen, p);
|
||||
if (!CHECK_LEN(header, p, plen, rdlen))
|
||||
return plen; /* bad packet */
|
||||
datap = p;
|
||||
|
||||
/* no option to add */
|
||||
if (optno == 0)
|
||||
return plen;
|
||||
|
||||
/* check if option already there */
|
||||
for (i = 0; i + 4 < rdlen; i += len + 4)
|
||||
{
|
||||
@@ -573,10 +577,13 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned
|
||||
return plen; /* Too big */
|
||||
}
|
||||
|
||||
PUTSHORT(optno, p);
|
||||
PUTSHORT(optlen, p);
|
||||
memcpy(p, opt, optlen);
|
||||
p += optlen;
|
||||
if (optno != 0)
|
||||
{
|
||||
PUTSHORT(optno, p);
|
||||
PUTSHORT(optlen, p);
|
||||
memcpy(p, opt, optlen);
|
||||
p += optlen;
|
||||
}
|
||||
|
||||
PUTSHORT(p - datap, lenp);
|
||||
return p - (unsigned char *)header;
|
||||
@@ -602,7 +609,7 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p
|
||||
if (!match)
|
||||
return 1; /* continue */
|
||||
|
||||
parm->plen = add_pseudoheader(parm->header, parm->plen, parm->limit, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen);
|
||||
parm->plen = add_pseudoheader(parm->header, parm->plen, parm->limit, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen, 0);
|
||||
|
||||
return 0; /* done */
|
||||
}
|
||||
@@ -681,9 +688,16 @@ size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, unio
|
||||
struct subnet_opt opt;
|
||||
|
||||
len = calc_subnet_opt(&opt, source);
|
||||
return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len);
|
||||
return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0);
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
size_t add_do_bit(struct dns_header *header, size_t plen, char *limit)
|
||||
{
|
||||
return add_pseudoheader(header, plen, (unsigned char *)limit, 0, NULL, 0, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
|
||||
{
|
||||
/* Section 9.2, Check that subnet option in reply matches. */
|
||||
@@ -878,7 +892,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name)
|
||||
expired and cleaned out that way.
|
||||
Return 1 if we reject an address because it look like part of dns-rebinding attack. */
|
||||
int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,
|
||||
char **ipsets, int is_sign, int check_rebind, int checking_disabled)
|
||||
char **ipsets, int is_sign, int check_rebind, int no_cache_dnssec, int secure)
|
||||
{
|
||||
unsigned char *p, *p1, *endrr, *namep;
|
||||
int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
|
||||
@@ -907,8 +921,9 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
int found = 0, cname_count = 5;
|
||||
struct crec *cpp = NULL;
|
||||
int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
|
||||
int secflag = secure ? F_DNSSECOK : 0;
|
||||
unsigned long cttl = ULONG_MAX, attl;
|
||||
|
||||
|
||||
namep = p;
|
||||
if (!extract_name(header, qlen, &p, name, 1, 4))
|
||||
return 0; /* bad packet */
|
||||
@@ -969,7 +984,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
goto cname_loop;
|
||||
}
|
||||
|
||||
cache_insert(name, &addr, now, cttl, name_encoding | F_REVERSE);
|
||||
cache_insert(name, &addr, now, cttl, name_encoding | secflag | F_REVERSE);
|
||||
found = 1;
|
||||
}
|
||||
|
||||
@@ -987,7 +1002,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
ttl = find_soa(header, qlen, NULL);
|
||||
}
|
||||
if (ttl)
|
||||
cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags);
|
||||
cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags | secflag);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1037,7 +1052,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
{
|
||||
if (!cname_count--)
|
||||
return 0; /* looped CNAMES */
|
||||
newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD);
|
||||
newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD | secflag);
|
||||
if (newc)
|
||||
{
|
||||
newc->addr.cname.target.cache = NULL;
|
||||
@@ -1080,7 +1095,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
}
|
||||
#endif
|
||||
|
||||
newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD);
|
||||
newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD | secflag);
|
||||
if (newc && cpp)
|
||||
{
|
||||
cpp->addr.cname.target.cache = newc;
|
||||
@@ -1106,7 +1121,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
pointing at this, inherit its TTL */
|
||||
if (ttl || cpp)
|
||||
{
|
||||
newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags);
|
||||
newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags | secflag);
|
||||
if (newc && cpp)
|
||||
{
|
||||
cpp->addr.cname.target.cache = newc;
|
||||
@@ -1118,15 +1133,13 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
}
|
||||
|
||||
/* Don't put stuff from a truncated packet into the cache.
|
||||
Don't cache replies where DNSSEC validation was turned off, either
|
||||
the upstream server told us so, or the original query specified it.
|
||||
Don't cache replies from non-recursive nameservers, since we may get a
|
||||
reply containing a CNAME but not its target, even though the target
|
||||
does exist. */
|
||||
if (!(header->hb3 & HB3_TC) &&
|
||||
!(header->hb4 & HB4_CD) &&
|
||||
(header->hb4 & HB4_RA) &&
|
||||
!checking_disabled)
|
||||
!no_cache_dnssec)
|
||||
cache_end_insert();
|
||||
|
||||
return 0;
|
||||
@@ -1437,7 +1450,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
int dryrun = 0, sec_reqd = 0;
|
||||
int is_sign;
|
||||
struct crec *crecp;
|
||||
int nxdomain = 0, auth = 1, trunc = 0;
|
||||
int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
|
||||
struct mx_srv_record *rec;
|
||||
|
||||
/* If there is an RFC2671 pseudoheader then it will be overwritten by
|
||||
@@ -1612,6 +1625,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
continue;
|
||||
|
||||
if (!(crecp->flags & F_DNSSECOK))
|
||||
sec_data = 0;
|
||||
|
||||
if (crecp->flags & F_NEG)
|
||||
{
|
||||
ans = 1;
|
||||
@@ -1785,6 +1801,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
|
||||
break;
|
||||
|
||||
if (!(crecp->flags & F_DNSSECOK))
|
||||
sec_data = 0;
|
||||
|
||||
if (crecp->flags & F_CNAME)
|
||||
{
|
||||
char *cname_target = cache_get_cname_target(crecp);
|
||||
@@ -1859,6 +1878,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME)) &&
|
||||
(qtype == T_CNAME || (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))))
|
||||
{
|
||||
if (!(crecp->flags & F_DNSSECOK))
|
||||
sec_data = 0;
|
||||
|
||||
ans = 1;
|
||||
if (!dryrun)
|
||||
{
|
||||
@@ -2037,7 +2059,13 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
/* truncation */
|
||||
if (trunc)
|
||||
header->hb3 |= HB3_TC;
|
||||
|
||||
|
||||
header->hb4 &= ~HB4_AD;
|
||||
|
||||
if (option_bool(OPT_DNSSEC_VALID) || option_bool(OPT_DNSSEC_PROXY))
|
||||
if (sec_data)
|
||||
header->hb4 |= HB4_AD;
|
||||
|
||||
if (nxdomain)
|
||||
SET_RCODE(header, NXDOMAIN);
|
||||
else
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -24,7 +24,7 @@ struct state {
|
||||
int clid_len, iaid, ia_type, interface, hostname_auth, lease_allocate;
|
||||
char *client_hostname, *hostname, *domain, *send_domain;
|
||||
struct dhcp_context *context;
|
||||
struct in6_addr *link_address, *fallback;
|
||||
struct in6_addr *link_address, *fallback, *ll_addr, *ula_addr;
|
||||
unsigned int xid, fqdn_flags;
|
||||
char *iface_name;
|
||||
void *packet_options, *end;
|
||||
@@ -73,7 +73,8 @@ static void calculate_times(struct dhcp_context *context, unsigned int *min_time
|
||||
|
||||
|
||||
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
|
||||
struct in6_addr *fallback, size_t sz, struct in6_addr *client_addr, time_t now)
|
||||
struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
|
||||
size_t sz, struct in6_addr *client_addr, time_t now)
|
||||
{
|
||||
struct dhcp_vendor *vendor;
|
||||
int msg_type;
|
||||
@@ -93,6 +94,8 @@ unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *if
|
||||
state.interface = interface;
|
||||
state.iface_name = iface_name;
|
||||
state.fallback = fallback;
|
||||
state.ll_addr = ll_addr;
|
||||
state.ula_addr = ula_addr;
|
||||
state.mac_len = 0;
|
||||
state.tags = NULL;
|
||||
state.link_address = NULL;
|
||||
@@ -1269,36 +1272,59 @@ static struct dhcp_netid *add_options(struct state *state, int do_refresh)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (opt_cfg->opt == OPTION6_DNS_SERVER)
|
||||
{
|
||||
done_dns = 1;
|
||||
if (opt_cfg->len == 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (opt_cfg->opt == OPTION6_REFRESH_TIME)
|
||||
done_refresh = 1;
|
||||
|
||||
o = new_opt6(opt_cfg->opt);
|
||||
if (opt_cfg->flags & DHOPT_ADDR6)
|
||||
{
|
||||
int j;
|
||||
struct in6_addr *a = (struct in6_addr *)opt_cfg->val;
|
||||
for (j = 0; j < opt_cfg->len; j+=IN6ADDRSZ, a++)
|
||||
{
|
||||
/* zero means "self" (but not in vendorclass options.) */
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(a))
|
||||
{
|
||||
if (!add_local_addrs(state->context))
|
||||
put_opt6(state->fallback, IN6ADDRSZ);
|
||||
int len, j;
|
||||
struct in6_addr *a;
|
||||
|
||||
if (opt_cfg->opt == OPTION6_DNS_SERVER)
|
||||
done_dns = 1;
|
||||
|
||||
for (a = (struct in6_addr *)opt_cfg->val, len = opt_cfg->len, j = 0;
|
||||
j < opt_cfg->len; j += IN6ADDRSZ, a++)
|
||||
if ((IN6_IS_ADDR_ULA_ZERO(a) && IN6_IS_ADDR_UNSPECIFIED(state->ula_addr)) ||
|
||||
(IN6_IS_ADDR_LINK_LOCAL_ZERO(a) && IN6_IS_ADDR_UNSPECIFIED(state->ll_addr)))
|
||||
len -= IN6ADDRSZ;
|
||||
|
||||
if (len != 0)
|
||||
{
|
||||
|
||||
o = new_opt6(opt_cfg->opt);
|
||||
|
||||
for (a = (struct in6_addr *)opt_cfg->val, j = 0; j < opt_cfg->len; j+=IN6ADDRSZ, a++)
|
||||
{
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(a))
|
||||
{
|
||||
if (!add_local_addrs(state->context))
|
||||
put_opt6(state->fallback, IN6ADDRSZ);
|
||||
}
|
||||
else if (IN6_IS_ADDR_ULA_ZERO(a))
|
||||
{
|
||||
if (!IN6_IS_ADDR_UNSPECIFIED(state->ula_addr))
|
||||
put_opt6(state->ula_addr, IN6ADDRSZ);
|
||||
}
|
||||
else if (IN6_IS_ADDR_LINK_LOCAL_ZERO(a))
|
||||
{
|
||||
if (!IN6_IS_ADDR_UNSPECIFIED(state->ll_addr))
|
||||
put_opt6(state->ll_addr, IN6ADDRSZ);
|
||||
}
|
||||
else
|
||||
put_opt6(a, IN6ADDRSZ);
|
||||
}
|
||||
else
|
||||
put_opt6(a, IN6ADDRSZ);
|
||||
}
|
||||
|
||||
end_opt6(o);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
o = new_opt6(opt_cfg->opt);
|
||||
if (opt_cfg->val)
|
||||
put_opt6(opt_cfg->val, opt_cfg->len);
|
||||
end_opt6(o);
|
||||
}
|
||||
else if (opt_cfg->val)
|
||||
put_opt6(opt_cfg->val, opt_cfg->len);
|
||||
end_opt6(o);
|
||||
}
|
||||
|
||||
if (daemon->port == NAMESERVER_PORT && !done_dns)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
66
src/util.c
66
src/util.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -109,10 +109,10 @@ static int check_name(char *in)
|
||||
|
||||
if (in[l-1] == '.')
|
||||
{
|
||||
if (l == 1) return 0;
|
||||
in[l-1] = 0;
|
||||
nowhite = 1;
|
||||
}
|
||||
|
||||
|
||||
for (; (c = *in); in++)
|
||||
{
|
||||
if (c == '.')
|
||||
@@ -482,6 +482,66 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
|
||||
return i;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
static int charval(char c)
|
||||
{
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
return c - 'A';
|
||||
|
||||
if (c >= 'a' && c <= 'z')
|
||||
return c - 'a' + 26;
|
||||
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0' + 52;
|
||||
|
||||
if (c == '+')
|
||||
return 62;
|
||||
|
||||
if (c == '/')
|
||||
return 63;
|
||||
|
||||
if (c == '=')
|
||||
return -1;
|
||||
|
||||
return -2;
|
||||
}
|
||||
|
||||
int parse_base64(char *in, char *out)
|
||||
{
|
||||
char *p = out;
|
||||
int i, val[4];
|
||||
|
||||
while (*in)
|
||||
{
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
while (*in == ' ')
|
||||
in++;
|
||||
if (*in == 0)
|
||||
return -1;
|
||||
if ((val[i] = charval(*in++)) == -2)
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (*in == ' ')
|
||||
in++;
|
||||
|
||||
if (val[1] == -1)
|
||||
return -1; /* too much padding */
|
||||
|
||||
*p++ = (val[0] << 2) | (val[1] >> 4);
|
||||
|
||||
if (val[2] != -1)
|
||||
*p++ = (val[1] << 4) | ( val[2] >> 2);
|
||||
|
||||
if (val[3] != -1)
|
||||
*p++ = (val[2] << 6) | val[3];
|
||||
}
|
||||
|
||||
return p - out;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* return 0 for no match, or (no matched octets) + 1 */
|
||||
int memcmp_masked(unsigned char *a, unsigned char *b, int len, unsigned int mask)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user