From 2d1b3a9899ba6c3bd23a5cd4ccdddac6d351c878 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 13 Apr 2021 09:38:14 +0200 Subject: [PATCH 1/4] Check zonefile is untouched if dnssec-policy none Make sure no DNSSEC contents are added to the zonefile if dnssec-policy is set to "none" (and no .state files exist for the zone). --- bin/tests/system/kasp/ns3/setup.sh | 1 + bin/tests/system/kasp/tests.sh | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/bin/tests/system/kasp/ns3/setup.sh b/bin/tests/system/kasp/ns3/setup.sh index 7e45193438..b70fce7cdd 100644 --- a/bin/tests/system/kasp/ns3/setup.sh +++ b/bin/tests/system/kasp/ns3/setup.sh @@ -77,6 +77,7 @@ zone="unsigned.kasp" echo_i "setting up zone: $zone" zonefile="${zone}.db" infile="${zone}.db.infile" +cp template.db.in $infile cp template.db.in $zonefile # Set up zone that stays unsigned. diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index d8a96c3d9f..d970491f2c 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -804,6 +804,13 @@ check_keys check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" check_apex check_subdomain +# Make sure the zone file is untouched. +n=$((n+1)) +echo_i "Make sure the zonefile for zone ${ZONE} is not edited ($n)" +ret=0 +diff "${DIR}/${ZONE}.db.infile" "${DIR}/${ZONE}.db" || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) # # Zone: insecure.kasp. From 511bc1b8828123c67be6d5ba70c2faaaeadabc3d Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 13 Apr 2021 16:45:16 +0200 Subject: [PATCH 2/4] Check for filename clashes /w dnssec-policy zones Just like with dynamic and/or inline-signing zones, check if no two or more zone configurations set the same filename. In these cases, the zone files are not read-only and named-checkconf should catch a configuration where multiple zone statements write to the same file. Add some bad configuration tests where KASP zones reference the same zone file. Update the good-kasp test to allow for two zones configure the same file name, dnssec-policy none. --- bin/tests/system/checkconf/bad-kasp10.conf | 26 ++++++++++++++++++++ bin/tests/system/checkconf/bad-kasp11.conf | 26 ++++++++++++++++++++ bin/tests/system/checkconf/bad-kasp12.conf | 28 ++++++++++++++++++++++ bin/tests/system/checkconf/bad-kasp13.conf | 26 ++++++++++++++++++++ bin/tests/system/checkconf/bad-kasp6.conf | 25 +++++++++++++++++++ bin/tests/system/checkconf/bad-kasp7.conf | 26 ++++++++++++++++++++ bin/tests/system/checkconf/bad-kasp8.conf | 26 ++++++++++++++++++++ bin/tests/system/checkconf/bad-kasp9.conf | 26 ++++++++++++++++++++ bin/tests/system/checkconf/good-kasp.conf | 9 +++++-- lib/bind9/check.c | 18 +++++++------- 10 files changed, 224 insertions(+), 12 deletions(-) create mode 100644 bin/tests/system/checkconf/bad-kasp10.conf create mode 100644 bin/tests/system/checkconf/bad-kasp11.conf create mode 100644 bin/tests/system/checkconf/bad-kasp12.conf create mode 100644 bin/tests/system/checkconf/bad-kasp13.conf create mode 100644 bin/tests/system/checkconf/bad-kasp6.conf create mode 100644 bin/tests/system/checkconf/bad-kasp7.conf create mode 100644 bin/tests/system/checkconf/bad-kasp8.conf create mode 100644 bin/tests/system/checkconf/bad-kasp9.conf diff --git a/bin/tests/system/checkconf/bad-kasp10.conf b/bin/tests/system/checkconf/bad-kasp10.conf new file mode 100644 index 0000000000..026fb52aaa --- /dev/null +++ b/bin/tests/system/checkconf/bad-kasp10.conf @@ -0,0 +1,26 @@ +/* + * 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. + */ + +// One zone with dnssec-policy 'none', one zone with dnssec-policy 'insecure', +// both using the same zone file. + +zone "example1.net" { + type master; + file "example.db"; + dnssec-policy "none"; +}; + +zone "example2.net" { + type master; + file "example.db"; + dnssec-policy "insecure"; +}; + diff --git a/bin/tests/system/checkconf/bad-kasp11.conf b/bin/tests/system/checkconf/bad-kasp11.conf new file mode 100644 index 0000000000..6d5f51813e --- /dev/null +++ b/bin/tests/system/checkconf/bad-kasp11.conf @@ -0,0 +1,26 @@ +/* + * 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. + */ + +// One zone with a dnssec-policy, the other with allow-update, +// with the same zone file. + +zone "example1.net" { + type master; + file "example.db"; + dnssec-policy "default"; +}; + +zone "example2.net" { + type master; + file "example.db"; + allow-update { any; }; +}; + diff --git a/bin/tests/system/checkconf/bad-kasp12.conf b/bin/tests/system/checkconf/bad-kasp12.conf new file mode 100644 index 0000000000..3251f61874 --- /dev/null +++ b/bin/tests/system/checkconf/bad-kasp12.conf @@ -0,0 +1,28 @@ +/* + * 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. + */ + +// One zone with a dnssec-policy, the other with update-policy, +// with the same zone file. + +zone "example1.net" { + type master; + file "example.db"; + dnssec-policy "default"; +}; + +zone "example2.net" { + type master; + file "example.db"; + update-policy { + grant * self * TXT; + }; +}; + diff --git a/bin/tests/system/checkconf/bad-kasp13.conf b/bin/tests/system/checkconf/bad-kasp13.conf new file mode 100644 index 0000000000..503859ba44 --- /dev/null +++ b/bin/tests/system/checkconf/bad-kasp13.conf @@ -0,0 +1,26 @@ +/* + * 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. + */ + +// One zone transitioning to insecure, the other with allow-update, +// with the same zone file. + +zone "example1.net" { + type master; + file "example.db"; + dnssec-policy "insecure"; +}; + +zone "example2.net" { + type master; + file "example.db"; + allow-update { any; }; +}; + diff --git a/bin/tests/system/checkconf/bad-kasp6.conf b/bin/tests/system/checkconf/bad-kasp6.conf new file mode 100644 index 0000000000..672f4df868 --- /dev/null +++ b/bin/tests/system/checkconf/bad-kasp6.conf @@ -0,0 +1,25 @@ +/* + * 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. + */ + +// Two zones with dnssec-policy with the same zone file. + +zone "example1.net" { + type master; + file "example.db"; + dnssec-policy "default"; +}; + +zone "example2.net" { + type master; + file "example.db"; + dnssec-policy "default"; +}; + diff --git a/bin/tests/system/checkconf/bad-kasp7.conf b/bin/tests/system/checkconf/bad-kasp7.conf new file mode 100644 index 0000000000..b7ba4a9b2e --- /dev/null +++ b/bin/tests/system/checkconf/bad-kasp7.conf @@ -0,0 +1,26 @@ +/* + * 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. + */ + +// Two zones with dnssec-policy 'insecure' (transitioning to insecure) +// with the same zone file. + +zone "example1.net" { + type master; + file "example.db"; + dnssec-policy "insecure"; +}; + +zone "example2.net" { + type master; + file "example.db"; + dnssec-policy "insecure"; +}; + diff --git a/bin/tests/system/checkconf/bad-kasp8.conf b/bin/tests/system/checkconf/bad-kasp8.conf new file mode 100644 index 0000000000..af4f1a3d5a --- /dev/null +++ b/bin/tests/system/checkconf/bad-kasp8.conf @@ -0,0 +1,26 @@ +/* + * 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. + */ + +// One zone with dnssec-policy, the other zone has 'dnssec-policy none', +// both with the same zone file. + +zone "example1.net" { + type master; + file "example.db"; + dnssec-policy "default"; +}; + +zone "example2.net" { + type master; + file "example.db"; + dnssec-policy "none"; +}; + diff --git a/bin/tests/system/checkconf/bad-kasp9.conf b/bin/tests/system/checkconf/bad-kasp9.conf new file mode 100644 index 0000000000..7fc5370afc --- /dev/null +++ b/bin/tests/system/checkconf/bad-kasp9.conf @@ -0,0 +1,26 @@ +/* + * 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. + */ + +// One zone with dnssec-policy, the other zone has 'dnssec-policy insecure' +// (transitioning to inseure), both with the same zone file. + +zone "example1.net" { + type master; + file "example.db"; + dnssec-policy "default"; +}; + +zone "example2.net" { + type master; + file "example.db"; + dnssec-policy "insecure"; +}; + diff --git a/bin/tests/system/checkconf/good-kasp.conf b/bin/tests/system/checkconf/good-kasp.conf index 87fdc9db58..2aa3091a8a 100644 --- a/bin/tests/system/checkconf/good-kasp.conf +++ b/bin/tests/system/checkconf/good-kasp.conf @@ -49,8 +49,13 @@ zone "example3" { file "example3.db"; dnssec-policy "default"; }; -zone "example4" { +zone "dnssec-policy-none-shared-zonefile1" { type master; - file "example4.db"; + file "shared.db"; + dnssec-policy "none"; +}; +zone "dnssec-policy-none-shared-zonefile2" { + type master; + file "shared.db"; dnssec-policy "none"; }; diff --git a/lib/bind9/check.c b/lib/bind9/check.c index ee00d2482a..042ede21e5 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -1192,9 +1192,7 @@ check_options(const cfg_obj_t *options, const cfg_obj_t *config, if (result == ISC_R_SUCCESS) { result = ISC_R_FAILURE; } - } - - if (bad_name) { + } else if (bad_name) { cfg_obj_log(obj, logctx, ISC_LOG_ERROR, "dnssec-policy name may not be 'insecure', " "'none', or 'default' (which are built-in " @@ -1202,9 +1200,9 @@ check_options(const cfg_obj_t *options, const cfg_obj_t *config, if (result == ISC_R_SUCCESS) { result = ISC_R_FAILURE; } + } else { + has_dnssecpolicy = true; } - - has_dnssecpolicy = true; } obj = NULL; @@ -3220,10 +3218,9 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, } /* - * If the zone type is rbt/rbt64 then master/hint zones - * require file clauses. - * If inline signing is used, then slave zones require a - * file clause as well + * If the zone type is rbt/rbt64 then master/hint zones require file + * clauses. If inline-signing is used, then slave zones require a + * file clause as well. */ obj = NULL; dlz = false; @@ -3261,7 +3258,8 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, result = tresult; } else if (tresult == ISC_R_SUCCESS && (ztype == CFG_ZONE_SLAVE || - ztype == CFG_ZONE_MIRROR || ddns)) + ztype == CFG_ZONE_MIRROR || ddns || + has_dnssecpolicy)) { tresult = fileexist(fileobj, files, true, logctx); if (tresult != ISC_R_SUCCESS) { From 0c09867e96c0a02ca2e9b95d6400f280a3f0b5eb Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 13 Apr 2021 17:00:49 +0200 Subject: [PATCH 3/4] Changes and release notes for [#2603] --- CHANGES | 5 ++++- doc/notes/notes-current.rst | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index d4f52c5179..77767f6047 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +5636. [bug] Check that zone files for 'dnssec-policy' zones are + only referenced once in 'named.conf'. [GL #2603] + 5635. [bug] Journal compaction could fail when a journal with invalid transaction headers was not detected at startup. [GL #2670] @@ -53,7 +56,7 @@ 5619. [protocol] Implement draft-vandijk-dnsop-nsec-ttl, updating the protocol such that NSEC(3) TTL values are set to the minimum of the SOA MINIMUM value and the SOA TTL. - [GL #2347]. + [GL #2347] 5618. [bug] When introducing change 5149, "rndc dumpdb" started to print a line above a stale RRset, indicating how diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index 2ca7b30e04..000395b127 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -88,3 +88,6 @@ Bug Fixes - Journal compaction could fail when a journal with invalid transaction headers was not detected at startup. :gl:`#2670` + +- ``named-checkconf`` now complains if zones with ``dnssec-policy`` reference + the same zone file more than once. :gl:`#2603` From 66f2cd228d9d6f02d9406e4e8469274b25adc3a4 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 14 Apr 2021 09:19:20 +0200 Subject: [PATCH 4/4] Use isdigit instead of checking character range When looking for key files, we could use isdigit rather than checking if the character is within the range [0-9]. Use (unsigned char) cast to ensure the value is representable in the unsigned char type (as suggested by the isdigit manpage). Change " & 0xff" occurrences to the recommended (unsigned char) type cast. --- bin/named/unix/os.c | 2 +- lib/bind9/check.c | 1 + lib/dns/dnssec.c | 13 +++---------- lib/dns/name.c | 4 ++-- lib/dns/rcode.c | 4 ++-- lib/dns/rdata/generic/x25_19.c | 5 +++-- lib/dns/rdata/in_1/atma_34.c | 4 ++-- lib/isc/tm.c | 2 +- lib/isc/unix/dir.c | 2 +- lib/isc/win32/dir.c | 2 +- lib/isc/win32/file.c | 2 +- lib/ns/query.c | 3 ++- 12 files changed, 20 insertions(+), 24 deletions(-) diff --git a/bin/named/unix/os.c b/bin/named/unix/os.c index 99d570a7c6..b434ff8e8b 100644 --- a/bin/named/unix/os.c +++ b/bin/named/unix/os.c @@ -379,7 +379,7 @@ all_digits(const char *s) { return (false); } while (*s != '\0') { - if (!isdigit((*s) & 0xff)) { + if (!isdigit((unsigned char)(*s))) { return (false); } s++; diff --git a/lib/bind9/check.c b/lib/bind9/check.c index 042ede21e5..a7837e6caf 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -11,6 +11,7 @@ /*! \file */ +#include #include #include #include diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c index f2a107b5fd..acba420e1f 100644 --- a/lib/dns/dnssec.c +++ b/lib/dns/dnssec.c @@ -11,6 +11,7 @@ /*! \file */ +#include #include #include #include @@ -1433,8 +1434,7 @@ dns_dnssec_findmatchingkeys(const dns_name_t *origin, const char *directory, alg = 0; for (i = len + 1 + 1; i < dir.entry.length; i++) { - if (dir.entry.name[i] < '0' || dir.entry.name[i] > '9') - { + if (!isdigit((unsigned char)dir.entry.name[i])) { break; } alg *= 10; @@ -1452,15 +1452,8 @@ dns_dnssec_findmatchingkeys(const dns_name_t *origin, const char *directory, } for (i++; i < dir.entry.length; i++) { - if (dir.entry.name[i] < '0' || dir.entry.name[i] > '9') - { + if (!isdigit((unsigned char)dir.entry.name[i])) { break; - - /* - * Did we not read exactly 5 more digits? - * Did we overflow? - * Did we correctly terminate? - */ } } diff --git a/lib/dns/name.c b/lib/dns/name.c index dc7f62b1f1..b6657f620a 100644 --- a/lib/dns/name.c +++ b/lib/dns/name.c @@ -1204,7 +1204,7 @@ dns_name_fromtext(dns_name_t *name, isc_buffer_t *source, POST(state); /* FALLTHROUGH */ case ft_escape: - if (!isdigit(c & 0xff)) { + if (!isdigit((unsigned char)c)) { if (count >= 63) { return (DNS_R_LABELTOOLONG); } @@ -1224,7 +1224,7 @@ dns_name_fromtext(dns_name_t *name, isc_buffer_t *source, state = ft_escdecimal; /* FALLTHROUGH */ case ft_escdecimal: - if (!isdigit(c & 0xff)) { + if (!isdigit((unsigned char)c)) { return (DNS_R_BADESCAPE); } value *= 10; diff --git a/lib/dns/rcode.c b/lib/dns/rcode.c index 2581970639..ef046b3322 100644 --- a/lib/dns/rcode.c +++ b/lib/dns/rcode.c @@ -219,8 +219,8 @@ maybe_numeric(unsigned int *valuep, isc_textregion_t *source, unsigned int max, char buffer[NUMBERSIZE]; int v; - if (!isdigit(source->base[0] & 0xff) || source->length > NUMBERSIZE - 1) - { + if (!isdigit((unsigned char)source->base[0]) || + source->length > NUMBERSIZE - 1) { return (ISC_R_BADNUMBER); } diff --git a/lib/dns/rdata/generic/x25_19.c b/lib/dns/rdata/generic/x25_19.c index 80ef247600..0ba32d582f 100644 --- a/lib/dns/rdata/generic/x25_19.c +++ b/lib/dns/rdata/generic/x25_19.c @@ -35,7 +35,8 @@ fromtext_x25(ARGS_FROMTEXT) { RETTOK(DNS_R_SYNTAX); } for (i = 0; i < token.value.as_textregion.length; i++) { - if (!isdigit(token.value.as_textregion.base[i] & 0xff)) { + if (!isdigit((unsigned char)token.value.as_textregion.base[i])) + { RETTOK(ISC_R_RANGE); } } @@ -125,7 +126,7 @@ fromstruct_x25(ARGS_FROMSTRUCT) { } for (i = 0; i < x25->x25_len; i++) { - if (!isdigit(x25->x25[i] & 0xff)) { + if (!isdigit((unsigned char)x25->x25[i])) { return (ISC_R_RANGE); } } diff --git a/lib/dns/rdata/in_1/atma_34.c b/lib/dns/rdata/in_1/atma_34.c index 893e8cda82..07a9198e53 100644 --- a/lib/dns/rdata/in_1/atma_34.c +++ b/lib/dns/rdata/in_1/atma_34.c @@ -91,7 +91,7 @@ fromtext_in_atma(ARGS_FROMTEXT) { lastwasperiod = true; continue; } - if ((sr->base[0] < '0') || (sr->base[0] > '9')) { + if (!isdigit((unsigned char)sr->base[0])) { RETTOK(DNS_R_SYNTAX); } RETERR(mem_tobuffer(target, sr->base, 1)); @@ -157,7 +157,7 @@ fromwire_in_atma(ARGS_FROMWIRE) { if (region.base[0] == 1) { unsigned int i; for (i = 1; i < region.length; i++) { - if (region.base[i] < '0' || region.base[i] > '9') { + if (!isdigit((unsigned char)region.base[i])) { return (DNS_R_FORMERR); } } diff --git a/lib/isc/tm.c b/lib/isc/tm.c index 5309b14664..9c0a8e36f1 100644 --- a/lib/isc/tm.c +++ b/lib/isc/tm.c @@ -89,7 +89,7 @@ conv_num(const char **buf, int *dest, int llim, int ulim) { /* The limit also determines the number of valid digits. */ int rulim = ulim; - if (**buf < '0' || **buf > '9') { + if (!isdigit((unsigned char)**buf)) { return (0); } diff --git a/lib/isc/unix/dir.c b/lib/isc/unix/dir.c index 29e4bb794e..49367956fd 100644 --- a/lib/isc/unix/dir.c +++ b/lib/isc/unix/dir.c @@ -230,7 +230,7 @@ isc_dir_createunique(char *templet) { */ p = x; while (*p != '\0') { - if (isdigit(*p & 0xff)) { + if (isdigit((unsigned char)*p)) { *p = 'a'; } else if (*p != 'z') { ++*p; diff --git a/lib/isc/win32/dir.c b/lib/isc/win32/dir.c index 006f60a981..cbfc0dc98e 100644 --- a/lib/isc/win32/dir.c +++ b/lib/isc/win32/dir.c @@ -273,7 +273,7 @@ isc_dir_createunique(char *templet) { */ p = x; while (*p != '\0') { - if (isdigit(*p & 0xff)) { + if (isdigit((unsigned char)*p)) { *p = 'a'; } else if (*p != 'z') { ++*p; diff --git a/lib/isc/win32/file.c b/lib/isc/win32/file.c index 624b24d71e..e1d3c89141 100644 --- a/lib/isc/win32/file.c +++ b/lib/isc/win32/file.c @@ -102,7 +102,7 @@ gettemp(char *path, bool binary, int *doopen) { if (*trv == 'z') { *trv++ = 'a'; } else { - if (isdigit(*trv)) { + if (isdigit((unsigned char)*trv)) { *trv = 'a'; } else { ++*trv; diff --git a/lib/ns/query.c b/lib/ns/query.c index f15101ced2..67c65f0fe5 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -11,6 +11,7 @@ /*! \file */ +#include #include #include #include @@ -5352,7 +5353,7 @@ get_root_key_sentinel_id(query_ctx_t *qctx, const char *ndata) { int i; for (i = 0; i < 5; i++) { - if (ndata[i] < '0' || ndata[i] > '9') { + if (!isdigit((unsigned char)ndata[i])) { return (false); } v *= 10;