diff --git a/CHANGES b/CHANGES
index 7677a804c1..cc4f9972b4 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,8 @@
+3829. [func] "dig +ttlunits" causes dig to print TTL values
+ with time-unit suffixes: w, d, h, m, s for
+ weeks, days, hours, minutes, and seconds. (Thanks
+ to Tony Finch.) [RT #35823]
+
3828. [placeholder]
3827. [func] "dnssec-signzone -N date" updates serial number
diff --git a/README b/README
index d1117ba018..499e3f5fe8 100644
--- a/README
+++ b/README
@@ -61,6 +61,9 @@ BIND 9.11.0
in single-line-per-record format.
- "dig" now supports sending arbitary EDNS options by specifying
them on the command line.
+ - "dig +ttlunits" causes dig to print TTL values with time-unit
+ suffixes: w, d, h, m, s for weeks, days, hours, minutes, and
+ seconds.
- "serial-update-format" can now be set to "date". On update,
the serial number will be set to the current date in YYYYMMDDNN
format.
diff --git a/bin/dig/dig.c b/bin/dig/dig.c
index 0b774bd9eb..8a8bf9f417 100644
--- a/bin/dig/dig.c
+++ b/bin/dig/dig.c
@@ -69,7 +69,7 @@ static isc_boolean_t short_form = ISC_FALSE, printcmd = ISC_TRUE,
ip6_int = ISC_FALSE, plusquest = ISC_FALSE, pluscomm = ISC_FALSE,
multiline = ISC_FALSE, nottl = ISC_FALSE, noclass = ISC_FALSE,
onesoa = ISC_FALSE, rrcomments = ISC_FALSE, use_usec = ISC_FALSE,
- nocrypto = ISC_FALSE;
+ nocrypto = ISC_FALSE, ttlunits = ISC_FALSE;
static isc_uint32_t splitwidth = 0xffffffff;
/*% opcode text */
@@ -218,6 +218,7 @@ help(void) {
" +[no]short (Disable everything except short\n"
" form of answer)\n"
" +[no]ttlid (Control display of ttls in records)\n"
+" +[no]ttlunits (Display TTLs in human-readable units)\n"
" +[no]all (Set or clear all display flags)\n"
" +[no]qr (Print question before sending)\n"
" +[no]nssearch (Search all authoritative nameservers)\n"
@@ -424,6 +425,8 @@ printrdataset(dns_name_t *owner_name, dns_rdataset_t *rdataset,
return(ISC_FALSE);
styleflags |= DNS_STYLEFLAG_REL_OWNER;
+ if (ttlunits)
+ styleflags |= DNS_STYLEFLAG_TTL_UNITS;
if (nottl)
styleflags |= DNS_STYLEFLAG_NO_TTL;
if (noclass)
@@ -483,6 +486,8 @@ printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
styleflags |= DNS_STYLEFLAG_COMMENT;
if (rrcomments)
styleflags |= DNS_STYLEFLAG_RRCOMMENT;
+ if (ttlunits)
+ styleflags |= DNS_STYLEFLAG_TTL_UNITS;
if (nottl)
styleflags |= DNS_STYLEFLAG_NO_TTL;
if (noclass)
@@ -1297,9 +1302,18 @@ plus_option(char *option, isc_boolean_t is_batchfile,
goto invalid_option;
}
break;
- case 't': /* ttlid */
- FULLCHECK("ttlid");
- nottl = ISC_TF(!state);
+ case 't':
+ switch (cmd[3]) {
+ case 'i': /* ttlid */
+ FULLCHECK("ttlid");
+ nottl = ISC_TF(!state);
+ break;
+ case 'u': /* ttlunits */
+ FULLCHECK("ttlunits");
+ nottl = ISC_FALSE;
+ ttlunits = ISC_TF(state);
+ break;
+ }
break;
default:
goto invalid_option;
diff --git a/bin/dig/dig.docbook b/bin/dig/dig.docbook
index ac03478a3e..8af7873338 100644
--- a/bin/dig/dig.docbook
+++ b/bin/dig/dig.docbook
@@ -510,6 +510,17 @@
+
+
+
+
+ Display [do not display] the TTL in friendly human-readable
+ time units of "s", "m", "h", "d", and "w", representing
+ seconds, minutes, hours, days and weeks. Implies +ttlid.
+
+
+
+
diff --git a/lib/dns/include/dns/masterdump.h b/lib/dns/include/dns/masterdump.h
index 4d5fd044da..7318184a95 100644
--- a/lib/dns/include/dns/masterdump.h
+++ b/lib/dns/include/dns/masterdump.h
@@ -106,6 +106,8 @@ typedef struct dns_master_style dns_master_style_t;
/*% Comment out data by prepending with ";" */
#define DNS_STYLEFLAG_COMMENTDATA 0x10000000U
+/*% Print TTL with human-readable units. */
+#define DNS_STYLEFLAG_TTL_UNITS 0x20000000U
ISC_LANG_BEGINDECLS
diff --git a/lib/dns/include/dns/ttl.h b/lib/dns/include/dns/ttl.h
index c2525183b7..fd5dc3f5da 100644
--- a/lib/dns/include/dns/ttl.h
+++ b/lib/dns/include/dns/ttl.h
@@ -38,6 +38,9 @@ ISC_LANG_BEGINDECLS
isc_result_t
dns_ttl_totext(isc_uint32_t src, isc_boolean_t verbose,
isc_buffer_t *target);
+isc_result_t
+dns_ttl_totext2(isc_uint32_t src, isc_boolean_t verbose,
+ isc_boolean_t upcase, isc_buffer_t *target);
/*%<
* Output a TTL or other time interval in a human-readable form.
* The time interval is given as a count of seconds in 'src'.
@@ -48,6 +51,12 @@ dns_ttl_totext(isc_uint32_t src, isc_boolean_t verbose,
* If 'verbose' is ISC_TRUE, use a verbose style like the SOA comments
* in "dig", like "1 week 2 days 3 hours 4 minutes 5 seconds".
*
+ * If 'upcase' is ISC_TRUE, we conform to the BIND 8 style in which
+ * the unit letter is capitalized if there is only a single unit
+ * letter to print (for example, "1m30s", but "2M")
+ *
+ * If 'upcase' is ISC_FALSE, unit letters are always in lower case.
+ *
* Returns:
* \li ISC_R_SUCCESS
* \li ISC_R_NOSPACE
diff --git a/lib/dns/masterdump.c b/lib/dns/masterdump.c
index 51d0ec5601..b2b2ab4246 100644
--- a/lib/dns/masterdump.c
+++ b/lib/dns/masterdump.c
@@ -494,15 +494,25 @@ rdataset_totext(dns_rdataset_t *rdataset,
unsigned int length;
INDENT_TO(ttl_column);
- length = snprintf(ttlbuf, sizeof(ttlbuf), "%u",
- rdataset->ttl);
- INSIST(length <= sizeof(ttlbuf));
- isc_buffer_availableregion(target, &r);
- if (r.length < length)
- return (ISC_R_NOSPACE);
- memmove(r.base, ttlbuf, length);
- isc_buffer_add(target, length);
- column += length;
+ if ((ctx->style.flags & DNS_STYLEFLAG_TTL_UNITS) != 0) {
+ length = target->used;
+ result = dns_ttl_totext2(rdataset->ttl,
+ ISC_FALSE, ISC_FALSE,
+ target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ column += target->used - length;
+ } else {
+ length = snprintf(ttlbuf, sizeof(ttlbuf), "%u",
+ rdataset->ttl);
+ INSIST(length <= sizeof(ttlbuf));
+ isc_buffer_availableregion(target, &r);
+ if (r.length < length)
+ return (ISC_R_NOSPACE);
+ memmove(r.base, ttlbuf, length);
+ isc_buffer_add(target, length);
+ column += length;
+ }
/*
* If the $TTL directive is not in use, the TTL we
diff --git a/lib/dns/ttl.c b/lib/dns/ttl.c
index d8ed357449..59d2ef42bb 100644
--- a/lib/dns/ttl.c
+++ b/lib/dns/ttl.c
@@ -79,6 +79,13 @@ ttlfmt(unsigned int t, const char *s, isc_boolean_t verbose,
*/
isc_result_t
dns_ttl_totext(isc_uint32_t src, isc_boolean_t verbose, isc_buffer_t *target) {
+ return (dns_ttl_totext2(src, verbose, ISC_TRUE, target));
+}
+
+isc_result_t
+dns_ttl_totext2(isc_uint32_t src, isc_boolean_t verbose,
+ isc_boolean_t upcase, isc_buffer_t *target)
+{
unsigned secs, mins, hours, days, weeks, x;
secs = src % 60; src /= 60;
@@ -116,7 +123,7 @@ dns_ttl_totext(isc_uint32_t src, isc_boolean_t verbose, isc_buffer_t *target) {
* in upper case. (Why? Because BIND 8 does that.
* Presumably it has a reason.)
*/
- if (x == 1 && !verbose) {
+ if (x == 1 && upcase && !verbose) {
isc_region_t region;
/*
* The unit letter is the last character in the