From 7eb5d61703672bfd01ec29984a4377ef33b15a59 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Wed, 19 Aug 2020 18:47:35 +1000 Subject: [PATCH 1/5] Tighten LOC parsing to reject period and/or m as a value. (cherry picked from commit 9225c67835a9d93122632ad879533f50faec8640) --- lib/dns/rdata/generic/loc_29.c | 27 +++++++++++++--- lib/dns/tests/rdata_test.c | 58 ++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 4 deletions(-) diff --git a/lib/dns/rdata/generic/loc_29.c b/lib/dns/rdata/generic/loc_29.c index 26e6381942..96022300c9 100644 --- a/lib/dns/rdata/generic/loc_29.c +++ b/lib/dns/rdata/generic/loc_29.c @@ -39,6 +39,7 @@ fromtext_loc(ARGS_FROMTEXT) { unsigned long latitude; unsigned long longitude; unsigned long altitude; + bool ok; REQUIRE(type == dns_rdatatype_loc); @@ -106,6 +107,7 @@ fromtext_loc(ARGS_FROMTEXT) { if (s1 < 0 || s1 > 59) { RETTOK(ISC_R_RANGE); } + ok = e != DNS_AS_STR(token); if (*e == '.') { const char *l; e++; @@ -116,6 +118,7 @@ fromtext_loc(ARGS_FROMTEXT) { if ((tmp = decvalue(*e++)) < 0) { RETTOK(DNS_R_SYNTAX); } + ok = true; s1 *= 10; s1 += tmp; } @@ -143,6 +146,9 @@ fromtext_loc(ARGS_FROMTEXT) { } else { s1 *= 1000; } + if (!ok) { + RETTOK(DNS_R_SYNTAX); + } if (d1 == 90 && s1 != 0) { RETTOK(ISC_R_RANGE); } @@ -210,6 +216,7 @@ getlong: if (s2 < 0 || s2 > 59) { RETTOK(ISC_R_RANGE); } + ok = e != DNS_AS_STR(token); if (*e == '.') { const char *l; e++; @@ -220,6 +227,7 @@ getlong: if ((tmp = decvalue(*e++)) < 0) { RETTOK(DNS_R_SYNTAX); } + ok = true; s2 *= 10; s2 += tmp; } @@ -247,6 +255,9 @@ getlong: } else { s2 *= 1000; } + if (!ok) { + RETTOK(DNS_R_SYNTAX); + } if (d2 == 180 && s2 != 0) { RETTOK(ISC_R_RANGE); } @@ -277,6 +288,7 @@ getalt: RETTOK(ISC_R_RANGE); } cm = 0; + ok = e != DNS_AS_STR(token); if (*e == '.') { e++; for (i = 0; i < 2; i++) { @@ -286,6 +298,7 @@ getalt: if ((tmp = decvalue(*e++)) < 0) { return (DNS_R_SYNTAX); } + ok = true; cm *= 10; if (m < 0) { cm -= tmp; @@ -299,7 +312,7 @@ getalt: if (*e == 'm') { e++; } - if (*e != 0) { + if (!ok || *e != 0) { RETTOK(DNS_R_SYNTAX); } if (m == -100000 && cm != 0) { @@ -333,6 +346,7 @@ getalt: RETTOK(ISC_R_RANGE); } cm = 0; + ok = e != DNS_AS_STR(token); if (*e == '.') { e++; for (i = 0; i < 2; i++) { @@ -342,6 +356,7 @@ getalt: if ((tmp = decvalue(*e++)) < 0) { RETTOK(DNS_R_SYNTAX); } + ok = true; cm *= 10; cm += tmp; } @@ -351,7 +366,7 @@ getalt: if (*e == 'm') { e++; } - if (*e != 0) { + if (!ok || *e != 0) { RETTOK(DNS_R_SYNTAX); } /* @@ -394,6 +409,7 @@ getalt: RETTOK(ISC_R_RANGE); } cm = 0; + ok = e != DNS_AS_STR(token); if (*e == '.') { e++; for (i = 0; i < 2; i++) { @@ -403,6 +419,7 @@ getalt: if ((tmp = decvalue(*e++)) < 0) { RETTOK(DNS_R_SYNTAX); } + ok = true; cm *= 10; cm += tmp; } @@ -412,7 +429,7 @@ getalt: if (*e == 'm') { e++; } - if (*e != 0) { + if (!ok || *e != 0) { RETTOK(DNS_R_SYNTAX); } /* @@ -453,6 +470,7 @@ getalt: RETTOK(ISC_R_RANGE); } cm = 0; + ok = e != DNS_AS_STR(token); if (*e == '.') { e++; for (i = 0; i < 2; i++) { @@ -462,6 +480,7 @@ getalt: if ((tmp = decvalue(*e++)) < 0) { RETTOK(DNS_R_SYNTAX); } + ok = true; cm *= 10; cm += tmp; } @@ -471,7 +490,7 @@ getalt: if (*e == 'm') { e++; } - if (*e != 0) { + if (!ok || *e != 0) { RETTOK(DNS_R_SYNTAX); } /* diff --git a/lib/dns/tests/rdata_test.c b/lib/dns/tests/rdata_test.c index 41529c6610..084d8d4c72 100644 --- a/lib/dns/tests/rdata_test.c +++ b/lib/dns/tests/rdata_test.c @@ -1969,6 +1969,63 @@ key(void **state) { dns_rdatatype_key, sizeof(dns_rdata_key_t)); } +/* + * LOC tests. + */ +static void +loc(void **state) { + text_ok_t text_ok[] = { + TEXT_VALID_CHANGED("0 N 0 E 0", "0 0 0.000 N 0 0 0.000 E 0.00m " + "1m 10000m 10m"), + TEXT_VALID_CHANGED("0 S 0 W 0", "0 0 0.000 N 0 0 0.000 E 0.00m " + "1m 10000m 10m"), + TEXT_VALID_CHANGED("0 0 N 0 0 E 0", "0 0 0.000 N 0 0 0.000 E " + "0.00m 1m 10000m 10m"), + TEXT_VALID_CHANGED("0 0 0 N 0 0 0 E 0", + "0 0 0.000 N 0 0 0.000 E 0.00m 1m 10000m " + "10m"), + TEXT_VALID_CHANGED("0 0 0 N 0 0 0 E 0", + "0 0 0.000 N 0 0 0.000 E 0.00m 1m 10000m " + "10m"), + TEXT_VALID_CHANGED("0 0 0. N 0 0 0. E 0", + "0 0 0.000 N 0 0 0.000 E 0.00m 1m 10000m " + "10m"), + TEXT_VALID_CHANGED("0 0 .0 N 0 0 .0 E 0", + "0 0 0.000 N 0 0 0.000 E 0.00m 1m 10000m " + "10m"), + TEXT_INVALID("0 0 . N 0 0 0. E 0"), + TEXT_INVALID("0 0 0. N 0 0 . E 0"), + TEXT_INVALID("0 0 0. N 0 0 0. E m"), + TEXT_INVALID("0 0 0. N 0 0 0. E 0 ."), + TEXT_INVALID("0 0 0. N 0 0 0. E 0 m"), + TEXT_INVALID("0 0 0. N 0 0 0. E 0 0 ."), + TEXT_INVALID("0 0 0. N 0 0 0. E 0 0 m"), + TEXT_INVALID("0 0 0. N 0 0 0. E 0 0 0 ."), + TEXT_INVALID("0 0 0. N 0 0 0. E 0 0 0 m"), + TEXT_VALID_CHANGED("90 N 180 E 0", "90 0 0.000 N 180 0 0.000 E " + "0.00m 1m 10000m 10m"), + TEXT_INVALID("90 1 N 180 E 0"), + TEXT_INVALID("90 0 1 N 180 E 0"), + TEXT_INVALID("90 N 180 1 E 0"), + TEXT_INVALID("90 N 180 0 1 E 0"), + TEXT_VALID_CHANGED("90 S 180 W 0", "90 0 0.000 S 180 0 0.000 W " + "0.00m 1m 10000m 10m"), + TEXT_INVALID("90 1 S 180 W 0"), + TEXT_INVALID("90 0 1 S 180 W 0"), + TEXT_INVALID("90 S 180 1 W 0"), + TEXT_INVALID("90 S 180 0 1 W 0"), + /* + * Sentinel. + */ + TEXT_SENTINEL() + }; + + UNUSED(state); + + check_rdata(text_ok, 0, NULL, false, dns_rdataclass_in, + dns_rdatatype_loc, sizeof(dns_rdata_loc_t)); +} + /* * http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt * @@ -2579,6 +2636,7 @@ main(int argc, char **argv) { cmocka_unit_test_setup_teardown(hip, _setup, _teardown), cmocka_unit_test_setup_teardown(isdn, _setup, _teardown), cmocka_unit_test_setup_teardown(key, _setup, _teardown), + cmocka_unit_test_setup_teardown(loc, _setup, _teardown), cmocka_unit_test_setup_teardown(nimloc, _setup, _teardown), cmocka_unit_test_setup_teardown(nsec, _setup, _teardown), cmocka_unit_test_setup_teardown(nsec3, _setup, _teardown), From 06b76b2b1605bfc45e42698443be416f1b05eddd Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Sun, 23 Aug 2020 01:35:19 +1000 Subject: [PATCH 2/5] Check LOC's altitude field is properly parsed and encoded. (cherry picked from commit 888dfd78c7f7c02f192efa4af13e07fc17ed85c3) --- lib/dns/tests/rdata_test.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/dns/tests/rdata_test.c b/lib/dns/tests/rdata_test.c index 084d8d4c72..e48c90f10a 100644 --- a/lib/dns/tests/rdata_test.c +++ b/lib/dns/tests/rdata_test.c @@ -2014,6 +2014,11 @@ loc(void **state) { TEXT_INVALID("90 0 1 S 180 W 0"), TEXT_INVALID("90 S 180 1 W 0"), TEXT_INVALID("90 S 180 0 1 W 0"), + TEXT_VALID("0 0 0.000 N 0 0 0.000 E -0.95m 1m 10000m 10m"), + TEXT_VALID("0 0 0.000 N 0 0 0.000 E -0.05m 1m 10000m 10m"), + TEXT_VALID("0 0 0.000 N 0 0 0.000 E -100000.00m 1m 10000m 10m"), + TEXT_VALID("0 0 0.000 N 0 0 0.000 E 42849672.95m 1m 10000m " + "10m"), /* * Sentinel. */ From baf93342d088b2900c21b6e11b65f37d5618697a Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Sun, 23 Aug 2020 01:38:17 +1000 Subject: [PATCH 3/5] Correctly encode LOC records with non integer negative altitudes. (cherry picked from commit 337cc878fa5c6a93664b402a5fb7ee06d9b3a0f2) --- lib/dns/rdata/generic/loc_29.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/dns/rdata/generic/loc_29.c b/lib/dns/rdata/generic/loc_29.c index 96022300c9..ce9235f01a 100644 --- a/lib/dns/rdata/generic/loc_29.c +++ b/lib/dns/rdata/generic/loc_29.c @@ -27,6 +27,7 @@ fromtext_loc(ARGS_FROMTEXT) { unsigned char version; bool east = false; bool north = false; + bool negative = false; long tmp; long m; long cm; @@ -280,6 +281,9 @@ getalt: */ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, false)); + if (DNS_AS_STR(token)[0] == '-') { + negative = true; + } m = strtol(DNS_AS_STR(token), &e, 10); if (*e != 0 && *e != '.' && *e != 'm') { RETTOK(DNS_R_SYNTAX); @@ -324,6 +328,9 @@ getalt: /* * Adjust base. */ + if (m < 0 || negative) { + cm = -cm; + } altitude = m + 100000; altitude *= 100; altitude += cm; From c2ee9eea3adfe87bcd7a51e808a97c02006d6088 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Tue, 25 Aug 2020 11:03:30 +1000 Subject: [PATCH 4/5] Refactor totext_loc (cherry picked from commit 2ca4d3503779106c4608b3ca3f4832582e8732e3) --- lib/dns/rdata/generic/loc_29.c | 805 ++++++++++++++------------------- lib/dns/tests/rdata_test.c | 2 + 2 files changed, 350 insertions(+), 457 deletions(-) diff --git a/lib/dns/rdata/generic/loc_29.c b/lib/dns/rdata/generic/loc_29.c index ce9235f01a..5bd6e800a5 100644 --- a/lib/dns/rdata/generic/loc_29.c +++ b/lib/dns/rdata/generic/loc_29.c @@ -16,283 +16,75 @@ #define RRTYPE_LOC_ATTRIBUTES (0) -static inline isc_result_t -fromtext_loc(ARGS_FROMTEXT) { - isc_token_t token; - int d1, m1, s1; - int d2, m2, s2; - unsigned char size; - unsigned char hp; - unsigned char vp; - unsigned char version; - bool east = false; - bool north = false; - bool negative = false; +static isc_result_t +loc_getdecimal(const char *str, unsigned long max, size_t precision, char units, + unsigned long *valuep) { + bool ok; + char *e; + size_t i; + long tmp; + unsigned long value; + + value = strtoul(str, &e, 10); + if (*e != 0 && *e != '.' && *e != units) { + return (DNS_R_SYNTAX); + } + if (value > max) { + return (ISC_R_RANGE); + } + ok = e != str; + if (*e == '.') { + e++; + for (i = 0; i < precision; i++) { + if (*e == 0 || *e == units) { + break; + } + if ((tmp = decvalue(*e++)) < 0) { + return (DNS_R_SYNTAX); + } + ok = true; + value *= 10; + value += tmp; + } + for (; i < precision; i++) { + value *= 10; + } + } else { + for (i = 0; i < precision; i++) { + value *= 10; + } + } + if (*e != 0 && *e == units) { + e++; + } + if (!ok || *e != 0) { + return (DNS_R_SYNTAX); + } + *valuep = value; + return (ISC_R_SUCCESS); +} + +static isc_result_t +loc_getprecision(const char *str, unsigned char *valuep) { + unsigned long poweroften[8] = { 1, 10, 100, 1000, + 10000, 100000, 1000000, 10000000 }; + unsigned long m, cm; + bool ok; + char *e; + size_t i; long tmp; - long m; - long cm; - long poweroften[8] = { 1, 10, 100, 1000, - 10000, 100000, 1000000, 10000000 }; int man; int exp; - char *e; - int i; - unsigned long latitude; - unsigned long longitude; - unsigned long altitude; - bool ok; - REQUIRE(type == dns_rdatatype_loc); - - UNUSED(type); - UNUSED(rdclass); - UNUSED(origin); - UNUSED(options); - - /* - * Defaults. - */ - m1 = s1 = 0; - m2 = s2 = 0; - size = 0x12; /* 1.00m */ - hp = 0x16; /* 10000.00 m */ - vp = 0x13; /* 10.00 m */ - version = 0; - - /* - * Degrees. - */ - RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number, - false)); - if (token.value.as_ulong > 90U) { - RETTOK(ISC_R_RANGE); - } - d1 = (int)token.value.as_ulong; - /* - * Minutes. - */ - RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, - false)); - if (strcasecmp(DNS_AS_STR(token), "N") == 0) { - north = true; - } - if (north || strcasecmp(DNS_AS_STR(token), "S") == 0) { - goto getlong; - } - m1 = strtol(DNS_AS_STR(token), &e, 10); - if (*e != 0) { - RETTOK(DNS_R_SYNTAX); - } - if (m1 < 0 || m1 > 59) { - RETTOK(ISC_R_RANGE); - } - if (d1 == 90 && m1 != 0) { - RETTOK(ISC_R_RANGE); - } - - /* - * Seconds. - */ - RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, - false)); - if (strcasecmp(DNS_AS_STR(token), "N") == 0) { - north = true; - } - if (north || strcasecmp(DNS_AS_STR(token), "S") == 0) { - goto getlong; - } - s1 = strtol(DNS_AS_STR(token), &e, 10); - if (*e != 0 && *e != '.') { - RETTOK(DNS_R_SYNTAX); - } - if (s1 < 0 || s1 > 59) { - RETTOK(ISC_R_RANGE); - } - ok = e != DNS_AS_STR(token); - if (*e == '.') { - const char *l; - e++; - for (i = 0; i < 3; i++) { - if (*e == 0) { - break; - } - if ((tmp = decvalue(*e++)) < 0) { - RETTOK(DNS_R_SYNTAX); - } - ok = true; - s1 *= 10; - s1 += tmp; - } - for (; i < 3; i++) - s1 *= 10; - l = e; - while (*e != 0) { - if (decvalue(*e++) < 0) { - RETTOK(DNS_R_SYNTAX); - } - } - if (*l != '\0' && callbacks != NULL) { - const char *file = isc_lex_getsourcename(lexer); - unsigned long line = isc_lex_getsourceline(lexer); - - if (file == NULL) { - file = "UNKNOWN"; - } - (*callbacks->warn)(callbacks, - "%s: %s:%u: '%s' extra " - "precision digits ignored", - "dns_rdata_fromtext", file, line, - DNS_AS_STR(token)); - } - } else { - s1 *= 1000; - } - if (!ok) { - RETTOK(DNS_R_SYNTAX); - } - if (d1 == 90 && s1 != 0) { - RETTOK(ISC_R_RANGE); - } - - /* - * Direction. - */ - RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, - false)); - if (strcasecmp(DNS_AS_STR(token), "N") == 0) { - north = true; - } - if (!north && strcasecmp(DNS_AS_STR(token), "S") != 0) { - RETTOK(DNS_R_SYNTAX); - } - -getlong: - /* - * Degrees. - */ - RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number, - false)); - if (token.value.as_ulong > 180U) { - RETTOK(ISC_R_RANGE); - } - d2 = (int)token.value.as_ulong; - - /* - * Minutes. - */ - RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, - false)); - if (strcasecmp(DNS_AS_STR(token), "E") == 0) { - east = true; - } - if (east || strcasecmp(DNS_AS_STR(token), "W") == 0) { - goto getalt; - } - m2 = strtol(DNS_AS_STR(token), &e, 10); - if (*e != 0) { - RETTOK(DNS_R_SYNTAX); - } - if (m2 < 0 || m2 > 59) { - RETTOK(ISC_R_RANGE); - } - if (d2 == 180 && m2 != 0) { - RETTOK(ISC_R_RANGE); - } - - /* - * Seconds. - */ - RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, - false)); - if (strcasecmp(DNS_AS_STR(token), "E") == 0) { - east = true; - } - if (east || strcasecmp(DNS_AS_STR(token), "W") == 0) { - goto getalt; - } - s2 = strtol(DNS_AS_STR(token), &e, 10); - if (*e != 0 && *e != '.') { - RETTOK(DNS_R_SYNTAX); - } - if (s2 < 0 || s2 > 59) { - RETTOK(ISC_R_RANGE); - } - ok = e != DNS_AS_STR(token); - if (*e == '.') { - const char *l; - e++; - for (i = 0; i < 3; i++) { - if (*e == 0) { - break; - } - if ((tmp = decvalue(*e++)) < 0) { - RETTOK(DNS_R_SYNTAX); - } - ok = true; - s2 *= 10; - s2 += tmp; - } - for (; i < 3; i++) - s2 *= 10; - l = e; - while (*e != 0) { - if (decvalue(*e++) < 0) { - RETTOK(DNS_R_SYNTAX); - } - } - if (*l != '\0' && callbacks != NULL) { - const char *file = isc_lex_getsourcename(lexer); - unsigned long line = isc_lex_getsourceline(lexer); - - if (file == NULL) { - file = "UNKNOWN"; - } - (*callbacks->warn)(callbacks, - "%s: %s:%u: '%s' extra " - "precision digits ignored", - "dns_rdata_fromtext", file, line, - DNS_AS_STR(token)); - } - } else { - s2 *= 1000; - } - if (!ok) { - RETTOK(DNS_R_SYNTAX); - } - if (d2 == 180 && s2 != 0) { - RETTOK(ISC_R_RANGE); - } - - /* - * Direction. - */ - RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, - false)); - if (strcasecmp(DNS_AS_STR(token), "E") == 0) { - east = true; - } - if (!east && strcasecmp(DNS_AS_STR(token), "W") != 0) { - RETTOK(DNS_R_SYNTAX); - } - -getalt: - /* - * Altitude. - */ - RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, - false)); - if (DNS_AS_STR(token)[0] == '-') { - negative = true; - } - m = strtol(DNS_AS_STR(token), &e, 10); + m = strtoul(str, &e, 10); if (*e != 0 && *e != '.' && *e != 'm') { - RETTOK(DNS_R_SYNTAX); + return (DNS_R_SYNTAX); } - if (m < -100000 || m > 42849672) { - RETTOK(ISC_R_RANGE); + if (m > 90000000) { + return (ISC_R_RANGE); } cm = 0; - ok = e != DNS_AS_STR(token); + ok = e != str; if (*e == '.') { e++; for (i = 0; i < 2; i++) { @@ -304,141 +96,19 @@ getalt: } ok = true; cm *= 10; - if (m < 0) { - cm -= tmp; - } else { - cm += tmp; - } - } - for (; i < 2; i++) - cm *= 10; - } - if (*e == 'm') { - e++; - } - if (!ok || *e != 0) { - RETTOK(DNS_R_SYNTAX); - } - if (m == -100000 && cm != 0) { - RETTOK(ISC_R_RANGE); - } - if (m == 42849672 && cm > 95) { - RETTOK(ISC_R_RANGE); - } - /* - * Adjust base. - */ - if (m < 0 || negative) { - cm = -cm; - } - altitude = m + 100000; - altitude *= 100; - altitude += cm; - - /* - * Size: optional. - */ - RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, - true)); - if (token.type == isc_tokentype_eol || token.type == isc_tokentype_eof) - { - isc_lex_ungettoken(lexer, &token); - goto encode; - } - m = strtol(DNS_AS_STR(token), &e, 10); - if (*e != 0 && *e != '.' && *e != 'm') { - RETTOK(DNS_R_SYNTAX); - } - if (m < 0 || m > 90000000) { - RETTOK(ISC_R_RANGE); - } - cm = 0; - ok = e != DNS_AS_STR(token); - if (*e == '.') { - e++; - for (i = 0; i < 2; i++) { - if (*e == 0 || *e == 'm') { - break; - } - if ((tmp = decvalue(*e++)) < 0) { - RETTOK(DNS_R_SYNTAX); - } - ok = true; - cm *= 10; cm += tmp; } - for (; i < 2; i++) + for (; i < 2; i++) { cm *= 10; + } } if (*e == 'm') { e++; } if (!ok || *e != 0) { - RETTOK(DNS_R_SYNTAX); + return (DNS_R_SYNTAX); } - /* - * We don't just multiply out as we will overflow. - */ - if (m > 0) { - for (exp = 0; exp < 7; exp++) { - if (m < poweroften[exp + 1]) { - break; - } - } - man = m / poweroften[exp]; - exp += 2; - } else { - if (cm >= 10) { - man = cm / 10; - exp = 1; - } else { - man = cm; - exp = 0; - } - } - size = (man << 4) + exp; - /* - * Horizontal precision: optional. - */ - RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, - true)); - if (token.type == isc_tokentype_eol || token.type == isc_tokentype_eof) - { - isc_lex_ungettoken(lexer, &token); - goto encode; - } - m = strtol(DNS_AS_STR(token), &e, 10); - if (*e != 0 && *e != '.' && *e != 'm') { - RETTOK(DNS_R_SYNTAX); - } - if (m < 0 || m > 90000000) { - RETTOK(ISC_R_RANGE); - } - cm = 0; - ok = e != DNS_AS_STR(token); - if (*e == '.') { - e++; - for (i = 0; i < 2; i++) { - if (*e == 0 || *e == 'm') { - break; - } - if ((tmp = decvalue(*e++)) < 0) { - RETTOK(DNS_R_SYNTAX); - } - ok = true; - cm *= 10; - cm += tmp; - } - for (; i < 2; i++) - cm *= 10; - } - if (*e == 'm') { - e++; - } - if (!ok || *e != 0) { - RETTOK(DNS_R_SYNTAX); - } /* * We don't just multiply out as we will overflow. */ @@ -457,89 +127,310 @@ getalt: man = cm; exp = 0; } - hp = (man << 4) + exp; + *valuep = (man << 4) + exp; + return (ISC_R_SUCCESS); +} + +static isc_result_t +get_degrees(isc_lex_t *lexer, isc_token_t *token, unsigned long *d) { + RETERR(isc_lex_getmastertoken(lexer, token, isc_tokentype_number, + false)); + *d = token->value.as_ulong; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +check_coordinate(unsigned long d, unsigned long m, unsigned long s, + unsigned long maxd) { + if (d > maxd || m > 59U) { + return (ISC_R_RANGE); + } + if (d == maxd && (m != 0 || s != 0)) { + return (ISC_R_RANGE); + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +get_minutes(isc_lex_t *lexer, isc_token_t *token, unsigned long *m) { + RETERR(isc_lex_getmastertoken(lexer, token, isc_tokentype_number, + false)); + + *m = token->value.as_ulong; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +get_seconds(isc_lex_t *lexer, isc_token_t *token, unsigned long *s) { + RETERR(isc_lex_getmastertoken(lexer, token, isc_tokentype_string, + false)); + RETERR(loc_getdecimal(DNS_AS_STR(*token), 59, 3, '\0', s)); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +get_direction(isc_lex_t *lexer, isc_token_t *token, const char *directions, + int *direction) { + RETERR(isc_lex_getmastertoken(lexer, token, isc_tokentype_string, + false)); + if (DNS_AS_STR(*token)[0] == directions[1] && + DNS_AS_STR(*token)[1] == 0) { + *direction = DNS_AS_STR(*token)[0]; + return (ISC_R_SUCCESS); + } + + if (DNS_AS_STR(*token)[0] == directions[0] && + DNS_AS_STR(*token)[1] == 0) { + *direction = DNS_AS_STR(*token)[0]; + return (ISC_R_SUCCESS); + } + + *direction = 0; + isc_lex_ungettoken(lexer, token); + return (ISC_R_SUCCESS); +} + +static isc_result_t +loc_getcoordinate(isc_lex_t *lexer, unsigned long *dp, unsigned long *mp, + unsigned long *sp, const char *directions, int *directionp, + unsigned long maxd) { + isc_result_t result = ISC_R_SUCCESS; + isc_token_t token; + unsigned long d, m, s; + int direction = 0; + + m = 0; + s = 0; /* - * Vertical precision: optional. + * Degrees. */ + RETERR(get_degrees(lexer, &token, &d)); + RETTOK(check_coordinate(d, m, s, maxd)); + + /* + * Minutes. + */ + RETERR(get_direction(lexer, &token, directions, &direction)); + if (direction > 0) { + goto done; + } + + RETERR(get_minutes(lexer, &token, &m)); + RETTOK(check_coordinate(d, m, s, maxd)); + + /* + * Seconds. + */ + RETERR(get_direction(lexer, &token, directions, &direction)); + if (direction > 0) { + goto done; + } + + result = get_seconds(lexer, &token, &s); + if (result == ISC_R_RANGE || result == DNS_R_SYNTAX) { + RETTOK(result); + } + RETERR(result); + RETTOK(check_coordinate(d, m, s, maxd)); + + /* + * Direction. + */ + RETERR(get_direction(lexer, &token, directions, &direction)); + if (direction == -1) { + RETERR(DNS_R_SYNTAX); + } +done: + + *directionp = direction; + *dp = d; + *mp = m; + *sp = s; + + return (ISC_R_SUCCESS); +} + +static inline isc_result_t +loc_getlatitude(isc_lex_t *lexer, unsigned long *latitude) { + unsigned long d1 = 0, m1 = 0, s1 = 0; + int direction = 0; + + RETERR(loc_getcoordinate(lexer, &d1, &m1, &s1, "SN", &direction, 90U)); + + switch (direction) { + case 'N': + *latitude = 0x80000000 + (d1 * 3600 + m1 * 60) * 1000 + s1; + break; + case 'S': + *latitude = 0x80000000 - (d1 * 3600 + m1 * 60) * 1000 - s1; + break; + default: + INSIST(0); + ISC_UNREACHABLE(); + } + + return (ISC_R_SUCCESS); +} + +static inline isc_result_t +loc_getlongitude(isc_lex_t *lexer, unsigned long *longitude) { + unsigned long d2 = 0, m2 = 0, s2 = 0; + int direction = 0; + + RETERR(loc_getcoordinate(lexer, &d2, &m2, &s2, "WE", &direction, 180U)); + + switch (direction) { + case 'E': + *longitude = 0x80000000 + (d2 * 3600 + m2 * 60) * 1000 + s2; + break; + case 'W': + *longitude = 0x80000000 - (d2 * 3600 + m2 * 60) * 1000 - s2; + break; + default: + INSIST(0); + ISC_UNREACHABLE(); + } + + return (ISC_R_SUCCESS); +} + +static inline isc_result_t +loc_getaltitude(isc_lex_t *lexer, unsigned long *altitude) { + isc_token_t token; + unsigned long cm; + const char *str; + + RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, + false)); + str = DNS_AS_STR(token); + if (DNS_AS_STR(token)[0] == '-') { + RETTOK(loc_getdecimal(str + 1, 100000, 2, 'm', &cm)); + if (cm > 10000000UL) { + RETTOK(ISC_R_RANGE); + } + *altitude = 10000000 - cm; + } else { + RETTOK(loc_getdecimal(str, 42849672, 2, 'm', &cm)); + if (cm > 4284967295UL) { + RETTOK(ISC_R_RANGE); + } + *altitude = 10000000 + cm; + } + + return (ISC_R_SUCCESS); +} + +static inline isc_result_t +loc_getoptionalprecision(isc_lex_t *lexer, unsigned char *valuep) { + isc_token_t token; + RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, true)); if (token.type == isc_tokentype_eol || token.type == isc_tokentype_eof) { isc_lex_ungettoken(lexer, &token); + return (ISC_R_NOMORE); + } + RETTOK(loc_getprecision(DNS_AS_STR(token), valuep)); + + return (ISC_R_SUCCESS); +} + +static inline isc_result_t +loc_getsize(isc_lex_t *lexer, unsigned char *sizep) { + return (loc_getoptionalprecision(lexer, sizep)); +} + +static inline isc_result_t +loc_gethorizontalprecision(isc_lex_t *lexer, unsigned char *hpp) { + return (loc_getoptionalprecision(lexer, hpp)); +} + +static inline isc_result_t +loc_getverticalprecision(isc_lex_t *lexer, unsigned char *vpp) { + return (loc_getoptionalprecision(lexer, vpp)); +} + +/* The LOC record is expressed in a master file in the following format: + * + * LOC ( d1 [m1 [s1]] {"N"|"S"} d2 [m2 [s2]] + * {"E"|"W"} alt["m"] [siz["m"] [hp["m"] + * [vp["m"]]]] ) + * + * (The parentheses are used for multi-line data as specified in [RFC + * 1035] section 5.1.) + * + * where: + * + * d1: [0 .. 90] (degrees latitude) + * d2: [0 .. 180] (degrees longitude) + * m1, m2: [0 .. 59] (minutes latitude/longitude) + * s1, s2: [0 .. 59.999] (seconds latitude/longitude) + * alt: [-100000.00 .. 42849672.95] BY .01 (altitude in meters) + * siz, hp, vp: [0 .. 90000000.00] (size/precision in meters) + * + * If omitted, minutes and seconds default to zero, size defaults to 1m, + * horizontal precision defaults to 10000m, and vertical precision + * defaults to 10m. These defaults are chosen to represent typical + * ZIP/postal code area sizes, since it is often easy to find + * approximate geographical location by ZIP/postal code. + */ +static inline isc_result_t +fromtext_loc(ARGS_FROMTEXT) { + isc_result_t result = ISC_R_SUCCESS; + unsigned long latitude = 0; + unsigned long longitude = 0; + unsigned long altitude = 0; + unsigned char size = 0x12; /* Default: 1.00m */ + unsigned char hp = 0x16; /* Default: 10000.00 m */ + unsigned char vp = 0x13; /* Default: 10.00 m */ + unsigned char version = 0; + + REQUIRE(type == dns_rdatatype_loc); + + UNUSED(type); + UNUSED(rdclass); + UNUSED(origin); + UNUSED(options); + UNUSED(callbacks); + + RETERR(loc_getlatitude(lexer, &latitude)); + RETERR(loc_getlongitude(lexer, &longitude)); + RETERR(loc_getaltitude(lexer, &altitude)); + result = loc_getsize(lexer, &size); + if (result == ISC_R_NOMORE) { + result = ISC_R_SUCCESS; goto encode; } - m = strtol(DNS_AS_STR(token), &e, 10); - if (*e != 0 && *e != '.' && *e != 'm') { - RETTOK(DNS_R_SYNTAX); + RETERR(result); + result = loc_gethorizontalprecision(lexer, &hp); + if (result == ISC_R_NOMORE) { + result = ISC_R_SUCCESS; + goto encode; } - if (m < 0 || m > 90000000) { - RETTOK(ISC_R_RANGE); + RETERR(result); + result = loc_getverticalprecision(lexer, &vp); + if (result == ISC_R_NOMORE) { + result = ISC_R_SUCCESS; + goto encode; } - cm = 0; - ok = e != DNS_AS_STR(token); - if (*e == '.') { - e++; - for (i = 0; i < 2; i++) { - if (*e == 0 || *e == 'm') { - break; - } - if ((tmp = decvalue(*e++)) < 0) { - RETTOK(DNS_R_SYNTAX); - } - ok = true; - cm *= 10; - cm += tmp; - } - for (; i < 2; i++) - cm *= 10; - } - if (*e == 'm') { - e++; - } - if (!ok || *e != 0) { - RETTOK(DNS_R_SYNTAX); - } - /* - * We don't just multiply out as we will overflow. - */ - if (m > 0) { - for (exp = 0; exp < 7; exp++) { - if (m < poweroften[exp + 1]) { - break; - } - } - man = m / poweroften[exp]; - exp += 2; - } else if (cm >= 10) { - man = cm / 10; - exp = 1; - } else { - man = cm; - exp = 0; - } - vp = (man << 4) + exp; - + RETERR(result); encode: RETERR(mem_tobuffer(target, &version, 1)); RETERR(mem_tobuffer(target, &size, 1)); RETERR(mem_tobuffer(target, &hp, 1)); RETERR(mem_tobuffer(target, &vp, 1)); - if (north) { - latitude = 0x80000000 + (d1 * 3600 + m1 * 60) * 1000 + s1; - } else { - latitude = 0x80000000 - (d1 * 3600 + m1 * 60) * 1000 - s1; - } + RETERR(uint32_tobuffer(latitude, target)); - - if (east) { - longitude = 0x80000000 + (d2 * 3600 + m2 * 60) * 1000 + s2; - } else { - longitude = 0x80000000 - (d2 * 3600 + m2 * 60) * 1000 - s2; - } RETERR(uint32_tobuffer(longitude, target)); + RETERR(uint32_tobuffer(altitude, target)); - return (uint32_tobuffer(altitude, target)); + return (result); } static inline isc_result_t diff --git a/lib/dns/tests/rdata_test.c b/lib/dns/tests/rdata_test.c index e48c90f10a..f5c69b101c 100644 --- a/lib/dns/tests/rdata_test.c +++ b/lib/dns/tests/rdata_test.c @@ -1993,6 +1993,8 @@ loc(void **state) { TEXT_VALID_CHANGED("0 0 .0 N 0 0 .0 E 0", "0 0 0.000 N 0 0 0.000 E 0.00m 1m 10000m " "10m"), + TEXT_INVALID("0 North 0 East 0"), + TEXT_INVALID("0 South 0 West 0"), TEXT_INVALID("0 0 . N 0 0 0. E 0"), TEXT_INVALID("0 0 0. N 0 0 . E 0"), TEXT_INVALID("0 0 0. N 0 0 0. E m"), From d8cb5ac0dbd81ac2263254aa9c80a162ce03ab63 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Wed, 19 Aug 2020 17:54:06 +1000 Subject: [PATCH 5/5] Add CHANGES note (cherry picked from commit d9594cffabc2f689e38a5ab4da0867cc870b6a85) --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index 53842dead2..7bfdddca4c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +5492. [bug] Tighten LOC parsing to reject period and/or m as a + value. Correct handling of negative altitudes which + are not whole metres. [GL #2074] + 5491. [bug] rbtversion->glue_table_size could be read without the appropriate lock being held. [GL #2080]