]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Extend ttlval to accept ISO 8601 durations
authorMatthijs Mekking <matthijs@isc.org>
Mon, 2 Sep 2019 13:46:28 +0000 (15:46 +0200)
committerMatthijs Mekking <matthijs@isc.org>
Wed, 6 Nov 2019 21:31:44 +0000 (22:31 +0100)
The ttlval configuration types are replaced by duration configuration
types. The duration is an ISO 8601 duration that is going to be used
for DNSSEC key timings such as key lifetimes, signature resign
intervals and refresh periods, etc. But it is also still allowed to
use the BIND ttlval ways of configuring intervals (number plus
optional unit).

A duration is stored as an array of 7 different time parts.
A duration can either be expressed in weeks, or in a combination of
the other datetime indicators.

Add several unit tests to ensure the correct value is parsed given
different string values.

17 files changed:
bin/check/named-checkconf.c
bin/named/named.conf.docbook
bin/named/server.c
bin/named/zoneconf.c
doc/arm/master.zoneopt.xml
doc/arm/options.grammar.xml
doc/arm/redirect.zoneopt.xml
lib/bind9/check.c
lib/isccfg/include/isccfg/cfg.h
lib/isccfg/include/isccfg/grammar.h
lib/isccfg/namedconf.c
lib/isccfg/parser.c
lib/isccfg/tests/Kyuafile
lib/isccfg/tests/Makefile.in
lib/isccfg/tests/duration_test.c [new file with mode: 0644]
lib/isccfg/win32/libisccfg.def
util/copyrights

index a0dea04667a57bf4a26b0970233b371a38530153..c8b12911bbeed2a15ea3a6f9e6d0aeb2fb3d6035 100644 (file)
@@ -421,7 +421,7 @@ configure_zone(const char *vclass, const char *view,
 
        obj = NULL;
        if (get_maps(maps, "max-zone-ttl", &obj)) {
-               maxttl = cfg_obj_asuint32(obj);
+               maxttl = cfg_obj_asduration(obj);
                zone_options |= DNS_ZONEOPT_CHECKTTL;
        }
 
index a013873e18bb17849f98415e051135908afd2b51..ecae8014cc412c49a4d0a567e30a267f28b94506 100644 (file)
@@ -208,7 +208,7 @@ options {
            [ dscp <replaceable>integer</replaceable> ] { ( <replaceable>masters</replaceable> | <replaceable>ipv4_address</replaceable> [ port
            <replaceable>integer</replaceable> ] | <replaceable>ipv6_address</replaceable> [ port <replaceable>integer</replaceable> ] ) [ key
            <replaceable>string</replaceable> ]; ... } ] [ zone-directory <replaceable>quoted_string</replaceable> ] [
-           in-memory <replaceable>boolean</replaceable> ] [ min-update-interval <replaceable>ttlval</replaceable> ]; ... };
+           in-memory <replaceable>boolean</replaceable> ] [ min-update-interval <replaceable>duration</replaceable> ]; ... };
        check-dup-records ( fail | warn | ignore );
        check-integrity <replaceable>boolean</replaceable>;
        check-mx ( fail | warn | ignore );
@@ -290,18 +290,18 @@ options {
        fstrm-set-output-notify-threshold <replaceable>integer</replaceable>;
        fstrm-set-output-queue-model ( mpsc | spsc );
        fstrm-set-output-queue-size <replaceable>integer</replaceable>;
-       fstrm-set-reopen-interval <replaceable>ttlval</replaceable>;
+       fstrm-set-reopen-interval <replaceable>duration</replaceable>;
        geoip-directory ( <replaceable>quoted_string</replaceable> | none );
        glue-cache <replaceable>boolean</replaceable>;
        heartbeat-interval <replaceable>integer</replaceable>;
        hostname ( <replaceable>quoted_string</replaceable> | none );
        inline-signing <replaceable>boolean</replaceable>;
-       interface-interval <replaceable>ttlval</replaceable>;
+       interface-interval <replaceable>duration</replaceable>;
        ixfr-from-differences ( primary | master | secondary | slave |
            <replaceable>boolean</replaceable> );
        keep-response-order { <replaceable>address_match_element</replaceable>; ... };
        key-directory <replaceable>quoted_string</replaceable>;
-       lame-ttl <replaceable>ttlval</replaceable>;
+       lame-ttl <replaceable>duration</replaceable>;
        listen-on [ port <replaceable>integer</replaceable> ] [ dscp
            <replaceable>integer</replaceable> ] {
            <replaceable>address_match_element</replaceable>; ... };
@@ -315,28 +315,28 @@ options {
        masterfile-style ( full | relative );
        match-mapped-addresses <replaceable>boolean</replaceable>;
        max-cache-size ( default | unlimited | <replaceable>sizeval</replaceable> | <replaceable>percentage</replaceable> );
-       max-cache-ttl <replaceable>ttlval</replaceable>;
+       max-cache-ttl <replaceable>duration</replaceable>;
        max-clients-per-query <replaceable>integer</replaceable>;
        max-journal-size ( default | unlimited | <replaceable>sizeval</replaceable> );
-       max-ncache-ttl <replaceable>ttlval</replaceable>;
+       max-ncache-ttl <replaceable>duration</replaceable>;
        max-records <replaceable>integer</replaceable>;
        max-recursion-depth <replaceable>integer</replaceable>;
        max-recursion-queries <replaceable>integer</replaceable>;
        max-refresh-time <replaceable>integer</replaceable>;
        max-retry-time <replaceable>integer</replaceable>;
        max-rsa-exponent-size <replaceable>integer</replaceable>;
-       max-stale-ttl <replaceable>ttlval</replaceable>;
+       max-stale-ttl <replaceable>duration</replaceable>;
        max-transfer-idle-in <replaceable>integer</replaceable>;
        max-transfer-idle-out <replaceable>integer</replaceable>;
        max-transfer-time-in <replaceable>integer</replaceable>;
        max-transfer-time-out <replaceable>integer</replaceable>;
        max-udp-size <replaceable>integer</replaceable>;
-       max-zone-ttl ( unlimited | <replaceable>ttlval</replaceable> );
+       max-zone-ttl ( unlimited | <replaceable>duration</replaceable> );
        memstatistics <replaceable>boolean</replaceable>;
        memstatistics-file <replaceable>quoted_string</replaceable>;
        message-compression <replaceable>boolean</replaceable>;
-       min-cache-ttl <replaceable>ttlval</replaceable>;
-       min-ncache-ttl <replaceable>ttlval</replaceable>;
+       min-cache-ttl <replaceable>duration</replaceable>;
+       min-ncache-ttl <replaceable>duration</replaceable>;
        min-refresh-time <replaceable>integer</replaceable>;
        min-retry-time <replaceable>integer</replaceable>;
        minimal-any <replaceable>boolean</replaceable>;
@@ -353,8 +353,8 @@ options {
        notify-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * ) ]
            [ dscp <replaceable>integer</replaceable> ];
        notify-to-soa <replaceable>boolean</replaceable>;
-       nta-lifetime <replaceable>ttlval</replaceable>;
-       nta-recheck <replaceable>ttlval</replaceable>;
+       nta-lifetime <replaceable>duration</replaceable>;
+       nta-recheck <replaceable>duration</replaceable>;
        nxdomain-redirect <replaceable>string</replaceable>;
        pid-file ( <replaceable>quoted_string</replaceable> | none );
        port <replaceable>integer</replaceable>;
@@ -401,13 +401,13 @@ options {
        response-padding { <replaceable>address_match_element</replaceable>; ... } block-size
            <replaceable>integer</replaceable>;
        response-policy { zone <replaceable>string</replaceable> [ add-soa <replaceable>boolean</replaceable> ] [ log
-           <replaceable>boolean</replaceable> ] [ max-policy-ttl <replaceable>ttlval</replaceable> ] [ min-update-interval
-           <replaceable>ttlval</replaceable> ] [ policy ( cname | disabled | drop | given | no-op |
+           <replaceable>boolean</replaceable> ] [ max-policy-ttl <replaceable>duration</replaceable> ] [ min-update-interval
+           <replaceable>duration</replaceable> ] [ policy ( cname | disabled | drop | given | no-op |
            nodata | nxdomain | passthru | tcp-only <replaceable>quoted_string</replaceable> ) ] [
            recursive-only <replaceable>boolean</replaceable> ] [ nsip-enable <replaceable>boolean</replaceable> ] [
            nsdname-enable <replaceable>boolean</replaceable> ]; ... } [ add-soa <replaceable>boolean</replaceable> ] [
-           break-dnssec <replaceable>boolean</replaceable> ] [ max-policy-ttl <replaceable>ttlval</replaceable> ] [
-           min-update-interval <replaceable>ttlval</replaceable> ] [ min-ns-dots <replaceable>integer</replaceable> ] [
+           break-dnssec <replaceable>boolean</replaceable> ] [ max-policy-ttl <replaceable>duration</replaceable> ] [
+           min-update-interval <replaceable>duration</replaceable> ] [ min-ns-dots <replaceable>integer</replaceable> ] [
            nsip-wait-recurse <replaceable>boolean</replaceable> ] [ qname-wait-recurse <replaceable>boolean</replaceable> ]
            [ recursive-only <replaceable>boolean</replaceable> ] [ nsip-enable <replaceable>boolean</replaceable> ] [
            nsdname-enable <replaceable>boolean</replaceable> ] [ dnsrps-enable <replaceable>boolean</replaceable> ] [
@@ -421,7 +421,7 @@ options {
        serial-query-rate <replaceable>integer</replaceable>;
        serial-update-method ( date | increment | unixtime );
        server-id ( <replaceable>quoted_string</replaceable> | none | hostname );
-       servfail-ttl <replaceable>ttlval</replaceable>;
+       servfail-ttl <replaceable>duration</replaceable>;
        session-keyalg <replaceable>string</replaceable>;
        session-keyfile ( <replaceable>quoted_string</replaceable> | none );
        session-keyname <replaceable>string</replaceable>;
@@ -432,7 +432,7 @@ options {
        sortlist { <replaceable>address_match_element</replaceable>; ... };
        stacksize ( default | unlimited | <replaceable>sizeval</replaceable> );
        stale-answer-enable <replaceable>boolean</replaceable>;
-       stale-answer-ttl <replaceable>ttlval</replaceable>;
+       stale-answer-ttl <replaceable>duration</replaceable>;
        startup-notify-rate <replaceable>integer</replaceable>;
        statistics-file <replaceable>quoted_string</replaceable>;
        synth-from-dnssec <replaceable>boolean</replaceable>;
@@ -564,7 +564,7 @@ view <replaceable>string</replaceable> [ <replaceable>class</replaceable> ] {
            [ dscp <replaceable>integer</replaceable> ] { ( <replaceable>masters</replaceable> | <replaceable>ipv4_address</replaceable> [ port
            <replaceable>integer</replaceable> ] | <replaceable>ipv6_address</replaceable> [ port <replaceable>integer</replaceable> ] ) [ key
            <replaceable>string</replaceable> ]; ... } ] [ zone-directory <replaceable>quoted_string</replaceable> ] [
-           in-memory <replaceable>boolean</replaceable> ] [ min-update-interval <replaceable>ttlval</replaceable> ]; ... };
+           in-memory <replaceable>boolean</replaceable> ] [ min-update-interval <replaceable>duration</replaceable> ]; ... };
        check-dup-records ( fail | warn | ignore );
        check-integrity <replaceable>boolean</replaceable>;
        check-mx ( fail | warn | ignore );
@@ -642,7 +642,7 @@ view <replaceable>string</replaceable> [ <replaceable>class</replaceable> ] {
                secret <replaceable>string</replaceable>;
        };
        key-directory <replaceable>quoted_string</replaceable>;
-       lame-ttl <replaceable>ttlval</replaceable>;
+       lame-ttl <replaceable>duration</replaceable>;
        lmdb-mapsize <replaceable>sizeval</replaceable>;
        managed-keys { <replaceable>string</replaceable> (
            static-key | initial-key
@@ -655,25 +655,25 @@ view <replaceable>string</replaceable> [ <replaceable>class</replaceable> ] {
        match-destinations { <replaceable>address_match_element</replaceable>; ... };
        match-recursive-only <replaceable>boolean</replaceable>;
        max-cache-size ( default | unlimited | <replaceable>sizeval</replaceable> | <replaceable>percentage</replaceable> );
-       max-cache-ttl <replaceable>ttlval</replaceable>;
+       max-cache-ttl <replaceable>duration</replaceable>;
        max-clients-per-query <replaceable>integer</replaceable>;
        max-journal-size ( default | unlimited | <replaceable>sizeval</replaceable> );
-       max-ncache-ttl <replaceable>ttlval</replaceable>;
+       max-ncache-ttl <replaceable>duration</replaceable>;
        max-records <replaceable>integer</replaceable>;
        max-recursion-depth <replaceable>integer</replaceable>;
        max-recursion-queries <replaceable>integer</replaceable>;
        max-refresh-time <replaceable>integer</replaceable>;
        max-retry-time <replaceable>integer</replaceable>;
-       max-stale-ttl <replaceable>ttlval</replaceable>;
+       max-stale-ttl <replaceable>duration</replaceable>;
        max-transfer-idle-in <replaceable>integer</replaceable>;
        max-transfer-idle-out <replaceable>integer</replaceable>;
        max-transfer-time-in <replaceable>integer</replaceable>;
        max-transfer-time-out <replaceable>integer</replaceable>;
        max-udp-size <replaceable>integer</replaceable>;
-       max-zone-ttl ( unlimited | <replaceable>ttlval</replaceable> );
+       max-zone-ttl ( unlimited | <replaceable>duration</replaceable> );
        message-compression <replaceable>boolean</replaceable>;
-       min-cache-ttl <replaceable>ttlval</replaceable>;
-       min-ncache-ttl <replaceable>ttlval</replaceable>;
+       min-cache-ttl <replaceable>duration</replaceable>;
+       min-ncache-ttl <replaceable>duration</replaceable>;
        min-refresh-time <replaceable>integer</replaceable>;
        min-retry-time <replaceable>integer</replaceable>;
        minimal-any <replaceable>boolean</replaceable>;
@@ -689,8 +689,8 @@ view <replaceable>string</replaceable> [ <replaceable>class</replaceable> ] {
        notify-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * ) ]
            [ dscp <replaceable>integer</replaceable> ];
        notify-to-soa <replaceable>boolean</replaceable>;
-       nta-lifetime <replaceable>ttlval</replaceable>;
-       nta-recheck <replaceable>ttlval</replaceable>;
+       nta-lifetime <replaceable>duration</replaceable>;
+       nta-recheck <replaceable>duration</replaceable>;
        nxdomain-redirect <replaceable>string</replaceable>;
        plugin ( query ) <replaceable>string</replaceable> [ {
            <replaceable>unspecified-text</replaceable> } ];
@@ -732,13 +732,13 @@ view <replaceable>string</replaceable> [ <replaceable>class</replaceable> ] {
        response-padding { <replaceable>address_match_element</replaceable>; ... } block-size
            <replaceable>integer</replaceable>;
        response-policy { zone <replaceable>string</replaceable> [ add-soa <replaceable>boolean</replaceable> ] [ log
-           <replaceable>boolean</replaceable> ] [ max-policy-ttl <replaceable>ttlval</replaceable> ] [ min-update-interval
-           <replaceable>ttlval</replaceable> ] [ policy ( cname | disabled | drop | given | no-op |
+           <replaceable>boolean</replaceable> ] [ max-policy-ttl <replaceable>duration</replaceable> ] [ min-update-interval
+           <replaceable>duration</replaceable> ] [ policy ( cname | disabled | drop | given | no-op |
            nodata | nxdomain | passthru | tcp-only <replaceable>quoted_string</replaceable> ) ] [
            recursive-only <replaceable>boolean</replaceable> ] [ nsip-enable <replaceable>boolean</replaceable> ] [
            nsdname-enable <replaceable>boolean</replaceable> ]; ... } [ add-soa <replaceable>boolean</replaceable> ] [
-           break-dnssec <replaceable>boolean</replaceable> ] [ max-policy-ttl <replaceable>ttlval</replaceable> ] [
-           min-update-interval <replaceable>ttlval</replaceable> ] [ min-ns-dots <replaceable>integer</replaceable> ] [
+           break-dnssec <replaceable>boolean</replaceable> ] [ max-policy-ttl <replaceable>duration</replaceable> ] [
+           min-update-interval <replaceable>duration</replaceable> ] [ min-ns-dots <replaceable>integer</replaceable> ] [
            nsip-wait-recurse <replaceable>boolean</replaceable> ] [ qname-wait-recurse <replaceable>boolean</replaceable> ]
            [ recursive-only <replaceable>boolean</replaceable> ] [ nsip-enable <replaceable>boolean</replaceable> ] [
            nsdname-enable <replaceable>boolean</replaceable> ] [ dnsrps-enable <replaceable>boolean</replaceable> ] [
@@ -783,14 +783,14 @@ view <replaceable>string</replaceable> [ <replaceable>class</replaceable> ] {
                    <replaceable>integer</replaceable> | * ) ] [ dscp <replaceable>integer</replaceable> ];
                transfers <replaceable>integer</replaceable>;
        };
-       servfail-ttl <replaceable>ttlval</replaceable>;
+       servfail-ttl <replaceable>duration</replaceable>;
        sig-signing-nodes <replaceable>integer</replaceable>;
        sig-signing-signatures <replaceable>integer</replaceable>;
        sig-signing-type <replaceable>integer</replaceable>;
        sig-validity-interval <replaceable>integer</replaceable> [ <replaceable>integer</replaceable> ];
        sortlist { <replaceable>address_match_element</replaceable>; ... };
        stale-answer-enable <replaceable>boolean</replaceable>;
-       stale-answer-ttl <replaceable>ttlval</replaceable>;
+       stale-answer-ttl <replaceable>duration</replaceable>;
        synth-from-dnssec <replaceable>boolean</replaceable>;
        transfer-format ( many-answers | one-answer );
        transfer-source ( <replaceable>ipv4_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * ) ] [
@@ -867,7 +867,7 @@ view <replaceable>string</replaceable> [ <replaceable>class</replaceable> ] {
                max-transfer-idle-out <replaceable>integer</replaceable>;
                max-transfer-time-in <replaceable>integer</replaceable>;
                max-transfer-time-out <replaceable>integer</replaceable>;
-               max-zone-ttl ( unlimited | <replaceable>ttlval</replaceable> );
+               max-zone-ttl ( unlimited | <replaceable>duration</replaceable> );
                min-refresh-time <replaceable>integer</replaceable>;
                min-retry-time <replaceable>integer</replaceable>;
                multi-master <replaceable>boolean</replaceable>;
@@ -967,7 +967,7 @@ zone <replaceable>string</replaceable> [ <replaceable>class</replaceable> ] {
        max-transfer-idle-out <replaceable>integer</replaceable>;
        max-transfer-time-in <replaceable>integer</replaceable>;
        max-transfer-time-out <replaceable>integer</replaceable>;
-       max-zone-ttl ( unlimited | <replaceable>ttlval</replaceable> );
+       max-zone-ttl ( unlimited | <replaceable>duration</replaceable> );
        min-refresh-time <replaceable>integer</replaceable>;
        min-retry-time <replaceable>integer</replaceable>;
        multi-master <replaceable>boolean</replaceable>;
index e7f87e349e9e72d9180f7abb6a1a500ac86c226f..8730225864efe06411a8c9c82cb3db93d2b4f3c9 100644 (file)
@@ -2039,7 +2039,13 @@ conf_dnsrps_num(const cfg_obj_t *obj, const char *name,
                return;
        }
 
-       conf_dnsrps_sadd(ctx, " %s %d", name, cfg_obj_asuint32(sub_obj));
+       if (cfg_obj_isduration(sub_obj)) {
+               conf_dnsrps_sadd(ctx, " %s %d", name,
+                                cfg_obj_asduration(sub_obj));
+       } else {
+               conf_dnsrps_sadd(ctx, " %s %d", name,
+                                cfg_obj_asuint32(sub_obj));
+       }
 }
 
 /*
@@ -2221,15 +2227,15 @@ configure_rpz_zone(dns_view_t *view, const cfg_listelt_t *element,
        }
 
        obj = cfg_tuple_get(rpz_obj, "max-policy-ttl");
-       if (cfg_obj_isuint32(obj)) {
-               zone->max_policy_ttl = cfg_obj_asuint32(obj);
+       if (cfg_obj_isduration(obj)) {
+               zone->max_policy_ttl = cfg_obj_asduration(obj);
        } else {
                zone->max_policy_ttl = ttl_default;
        }
 
        obj = cfg_tuple_get(rpz_obj, "min-update-interval");
-       if (cfg_obj_isuint32(obj)) {
-               zone->min_update_interval = cfg_obj_asuint32(obj);
+       if (cfg_obj_isduration(obj)) {
+               zone->min_update_interval = cfg_obj_asduration(obj);
        } else {
                zone->min_update_interval = minupdateinterval_default;
        }
@@ -2448,14 +2454,14 @@ configure_rpz(dns_view_t *view, const cfg_obj_t **maps,
        }
 
        sub_obj = cfg_tuple_get(rpz_obj, "max-policy-ttl");
-       if (cfg_obj_isuint32(sub_obj))
-               ttl_default = cfg_obj_asuint32(sub_obj);
+       if (cfg_obj_isduration(sub_obj))
+               ttl_default = cfg_obj_asduration(sub_obj);
        else
                ttl_default = DNS_RPZ_MAX_TTL_DEFAULT;
 
        sub_obj = cfg_tuple_get(rpz_obj, "min-update-interval");
-       if (cfg_obj_isuint32(sub_obj))
-               minupdateinterval_default = cfg_obj_asuint32(sub_obj);
+       if (cfg_obj_isduration(sub_obj))
+               minupdateinterval_default = cfg_obj_asduration(sub_obj);
        else
                minupdateinterval_default = DNS_RPZ_MINUPDATEINTERVAL_DEFAULT;
 
@@ -2992,8 +2998,8 @@ configure_catz_zone(dns_view_t *view, const cfg_obj_t *config,
        }
 
        obj = cfg_tuple_get(catz_obj, "min-update-interval");
-       if (obj != NULL && cfg_obj_isuint32(obj))
-               opts->min_update_interval = cfg_obj_asuint32(obj);
+       if (obj != NULL && cfg_obj_isduration(obj))
+               opts->min_update_interval = cfg_obj_asduration(obj);
 
   cleanup:
        if (pview != NULL)
@@ -3641,7 +3647,7 @@ configure_dnstap(const cfg_obj_t **maps, dns_view_t *view) {
                result = named_config_get(maps, "fstrm-set-reopen-interval",
                                       &obj);
                if (result == ISC_R_SUCCESS) {
-                       i = cfg_obj_asuint32(obj);
+                       i = cfg_obj_asduration(obj);
                        fstrm_iothr_options_set_reopen_interval(fopt, i);
                }
 
@@ -4217,22 +4223,22 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
        obj = NULL;
        result = named_config_get(maps, "max-cache-ttl", &obj);
        INSIST(result == ISC_R_SUCCESS);
-       view->maxcachettl = cfg_obj_asuint32(obj);
+       view->maxcachettl = cfg_obj_asduration(obj);
 
        obj = NULL;
        result = named_config_get(maps, "max-ncache-ttl", &obj);
        INSIST(result == ISC_R_SUCCESS);
-       view->maxncachettl = cfg_obj_asuint32(obj);
+       view->maxncachettl = cfg_obj_asduration(obj);
 
        obj = NULL;
        result = named_config_get(maps, "min-cache-ttl", &obj);
        INSIST(result == ISC_R_SUCCESS);
-       view->mincachettl = cfg_obj_asuint32(obj);
+       view->mincachettl = cfg_obj_asduration(obj);
 
        obj = NULL;
        result = named_config_get(maps, "min-ncache-ttl", &obj);
        INSIST(result == ISC_R_SUCCESS);
-       view->minncachettl = cfg_obj_asuint32(obj);
+       view->minncachettl = cfg_obj_asduration(obj);
 
        obj = NULL;
        result = named_config_get(maps, "synth-from-dnssec", &obj);
@@ -4242,7 +4248,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
        obj = NULL;
        result = named_config_get(maps, "max-stale-ttl", &obj);
        INSIST(result == ISC_R_SUCCESS);
-       max_stale_ttl = ISC_MAX(cfg_obj_asuint32(obj), 1);
+       max_stale_ttl = ISC_MAX(cfg_obj_asduration(obj), 1);
 
        obj = NULL;
        result = named_config_get(maps, "stale-answer-enable", &obj);
@@ -4392,7 +4398,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
        obj = NULL;
        result = named_config_get(maps, "stale-answer-ttl", &obj);
        INSIST(result == ISC_R_SUCCESS);
-       view->staleanswerttl = ISC_MAX(cfg_obj_asuint32(obj), 1);
+       view->staleanswerttl = ISC_MAX(cfg_obj_asduration(obj), 1);
 
        /*
         * Resolver.
@@ -4512,7 +4518,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
        obj = NULL;
        result = named_config_get(maps, "lame-ttl", &obj);
        INSIST(result == ISC_R_SUCCESS);
-       lame_ttl = cfg_obj_asuint32(obj);
+       lame_ttl = cfg_obj_asduration(obj);
        if (lame_ttl > 1800)
                lame_ttl = 1800;
        dns_resolver_setlamettl(view->resolver, lame_ttl);
@@ -5216,12 +5222,12 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
        obj = NULL;
        result = named_config_get(maps, "nta-recheck", &obj);
        INSIST(result == ISC_R_SUCCESS);
-       view->nta_recheck = cfg_obj_asuint32(obj);
+       view->nta_recheck = cfg_obj_asduration(obj);
 
        obj = NULL;
        result = named_config_get(maps, "nta-lifetime", &obj);
        INSIST(result == ISC_R_SUCCESS);
-       view->nta_lifetime = cfg_obj_asuint32(obj);
+       view->nta_lifetime = cfg_obj_asduration(obj);
 
        obj = NULL;
        result = named_config_get(maps, "preferred-glue", &obj);
@@ -5464,7 +5470,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
        obj = NULL;
        result = named_config_get(maps, "servfail-ttl", &obj);
        INSIST(result == ISC_R_SUCCESS);
-       fail_ttl  = cfg_obj_asuint32(obj);
+       fail_ttl  = cfg_obj_asduration(obj);
        if (fail_ttl > 30)
                fail_ttl = 30;
        dns_view_setfailttl(view, fail_ttl);
@@ -8560,7 +8566,7 @@ load_configuration(const char *filename, named_server_t *server,
        obj = NULL;
        result = named_config_get(maps, "interface-interval", &obj);
        INSIST(result == ISC_R_SUCCESS);
-       interface_interval = cfg_obj_asuint32(obj) * 60;
+       interface_interval = cfg_obj_asduration(obj);
        if (interface_interval == 0) {
                CHECK(isc_timer_reset(server->interface_timer,
                                      isc_timertype_inactive,
index aebea7aa1a0ed4bb60a958593bb8237b85280892..70b3bdba77a95c27156e9d6630d05a4eee05ddeb 100644 (file)
@@ -1045,8 +1045,8 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
        } else if (result == ISC_R_SUCCESS) {
                dns_ttl_t maxttl = 0;   /* unlimited */
 
-               if (cfg_obj_isuint32(obj))
-                       maxttl = cfg_obj_asuint32(obj);
+               if (cfg_obj_isduration(obj))
+                       maxttl = cfg_obj_asduration(obj);
                dns_zone_setmaxttl(zone, maxttl);
                if (raw != NULL)
                        dns_zone_setmaxttl(raw, maxttl);
index d6124458809b1d9cced09677ccaee466353ffdab..0ed86ecc750e1c5d09cef14ac7d9eb5b2d8f3750 100644 (file)
@@ -51,7 +51,7 @@
        <command>max-records</command> <replaceable>integer</replaceable>;
        <command>max-transfer-idle-out</command> <replaceable>integer</replaceable>;
        <command>max-transfer-time-out</command> <replaceable>integer</replaceable>;
-       <command>max-zone-ttl</command> ( unlimited | <replaceable>ttlval</replaceable> );
+       <command>max-zone-ttl</command> ( unlimited | <replaceable>duration</replaceable> );
        <command>notify</command> ( explicit | master-only | <replaceable>boolean</replaceable> );
        <command>notify-delay</command> <replaceable>integer</replaceable>;
        <command>notify-source</command> ( <replaceable>ipv4_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * ) ] [ dscp <replaceable>integer</replaceable> ];
index 3cd76e6d3ddcc0d61c0c12fe984500604ba76f2e..64a95defb4994aa213669f76b67a314eb570c700 100644 (file)
@@ -45,7 +45,7 @@
            [ dscp <replaceable>integer</replaceable> ] { ( <replaceable>masters</replaceable> | <replaceable>ipv4_address</replaceable> [ port
            <replaceable>integer</replaceable> ] | <replaceable>ipv6_address</replaceable> [ port <replaceable>integer</replaceable> ] ) [ key
            <replaceable>string</replaceable> ]; ... } ] [ zone-directory <replaceable>quoted_string</replaceable> ] [
-           <command>in-memory</command> <replaceable>boolean</replaceable> ] [ min-update-interval <replaceable>ttlval</replaceable> ]; ... };
+           <command>in-memory</command> <replaceable>boolean</replaceable> ] [ min-update-interval <replaceable>duration</replaceable> ]; ... };
        <command>check-dup-records</command> ( fail | warn | ignore );
        <command>check-integrity</command> <replaceable>boolean</replaceable>;
        <command>check-mx</command> ( fail | warn | ignore );
        <command>fstrm-set-output-notify-threshold</command> <replaceable>integer</replaceable>;
        <command>fstrm-set-output-queue-model</command> ( mpsc | spsc );
        <command>fstrm-set-output-queue-size</command> <replaceable>integer</replaceable>;
-       <command>fstrm-set-reopen-interval</command> <replaceable>ttlval</replaceable>;
+       <command>fstrm-set-reopen-interval</command> <replaceable>duration</replaceable>;
        <command>geoip-directory</command> ( <replaceable>quoted_string</replaceable> | none );
        <command>glue-cache</command> <replaceable>boolean</replaceable>;
        <command>heartbeat-interval</command> <replaceable>integer</replaceable>;
        <command>hostname</command> ( <replaceable>quoted_string</replaceable> | none );
        <command>inline-signing</command> <replaceable>boolean</replaceable>;
-       <command>interface-interval</command> <replaceable>ttlval</replaceable>;
+       <command>interface-interval</command> <replaceable>duration</replaceable>;
        <command>ixfr-from-differences</command> ( primary | master | secondary | slave |
            <replaceable>boolean</replaceable> );
        <command>keep-response-order</command> { <replaceable>address_match_element</replaceable>; ... };
        <command>key-directory</command> <replaceable>quoted_string</replaceable>;
-       <command>lame-ttl</command> <replaceable>ttlval</replaceable>;
+       <command>lame-ttl</command> <replaceable>duration</replaceable>;
        <command>listen-on</command> [ port <replaceable>integer</replaceable> ] [ dscp
            <replaceable>integer</replaceable> ] {
            <replaceable>address_match_element</replaceable>; ... };
        <command>masterfile-style</command> ( full | relative );
        <command>match-mapped-addresses</command> <replaceable>boolean</replaceable>;
        <command>max-cache-size</command> ( default | unlimited | <replaceable>sizeval</replaceable> | <replaceable>percentage</replaceable> );
-       <command>max-cache-ttl</command> <replaceable>ttlval</replaceable>;
+       <command>max-cache-ttl</command> <replaceable>duration</replaceable>;
        <command>max-clients-per-query</command> <replaceable>integer</replaceable>;
        <command>max-journal-size</command> ( default | unlimited | <replaceable>sizeval</replaceable> );
-       <command>max-ncache-ttl</command> <replaceable>ttlval</replaceable>;
+       <command>max-ncache-ttl</command> <replaceable>duration</replaceable>;
        <command>max-records</command> <replaceable>integer</replaceable>;
        <command>max-recursion-depth</command> <replaceable>integer</replaceable>;
        <command>max-recursion-queries</command> <replaceable>integer</replaceable>;
        <command>max-refresh-time</command> <replaceable>integer</replaceable>;
        <command>max-retry-time</command> <replaceable>integer</replaceable>;
        <command>max-rsa-exponent-size</command> <replaceable>integer</replaceable>;
-       <command>max-stale-ttl</command> <replaceable>ttlval</replaceable>;
+       <command>max-stale-ttl</command> <replaceable>duration</replaceable>;
        <command>max-transfer-idle-in</command> <replaceable>integer</replaceable>;
        <command>max-transfer-idle-out</command> <replaceable>integer</replaceable>;
        <command>max-transfer-time-in</command> <replaceable>integer</replaceable>;
        <command>max-transfer-time-out</command> <replaceable>integer</replaceable>;
        <command>max-udp-size</command> <replaceable>integer</replaceable>;
-       <command>max-zone-ttl</command> ( unlimited | <replaceable>ttlval</replaceable> );
+       <command>max-zone-ttl</command> ( unlimited | <replaceable>duration</replaceable> );
        <command>memstatistics</command> <replaceable>boolean</replaceable>;
        <command>memstatistics-file</command> <replaceable>quoted_string</replaceable>;
        <command>message-compression</command> <replaceable>boolean</replaceable>;
-       <command>min-cache-ttl</command> <replaceable>ttlval</replaceable>;
-       <command>min-ncache-ttl</command> <replaceable>ttlval</replaceable>;
+       <command>min-cache-ttl</command> <replaceable>duration</replaceable>;
+       <command>min-ncache-ttl</command> <replaceable>duration</replaceable>;
        <command>min-refresh-time</command> <replaceable>integer</replaceable>;
        <command>min-retry-time</command> <replaceable>integer</replaceable>;
        <command>minimal-any</command> <replaceable>boolean</replaceable>;
        <command>notify-source-v6</command> ( <replaceable>ipv6_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * ) ]
            [ dscp <replaceable>integer</replaceable> ];
        <command>notify-to-soa</command> <replaceable>boolean</replaceable>;
-       <command>nta-lifetime</command> <replaceable>ttlval</replaceable>;
-       <command>nta-recheck</command> <replaceable>ttlval</replaceable>;
+       <command>nta-lifetime</command> <replaceable>duration</replaceable>;
+       <command>nta-recheck</command> <replaceable>duration</replaceable>;
        <command>nxdomain-redirect</command> <replaceable>string</replaceable>;
        <command>pid-file</command> ( <replaceable>quoted_string</replaceable> | none );
        <command>port</command> <replaceable>integer</replaceable>;
        <command>response-padding</command> { <replaceable>address_match_element</replaceable>; ... } block-size
            <replaceable>integer</replaceable>;
        <command>response-policy</command> { zone <replaceable>string</replaceable> [ add-soa <replaceable>boolean</replaceable> ] [ log
-           <replaceable>boolean</replaceable> ] [ max-policy-ttl <replaceable>ttlval</replaceable> ] [ min-update-interval
-           <replaceable>ttlval</replaceable> ] [ policy ( cname | disabled | drop | given | no-op |
+           <replaceable>boolean</replaceable> ] [ max-policy-ttl <replaceable>duration</replaceable> ] [ min-update-interval
+           <replaceable>duration</replaceable> ] [ policy ( cname | disabled | drop | given | no-op |
            <command>nodata</command> | nxdomain | passthru | tcp-only <replaceable>quoted_string</replaceable> ) ] [
            <command>recursive-only</command> <replaceable>boolean</replaceable> ] [ nsip-enable <replaceable>boolean</replaceable> ] [
            <command>nsdname-enable</command> <replaceable>boolean</replaceable> ]; ... } [ add-soa <replaceable>boolean</replaceable> ] [
-           <command>break-dnssec</command> <replaceable>boolean</replaceable> ] [ max-policy-ttl <replaceable>ttlval</replaceable> ] [
-           <command>min-update-interval</command> <replaceable>ttlval</replaceable> ] [ min-ns-dots <replaceable>integer</replaceable> ] [
+           <command>break-dnssec</command> <replaceable>boolean</replaceable> ] [ max-policy-ttl <replaceable>duration</replaceable> ] [
+           <command>min-update-interval</command> <replaceable>duration</replaceable> ] [ min-ns-dots <replaceable>integer</replaceable> ] [
            <command>nsip-wait-recurse</command> <replaceable>boolean</replaceable> ] [ qname-wait-recurse <replaceable>boolean</replaceable> ]
            [ recursive-only <replaceable>boolean</replaceable> ] [ nsip-enable <replaceable>boolean</replaceable> ] [
            <command>nsdname-enable</command> <replaceable>boolean</replaceable> ] [ dnsrps-enable <replaceable>boolean</replaceable> ] [
        <command>serial-query-rate</command> <replaceable>integer</replaceable>;
        <command>serial-update-method</command> ( date | increment | unixtime );
        <command>server-id</command> ( <replaceable>quoted_string</replaceable> | none | hostname );
-       <command>servfail-ttl</command> <replaceable>ttlval</replaceable>;
+       <command>servfail-ttl</command> <replaceable>duration</replaceable>;
        <command>session-keyalg</command> <replaceable>string</replaceable>;
        <command>session-keyfile</command> ( <replaceable>quoted_string</replaceable> | none );
        <command>session-keyname</command> <replaceable>string</replaceable>;
        <command>sortlist</command> { <replaceable>address_match_element</replaceable>; ... };
        <command>stacksize</command> ( default | unlimited | <replaceable>sizeval</replaceable> );
        <command>stale-answer-enable</command> <replaceable>boolean</replaceable>;
-       <command>stale-answer-ttl</command> <replaceable>ttlval</replaceable>;
+       <command>stale-answer-ttl</command> <replaceable>duration</replaceable>;
        <command>startup-notify-rate</command> <replaceable>integer</replaceable>;
        <command>statistics-file</command> <replaceable>quoted_string</replaceable>;
        <command>synth-from-dnssec</command> <replaceable>boolean</replaceable>;
index 335cfa03877fbf4e41e4513b62ab0105eba4b5cc..91622cc2c1a2685d9bd0a946a21969a7463a3379 100644 (file)
@@ -21,7 +21,7 @@
        <command>masterfile-style</command> ( full | relative );
        <command>masters</command> [ port <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ] { ( <replaceable>masters</replaceable> | <replaceable>ipv4_address</replaceable> [ port <replaceable>integer</replaceable> ] | <replaceable>ipv6_address</replaceable> [ port <replaceable>integer</replaceable> ] ) [ key <replaceable>string</replaceable> ]; ... };
        <command>max-records</command> <replaceable>integer</replaceable>;
-       <command>max-zone-ttl</command> ( unlimited | <replaceable>ttlval</replaceable> );
+       <command>max-zone-ttl</command> ( unlimited | <replaceable>duration</replaceable> );
        <command>zone-statistics</command> ( full | terse | none | <replaceable>boolean</replaceable> );
 };
 </programlisting>
index 1426c4186726cf51ab7336f48638f45e85b2f9fd..86d74821572cf7a6dec2df85c4f12ef81d6ae9b5 100644 (file)
@@ -929,7 +929,11 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx,
                (void)cfg_map_get(options, intervals[i].name, &obj);
                if (obj == NULL)
                        continue;
-               val = cfg_obj_asuint32(obj);
+               if (cfg_obj_isduration(obj)) {
+                       val = cfg_obj_asduration(obj);
+               } else {
+                       val = cfg_obj_asuint32(obj);
+               }
                if (val > intervals[i].max) {
                        cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
                                    "%s '%u' is out of range (0..%u)",
@@ -1176,7 +1180,7 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx,
        obj = NULL;
        (void)cfg_map_get(options, "nta-lifetime", &obj);
        if (obj != NULL) {
-               lifetime = cfg_obj_asuint32(obj);
+               lifetime = cfg_obj_asduration(obj);
                if (lifetime > 604800) {        /* 7 days */
                        cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
                                    "'nta-lifetime' cannot exceed one week");
@@ -1193,7 +1197,7 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx,
        obj = NULL;
        (void)cfg_map_get(options, "nta-recheck", &obj);
        if (obj != NULL) {
-               uint32_t recheck = cfg_obj_asuint32(obj);
+               uint32_t recheck = cfg_obj_asduration(obj);
                if (recheck > 604800) {         /* 7 days */
                        cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
                                    "'nta-recheck' cannot exceed one week");
@@ -1271,7 +1275,11 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx,
                if (obj == NULL)
                        continue;
 
-               value = cfg_obj_asuint32(obj);
+               if (cfg_obj_isduration(obj)) {
+                       value = cfg_obj_asduration(obj);
+               } else {
+                       value = cfg_obj_asuint32(obj);
+               }
                if (value < fstrm[i].min ||
                    (fstrm[i].max != 0U && value > fstrm[i].max)) {
                        if (fstrm[i].max != 0U)
index 35729a4991992a00719b514b19bbb4239c3df165..2aefdce11000a0c5094a7e345c5111096c57b2b8 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <inttypes.h>
 #include <stdbool.h>
+#include <time.h>
 
 #include <isc/formatcheck.h>
 #include <isc/lang.h>
@@ -331,6 +332,24 @@ cfg_obj_aspercentage(const cfg_obj_t *obj);
  * \li     A 32-bit unsigned integer.
  */
 
+bool
+cfg_obj_isduration(const cfg_obj_t *obj);
+/*%<
+ * Return true iff 'obj' is of duration type.
+ */
+
+uint32_t
+cfg_obj_asduration(const cfg_obj_t *obj);
+/*%<
+ * Returns the value of a configuration object of duration
+ *
+ * Requires:
+ * \li     'obj' points to a valid configuration object of duration type.
+ *
+ * Returns:
+ * \li     A duration in seconds.
+ */
+
 bool
 cfg_obj_isstring(const cfg_obj_t *obj);
 /*%<
index e931282a0f4cd9159959f62c4becb942b795ce42..ad3dd81d176a9f4c0d6a38d78648a087f113d40f 100644 (file)
@@ -82,6 +82,9 @@ typedef struct cfg_printer cfg_printer_t;
 typedef ISC_LIST(cfg_listelt_t) cfg_list_t;
 typedef struct cfg_map cfg_map_t;
 typedef struct cfg_rep cfg_rep_t;
+typedef struct cfg_duration cfg_duration_t;
+
+#define CFG_DURATION_MAXLEN 64
 
 /*
  * Function types for configuration object methods
@@ -154,6 +157,24 @@ struct cfg_netprefix {
        unsigned int prefixlen;
 };
 
+/*%
+ * A configuration object to store ISO 8601 durations.
+ */
+struct cfg_duration {
+       /*
+        * The duration is stored in multiple parts:
+        * [0] Years
+        * [1] Months
+        * [2] Weeks
+        * [3] Days
+        * [4] Hours
+        * [5] Minutes
+        * [6] Seconds
+        */
+       uint32_t parts[7];
+       bool iso8601;
+};
+
 /*%
  * A configuration data representation.
  */
@@ -183,6 +204,7 @@ struct cfg_obj {
                        isc_dscp_t      dscp;
                } sockaddrdscp;
                cfg_netprefix_t netprefix;
+               cfg_duration_t  duration;
        }               value;
        isc_refcount_t  references;     /*%< reference counter */
        const char *    file;
@@ -290,6 +312,7 @@ LIBISCCFG_EXTERNAL_DATA extern cfg_rep_t cfg_rep_netprefix;
 LIBISCCFG_EXTERNAL_DATA extern cfg_rep_t cfg_rep_void;
 LIBISCCFG_EXTERNAL_DATA extern cfg_rep_t cfg_rep_fixedpoint;
 LIBISCCFG_EXTERNAL_DATA extern cfg_rep_t cfg_rep_percentage;
+LIBISCCFG_EXTERNAL_DATA extern cfg_rep_t cfg_rep_duration;
 /*@}*/
 
 /*@{*/
@@ -320,6 +343,7 @@ LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_token;
 LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_unsupported;
 LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_fixedpoint;
 LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_percentage;
+LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_duration;
 /*@}*/
 
 isc_result_t
@@ -504,6 +528,13 @@ cfg_parse_percentage(cfg_parser_t *pctx, const cfg_type_t *type,
 void
 cfg_print_percentage(cfg_printer_t *pctx, const cfg_obj_t *obj);
 
+isc_result_t
+cfg_parse_duration(cfg_parser_t *pctx, const cfg_type_t *type,
+                  cfg_obj_t **ret);
+
+void
+cfg_print_duration(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
 isc_result_t
 cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
 
index d427ec488d9ba79d445df53a96b1c1d786233d6f..3282a8b01f6bfaef794ca01955d4eed4dee7aafa 100644 (file)
@@ -121,7 +121,6 @@ static cfg_type_t cfg_type_sizeval;
 static cfg_type_t cfg_type_sockaddr4wild;
 static cfg_type_t cfg_type_sockaddr6wild;
 static cfg_type_t cfg_type_statschannels;
-static cfg_type_t cfg_type_ttlval;
 static cfg_type_t cfg_type_view;
 static cfg_type_t cfg_type_viewopts;
 static cfg_type_t cfg_type_zone;
@@ -1054,7 +1053,7 @@ options_clauses[] = {
        { "fstrm-set-output-notify-threshold", &cfg_type_uint32, 0 },
        { "fstrm-set-output-queue-model", &cfg_type_fstrm_model, 0 },
        { "fstrm-set-output-queue-size", &cfg_type_uint32, 0 },
-       { "fstrm-set-reopen-interval", &cfg_type_ttlval, 0 },
+       { "fstrm-set-reopen-interval", &cfg_type_duration, 0 },
 #else
        { "fstrm-set-buffer-hint", &cfg_type_uint32,
          CFG_CLAUSEFLAG_NOTCONFIGURED },
@@ -1068,7 +1067,7 @@ options_clauses[] = {
          CFG_CLAUSEFLAG_NOTCONFIGURED },
        { "fstrm-set-output-queue-size", &cfg_type_uint32,
          CFG_CLAUSEFLAG_NOTCONFIGURED },
-       { "fstrm-set-reopen-interval", &cfg_type_ttlval,
+       { "fstrm-set-reopen-interval", &cfg_type_duration,
          CFG_CLAUSEFLAG_NOTCONFIGURED },
 #endif /* HAVE_DNSTAP */
 #if defined(HAVE_GEOIP2)
@@ -1083,7 +1082,7 @@ options_clauses[] = {
        { "host-statistics", &cfg_type_boolean, CFG_CLAUSEFLAG_ANCIENT },
        { "host-statistics-max", &cfg_type_uint32, CFG_CLAUSEFLAG_ANCIENT },
        { "hostname", &cfg_type_qstringornone, 0 },
-       { "interface-interval", &cfg_type_ttlval, 0 },
+       { "interface-interval", &cfg_type_duration, 0 },
        { "keep-response-order", &cfg_type_bracketed_aml, 0 },
        { "listen-on", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
        { "listen-on-v6", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
@@ -1611,8 +1610,8 @@ static cfg_tuplefielddef_t rpz_zone_fields[] = {
        { "zone name", &cfg_type_rpz_zone, 0 },
        { "add-soa", &cfg_type_boolean, 0 },
        { "log", &cfg_type_boolean, 0 },
-       { "max-policy-ttl", &cfg_type_ttlval, 0 },
-       { "min-update-interval", &cfg_type_ttlval, 0 },
+       { "max-policy-ttl", &cfg_type_duration, 0 },
+       { "min-update-interval", &cfg_type_duration, 0 },
        { "policy", &cfg_type_rpz_policy, 0 },
        { "recursive-only", &cfg_type_boolean, 0 },
        { "nsip-enable", &cfg_type_boolean, 0 },
@@ -1633,8 +1632,8 @@ static cfg_tuplefielddef_t rpz_fields[] = {
        { "zone list", &cfg_type_rpz_list, 0 },
        { "add-soa", &cfg_type_boolean, 0 },
        { "break-dnssec", &cfg_type_boolean, 0 },
-       { "max-policy-ttl", &cfg_type_ttlval, 0 },
-       { "min-update-interval", &cfg_type_ttlval, 0 },
+       { "max-policy-ttl", &cfg_type_duration, 0 },
+       { "min-update-interval", &cfg_type_duration, 0 },
        { "min-ns-dots", &cfg_type_uint32, 0 },
        { "nsip-wait-recurse", &cfg_type_boolean, 0 },
        { "qname-wait-recurse", &cfg_type_boolean, 0 },
@@ -1671,7 +1670,7 @@ static cfg_tuplefielddef_t catz_zone_fields[] = {
        { "default-masters", &cfg_type_namesockaddrkeylist, 0 },
        { "zone-directory", &cfg_type_qstring, 0 },
        { "in-memory", &cfg_type_boolean, 0 },
-       { "min-update-interval", &cfg_type_ttlval, 0 },
+       { "min-update-interval", &cfg_type_duration, 0 },
        { NULL, NULL, 0 }
 };
 static cfg_type_t cfg_type_catz_tuple = {
@@ -1899,7 +1898,7 @@ view_clauses[] = {
        { "filter-aaaa-on-v6", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE  },
        { "glue-cache", &cfg_type_boolean, 0 },
        { "ixfr-from-differences", &cfg_type_ixfrdifftype, 0 },
-       { "lame-ttl", &cfg_type_ttlval, 0 },
+       { "lame-ttl", &cfg_type_duration, 0 },
 #ifdef HAVE_LMDB
        { "lmdb-mapsize", &cfg_type_sizeval, 0 },
 #else
@@ -1907,16 +1906,16 @@ view_clauses[] = {
 #endif
        { "max-acache-size", &cfg_type_sizenodefault, CFG_CLAUSEFLAG_OBSOLETE },
        { "max-cache-size", &cfg_type_sizeorpercent, 0 },
-       { "max-cache-ttl", &cfg_type_ttlval, 0 },
+       { "max-cache-ttl", &cfg_type_duration, 0 },
        { "max-clients-per-query", &cfg_type_uint32, 0 },
-       { "max-ncache-ttl", &cfg_type_ttlval, 0 },
+       { "max-ncache-ttl", &cfg_type_duration, 0 },
        { "max-recursion-depth", &cfg_type_uint32, 0 },
        { "max-recursion-queries", &cfg_type_uint32, 0 },
-       { "max-stale-ttl", &cfg_type_ttlval, 0 },
+       { "max-stale-ttl", &cfg_type_duration, 0 },
        { "max-udp-size", &cfg_type_uint32, 0 },
        { "message-compression", &cfg_type_boolean, 0 },
-       { "min-cache-ttl", &cfg_type_ttlval, 0 },
-       { "min-ncache-ttl", &cfg_type_ttlval, 0 },
+       { "min-cache-ttl", &cfg_type_duration, 0 },
+       { "min-ncache-ttl", &cfg_type_duration, 0 },
        { "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_ANCIENT },
        { "minimal-any", &cfg_type_boolean, 0 },
        { "minimal-responses", &cfg_type_minimal, 0 },
@@ -1924,8 +1923,8 @@ view_clauses[] = {
        { "no-case-compress", &cfg_type_bracketed_aml, 0 },
        { "nocookie-udp-size", &cfg_type_uint32, 0 },
        { "nosit-udp-size", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE },
-       { "nta-lifetime", &cfg_type_ttlval, 0 },
-       { "nta-recheck", &cfg_type_ttlval, 0 },
+       { "nta-lifetime", &cfg_type_duration, 0 },
+       { "nta-recheck", &cfg_type_duration, 0 },
        { "nxdomain-redirect", &cfg_type_astring, 0 },
        { "preferred-glue", &cfg_type_astring, 0 },
        { "prefetch", &cfg_type_prefetch, 0 },
@@ -1955,10 +1954,10 @@ view_clauses[] = {
        { "root-key-sentinel", &cfg_type_boolean, 0 },
        { "rrset-order", &cfg_type_rrsetorder, 0 },
        { "send-cookie", &cfg_type_boolean, 0 },
-       { "servfail-ttl", &cfg_type_ttlval, 0 },
+       { "servfail-ttl", &cfg_type_duration, 0 },
        { "sortlist", &cfg_type_bracketed_aml, 0 },
        { "stale-answer-enable", &cfg_type_boolean, 0 },
-       { "stale-answer-ttl", &cfg_type_ttlval, 0 },
+       { "stale-answer-ttl", &cfg_type_duration, 0 },
        { "suppress-initial-notify", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI },
        { "synth-from-dnssec", &cfg_type_boolean, 0 },
        { "topology", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_ANCIENT },
@@ -3761,54 +3760,14 @@ static cfg_type_t cfg_type_masterselement = {
         doc_masterselement, NULL, NULL
 };
 
-static isc_result_t
-parse_ttlval(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
-       isc_result_t result;
-       cfg_obj_t *obj = NULL;
-       uint32_t ttl;
-
-       UNUSED(type);
-
-       CHECK(cfg_gettoken(pctx, 0));
-       if (pctx->token.type != isc_tokentype_string) {
-               result = ISC_R_UNEXPECTEDTOKEN;
-               goto cleanup;
-       }
-
-       result = dns_ttl_fromtext(&pctx->token.value.as_textregion, &ttl);
-       if (result == ISC_R_RANGE ) {
-               cfg_parser_error(pctx, CFG_LOG_NEAR, "TTL out of range ");
-               return (result);
-       } else if (result != ISC_R_SUCCESS)
-               goto cleanup;
-
-       CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
-       obj->value.uint32 = ttl;
-       *ret = obj;
-       return (ISC_R_SUCCESS);
-
- cleanup:
-       cfg_parser_error(pctx, CFG_LOG_NEAR,
-                        "expected integer and optional unit");
-       return (result);
-}
-
-/*%
- * A TTL value (number + optional unit).
- */
-static cfg_type_t cfg_type_ttlval = {
-       "ttlval", parse_ttlval, cfg_print_uint64, cfg_doc_terminal,
-       &cfg_rep_uint64, NULL
-};
-
 static isc_result_t
 parse_maxttl(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
-       return (cfg_parse_enum_or_other(pctx, type, &cfg_type_ttlval, ret));
+       return (cfg_parse_enum_or_other(pctx, type, &cfg_type_duration, ret));
 }
 
 static void
 doc_maxttl(cfg_printer_t *pctx, const cfg_type_t *type) {
-       cfg_doc_enum_or_other(pctx, type, &cfg_type_ttlval);
+       cfg_doc_enum_or_other(pctx, type, &cfg_type_duration);
 }
 
 /*%
index 0c7987b8810273157556ba872fac4fb9db7f2fba..68db91a1d18a024209e96d81667b328e6d4fe315 100644 (file)
@@ -34,6 +34,8 @@
 #include <isccfg/grammar.h>
 #include <isccfg/log.h>
 
+#include <dns/ttl.h>
+
 /* Shorthand */
 #define CAT CFG_LOGCATEGORY_CONFIG
 #define MOD CFG_LOGMODULE_PARSER
@@ -132,6 +134,7 @@ LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_fixedpoint =
        { "fixedpoint", free_noop };
 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_percentage =
        { "percentage", free_noop };
+LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_duration = { "duration", free_noop };
 
 /*
  * Configuration type definitions.
@@ -977,9 +980,353 @@ LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_uint64 = {
        &cfg_rep_uint64, NULL
 };
 
+/*
+ * Get the number of digits in a number.
+ */
+static size_t
+numlen(time_t num) {
+       uint32_t period = (uint32_t) num;
+       size_t count = 0;
+
+       if (period == 0) {
+               return 1;
+       }
+       while (period > 0) {
+               count++;
+               period /= 10;
+       }
+       return (count);
+}
+
+/*
+ * duration
+ */
+void
+cfg_print_duration(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+       char buf[CFG_DURATION_MAXLEN];
+       char *str;
+       const char *indicators = "YMWDHMS";
+       int count, i;
+       int durationlen[7];
+       cfg_duration_t duration;
+       /*
+        * D ? The duration has a date part.
+        * T ? The duration has a time part.
+        */
+       bool D = false, T = false;
+
+       REQUIRE(pctx != NULL);
+       REQUIRE(obj != NULL);
+
+       duration = obj->value.duration;
+
+       /* If this is not an ISO 8601 duration, just print it as a number. */
+       if (!duration.iso8601) {
+               return (cfg_print_rawuint(pctx, duration.parts[6]));
+       }
+
+       /* Calculate length of string. */
+       buf[0] = 'P';
+       buf[1] = '\0';
+       str = &buf[1];
+       count = 2;
+       for (i = 0; i < 6; i++) {
+               if (duration.parts[i] > 0) {
+                       durationlen[i] = 1 + numlen(duration.parts[i]);
+                       if (i < 4) {
+                               D = true;
+                       } else {
+                               T = true;
+                       }
+               } else {
+                       durationlen[i] = 0;
+               }
+               count += durationlen[i];
+       }
+       /*
+        * Special case for seconds which is not taken into account in the
+        * above for loop: Count the length of the seconds part if it is
+        * non-zero, or if all the other parts are also zero.  In the latter
+        * case this function will print "PT0S".
+        */
+       if (duration.parts[6] > 0 ||
+           (!D && !duration.parts[4] && !duration.parts[5])) {
+               durationlen[6] = 1 + numlen(duration.parts[6]);
+               T = true;
+               count += durationlen[6];
+       }
+       /* Add one character for the time indicator. */
+       if (T) {
+               count++;
+       }
+       INSIST(count < CFG_DURATION_MAXLEN);
+
+       /* Now print the duration. */
+       for (i = 0; i < 6; i++) {
+               /*
+                * We don't check here if weeks and other time indicator are
+                * used mutually exclusively.
+                */
+               if (duration.parts[i] > 0) {
+                       snprintf(str, durationlen[i]+2, "%u%c",
+                                (uint32_t) duration.parts[i], indicators[i]);
+                       str += durationlen[i]+1;
+               }
+               if (i == 3 && T) {
+                       snprintf(str, 2, "T");
+                       str += 1;
+               }
+
+       }
+       /* Special case for seconds. */
+       if (duration.parts[6] > 0 ||
+           (!D && !duration.parts[4] && !duration.parts[3])) {
+               snprintf(str, durationlen[6]+2, "%u%c",
+                        (uint32_t) duration.parts[6], indicators[6]);
+       }
+       cfg_print_chars(pctx, buf, strlen(buf));
+}
+
+bool
+cfg_obj_isduration(const cfg_obj_t *obj) {
+       REQUIRE(obj != NULL);
+       return (obj->type->rep == &cfg_rep_duration);
+}
+
+uint32_t
+cfg_obj_asduration(const cfg_obj_t *obj) {
+       REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_duration);
+       uint32_t duration = 0;
+       duration += obj->value.duration.parts[6];               // Seconds
+       duration += obj->value.duration.parts[5]*60;            // Minutes
+       duration += obj->value.duration.parts[4]*3600;          // Hours
+       duration += obj->value.duration.parts[3]*86400;         // Days
+       duration += obj->value.duration.parts[2]*86400*7;       // Weaks
+       /*
+        * The below additions are not entirely correct
+        * because days may very per month and per year.
+        */
+       duration += obj->value.duration.parts[1]*86400*31;      // Months
+       duration += obj->value.duration.parts[0]*86400*365;     // Years
+       return (duration);
+}
+
+/*
+ * duration_fromtext initially taken from OpenDNSSEC code base.
+ * Modified to fit the BIND 9 code.
+ *
+ * Copyright (c) 2009-2018 NLNet Labs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+static isc_result_t
+duration_fromtext(isc_textregion_t *source, cfg_duration_t *duration) {
+       char buf[CFG_DURATION_MAXLEN];
+       char *P, *X, *T, *W, *str;
+       bool not_weeks = false;
+       int i;
+
+       /*
+        * Copy the buffer as it may not be NULL terminated.
+        * Anyone having a duration longer than 63 characters is crazy.
+        */
+       if (source->length > sizeof(buf) - 1) {
+               return (ISC_R_BADNUMBER);
+       }
+       /* Copy source->length bytes and NULL terminate. */
+       snprintf(buf, sizeof(buf), "%.*s", (int)source->length, source->base);
+       str = buf;
+
+       /* Clear out duration. */
+       for (i = 0; i < 7; i++) {
+               duration->parts[i] = 0;
+       }
+
+       /* Every duration starts with 'P' */
+       P = strchr(str, 'P');
+       if (!P) {
+       return (ISC_R_BADNUMBER);
+       }
+
+       /* Record the time indicator. */
+       T = strchr(str, 'T');
+
+       /* Record years. */
+       X = strchr(str, 'Y');
+       if (X) {
+               duration->parts[0] = atoi(str+1);
+               str = X;
+               not_weeks = true;
+       }
+       /* Record months. */
+       X = strchr(str, 'M');
+       /*
+        * M could be months or minutes. This is months if there is no time
+        * part, or this M indicator is before the time indicator.
+        */
+       if (X && (!T || (size_t) (X-P) < (size_t) (T-P))) {
+               duration->parts[1] = atoi(str+1);
+               str = X;
+               not_weeks = true;
+       }
+       /* Record days. */
+       X = strchr(str, 'D');
+       if (X) {
+               duration->parts[3] = atoi(str+1);
+               str = X;
+               not_weeks = true;
+       }
+
+       /* Time part? */
+       if (T) {
+               str = T;
+               not_weeks = true;
+       }
+
+       /* Record hours. */
+       X = strchr(str, 'H');
+       if (X && T) {
+               duration->parts[4] = atoi(str+1);
+               str = X;
+               not_weeks = true;
+       }
+       /* Record minutes. */
+       X = strrchr(str, 'M');
+       /*
+        * M could be months or minutes. This is minutes if there is a time
+        * part and the M indicator is behind the time indicator.
+        */
+       if (X && T && (size_t) (X-P) > (size_t) (T-P)) {
+               duration->parts[5] = atoi(str+1);
+               str = X;
+               not_weeks = true;
+       }
+       /* Record seconds. */
+       X = strchr(str, 'S');
+       if (X && T) {
+               duration->parts[6] = atoi(str+1);
+               str = X;
+               not_weeks = true;
+       }
+
+       /* Or is the duration configured in weeks? */
+       W = strchr(buf, 'W');
+       if (W) {
+               if (not_weeks) {
+                       /* Mix of weeks and other indicators is not allowed */
+                       return (ISC_R_BADNUMBER);
+               } else {
+                       duration->parts[2] = atoi(str+1);
+                       str = W;
+               }
+       }
+
+       /* Deal with trailing garbage. */
+       if (str[1] != '\0') {
+               return (ISC_R_BADNUMBER);
+       }
+
+       return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+cfg_parse_duration(cfg_parser_t *pctx, const cfg_type_t *type,
+                  cfg_obj_t **ret)
+{
+       isc_result_t result;
+       cfg_obj_t *obj = NULL;
+       cfg_duration_t duration;
+
+       UNUSED(type);
+
+       CHECK(cfg_gettoken(pctx, 0));
+       if (pctx->token.type != isc_tokentype_string) {
+               result = ISC_R_UNEXPECTEDTOKEN;
+               goto cleanup;
+       }
+
+       if (TOKEN_STRING(pctx)[0] == 'P') {
+               result = duration_fromtext(&pctx->token.value.as_textregion,
+                                          &duration);
+               duration.iso8601 = true;
+       } else {
+               uint32_t ttl;
+               result = dns_ttl_fromtext(&pctx->token.value.as_textregion,
+                                         &ttl);
+               /*
+                * With dns_ttl_fromtext() the information on optional units.
+                * is lost, and is treated as seconds from now on.
+                */
+               duration.parts[0] = 0;
+               duration.parts[1] = 0;
+               duration.parts[2] = 0;
+               duration.parts[3] = 0;
+               duration.parts[4] = 0;
+               duration.parts[5] = 0;
+               duration.parts[6] = ttl;
+               duration.iso8601 = false;
+       }
+       if (result == ISC_R_RANGE) {
+               cfg_parser_error(pctx, CFG_LOG_NEAR,
+                                "duration or TTL out of range");
+               return (result);
+       } else if (result != ISC_R_SUCCESS) {
+               goto cleanup;
+       }
+       CHECK(cfg_create_obj(pctx, &cfg_type_duration, &obj));
+       obj->value.duration = duration;
+       *ret = obj;
+       return (ISC_R_SUCCESS);
+
+cleanup:
+       cfg_parser_error(pctx, CFG_LOG_NEAR,
+                        "expected ISO 8601 duration or TTL value");
+       return (result);
+}
+
+/*%
+ * A duration as defined by ISO 8601 (P[n]Y[n]M[n]DT[n]H[n]M[n]S).
+ * - P is the duration indicator ("period") placed at the start.
+ * - Y is the year indicator that follows the value for the number of years.
+ * - M is the month indicator that follows the value for the number of months.
+ * - D is the day indicator that follows the value for the number of days.
+ * - T is the time indicator that precedes the time components.
+ * - H is the hour indicator that follows the value for the number of hours.
+ * - M is the minute indicator that follows the value for the number of
+ *   minutes.
+ * - S is the second indicator that follows the value for the number of
+ *   seconds.
+ *
+ * A duration can also be a TTL value (number + optional unit).
+ */
+LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_duration = {
+       "duration", cfg_parse_duration, cfg_print_duration, cfg_doc_terminal,
+       &cfg_rep_duration, NULL
+};
+
 /*
  * qstring (quoted string), ustring (unquoted string), astring
- * (any string)
+ * (any string), sstring (secret string)
  */
 
 /* Create a string object from a null-terminated C string. */
index dc14aace41c5b76fa92c63f18c29824068056b9e..89d50352f2d51d099d14051a43d4978935297996 100644 (file)
@@ -1,4 +1,5 @@
 syntax(2)
 test_suite('bind9')
 
+tap_test_program{name='duration_test'}
 tap_test_program{name='parser_test'}
index 9e659620982ce2911ac8a6947258518d12dbf756..4517992e038b9a81ad6cef91fa43f675df7c1097 100644 (file)
@@ -30,13 +30,18 @@ ISCCFGDEPLIBS = ../libisccfg.@A@
 LIBS =         @LIBS@ @CMOCKA_LIBS@
 
 OBJS =
-SRCS =         parser_test.c
+SRCS =         duration_test.c parser_test.c
 
 SUBDIRS =
-TARGETS =      parser_test@EXEEXT@
+TARGETS =      duration_test@EXEEXT@ parser_test@EXEEXT@
 
 @BIND9_MAKE_RULES@
 
+duration_test@EXEEXT@: duration_test.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS} ${ISCCFGDEPLIBS}
+       ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} \
+               ${LDFLAGS} -o $@ duration_test.@O@ \
+               ${ISCCFGLIBS} ${DNSLIBS} ${ISCLIBS} ${LIBS}
+
 parser_test@EXEEXT@: parser_test.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS} ${ISCCFGDEPLIBS}
        ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} \
                ${LDFLAGS} -o $@ parser_test.@O@ \
diff --git a/lib/isccfg/tests/duration_test.c b/lib/isccfg/tests/duration_test.c
new file mode 100644 (file)
index 0000000..2e0249b
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#if HAVE_CMOCKA
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+
+#include <sched.h> /* IWYU pragma: keep */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define UNIT_TESTING
+#include <cmocka.h>
+
+#include <isc/buffer.h>
+#include <isc/lex.h>
+#include <isc/log.h>
+#include <isc/mem.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#include <isccfg/cfg.h>
+#include <isccfg/grammar.h>
+#include <isccfg/namedconf.h>
+
+#define CHECK(r) \
+       do { \
+               result = (r); \
+               if (result != ISC_R_SUCCESS) \
+                       goto cleanup; \
+       } while (0)
+
+isc_mem_t *mctx = NULL;
+isc_log_t *lctx = NULL;
+static isc_logcategory_t categories[] = {
+               { "",                0 },
+               { "client",          0 },
+               { "network",         0 },
+               { "update",          0 },
+               { "queries",         0 },
+               { "unmatched",       0 },
+               { "update-security", 0 },
+               { "query-errors",    0 },
+               { NULL,              0 }
+};
+
+static void
+cleanup() {
+       if (lctx != NULL) {
+               isc_log_destroy(&lctx);
+       }
+       if (mctx != NULL) {
+               isc_mem_destroy(&mctx);
+       }
+}
+
+static isc_result_t
+setup() {
+       isc_result_t result;
+
+       isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
+       isc_mem_create(&mctx);
+
+       isc_logdestination_t destination;
+       isc_logconfig_t *logconfig = NULL;
+
+       CHECK(isc_log_create(mctx, &lctx, &logconfig));
+       isc_log_registercategories(lctx, categories);
+       isc_log_setcontext(lctx);
+
+       destination.file.stream = stderr;
+       destination.file.name = NULL;
+       destination.file.versions = ISC_LOG_ROLLNEVER;
+       destination.file.maximum_size = 0;
+       CHECK(isc_log_createchannel(logconfig, "stderr",
+                                   ISC_LOG_TOFILEDESC,
+                                   ISC_LOG_DYNAMIC,
+                                   &destination, 0));
+       CHECK(isc_log_usechannel(logconfig, "stderr", NULL, NULL));
+
+       return (ISC_R_SUCCESS);
+
+  cleanup:
+       cleanup();
+       return (result);
+}
+
+struct duration_conf {
+       const char* string;
+       uint32_t time;
+};
+typedef struct duration_conf duration_conf_t;
+
+/* test cfg_obj_asduration() */
+static void
+cfg_obj_asduration_test(void **state) {
+       isc_result_t result;
+       duration_conf_t durations[] = {
+               { .string = "PT0S", .time = 0 },
+               { .string = "PT42S", .time = 42 },
+               { .string = "PT10M", .time = 600 },
+               { .string = "PT10M4S", .time = 604 },
+               { .string = "PT2H", .time = 7200 },
+               { .string = "PT2H3S", .time = 7203 },
+               { .string = "PT2H1M3S", .time = 7263 },
+               { .string = "P7D", .time = 604800 },
+               { .string = "P7DT2H", .time = 612000 },
+               { .string = "P2W", .time = 1209600 },
+               { .string = "P3M", .time = 8035200 },
+               { .string = "P3MT10M", .time = 8035800 },
+               { .string = "P5Y", .time = 157680000 },
+               { .string = "P5YT2H", .time = 157687200 },
+               { .string = "P1Y1M1DT1H1M1S", .time = 34304461 },
+               { .string = "0", .time = 0 },
+               { .string = "30", .time = 30 },
+               { .string = "42s", .time = 42 },
+               { .string = "10m", .time = 600 },
+               { .string = "2h", .time = 7200 },
+               { .string = "7d", .time = 604800 },
+               { .string = "2w", .time = 1209600 },
+       };
+       int num = 22;
+       isc_buffer_t buf1;
+       cfg_parser_t *p1 = NULL;
+       cfg_obj_t *c1 = NULL;
+
+       UNUSED(state);
+
+       setup();
+
+       for (int i = 0; i < num; i++) {
+               const cfg_listelt_t *element;
+               const cfg_obj_t *kasps = NULL;
+               char conf[64];
+               sprintf(&conf[0],
+                       "dnssec-policy \"dp\"\n{\nsignatures-refresh %s;\n};\n",
+                       durations[i].string);
+
+               isc_buffer_init(&buf1, conf, strlen(conf) - 1);
+               isc_buffer_add(&buf1, strlen(conf) - 1);
+
+               /* Parse with default line numbering */
+               result = cfg_parser_create(mctx, lctx, &p1);
+               assert_int_equal(result, ISC_R_SUCCESS);
+
+               result = cfg_parse_buffer(p1, &buf1, "text1", 0,
+                                         &cfg_type_namedconf, 0, &c1);
+               assert_int_equal(result, ISC_R_SUCCESS);
+
+               (void)cfg_map_get(c1, "dnssec-policy", &kasps);
+               assert_non_null(kasps);
+               for (element = cfg_list_first(kasps);
+                    element != NULL;
+                    element = cfg_list_next(element))
+               {
+                       const cfg_obj_t *d1 = NULL;
+                       const cfg_obj_t *kopts = NULL;
+                       cfg_obj_t *kconf = cfg_listelt_value(element);
+                       assert_non_null(kconf);
+
+                       kopts = cfg_tuple_get(kconf, "options");
+                       result = cfg_map_get(kopts, "signatures-refresh", &d1);
+                       assert_int_equal(result, ISC_R_SUCCESS);
+
+                       assert_int_equal(durations[i].time,
+                                        cfg_obj_asduration(d1));
+               }
+
+               cfg_obj_destroy(p1, &c1);
+               cfg_parser_destroy(&p1);
+       }
+
+       cleanup();
+}
+
+int
+main(void) {
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test(cfg_obj_asduration_test),
+       };
+
+       return (cmocka_run_group_tests(tests, NULL, NULL));
+}
+
+#else /* HAVE_CMOCKA */
+
+#include <stdio.h>
+
+int
+main(void) {
+       printf("1..0 # Skipped: cmocka not available\n");
+       return (0);
+}
+
+#endif
index 96039fbf04587febba884fa756180de31d04de7f..61e6e86ce4cfed0e2a7715d2efc32c855b19369f 100644 (file)
@@ -36,6 +36,7 @@ cfg_map_get
 cfg_map_getname
 cfg_map_nextclause
 cfg_obj_asboolean
+cfg_obj_asduration
 cfg_obj_asfixedpoint
 cfg_obj_asnetprefix
 cfg_obj_aspercentage
@@ -48,6 +49,7 @@ cfg_obj_destroy
 cfg_obj_file
 cfg_obj_getdscp
 cfg_obj_isboolean
+cfg_obj_isduration
 cfg_obj_isfixedpoint
 cfg_obj_islist
 cfg_obj_ismap
@@ -68,6 +70,7 @@ cfg_parse_boolean
 cfg_parse_bracketed_list
 cfg_parse_buffer
 cfg_parse_dscp
+cfg_parse_duration
 cfg_parse_enum
 cfg_parse_enum_or_other
 cfg_parse_file
@@ -107,6 +110,7 @@ cfg_print_bracketed_list
 cfg_print_chars
 cfg_print_clauseflags
 cfg_print_cstr
+cfg_print_duration
 cfg_print_fixedpoint
 cfg_print_grammar
 cfg_print_indent
@@ -131,6 +135,7 @@ cfg_ungettoken
 ; Exported Data
 
 ;cfg_rep_boolean
+;cfg_rep_duration
 ;cfg_rep_fixedpoint
 ;cfg_rep_list
 ;cfg_rep_map
index 5671c1c42a5e9da349f920c43b31dc480ba92662..d10b5bcb559cf60ce00d828cd6426507a23a49bc 100644 (file)
 ./lib/isccfg/namedconf.c                       C       2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019
 ./lib/isccfg/parser.c                          C       2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019
 ./lib/isccfg/tests/Kyuafile                    X       2017,2018,2019
+./lib/isccfg/tests/duration_test.c             C       2019
 ./lib/isccfg/tests/parser_test.c               C       2016,2018,2019
 ./lib/isccfg/version.c                         C       1998,1999,2000,2001,2004,2005,2007,2016,2018,2019
 ./lib/isccfg/win32/DLLMain.c                   C       2001,2004,2007,2016,2018,2019