/* * 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. */ /* RFC1876 */ #ifndef RDATA_GENERIC_LOC_29_C #define RDATA_GENERIC_LOC_29_C #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; 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; 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); 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); 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 (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); 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); 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 (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)); m = strtol(DNS_AS_STR(token), &e, 10); if (*e != 0 && *e != '.' && *e != 'm') RETTOK(DNS_R_SYNTAX); if (m < -100000 || m > 42849672) RETTOK(ISC_R_RANGE); cm = 0; if (*e == '.') { e++; for (i = 0; i < 2; i++) { if (*e == 0 || *e == 'm') break; if ((tmp = decvalue(*e++)) < 0) return (DNS_R_SYNTAX); cm *= 10; if (m < 0) cm -= tmp; else cm += tmp; } for (; i < 2; i++) cm *= 10; } if (*e == 'm') e++; if (*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. */ 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; if (*e == '.') { e++; for (i = 0; i < 2; i++) { if (*e == 0 || *e == 'm') break; if ((tmp = decvalue(*e++)) < 0) RETTOK(DNS_R_SYNTAX); cm *= 10; cm += tmp; } for (; i < 2; i++) cm *= 10; } if (*e == 'm') e++; if (*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; } } 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; if (*e == '.') { e++; for (i = 0; i < 2; i++) { if (*e == 0 || *e == 'm') break; if ((tmp = decvalue(*e++)) < 0) RETTOK(DNS_R_SYNTAX); cm *= 10; cm += tmp; } for (; i < 2; i++) cm *= 10; } if (*e == 'm') e++; if (*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; } hp = (man << 4) + exp; /* * Vertical 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; if (*e == '.') { e++; for (i = 0; i < 2; i++) { if (*e == 0 || *e == 'm') break; if ((tmp = decvalue(*e++)) < 0) RETTOK(DNS_R_SYNTAX); cm *= 10; cm += tmp; } for (; i < 2; i++) cm *= 10; } if (*e == 'm') e++; if (*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; 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)); return (uint32_tobuffer(altitude, target)); } static inline isc_result_t totext_loc(ARGS_TOTEXT) { int d1, m1, s1, fs1; int d2, m2, s2, fs2; unsigned long latitude; unsigned long longitude; unsigned long altitude; bool north; bool east; bool below; isc_region_t sr; char buf[sizeof("89 59 59.999 N 179 59 59.999 E " "-42849672.95m 90000000m 90000000m 90000000m")]; char sbuf[sizeof("90000000m")]; char hbuf[sizeof("90000000m")]; char vbuf[sizeof("90000000m")]; unsigned char size, hp, vp; unsigned long poweroften[8] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000 }; UNUSED(tctx); REQUIRE(rdata->type == dns_rdatatype_loc); REQUIRE(rdata->length != 0); dns_rdata_toregion(rdata, &sr); if (sr.base[0] != 0) return (ISC_R_NOTIMPLEMENTED); REQUIRE(rdata->length == 16); size = sr.base[1]; INSIST((size&0x0f) < 10 && (size>>4) < 10); if ((size&0x0f)> 1) { snprintf(sbuf, sizeof(sbuf), "%lum", (size>>4) * poweroften[(size&0x0f)-2]); } else { snprintf(sbuf, sizeof(sbuf), "0.%02lum", (size>>4) * poweroften[(size&0x0f)]); } hp = sr.base[2]; INSIST((hp&0x0f) < 10 && (hp>>4) < 10); if ((hp&0x0f)> 1) { snprintf(hbuf, sizeof(hbuf), "%lum", (hp>>4) * poweroften[(hp&0x0f)-2]); } else { snprintf(hbuf, sizeof(hbuf), "0.%02lum", (hp>>4) * poweroften[(hp&0x0f)]); } vp = sr.base[3]; INSIST((vp&0x0f) < 10 && (vp>>4) < 10); if ((vp&0x0f)> 1) { snprintf(vbuf, sizeof(vbuf), "%lum", (vp>>4) * poweroften[(vp&0x0f)-2]); } else { snprintf(vbuf, sizeof(vbuf), "0.%02lum", (vp>>4) * poweroften[(vp&0x0f)]); } isc_region_consume(&sr, 4); latitude = uint32_fromregion(&sr); isc_region_consume(&sr, 4); if (latitude >= 0x80000000) { north = true; latitude -= 0x80000000; } else { north = false; latitude = 0x80000000 - latitude; } fs1 = (int)(latitude % 1000); latitude /= 1000; s1 = (int)(latitude % 60); latitude /= 60; m1 = (int)(latitude % 60); latitude /= 60; d1 = (int)latitude; INSIST(latitude <= 90U); longitude = uint32_fromregion(&sr); isc_region_consume(&sr, 4); if (longitude >= 0x80000000) { east = true; longitude -= 0x80000000; } else { east = false; longitude = 0x80000000 - longitude; } fs2 = (int)(longitude % 1000); longitude /= 1000; s2 = (int)(longitude % 60); longitude /= 60; m2 = (int)(longitude % 60); longitude /= 60; d2 = (int)longitude; INSIST(longitude <= 180U); altitude = uint32_fromregion(&sr); isc_region_consume(&sr, 4); if (altitude < 10000000U) { below = true; altitude = 10000000 - altitude; } else { below =false; altitude -= 10000000; } snprintf(buf, sizeof(buf), "%d %d %d.%03d %s %d %d %d.%03d %s %s%lu.%02lum %s %s %s", d1, m1, s1, fs1, north ? "N" : "S", d2, m2, s2, fs2, east ? "E" : "W", below ? "-" : "", altitude/100, altitude % 100, sbuf, hbuf, vbuf); return (str_totext(buf, target)); } static inline isc_result_t fromwire_loc(ARGS_FROMWIRE) { isc_region_t sr; unsigned char c; unsigned long latitude; unsigned long longitude; REQUIRE(type == dns_rdatatype_loc); UNUSED(type); UNUSED(rdclass); UNUSED(dctx); UNUSED(options); isc_buffer_activeregion(source, &sr); if (sr.length < 1) return (ISC_R_UNEXPECTEDEND); if (sr.base[0] != 0) { /* Treat as unknown. */ isc_buffer_forward(source, sr.length); return (mem_tobuffer(target, sr.base, sr.length)); } if (sr.length < 16) return (ISC_R_UNEXPECTEDEND); /* * Size. */ c = sr.base[1]; if (c != 0) if ((c&0xf) > 9 || ((c>>4)&0xf) > 9 || ((c>>4)&0xf) == 0) return (ISC_R_RANGE); /* * Horizontal precision. */ c = sr.base[2]; if (c != 0) if ((c&0xf) > 9 || ((c>>4)&0xf) > 9 || ((c>>4)&0xf) == 0) return (ISC_R_RANGE); /* * Vertical precision. */ c = sr.base[3]; if (c != 0) if ((c&0xf) > 9 || ((c>>4)&0xf) > 9 || ((c>>4)&0xf) == 0) return (ISC_R_RANGE); isc_region_consume(&sr, 4); /* * Latitude. */ latitude = uint32_fromregion(&sr); if (latitude < (0x80000000UL - 90 * 3600000) || latitude > (0x80000000UL + 90 * 3600000)) return (ISC_R_RANGE); isc_region_consume(&sr, 4); /* * Longitude. */ longitude = uint32_fromregion(&sr); if (longitude < (0x80000000UL - 180 * 3600000) || longitude > (0x80000000UL + 180 * 3600000)) return (ISC_R_RANGE); /* * Altitude. * All values possible. */ isc_buffer_activeregion(source, &sr); isc_buffer_forward(source, 16); return (mem_tobuffer(target, sr.base, 16)); } static inline isc_result_t towire_loc(ARGS_TOWIRE) { UNUSED(cctx); REQUIRE(rdata->type == dns_rdatatype_loc); REQUIRE(rdata->length != 0); return (mem_tobuffer(target, rdata->data, rdata->length)); } static inline int compare_loc(ARGS_COMPARE) { isc_region_t r1; isc_region_t r2; REQUIRE(rdata1->type == rdata2->type); REQUIRE(rdata1->rdclass == rdata2->rdclass); REQUIRE(rdata1->type == dns_rdatatype_loc); REQUIRE(rdata1->length != 0); REQUIRE(rdata2->length != 0); dns_rdata_toregion(rdata1, &r1); dns_rdata_toregion(rdata2, &r2); return (isc_region_compare(&r1, &r2)); } static inline isc_result_t fromstruct_loc(ARGS_FROMSTRUCT) { dns_rdata_loc_t *loc = source; uint8_t c; REQUIRE(type == dns_rdatatype_loc); REQUIRE(source != NULL); REQUIRE(loc->common.rdtype == type); REQUIRE(loc->common.rdclass == rdclass); UNUSED(type); UNUSED(rdclass); if (loc->v.v0.version != 0) return (ISC_R_NOTIMPLEMENTED); RETERR(uint8_tobuffer(loc->v.v0.version, target)); c = loc->v.v0.size; if ((c&0xf) > 9 || ((c>>4)&0xf) > 9 || ((c>>4)&0xf) == 0) return (ISC_R_RANGE); RETERR(uint8_tobuffer(loc->v.v0.size, target)); c = loc->v.v0.horizontal; if ((c&0xf) > 9 || ((c>>4)&0xf) > 9 || ((c>>4)&0xf) == 0) return (ISC_R_RANGE); RETERR(uint8_tobuffer(loc->v.v0.horizontal, target)); c = loc->v.v0.vertical; if ((c&0xf) > 9 || ((c>>4)&0xf) > 9 || ((c>>4)&0xf) == 0) return (ISC_R_RANGE); RETERR(uint8_tobuffer(loc->v.v0.vertical, target)); if (loc->v.v0.latitude < (0x80000000UL - 90 * 3600000) || loc->v.v0.latitude > (0x80000000UL + 90 * 3600000)) return (ISC_R_RANGE); RETERR(uint32_tobuffer(loc->v.v0.latitude, target)); if (loc->v.v0.longitude < (0x80000000UL - 180 * 3600000) || loc->v.v0.longitude > (0x80000000UL + 180 * 3600000)) return (ISC_R_RANGE); RETERR(uint32_tobuffer(loc->v.v0.longitude, target)); return (uint32_tobuffer(loc->v.v0.altitude, target)); } static inline isc_result_t tostruct_loc(ARGS_TOSTRUCT) { dns_rdata_loc_t *loc = target; isc_region_t r; uint8_t version; REQUIRE(rdata->type == dns_rdatatype_loc); REQUIRE(target != NULL); REQUIRE(rdata->length != 0); UNUSED(mctx); dns_rdata_toregion(rdata, &r); version = uint8_fromregion(&r); if (version != 0) return (ISC_R_NOTIMPLEMENTED); loc->common.rdclass = rdata->rdclass; loc->common.rdtype = rdata->type; ISC_LINK_INIT(&loc->common, link); loc->v.v0.version = version; isc_region_consume(&r, 1); loc->v.v0.size = uint8_fromregion(&r); isc_region_consume(&r, 1); loc->v.v0.horizontal = uint8_fromregion(&r); isc_region_consume(&r, 1); loc->v.v0.vertical = uint8_fromregion(&r); isc_region_consume(&r, 1); loc->v.v0.latitude = uint32_fromregion(&r); isc_region_consume(&r, 4); loc->v.v0.longitude = uint32_fromregion(&r); isc_region_consume(&r, 4); loc->v.v0.altitude = uint32_fromregion(&r); isc_region_consume(&r, 4); return (ISC_R_SUCCESS); } static inline void freestruct_loc(ARGS_FREESTRUCT) { dns_rdata_loc_t *loc = source; REQUIRE(source != NULL); REQUIRE(loc->common.rdtype == dns_rdatatype_loc); UNUSED(source); UNUSED(loc); } static inline isc_result_t additionaldata_loc(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_loc); UNUSED(rdata); UNUSED(add); UNUSED(arg); return (ISC_R_SUCCESS); } static inline isc_result_t digest_loc(ARGS_DIGEST) { isc_region_t r; REQUIRE(rdata->type == dns_rdatatype_loc); dns_rdata_toregion(rdata, &r); return ((digest)(arg, &r)); } static inline bool checkowner_loc(ARGS_CHECKOWNER) { REQUIRE(type == dns_rdatatype_loc); UNUSED(name); UNUSED(type); UNUSED(rdclass); UNUSED(wildcard); return (true); } static inline bool checknames_loc(ARGS_CHECKNAMES) { REQUIRE(rdata->type == dns_rdatatype_loc); UNUSED(rdata); UNUSED(owner); UNUSED(bad); return (true); } static inline int casecompare_loc(ARGS_COMPARE) { return (compare_loc(rdata1, rdata2)); } #endif /* RDATA_GENERIC_LOC_29_C */