summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Kelley <simon@thekelleys.org.uk>2015-04-21 22:57:06 +0100
committerSimon Kelley <simon@thekelleys.org.uk>2015-04-21 22:57:06 +0100
commitcbe379ad6b52a538a4416a7cd992817e5637ccf9 (patch)
tree72bef1eb68e8109747df0d6fa9e950e242490b49
parent338b340be9e7198f5c0f68133d070d6598a0814c (diff)
downloaddnsmasq-escape_names.tar.gz
Handle domain names with '.' or /000 within labels.v2.73rc5escape_names
Only in DNSSEC mode, where we might need to validate or store such names. In none-DNSSEC mode, simply don't cache these, as before.
-rw-r--r--src/dns-protocol.h4
-rw-r--r--src/dnsmasq.c15
-rw-r--r--src/dnssec.c40
-rw-r--r--src/rfc1035.c16
-rw-r--r--src/util.c9
5 files changed, 71 insertions, 13 deletions
diff --git a/src/dns-protocol.h b/src/dns-protocol.h
index 16fade3..7f5d686 100644
--- a/src/dns-protocol.h
+++ b/src/dns-protocol.h
@@ -142,3 +142,7 @@ struct dns_header {
#define ADD_RDLEN(header, pp, plen, len) \
(!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1))
+
+/* Escape character in our presentation format for names.
+ Cannot be '.' or /000 and must be !isprint() */
+#define NAME_ESCAPE 1
diff --git a/src/dnsmasq.c b/src/dnsmasq.c
index 20b15c0..19a6428 100644
--- a/src/dnsmasq.c
+++ b/src/dnsmasq.c
@@ -102,8 +102,19 @@ int main (int argc, char **argv)
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
- daemon->keyname = safe_malloc(MAXDNAME);
- daemon->workspacename = safe_malloc(MAXDNAME);
+ /* Note that both /000 and '.' are allowed within labels. These get
+ represented in presentation format using NAME_ESCAPE as an escape
+ character when in DNSSEC mode.
+ In theory, if all the characters in a name were /000 or
+ '.' or NAME_ESCAPE then all would have to be escaped, so the
+ presentation format would be twice as long as the spec.
+
+ daemon->namebuff was previously allocated by the option-reading
+ code before we knew if we're in DNSSEC mode, so reallocate here. */
+ free(daemon->namebuff);
+ daemon->namebuff = safe_malloc(MAXDNAME * 2);
+ daemon->keyname = safe_malloc(MAXDNAME * 2);
+ daemon->workspacename = safe_malloc(MAXDNAME * 2);
}
#endif
diff --git a/src/dnssec.c b/src/dnssec.c
index 05e0983..c116a7b 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -321,10 +321,18 @@ static int verify(struct blockdata *key_data, unsigned int key_len, unsigned cha
thus generating names in canonical form.
Calling to_wire followed by from_wire is almost an identity,
except that the UC remains mapped to LC.
+
+ Note that both /000 and '.' are allowed within labels. These get
+ represented in presentation format using NAME_ESCAPE as an escape
+ character. In theory, if all the characters in a name were /000 or
+ '.' or NAME_ESCAPE then all would have to be escaped, so the
+ presentation format would be twice as long as the spec (1024).
+ The buffers are all delcared as 2049 (allowing for the trailing zero)
+ for this reason.
*/
static int to_wire(char *name)
{
- unsigned char *l, *p, term;
+ unsigned char *l, *p, *q, term;
int len;
for (l = (unsigned char*)name; *l != 0; l = p)
@@ -332,7 +340,10 @@ static int to_wire(char *name)
for (p = l; *p != '.' && *p != 0; p++)
if (*p >= 'A' && *p <= 'Z')
*p = *p - 'A' + 'a';
-
+ else if (*p == NAME_ESCAPE)
+ for (q = p; *q; q++)
+ *q = *(q+1);
+
term = *p;
if ((len = p - l) != 0)
@@ -351,13 +362,23 @@ static int to_wire(char *name)
/* Note: no compression allowed in input. */
static void from_wire(char *name)
{
- unsigned char *l;
+ unsigned char *l, *p, *last;
int len;
-
+
+ for (last = (unsigned char *)name; *last != 0; last += *last+1);
+
for (l = (unsigned char *)name; *l != 0; l += len+1)
{
len = *l;
memmove(l, l+1, len);
+ for (p = l; p < l + len; p++)
+ if (*p == '.' || *p == 0 || *p == NAME_ESCAPE)
+ {
+ memmove(p+1, p, 1 + last - p);
+ len++;
+ *p++ = NAME_ESCAPE;
+ }
+
l[len] = '.';
}
@@ -645,7 +666,7 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
if (left1 != 0)
memmove(buff1, buff1 + len1 - left1, left1);
- if ((len1 = get_rdata(header, plen, end1, buff1 + left1, MAXDNAME - left1, &p1, &dp1)) == 0)
+ if ((len1 = get_rdata(header, plen, end1, buff1 + left1, (MAXDNAME * 2) - left1, &p1, &dp1)) == 0)
{
quit = 1;
len1 = end1 - p1;
@@ -656,7 +677,7 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
if (left2 != 0)
memmove(buff2, buff2 + len2 - left2, left2);
- if ((len2 = get_rdata(header, plen, end2, buff2 + left2, MAXDNAME - left2, &p2, &dp2)) == 0)
+ if ((len2 = get_rdata(header, plen, end2, buff2 + left2, (MAXDNAME *2) - left2, &p2, &dp2)) == 0)
{
quit = 1;
len2 = end2 - p2;
@@ -902,10 +923,11 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
end = p + rdlen;
- /* canonicalise rdata and calculate length of same, use name buffer as workspace */
+ /* canonicalise rdata and calculate length of same, use name buffer as workspace.
+ Note that name buffer is twice MAXDNAME long in DNSSEC mode. */
cp = p;
dp = rr_desc;
- for (len = 0; (seg = get_rdata(header, plen, end, name, MAXDNAME, &cp, &dp)) != 0; len += seg);
+ for (len = 0; (seg = get_rdata(header, plen, end, name, MAXDNAME * 2, &cp, &dp)) != 0; len += seg);
len += end - cp;
len = htons(len);
hash->update(ctx, 2, (unsigned char *)&len);
@@ -913,7 +935,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
/* Now canonicalise again and digest. */
cp = p;
dp = rr_desc;
- while ((seg = get_rdata(header, plen, end, name, MAXDNAME, &cp, &dp)))
+ while ((seg = get_rdata(header, plen, end, name, MAXDNAME * 2, &cp, &dp)))
hash->update(ctx, seg, (unsigned char *)name);
if (cp != end)
hash->update(ctx, end - cp, cp);
diff --git a/src/rfc1035.c b/src/rfc1035.c
index a995ab5..19fecc8 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -128,6 +128,15 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
if (isExtract)
{
unsigned char c = *p;
+#ifdef HAVE_DNSSEC
+ if (option_bool(OPT_DNSSEC_VALID))
+ {
+ if (c == 0 || c == '.' || c == NAME_ESCAPE)
+ *cp++ = NAME_ESCAPE;
+ *cp++ = c;
+ }
+ else
+#endif
if (c != 0 && c != '.')
*cp++ = c;
else
@@ -144,9 +153,14 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
cp++;
if (c1 >= 'A' && c1 <= 'Z')
c1 += 'a' - 'A';
+#ifdef HAVE_DNSSEC
+ if (option_bool(OPT_DNSSEC_VALID) && c1 == NAME_ESCAPE)
+ c1 = *cp++;
+#endif
+
if (c2 >= 'A' && c2 <= 'Z')
c2 += 'a' - 'A';
-
+
if (c1 != c2)
retvalue = 2;
}
diff --git a/src/util.c b/src/util.c
index 648bc4d..0c1a48b 100644
--- a/src/util.c
+++ b/src/util.c
@@ -226,7 +226,14 @@ unsigned char *do_rfc1035_name(unsigned char *p, char *sval)
{
unsigned char *cp = p++;
for (j = 0; *sval && (*sval != '.'); sval++, j++)
- *p++ = *sval;
+ {
+#ifdef HAVE_DNSSEC
+ if (option_bool(OPT_DNSSEC_VALID) && *sval == NAME_ESCAPE)
+ *p++ = *(++sval);
+ else
+#endif
+ *p++ = *sval;
+ }
*cp = j;
if (*sval)
sval++;