]> git.ipfire.org Git - people/ms/dnsmasq.git/commitdiff
Handle domain names with '.' or /000 within labels.
authorSimon Kelley <simon@thekelleys.org.uk>
Tue, 21 Apr 2015 21:57:06 +0000 (22:57 +0100)
committerSimon Kelley <simon@thekelleys.org.uk>
Tue, 21 Apr 2015 21:57:06 +0000 (22:57 +0100)
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.

src/dns-protocol.h
src/dnsmasq.c
src/dnssec.c
src/rfc1035.c
src/util.c

index 16fade33d98cda7bfef1a267de4b5f7d49b5ca61..7f5d686bb1503fdf93405165005a5c6bf6c86fb4 100644 (file)
@@ -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
index 20b15c05103a922a98a6413433249235b546e941..19a6428b09e80cb04f399819a50d62cc9fa95ad7 100644 (file)
@@ -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
 
index 05e0983cb25134ba7e4d5d8f0d48678efebd455b..c116a7b5f6f45a7699abfaf82eef7f8c986eed85 100644 (file)
@@ -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);
index a995ab50d74adde068c8839684f9b3a44f4976d0..19fecc818c06211422982e9ce62aff414d5fcdb4 100644 (file)
@@ -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;
                  }
index 648bc4d4b4283cef8e4fb8df7d82dcf739d65b3e..0c1a48b4700ae0288724752b1df6b96e5589905c 100644 (file)
@@ -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++;