From 7878834878cd75f8838730cf0f82a3efb3cc9eb0 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Mon, 8 Feb 2021 16:55:08 +0100 Subject: [PATCH] Add a test parser for SVCB value-lists --- pdns/dnslabeltext.rl | 75 ++++++++++++++++++++++++++++++++++++++++++++ pdns/misc.hh | 1 + pdns/test-misc_hh.cc | 75 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+) diff --git a/pdns/dnslabeltext.rl b/pdns/dnslabeltext.rl index 393a521720..3eabf72748 100644 --- a/pdns/dnslabeltext.rl +++ b/pdns/dnslabeltext.rl @@ -240,6 +240,81 @@ size_t parseRFC1035CharString(const std::string &in, std::string &val) { return counter; } +size_t parseSVCBValueList(const std::string &in, std::vector &val) { + val.clear(); + const char *p = in.c_str(); + const char *pe = p + in.size(); + int cs = 0; + uint8_t escaped_octet = 0; + // Keeps track of how many chars we read from the source string + size_t counter=0; + + // Here we store the parsed value until we hit a comma or are done + std::string tmp; + +%%{ + machine dns_text_to_value_list; + + action doEscapedNumber { + escaped_octet *= 10; + escaped_octet += fc-'0'; + counter++; + } + + action doneEscapedNumber { + tmp += escaped_octet; + escaped_octet = 0; + } + + action addToVal { + tmp += fc; + counter++; + } + + action handleComma { + val.push_back(tmp); + tmp.clear(); + counter++; + } + + action incrementCounter { + counter++; + } + + # generated rules, define required actions + DIGIT = 0x30..0x39; + DQUOTE = "\""; + HTAB = "\t"; + SP = " "; + WSP = (SP | HTAB)@addToVal; + non_special = "!" | 0x23..0x27 | 0x2a..0x2b | 0x2d..0x3a | 0x3c..0x5b | 0x5d..0x7e; + non_digit = 0x21..0x2f | 0x3a..0x7e; + dec_octet = ( ( "0" | "1" ) DIGIT{2} ) | ( "2" ( ( 0x30..0x34 DIGIT ) | ( "5" 0x30..0x35 ) ) ); + escaped = '\\'@incrementCounter ( non_digit$addToVal | dec_octet$doEscapedNumber@doneEscapedNumber ); + contiguous = ( non_special$addToVal | escaped )+; + comma = ','; + quoted_sepped = ( contiguous | ('\\'? WSP) )* (comma@handleComma ( contiguous | ('\\'? WSP) )+ )*; + unquoted_sepped = (contiguous (comma@handleComma contiguous)*); + quoted = DQUOTE@incrementCounter quoted_sepped DQUOTE@incrementCounter; + char_string = (quoted | unquoted_sepped); + + # instantiate machine rules + main := char_string; + write data; + write init; +}%% + + // silence warnings + (void) dns_text_to_value_list_first_final; + (void) dns_text_to_value_list_error; + (void) dns_text_to_value_list_en_main; + %% write exec; + + // Add the last-parsed value to val + // ideally, we'd use a transition as well, but too many hours were wasted trying that + val.push_back(tmp); + return counter; +} #if 0 diff --git a/pdns/misc.hh b/pdns/misc.hh index 193750446c..be78521df7 100644 --- a/pdns/misc.hh +++ b/pdns/misc.hh @@ -624,5 +624,6 @@ DNSName reverseNameFromIP(const ComboAddress& ip); std::string getCarbonHostName(); size_t parseRFC1035CharString(const std::string &in, std::string &val); // from ragel +size_t parseSVCBValueList(const std::string &in, vector &val); // from ragel std::string makeLuaString(const std::string& in); diff --git a/pdns/test-misc_hh.cc b/pdns/test-misc_hh.cc index 52ac44ff74..d53c0770fd 100644 --- a/pdns/test-misc_hh.cc +++ b/pdns/test-misc_hh.cc @@ -227,4 +227,79 @@ BOOST_AUTO_TEST_CASE(test_getCarbonHostName) BOOST_CHECK_EQUAL(my_hostname.size(), hostname.size()); } +BOOST_AUTO_TEST_CASE(test_parseSVCBValueList) +{ + vector out; + + parseSVCBValueList("foobar123", out); + BOOST_CHECK_EQUAL(out.size(), 1); + BOOST_CHECK_EQUAL(out[0], "foobar123"); + + parseSVCBValueList("h2,h3", out); + BOOST_CHECK_EQUAL(out.size(), 2); + BOOST_CHECK_EQUAL(out[0], "h2"); + BOOST_CHECK_EQUAL(out[1], "h3"); + + parseSVCBValueList("h2,h3-19,h3-20,h3-22", out); + BOOST_CHECK_EQUAL(out.size(), 4); + BOOST_CHECK_EQUAL(out[0], "h2"); + BOOST_CHECK_EQUAL(out[1], "h3-19"); + BOOST_CHECK_EQUAL(out[2], "h3-20"); + BOOST_CHECK_EQUAL(out[3], "h3-22"); + + parseSVCBValueList("foobar123,bazquux456", out); + BOOST_CHECK_EQUAL(out.size(), 2); + BOOST_CHECK_EQUAL(out[0], "foobar123"); + BOOST_CHECK_EQUAL(out[1], "bazquux456"); + + parseSVCBValueList("foobar123\\,bazquux456", out); + BOOST_CHECK_EQUAL(out.size(), 1); + BOOST_CHECK_EQUAL(out[0], "foobar123,bazquux456"); + + parseSVCBValueList("foobar123\\044bazquux456", out); + BOOST_CHECK_EQUAL(out.size(), 1); + BOOST_CHECK_EQUAL(out[0], "foobar123,bazquux456"); + + // Again, but quoted + parseSVCBValueList("\"foobar123\"", out); + BOOST_CHECK_EQUAL(out.size(), 1); + BOOST_CHECK_EQUAL(out[0], "foobar123"); + + parseSVCBValueList("\"foobar123,bazquux456\"", out); + BOOST_CHECK_EQUAL(out.size(), 2); + BOOST_CHECK_EQUAL(out[0], "foobar123"); + BOOST_CHECK_EQUAL(out[1], "bazquux456"); + + parseSVCBValueList("\"foobar123\\,bazquux456\"", out); + BOOST_CHECK_EQUAL(out.size(), 1); + BOOST_CHECK_EQUAL(out[0], "foobar123,bazquux456"); + + parseSVCBValueList("\"foobar123\\044bazquux456\"", out); + BOOST_CHECK_EQUAL(out.size(), 1); + BOOST_CHECK_EQUAL(out[0], "foobar123,bazquux456"); + + // Quoted, with some whitespace + parseSVCBValueList("\"foobar123 \"", out); + BOOST_CHECK_EQUAL(out.size(), 1); + BOOST_CHECK_EQUAL(out[0], "foobar123 "); + + parseSVCBValueList("\"foobar123 blabla bla,baz quux456\"", out); + BOOST_CHECK_EQUAL(out.size(), 2); + BOOST_CHECK_EQUAL(out[0], "foobar123 blabla bla"); + BOOST_CHECK_EQUAL(out[1], "baz quux456"); + + parseSVCBValueList("\"foobar123,baz quux456\"", out); + BOOST_CHECK_EQUAL(out.size(), 2); + BOOST_CHECK_EQUAL(out[0], "foobar123"); + BOOST_CHECK_EQUAL(out[1], "baz quux456"); + + parseSVCBValueList("\"foobar123 blabla bla\\,baz quux456\"", out); + BOOST_CHECK_EQUAL(out.size(), 1); + BOOST_CHECK_EQUAL(out[0], "foobar123 blabla bla,baz quux456"); + + parseSVCBValueList("\"foobar123 blabla bla\\044baz quux456\"", out); + BOOST_CHECK_EQUAL(out.size(), 1); + BOOST_CHECK_EQUAL(out[0], "foobar123 blabla bla,baz quux456"); +} + BOOST_AUTO_TEST_SUITE_END() -- 2.47.2