]> git.ipfire.org Git - thirdparty/ldns.git/commitdiff
Fix #130: Unless $TLL is defined, ttl defaults to the last explicitly stated value.
authorWillem Toorop <willem@nlnetlabs.nl>
Wed, 27 Oct 2021 11:51:05 +0000 (13:51 +0200)
committerWillem Toorop <willem@nlnetlabs.nl>
Wed, 27 Oct 2021 11:51:05 +0000 (13:51 +0200)
Thanks Benno

Changelog
dnssec_zone.c
rr.c
zone.c

index 2aea36b57b5c404aaea6e1f035f5f123995693ba..a47714914c421af8ccd1082074eaddb152736fac 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -51,6 +51,8 @@
          to two RRs. Thanks Emilio Caballero
        * Fix #131: Drill sig chasing breaks with gcc-11 and
          strict-aliasing. Thanks Stanislav Levin
+       * Fix #130: Unless $TLL is defined, ttl defaults to the last
+         explicitly stated value. Thanks Benno
 
 1.7.1  2019-07-26
        * bugfix: Manage verification paths for OpenSSL >= 1.1.0
index a731483a40dbce19fb14897cfb05a4815c656d30..3957225500c792b234d412978e35b36326d35ce2 100644 (file)
@@ -605,9 +605,13 @@ ldns_todo_nsec3_ents_node_free(ldns_rbnode_t *node, void *arg) {
        LDNS_FREE(node);
 }
 
+ldns_status _ldns_rr_new_frm_fp_l_internal(ldns_rr **newrr, FILE *fp,
+               uint32_t *default_ttl, ldns_rdf **origin, ldns_rdf **prev,
+               int *line_nr, bool *explicit_ttl);
+
 ldns_status
 ldns_dnssec_zone_new_frm_fp_l(ldns_dnssec_zone** z, FILE* fp, const ldns_rdf* origin,
-               uint32_t ttl, ldns_rr_class ATTR_UNUSED(c), int* line_nr)
+               uint32_t default_ttl, ldns_rr_class ATTR_UNUSED(c), int* line_nr)
 {
        ldns_rr* cur_rr;
        size_t i;
@@ -637,13 +641,19 @@ ldns_dnssec_zone_new_frm_fp_l(ldns_dnssec_zone** z, FILE* fp, const ldns_rdf* or
 #ifdef FASTER_DNSSEC_ZONE_NEW_FRM_FP
        ldns_zone* zone = NULL;
 #else
-       uint32_t  my_ttl = ttl;
+       ldns_rr  *prev_rr = NULL;
+       uint32_t   my_ttl = default_ttl;
+       /* RFC 1035 Section 5.1, says 'Omitted class and TTL values are default
+        * to the last explicitly stated values.'
+        */
+       bool ttl_from_TTL = false;
+       bool explicit_ttl = false;
 #endif
 
        ldns_rbtree_init(&todo_nsec3_ents, ldns_dname_compare_v);
 
 #ifdef FASTER_DNSSEC_ZONE_NEW_FRM_FP
-       status = ldns_zone_new_frm_fp_l(&zone, fp, origin,ttl, c, line_nr);
+       status = ldns_zone_new_frm_fp_l(&zone, fp, origin, default_ttl, c, line_nr);
        if (status != LDNS_STATUS_OK)
                goto error;
 #endif
@@ -673,13 +683,61 @@ ldns_dnssec_zone_new_frm_fp_l(ldns_dnssec_zone** z, FILE* fp, const ldns_rdf* or
                status = LDNS_STATUS_OK;
 #else
        while (!feof(fp)) {
+               /* If ttl came from $TTL line, then it should be the default.
+                * (RFC 2308 Section 4)
+                * Otherwise it "defaults to the last explicitly stated value"
+                * (RFC 1035 Section 5.1)
+                */
+               if (ttl_from_TTL)
+                       my_ttl = default_ttl;
                status = ldns_rr_new_frm_fp_l(&cur_rr, fp, &my_ttl, &my_origin,
-                               &my_prev, line_nr);
-
+                               &my_prev, line_nr, &explicit_ttl);
 #endif
                switch (status) {
                case LDNS_STATUS_OK:
+#ifndef FASTER_DNSSEC_ZONE_NEW_FRM_FP
+                       if (explicit_ttl) {
+                               if (!ttl_from_TTL) {
+                                       /* No $TTL, so ttl "defaults to the
+                                        * last explicitly stated value"
+                                        * (RFC 1035 Section 5.1)
+                                        */
+                                       my_ttl = ldns_rr_ttl(cur_rr);
+                               }
+                       /* When ttl is implicit, try to adhere to the rules as
+                        * much as posssible. (also for compatibility with bind)
+                        * This was changed when fixing an issue with ZONEMD
+                        * which hashes the TTL too.
+                        */
+                       } else if (ldns_rr_get_type(cur_rr) == LDNS_RR_TYPE_SIG
+                              ||  ldns_rr_get_type(cur_rr) == LDNS_RR_TYPE_RRSIG) {
+                               if (ldns_rr_rd_count(cur_rr) >= 4
+                               &&  ldns_rdf_get_type(ldns_rr_rdf(cur_rr, 3)) == LDNS_RDF_TYPE_INT32)
+
+                                       /* SIG without explicit ttl get ttl
+                                        * from the original_ttl field
+                                        * (RFC 2535 Section 7.2)
+                                        *
+                                        * Similarly for RRSIG, but stated less
+                                        * specifically in the spec.
+                                        * (RFC 4034 Section 3)
+                                        */
+                                       ldns_rr_set_ttl(cur_rr,
+                                           ldns_rdf2native_int32(
+                                               ldns_rr_rdf(rr, 3)));
+
+                       } else if (prev_rr
+                              &&  ldns_rr_get_type(prev_rr) == ldns_rr_get_type(cur_rr)
+                              &&  ldns_dname_compare( ldns_rr_owner(prev_rr)
+                                                    , ldns_rr_owner(cur_rr)) == 0)
+
+                               /* "TTLs of all RRs in an RRSet must be the same"
+                                * (RFC 2881 Section 5.2)
+                                */
+                               ldns_rr_set_ttl(cur_rr, ldns_rr_ttl(prev_rr));
 
+                       prev_rr = cur_rr;
+#endif
                        status = ldns_dnssec_zone_add_rr(newzone, cur_rr);
                        if (status ==
                                LDNS_STATUS_DNSSEC_NSEC3_ORIGINAL_NOT_FOUND) {
@@ -699,9 +757,16 @@ ldns_dnssec_zone_new_frm_fp_l(ldns_dnssec_zone** z, FILE* fp, const ldns_rdf* or
 
                        break;
 
+               case LDNS_STATUS_SYNTAX_TTL:    /* the ttl was set*/
+#ifndef FASTER_DNSSEC_ZONE_NEW_FRM_FP
+                       default_ttl = my_ttl;
+                       ttl_from_TTL = true;
+#endif
+                       status = LDNS_STATUS_OK;
+                       break;
+
 
                case LDNS_STATUS_SYNTAX_EMPTY:  /* empty line was seen */
-               case LDNS_STATUS_SYNTAX_TTL:    /* the ttl was set*/
                case LDNS_STATUS_SYNTAX_ORIGIN: /* the origin was set*/
                        status = LDNS_STATUS_OK;
                        break;
diff --git a/rr.c b/rr.c
index c98989a460954e7ef125fc066927c4f89b8dc83d..433e61d4dbee4a6c765bc3f900b1bf2ebb798407 100644 (file)
--- a/rr.c
+++ b/rr.c
@@ -111,8 +111,9 @@ ldns_rdf_type_maybe_quoted(ldns_rdf_type rdf_type)
  */
 static ldns_status
 ldns_rr_new_frm_str_internal(ldns_rr **newrr, const char *str,
-                                                        uint32_t default_ttl, const ldns_rdf *origin,
-                                                        ldns_rdf **prev, bool question)
+                             uint32_t default_ttl, const ldns_rdf *origin,
+                             ldns_rdf **prev, bool question,
+                            bool *explicit_ttl)
 {
        ldns_rr *new;
        const ldns_rr_descriptor *desc;
@@ -198,6 +199,9 @@ ldns_rr_new_frm_str_internal(ldns_rr **newrr, const char *str,
                } else {
                        ttl_val = default_ttl;
                }
+               if (explicit_ttl)
+                       *explicit_ttl = false;
+
                /* we not ASSUMING the TTL is missing and that
                 * the rest of the RR is still there. That is
                 * CLASS TYPE RDATA
@@ -217,6 +221,9 @@ ldns_rr_new_frm_str_internal(ldns_rr **newrr, const char *str,
                        strlcpy(type, ttl, type_sz);
                }
        } else {
+               if (explicit_ttl)
+                       *explicit_ttl = true;
+
                if (-1 == ldns_bget_token(
                                rr_buf, clas, "\t\n ", LDNS_SYNTAX_DATALEN)) {
 
@@ -259,7 +266,7 @@ ldns_rr_new_frm_str_internal(ldns_rr **newrr, const char *str,
        }
        ldns_buffer_new_frm_data(rd_buf, rdata, strlen(rdata));
 
-       if (strlen(owner) <= 1 && strncmp(owner, "@", 1) == 0) {
+       if (strncmp(owner, "@", 1) == 0) {
                if (origin) {
                        ldns_rr_set_owner(new, ldns_rdf_clone(origin));
                } else if (prev && *prev) {
@@ -669,7 +676,8 @@ ldns_rr_new_frm_str(ldns_rr **newrr, const char *str,
                                            default_ttl,
                                            origin,
                                            prev,
-                                           false);
+                                           false,
+                                           NULL);
 }
 
 ldns_status
@@ -681,7 +689,8 @@ ldns_rr_new_question_frm_str(ldns_rr **newrr, const char *str,
                                            0,
                                            origin,
                                            prev,
-                                           true);
+                                           true,
+                                           NULL);
 }
 
 /* Strip whitespace from the start and the end of <line>.  */
@@ -707,7 +716,9 @@ ldns_rr_new_frm_fp(ldns_rr **newrr, FILE *fp, uint32_t *ttl, ldns_rdf **origin,
 }
 
 ldns_status
-ldns_rr_new_frm_fp_l(ldns_rr **newrr, FILE *fp, uint32_t *default_ttl, ldns_rdf **origin, ldns_rdf **prev, int *line_nr)
+_ldns_rr_new_frm_fp_l_internal(ldns_rr **newrr, FILE *fp,
+               uint32_t *default_ttl, ldns_rdf **origin, ldns_rdf **prev,
+               int *line_nr, bool *explicit_ttl)
 {
        char *line = NULL;
        size_t limit = 0;
@@ -754,9 +765,11 @@ ldns_rr_new_frm_fp_l(ldns_rr **newrr, FILE *fp, uint32_t *default_ttl, ldns_rdf
                return LDNS_STATUS_SYNTAX_EMPTY;
        } else {
                if (origin && *origin) {
-                       s = ldns_rr_new_frm_str(&rr, (const char*) line, ttl, *origin, prev);
+                       s = ldns_rr_new_frm_str_internal(&rr, (const char*)line,
+                               ttl, *origin, prev, false, explicit_ttl);
                } else {
-                       s = ldns_rr_new_frm_str(&rr, (const char*) line, ttl, NULL, prev);
+                       s = ldns_rr_new_frm_str_internal(&rr, (const char*)line,
+                               ttl, NULL, prev, false, explicit_ttl);
                }
        }
        LDNS_FREE(line);
@@ -771,6 +784,14 @@ ldns_rr_new_frm_fp_l(ldns_rr **newrr, FILE *fp, uint32_t *default_ttl, ldns_rdf
        return s;
 }
 
+ldns_status
+ldns_rr_new_frm_fp_l(ldns_rr **newrr, FILE *fp, uint32_t *default_ttl,
+               ldns_rdf **origin, ldns_rdf **prev, int *line_nr)
+{
+       return _ldns_rr_new_frm_fp_l_internal(newrr, fp, default_ttl, origin,
+                       prev, line_nr, NULL);
+}
+
 void
 ldns_rr_set_owner(ldns_rr *rr, ldns_rdf *owner)
 {
diff --git a/zone.c b/zone.c
index 00b559bfa332045170ae5a46f8cd2aacc2a27cca..a3d534e5bba1633977aef4743b9be77fba0273a5 100644 (file)
--- a/zone.c
+++ b/zone.c
@@ -189,19 +189,28 @@ ldns_zone_new_frm_fp(ldns_zone **z, FILE *fp, const ldns_rdf *origin, uint32_t t
        return ldns_zone_new_frm_fp_l(z, fp, origin, ttl, c, NULL);
 }
 
+ldns_status _ldns_rr_new_frm_fp_l_internal(ldns_rr **newrr, FILE *fp,
+               uint32_t *default_ttl, ldns_rdf **origin, ldns_rdf **prev,
+               int *line_nr, bool *explicit_ttl);
+
 /* XXX: class is never used */
 ldns_status
-ldns_zone_new_frm_fp_l(ldns_zone **z, FILE *fp, const ldns_rdf *origin, uint32_t ttl,
-        ldns_rr_class ATTR_UNUSED(c), int *line_nr)
+ldns_zone_new_frm_fp_l(ldns_zone **z, FILE *fp, const ldns_rdf *origin,
+       uint32_t default_ttl, ldns_rr_class ATTR_UNUSED(c), int *line_nr)
 {
        ldns_zone *newzone;
-       ldns_rr *rr;
+       ldns_rr *rr, *prev_rr = NULL;
        uint32_t my_ttl;
        ldns_rdf *my_origin;
        ldns_rdf *my_prev;
        bool soa_seen = false;  /* 2 soa are an error */
        ldns_status s;
        ldns_status ret;
+       /* RFC 1035 Section 5.1, says 'Omitted class and TTL values are default
+        * to the last explicitly stated values.'
+        */
+       bool ttl_from_TTL = false;
+       bool explicit_ttl = false;
 
        /* most cases of error are memory problems */
        ret = LDNS_STATUS_MEM_ERR;
@@ -210,7 +219,7 @@ ldns_zone_new_frm_fp_l(ldns_zone **z, FILE *fp, const ldns_rdf *origin, uint32_t
        my_origin = NULL;
        my_prev = NULL;
 
-       my_ttl    = ttl;
+       my_ttl    = default_ttl;
        
        if (origin) {
                my_origin = ldns_rdf_clone(origin);
@@ -224,9 +233,58 @@ ldns_zone_new_frm_fp_l(ldns_zone **z, FILE *fp, const ldns_rdf *origin, uint32_t
        if (!newzone) goto error;
 
        while(!feof(fp)) {
-               s = ldns_rr_new_frm_fp_l(&rr, fp, &my_ttl, &my_origin, &my_prev, line_nr);
+               /* If ttl came from $TTL line, then it should be the default.
+                * (RFC 2308 Section 4)
+                * Otherwise it "defaults to the last explicitly stated value"
+                * (RFC 1035 Section 5.1)
+                */
+               if (ttl_from_TTL)
+                       my_ttl = default_ttl;
+               s = _ldns_rr_new_frm_fp_l_internal(&rr, fp, &my_ttl, &my_origin,
+                               &my_prev, line_nr, &explicit_ttl);
                switch (s) {
                case LDNS_STATUS_OK:
+                       if (explicit_ttl) {
+                               if (!ttl_from_TTL) {
+                                       /* No $TTL, so ttl "defaults to the
+                                        * last explicitly stated value"
+                                        * (RFC 1035 Section 5.1)
+                                        */
+                                       my_ttl = ldns_rr_ttl(rr);
+                               }
+                       /* When ttl is implicit, try to adhere to the rules as
+                        * much as posssible. (also for compatibility with bind)
+                        * This was changed when fixing an issue with ZONEMD
+                        * which hashes the TTL too.
+                        */
+                       } else if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_SIG
+                              ||  ldns_rr_get_type(rr) == LDNS_RR_TYPE_RRSIG) {
+                               if (ldns_rr_rd_count(rr) >= 4
+                               &&  ldns_rdf_get_type(ldns_rr_rdf(rr, 3)) == LDNS_RDF_TYPE_INT32)
+
+                                       /* SIG without explicit ttl get ttl
+                                        * from the original_ttl field
+                                        * (RFC 2535 Section 7.2)
+                                        *
+                                        * Similarly for RRSIG, but stated less
+                                        * specifically in the spec.
+                                        * (RFC 4034 Section 3)
+                                        */
+                                       ldns_rr_set_ttl(rr,
+                                           ldns_rdf2native_int32(
+                                               ldns_rr_rdf(rr, 3)));
+
+                       } else if (prev_rr
+                              &&  ldns_rr_get_type(prev_rr) == ldns_rr_get_type(rr)
+                              &&  ldns_dname_compare( ldns_rr_owner(prev_rr)
+                                                    , ldns_rr_owner(rr)) == 0)
+
+                               /* "TTLs of all RRs in an RRSet must be the same"
+                                * (RFC 2881 Section 5.2)
+                                */
+                               ldns_rr_set_ttl(rr, ldns_rr_ttl(prev_rr));
+
+                       prev_rr = rr;
                        if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_SOA) {
                                if (soa_seen) {
                                        /* second SOA 
@@ -255,6 +313,8 @@ ldns_zone_new_frm_fp_l(ldns_zone **z, FILE *fp, const ldns_rdf *origin, uint32_t
                        /* empty line was seen */
                case LDNS_STATUS_SYNTAX_TTL:
                        /* the function set the ttl */
+                       default_ttl = my_ttl;
+                       ttl_from_TTL = true;
                        break;
                case LDNS_STATUS_SYNTAX_ORIGIN:
                        /* the function set the origin */