diff options
Diffstat (limited to 'dnslabeltext.rl')
-rw-r--r-- | dnslabeltext.rl | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/dnslabeltext.rl b/dnslabeltext.rl new file mode 100644 index 0000000..27f4350 --- /dev/null +++ b/dnslabeltext.rl @@ -0,0 +1,315 @@ +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <string> +#include "dnsname.hh" +#include "namespaces.hh" +#include "dnswriter.hh" +#include "misc.hh" + +namespace { +void appendSplit(vector<string>& ret, string& segment, char c) +{ + if(segment.size()>254) { + ret.push_back(segment); + segment.clear(); + } + segment.append(1, c); +} + +} + +vector<string> segmentDNSText(const string& input ) +{ + // cerr<<"segmentDNSText("<<input<<")"<<endl; +%%{ + machine dnstext; + write data; + alphtype unsigned char; +}%% + (void)dnstext_error; // silence warnings + (void)dnstext_en_main; + const char *p = input.c_str(), *pe = input.c_str() + input.length(); + const char* eof = pe; + int cs; + char val = 0; + + string segment; + vector<string> 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; + unsigned char labellen=0; + unsigned int lenpos=0; + %%{ + action labelEnd { + if (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<std::string> &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<string> res=dnstext(blah); + cerr<<res.size()<<" segments"<<endl; + cerr<<res[0]<<endl; +} +#endif |