#include #include #include #include #include #include "dnsname.hh" #include "namespaces.hh" #include "dnswriter.hh" #include "misc.hh" namespace { void appendSplit(vector& ret, string& segment, char c) { if(segment.size()>254) { ret.push_back(segment); segment.clear(); } segment.append(1, c); } } vector segmentDNSText(const string& input ) { // cerr<<"segmentDNSText("< ret; %%{ action segmentEnd { ret.push_back(segment); segment.clear(); } action segmentBegin { segment.clear(); } action reportEscaped { char c = *fpc; appendSplit(ret, segment, c); } action reportEscapedNumber { char c = *fpc; val *= 10; val += c-'0'; } action doneEscapedNumber { appendSplit(ret, segment, val); val=0; } action reportPlain { appendSplit(ret, segment, *(fpc)); } escaped = '\\' (([^0-9]@reportEscaped) | ([0-9]{3}$reportEscapedNumber%doneEscapedNumber)); plain = ((extend-'\\'-'"')|'\n'|'\t') $ reportPlain; txtElement = escaped | plain; main := (('"' txtElement* '"' space?) >segmentBegin %segmentEnd)+; # Initialize and execute. write init; write exec; }%% if ( cs < dnstext_first_final ) { throw runtime_error("Unable to parse DNS TXT '"+input+"'"); } return ret; }; DNSName::string_t segmentDNSNameRaw(const char* realinput, size_t inputlen) { %%{ machine dnsnameraw; write data; alphtype unsigned char; }%% (void)dnsnameraw_error; // silence warnings (void)dnsnameraw_en_main; DNSName::string_t ret; if(!*realinput || *realinput == '.') { ret.append(1, (char)0); return ret; } ret.reserve(inputlen+1); const char *p = realinput, *pe = realinput + inputlen; const char* eof = pe; int cs; char val = 0; char labellen=0; unsigned int lenpos=0; %%{ action labelEnd { if (labellen < 0 || labellen > 63) { throw runtime_error("Unable to parse DNS name '"+string(realinput)+"': invalid label length "+std::to_string(labellen)); } ret[lenpos]=labellen; labellen=0; } action labelBegin { lenpos=ret.size(); ret.append(1, (char)0); labellen=0; } action reportEscaped { char c = *fpc; ret.append(1, c); labellen++; } action reportEscapedNumber { char c = *fpc; val *= 10; val += c-'0'; } action doneEscapedNumber { ret.append(1, val); labellen++; val=0; } action reportPlain { ret.append(1, *(fpc)); labellen++; } escaped = '\\' (([^0-9]@reportEscaped) | ([0-9]{3}$reportEscapedNumber%doneEscapedNumber)); plain = (extend-'\\'-'.') $ reportPlain; labelElement = escaped | plain; label = labelElement+ >labelBegin %labelEnd; main:= label ('.' label )* '.'?; #main := labelElement((labelElement+ '.') >labelBegin %labelEnd)+; # label = (plain | escaped | escdecb)+ >label_init %label_fin; # dnsname := '.'? label ('.' label >label_sep)* '.'?; # Initialize and execute. write init; write exec; }%% if ( cs < dnsnameraw_first_final ) { throw runtime_error("Unable to parse DNS name '"+string(realinput)+"': cs="+std::to_string(cs)); } ret.append(1, (char)0); return ret; }; // Reads an RFC 1035 character string from 'in', puts the resulting bytes in 'out'. // Returns the amount of bytes read from 'in' size_t parseRFC1035CharString(const std::string &in, std::string &val) { val.clear(); val.reserve(in.size()); 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; /* This parses an RFC 1035 char-string. * It was created from the ABNF in draft-ietf-dnsop-svcb-https-02 with * https://github.com/zinid/abnfc and modified to put all the characters in the * right place. */ %%{ machine dns_text_to_string; action doEscapedNumber { escaped_octet *= 10; escaped_octet += fc-'0'; counter++; } action doneEscapedNumber { val += escaped_octet; escaped_octet = 0; } action addToVal { val += fc; 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..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 )+; quoted = DQUOTE@incrementCounter ( contiguous | ( '\\'? WSP ) )* DQUOTE@incrementCounter; char_string = (contiguous | quoted); # instantiate machine rules main := char_string; write data; write init; }%% // silence warnings (void) dns_text_to_string_first_final; (void) dns_text_to_string_error; (void) dns_text_to_string_en_main; %% write exec; return counter; } size_t parseSVCBValueListFromParsedRFC1035CharString(const std::string &in, std::vector &val) { val.clear(); const char *p = in.c_str(); const char *pe = p + in.size(); int cs = 0; const char* eof = pe; // 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; alphtype unsigned char; action addToVal { tmp += fc; counter++; } action addToValNoIncrement { tmp += fc; } action addToVector { val.push_back(tmp); tmp.clear(); counter++; } action incrementCounter { counter++; } # generated rules, define required actions OCTET = 0x00..0xff; item_allowed = 0x00..0x2b | 0x2d..0x5b | 0x5d..0xff; escaped_item = ( item_allowed$addToVal | '\\,'$incrementCounter@addToValNoIncrement | '\\\\'$incrementCounter@addToValNoIncrement )+; comma_separated = ( escaped_item%addToVector ( ","@incrementCounter escaped_item%addToVector )* )?; # instantiate machine rules main := comma_separated; 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; if ( cs < dns_text_to_value_list_first_final ) { throw runtime_error("Unable to parse DNS SVCB value list '"+in+"'"); } return counter; } #if 0 int main() { //char blah[]="\"blah\" \"bleh\" \"bloeh\\\"bleh\" \"\\97enzo\""; char blah[]="\"v=spf1 ip4:67.106.74.128/25 ip4:63.138.42.224/28 ip4:65.204.46.224/27 \\013\\010ip4:66.104.217.176/28 \\013\\010ip4:209.48.147.0/27 ~all\""; //char blah[]="\"abc \\097\\098 def\""; printf("Input: '%s'\n", blah); vector res=dnstext(blah); cerr<