From 1f4618a41c92446ffaa3ca5956915ae834564681 Mon Sep 17 00:00:00 2001 From: Jeroen Koekkoek Date: Mon, 18 Sep 2023 10:05:52 +0200 Subject: [PATCH 1/2] Reserve specific type for WKS service bitmap --- include/zone.h | 3 ++- src/types.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/zone.h b/include/zone.h index ee74fd8..46efb5e 100644 --- a/include/zone.h +++ b/include/zone.h @@ -230,7 +230,8 @@ typedef enum { // ZONE_EUI64 (ZONE_HEX8?) // miscellaneous fields ZONE_SVC_PARAM, /**< SVCB service parameter */ - ZONE_TYPE_BITMAP /**< NSEC type bitmap */ + ZONE_TYPE_BITMAP, /**< NSEC type bitmap */ + ZONE_SERVICE_BITMAP /**< WKS service bitmap */ } zone_type_t; /** diff --git a/src/types.h b/src/types.h index 2ca3b2f..756690f 100644 --- a/src/types.h +++ b/src/types.h @@ -1984,7 +1984,7 @@ static const zone_field_info_t minfo_rdata_fields[] = { static const zone_field_info_t wks_rdata_fields[] = { FIELD("address", ZONE_IP4, 0), FIELD("protocol", ZONE_INT8, 0), - FIELD("bitmap", ZONE_WKS, 0) + FIELD("bitmap", ZONE_SERVICE_BITMAP, 0) }; static const zone_field_info_t mx_rdata_fields[] = { From 2601a12b14d43559f27846904883aa4fda202435 Mon Sep 17 00:00:00 2001 From: Jeroen Koekkoek Date: Mon, 16 Oct 2023 13:14:43 +0200 Subject: [PATCH 2/2] Add support for LOC RR --- include/zone.h | 1 + src/fallback/parser.c | 1 + src/generic/loc.h | 249 ++++++++++++++++++++++++++++++++++++++++++ src/haswell/parser.c | 1 + src/types.h | 135 ++++++++++++++++++++++- src/westmere/parser.c | 1 + tests/types.c | 13 +++ 7 files changed, 400 insertions(+), 1 deletion(-) create mode 100644 src/generic/loc.h diff --git a/include/zone.h b/include/zone.h index 46efb5e..5812afd 100644 --- a/include/zone.h +++ b/include/zone.h @@ -189,6 +189,7 @@ struct zone_string { const char *data; }; +// FIXME: probably best to rename this to mnemonic to stay with DNS terminology? typedef struct zone_symbol zone_symbol_t; struct zone_symbol { struct { diff --git a/src/fallback/parser.c b/src/fallback/parser.c index d3cb215..5b43b9f 100644 --- a/src/fallback/parser.c +++ b/src/fallback/parser.c @@ -28,6 +28,7 @@ #include "fallback/eui.h" #include "fallback/nsap.h" #include "generic/wks.h" +#include "generic/loc.h" #include "types.h" #include "fallback/type.h" #include "parser.h" diff --git a/src/generic/loc.h b/src/generic/loc.h new file mode 100644 index 0000000..abcdd12 --- /dev/null +++ b/src/generic/loc.h @@ -0,0 +1,249 @@ +/* + * loc.h -- Location Information (RFC1876) parser + * + * Copyright (c) 2023, NLnet Labs. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ +#ifndef LOC_H +#define LOC_H + +zone_nonnull_all +static zone_really_inline int32_t scan_degrees( + const char *text, size_t length, uint32_t *degrees) +{ + uint8_t digits[3]; + + digits[0] = (uint8_t)text[0] - '0'; + digits[1] = (uint8_t)text[1] - '0'; + digits[2] = (uint8_t)text[2] - '0'; + + switch (length) { + case 1: + *degrees = digits[0] * 3600000; + if (digits[0] > 9) + return -1; + return 0; + case 2: + *degrees = digits[0] * 36000000 + digits[1] * 3600000; + if (digits[0] > 9 || digits[1] > 9) + return -1; + return 0; + case 3: + *degrees = digits[0] * 360000000 + + digits[1] * 36000000 + + digits[2] * 3600000; + if (*degrees > 648000000u) + return -1; + if (digits[0] > 9 || digits[1] > 9 || digits[2] > 9) + return -1; + return 0; + default: + return -1; + } +} + +zone_nonnull_all +static zone_really_inline int64_t scan_minutes( + const char *text, size_t length, uint32_t *minutes) +{ + uint8_t digits[2]; + + digits[0] = (uint8_t)text[0] - '0'; + digits[1] = (uint8_t)text[1] - '0'; + + switch (length) { + case 1: + *minutes = digits[0] * 60000; + if (digits[0] > 9) + return -1; + return 0; + case 2: + *minutes = digits[0] * 600000 + digits[1] * 60000; + if (*minutes > 3600000 || digits[0] > 9 || digits[1] > 9) + return -1; + return 0; + default: + return -1; + } +} + +zone_nonnull_all +static zone_really_inline int64_t scan_seconds( + const char *text, size_t length, uint32_t *seconds) +{ + uint8_t digits[3]; + size_t count; + + digits[0] = (uint8_t)text[0] - '0'; + digits[1] = (uint8_t)text[1] - '0'; + + if (length == 1 || text[1] == '.') { + count = 1; + *seconds = digits[0] * 1000; + if (digits[0] > 9) + return -1; + digits[0] = (uint8_t)text[2] - '0'; + digits[1] = (uint8_t)text[3] - '0'; + digits[2] = (uint8_t)text[4] - '0'; + } else if (length == 2 || text[2] == '.') { + count = 2; + *seconds = digits[0] * 10000 + digits[1] * 1000; + if (*seconds > 60000 || digits[0] > 5 || digits[1] > 9) + return -1; + digits[0] = (uint8_t)text[3] - '0'; + digits[1] = (uint8_t)text[4] - '0'; + digits[2] = (uint8_t)text[5] - '0'; + } else { + return -1; + } + + switch (length - count) { + case 0: + return 0; + case 1: + return -1; + case 2: + *seconds += digits[0] * 100u; + if (digits[0] > 9) + return -1; + return 0; + case 3: + *seconds += digits[0] * 100u + digits[1] * 10u; + if (digits[0] > 9 || digits[1] > 9) + return -1; + return 0; + case 4: + *seconds += digits[0] * 100u + digits[1] * 10u + digits[2]; + if (digits[0] > 9 || digits[1] > 9 || digits[0] > 9) + return -1; + return 0; + default: + return -1; + } +} + +zone_nonnull((1,3)) +static zone_really_inline int32_t scan_altitude( + const char *text, size_t length, uint32_t *altitude) +{ + uint64_t negative = 0, limit = 11, maximum = 4284967295llu; + + if (text[0] == '-') + (void)(negative = 1), (void)(limit = 8), maximum = 10000000llu; + + length -= (text[length - 1] == 'm'); + + uint64_t meters = 0, index = negative; + for (;; index++) { + const uint8_t digit = (uint8_t)text[index] - '0'; + if (digit > 9) + break; + meters = meters * 10 + digit; + } + + uint64_t centimeters = meters * 100u; // convert to centimeters + if (text[index] == '.') { + uint8_t digits[2]; + limit += 1; + digits[0] = (uint8_t)text[index+1] - '0'; + digits[1] = (uint8_t)text[index+2] - '0'; + switch (length - index) { + case 1: + index += 1; + break; + case 2: + if (digits[0] > 9) + return -1; + centimeters += (uint64_t)digits[0] * 10u; + index += 2; + break; + case 3: + if (digits[0] > 9 || digits[1] > 9) + return -1; + centimeters += (uint64_t)digits[0] * 10u + (uint64_t)digits[1]; + index += 3; + break; + default: + return -1; + } + } + + if (index == negative || index > limit || index != length || centimeters > maximum) + return -1; + + if (negative) + *altitude = (uint32_t)(10000000llu - centimeters); + else + *altitude = (uint32_t)(10000000llu + centimeters); + + return 0; +} + +// converts ascii size/precision X * 10**Y(cm) to 0xXY +zone_nonnull((1,3)) +static zone_really_inline int32_t scan_precision( + const char *text, size_t length, uint8_t *scientific) +{ + uint64_t meters = 0, centimeters; + + // RFC1876 conversion routines + static uint64_t poweroften[10] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; + + length -= text[length - 1] == 'm'; + + size_t index = 0; + for (;; index++) { + const uint8_t digit = (uint8_t)text[index] - '0'; + if (digit > 9) + break; + meters = meters * 10 + digit; + } + + if (index == 0 || index > 8) // 0 .. 90000000.00 + return -1; // syntax error + + centimeters = meters * 100; // convert to centimeters + if (text[index] == '.') { + uint8_t digits[2]; + digits[0] = (uint8_t)text[index+1] - '0'; + digits[1] = (uint8_t)text[index+2] - '0'; + switch (length - index) { + case 1: + index += 1; + break; + case 2: + if (digits[0] > 9) + return -1; + index += 2; + centimeters += digits[0] * 10; + break; + case 3: + if (digits[0] > 9 || digits[1] > 9) + return -1; + index += 3; + centimeters += digits[0] * 10 + digits[1]; + break; + default: + return -1; + } + } + + if (index != length) + return -1; // syntax error + + uint8_t exponent = 0; + while (centimeters > poweroften[exponent+1] && exponent < 9) + exponent++; + + uint8_t mantissa = (uint8_t)(centimeters / poweroften[exponent]); + if (mantissa > 9u) + mantissa = 9u; + + *scientific = (uint8_t)(mantissa << 4) | exponent; + return 0; +} + +#endif // LOC_H diff --git a/src/haswell/parser.c b/src/haswell/parser.c index 0d4e0e8..6376bb2 100644 --- a/src/haswell/parser.c +++ b/src/haswell/parser.c @@ -30,6 +30,7 @@ #include "fallback/eui.h" #include "fallback/nsap.h" #include "generic/wks.h" +#include "generic/loc.h" #include "types.h" #include "westmere/type.h" #include "parser.h" diff --git a/src/types.h b/src/types.h index 756690f..7720094 100644 --- a/src/types.h +++ b/src/types.h @@ -915,6 +915,128 @@ static int32_t parse_aaaa_rdata( return accept_rr(parser, type); } +zone_nonnull_all +static int32_t check_loc_rr( + zone_parser_t *parser, const zone_type_info_t *type) +{ + if (parser->rdata->length != 16) + SYNTAX_ERROR(parser, "Invalid %s record", TNAME(type)); + return accept_rr(parser, type); + + // FIXME: check validity of latitude, longitude and latitude? +} + +zone_nonnull_all +static int32_t parse_loc_rdata( + zone_parser_t *parser, const zone_type_info_t *type, token_t *token) +{ + int32_t result; + uint32_t degrees, minutes, seconds; + uint32_t latitude, longitude, altitude; + const zone_field_info_t *fields = type->rdata.fields; + static const uint32_t defaults = 0x13161200; + + // RFC1876 section 3: + // If omitted, minutes and seconds default to zero, size defaults to 1m, + // horizontal precision defaults to 10000m, and vertical precision defaults + // to 10m. + memcpy(parser->rdata->octets, &defaults, sizeof(defaults)); + parser->rdata->length = 16; + + // latitude + if ((result = have_contiguous(parser, type, &fields[4], token)) < 0) + return result; + if (scan_degrees(token->data, token->length, °rees) == -1) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[4]), TNAME(type)); + lex(parser, token); + if (scan_minutes(token->data, token->length, &minutes) == -1) + goto north_south; // minutes default to zero + degrees += minutes; + lex(parser, token); + if (scan_seconds(token->data, token->length, &seconds) == -1) + goto north_south; // seconds default to zero + degrees += seconds; + + lex(parser, token); + if ((result = have_contiguous(parser, type, &fields[4], token)) < 0) + return result; +north_south: + if (token->data[0] == 'N') + latitude = htonl((1u<<31) + degrees); + else if (token->data[1] == 'S') + latitude = htonl((1u<<31) - degrees); + else + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[4]), TNAME(type)); + + memcpy(&parser->rdata->octets[4], &latitude, sizeof(latitude)); + + // longitude + lex(parser, token); + if ((result = have_contiguous(parser, type, &fields[5], token)) < 0) + return result; + if (scan_degrees(token->data, token->length, °rees) == -1) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[5]), TNAME(type)); + lex(parser, token); + if (scan_minutes(token->data, token->length, &minutes) == -1) + goto east_west; // minutes default to zero + degrees += minutes; + lex(parser, token); + if (scan_seconds(token->data, token->length, &seconds) == -1) + goto east_west; // seconds default to zero + degrees += seconds; + + lex(parser, token); + if ((result = have_contiguous(parser, type, &fields[5], token)) < 0) + return result; +east_west: + if (token->data[0] == 'E') + longitude = htonl((1u<<31) + degrees); + else if (token->data[0] == 'W') + longitude = htonl((1u<<31) - degrees); + else + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[5]), TNAME(type)); + + memcpy(&parser->rdata->octets[8], &longitude, sizeof(longitude)); + + // altitude + lex(parser, token); + if ((result = have_contiguous(parser, type, &fields[6], token)) < 0) + return result; + if (scan_altitude(token->data, token->length, &altitude) == -1) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[6]), TNAME(type)); + + altitude = htonl(altitude); + memcpy(&parser->rdata->octets[12], &altitude, sizeof(altitude)); + + // size + lex(parser, token); + if (token->code != CONTIGUOUS) + goto skip_optional; + if (scan_precision(token->data, token->length, &parser->rdata->octets[1])) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[1]), TNAME(type)); + + // horizontal precision + lex(parser, token); + if (token->code != CONTIGUOUS) + goto skip_optional; + if (scan_precision(token->data, token->length, &parser->rdata->octets[2])) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[2]), TNAME(type)); + + // vertical precision + lex(parser, token); + if (token->code != CONTIGUOUS) + goto skip_optional; + if (scan_precision(token->data, token->length, &parser->rdata->octets[3])) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[3]), TNAME(type)); + + lex(parser, token); +skip_optional: + if ((result = have_delimiter(parser, type, token)) < 0) + return result; + + return accept_rr(parser, type); +} + zone_nonnull_all static int32_t check_srv_rr( zone_parser_t *parser, const zone_type_info_t *type) @@ -2045,6 +2167,16 @@ static const zone_field_info_t aaaa_rdata_fields[] = { FIELD("address", ZONE_IP6, 0) }; +static const zone_field_info_t loc_rdata_fields[] = { + FIELD("version", ZONE_INT8, 0), + FIELD("size", ZONE_INT8, 0), + FIELD("horizontal precision", ZONE_INT8, 0), + FIELD("vertical precision", ZONE_INT8, 0), + FIELD("latitude", ZONE_INT32, 0), + FIELD("longitude", ZONE_INT32, 0), + FIELD("altitude", ZONE_INT32, 0) +}; + static const zone_field_info_t srv_rdata_fields[] = { FIELD("priority", ZONE_INT16, 0), FIELD("weight", ZONE_INT16, 0), @@ -2351,8 +2483,9 @@ static const type_descriptor_t types[] = { TYPE("AAAA", ZONE_AAAA, ZONE_IN, FIELDS(aaaa_rdata_fields), check_aaaa_rr, parse_aaaa_rdata), + TYPE("LOC", ZONE_LOC, ZONE_ANY, FIELDS(loc_rdata_fields), + check_loc_rr, parse_loc_rdata), - UNKNOWN_TYPE(29), UNKNOWN_TYPE(30), UNKNOWN_TYPE(31), UNKNOWN_TYPE(32), diff --git a/src/westmere/parser.c b/src/westmere/parser.c index cacec9c..f4c9216 100644 --- a/src/westmere/parser.c +++ b/src/westmere/parser.c @@ -30,6 +30,7 @@ #include "fallback/eui.h" #include "fallback/nsap.h" #include "generic/wks.h" +#include "generic/loc.h" #include "types.h" #include "westmere/type.h" #include "parser.h" diff --git a/tests/types.c b/tests/types.c index 0f2074e..7e16b78 100644 --- a/tests/types.c +++ b/tests/types.c @@ -261,6 +261,18 @@ static const rdata_t px_rdata = 0x07, 'A', 'D', 'M', 'D', '-', 'a', 'c', 0x04, 'C', '-', 'f', 'r', 0x00); +// RFC1876 +static const char loc_text[] = + PAD("cambridge-net.kei.com. LOC 42 21 54 N 71 06 18 W -24m 30m"); +static const rdata_t loc_rdata = + RDATA(0x00, // version (always 0) + 0x33, // size (default 1m) + 0x16, // horizontal precision (default 10000m) + 0x13, // vertical precision (default 10m) + 0x89, 0x17, 0x2d, 0xd0, // latitude + 0x70, 0xbe, 0x15, 0xf0, // longitude + 0x00, 0x98, 0x8d, 0x20); // altitude + static const char naptr_text[] = PAD(" NAPTR 100 50 \"s\" \"http+I2L+I2C+I2R\" \"\" _http._tcp.gatech.edu."); static const char naptr_generic_text[] = @@ -785,6 +797,7 @@ static const test_t tests[] = { { ZONE_KEY, key_generic_text, &key_rdata }, { ZONE_PX, px_text, &px_rdata }, { ZONE_PX, px_generic_text, &px_rdata }, + { ZONE_LOC, loc_text, &loc_rdata }, { ZONE_NAPTR, naptr_text, &naptr_rdata }, { ZONE_NAPTR, naptr_generic_text, &naptr_rdata }, { ZONE_KX, kx_text, &kx_rdata },