]>
Commit | Line | Data |
---|---|---|
bac8f21b BH |
1 | #include <stdlib.h> |
2 | #include <string.h> | |
3 | #include <stdio.h> | |
4 | #include <unistd.h> | |
5 | #include <string> | |
bae1b0a2 | 6 | #include "dnsname.hh" |
bac8f21b | 7 | #include "namespaces.hh" |
f56d26c9 | 8 | #include "dnswriter.hh" |
b1a048a9 | 9 | #include "misc.hh" |
bac8f21b | 10 | |
bac8f21b BH |
11 | namespace { |
12 | void appendSplit(vector<string>& ret, string& segment, char c) | |
13 | { | |
14 | if(segment.size()>254) { | |
15 | ret.push_back(segment); | |
16 | segment.clear(); | |
17 | } | |
18 | segment.append(1, c); | |
19 | } | |
3c115e0f | 20 | |
bac8f21b BH |
21 | } |
22 | ||
23 | vector<string> segmentDNSText(const string& input ) | |
24 | { | |
fc634e87 | 25 | // cerr<<"segmentDNSText("<<input<<")"<<endl; |
2fa33bce BH |
26 | %%{ |
27 | machine dnstext; | |
28 | write data; | |
916a0fda | 29 | alphtype unsigned char; |
2fa33bce | 30 | }%% |
f8499e52 BH |
31 | (void)dnstext_error; // silence warnings |
32 | (void)dnstext_en_main; | |
bac8f21b BH |
33 | const char *p = input.c_str(), *pe = input.c_str() + input.length(); |
34 | const char* eof = pe; | |
35 | int cs; | |
36 | char val = 0; | |
37 | ||
38 | string segment; | |
39 | vector<string> ret; | |
40 | ||
41 | %%{ | |
42 | action segmentEnd { | |
43 | ret.push_back(segment); | |
44 | segment.clear(); | |
45 | } | |
46 | action segmentBegin { | |
47 | segment.clear(); | |
48 | } | |
49 | ||
50 | action reportEscaped { | |
51 | char c = *fpc; | |
52 | appendSplit(ret, segment, c); | |
53 | } | |
54 | action reportEscapedNumber { | |
55 | char c = *fpc; | |
56 | val *= 10; | |
57 | val += c-'0'; | |
58 | ||
59 | } | |
60 | action doneEscapedNumber { | |
61 | appendSplit(ret, segment, val); | |
62 | val=0; | |
63 | } | |
64 | ||
65 | action reportPlain { | |
66 | appendSplit(ret, segment, *(fpc)); | |
67 | } | |
68 | ||
35261869 | 69 | escaped = '\\' (([^0-9]@reportEscaped) | ([0-9]{3}$reportEscapedNumber%doneEscapedNumber)); |
973efd35 | 70 | plain = ((extend-'\\'-'"')|'\n'|'\t') $ reportPlain; |
2fa33bce BH |
71 | txtElement = escaped | plain; |
72 | ||
73 | main := (('"' txtElement* '"' space?) >segmentBegin %segmentEnd)+; | |
bac8f21b BH |
74 | |
75 | # Initialize and execute. | |
76 | write init; | |
77 | write exec; | |
78 | }%% | |
79 | ||
80 | if ( cs < dnstext_first_final ) { | |
81 | throw runtime_error("Unable to parse DNS TXT '"+input+"'"); | |
82 | } | |
83 | ||
84 | return ret; | |
85 | }; | |
2fa33bce | 86 | |
bae1b0a2 | 87 | |
beb9fad5 | 88 | DNSName::string_t segmentDNSNameRaw(const char* realinput, size_t inputlen) |
3c115e0f | 89 | { |
90 | %%{ | |
bae1b0a2 | 91 | machine dnsnameraw; |
3c115e0f | 92 | write data; |
93 | alphtype unsigned char; | |
94 | }%% | |
bae1b0a2 | 95 | (void)dnsnameraw_error; // silence warnings |
96 | (void)dnsnameraw_en_main; | |
1f3151e8 | 97 | |
bae1b0a2 | 98 | DNSName::string_t ret; |
1f3151e8 | 99 | |
bae1b0a2 | 100 | if(!*realinput || *realinput == '.') { |
101 | ret.append(1, (char)0); | |
102 | return ret; | |
103 | } | |
1f3151e8 | 104 | |
bae1b0a2 | 105 | ret.reserve(inputlen+1); |
1f3151e8 | 106 | |
bae1b0a2 | 107 | const char *p = realinput, *pe = realinput + inputlen; |
3c115e0f | 108 | const char* eof = pe; |
109 | int cs; | |
110 | char val = 0; | |
abb8aeb0 | 111 | unsigned char labellen=0; |
bae1b0a2 | 112 | unsigned int lenpos=0; |
3c115e0f | 113 | %%{ |
114 | action labelEnd { | |
abb8aeb0 | 115 | if (labellen > 63) { |
d0a4a4bc RG |
116 | throw runtime_error("Unable to parse DNS name '"+string(realinput)+"': invalid label length "+std::to_string(labellen)); |
117 | } | |
bae1b0a2 | 118 | ret[lenpos]=labellen; |
119 | labellen=0; | |
3c115e0f | 120 | } |
121 | action labelBegin { | |
bae1b0a2 | 122 | lenpos=ret.size(); |
123 | ret.append(1, (char)0); | |
124 | labellen=0; | |
3c115e0f | 125 | } |
126 | ||
127 | action reportEscaped { | |
128 | char c = *fpc; | |
bae1b0a2 | 129 | ret.append(1, c); |
130 | labellen++; | |
3c115e0f | 131 | } |
132 | action reportEscapedNumber { | |
133 | char c = *fpc; | |
134 | val *= 10; | |
135 | val += c-'0'; | |
3c115e0f | 136 | } |
137 | action doneEscapedNumber { | |
bae1b0a2 | 138 | ret.append(1, val); |
139 | labellen++; | |
3c115e0f | 140 | val=0; |
141 | } | |
142 | ||
143 | action reportPlain { | |
bae1b0a2 | 144 | ret.append(1, *(fpc)); |
145 | labellen++; | |
3c115e0f | 146 | } |
147 | ||
148 | escaped = '\\' (([^0-9]@reportEscaped) | ([0-9]{3}$reportEscapedNumber%doneEscapedNumber)); | |
564ec901 | 149 | plain = (extend-'\\'-'.') $ reportPlain; |
3c115e0f | 150 | labelElement = escaped | plain; |
151 | ||
bae1b0a2 | 152 | label = labelElement+ >labelBegin %labelEnd; |
153 | ||
154 | main:= label ('.' label )* '.'?; | |
155 | ||
156 | #main := labelElement((labelElement+ '.') >labelBegin %labelEnd)+; | |
157 | ||
158 | # label = (plain | escaped | escdecb)+ >label_init %label_fin; | |
159 | # dnsname := '.'? label ('.' label >label_sep)* '.'?; | |
3c115e0f | 160 | |
161 | # Initialize and execute. | |
162 | write init; | |
163 | write exec; | |
164 | }%% | |
165 | ||
bae1b0a2 | 166 | if ( cs < dnsnameraw_first_final ) { |
167 | throw runtime_error("Unable to parse DNS name '"+string(realinput)+"': cs="+std::to_string(cs)); | |
3c115e0f | 168 | } |
bae1b0a2 | 169 | ret.append(1, (char)0); |
3c115e0f | 170 | return ret; |
171 | }; | |
172 | ||
b1a048a9 PL |
173 | // Reads an RFC 1035 character string from 'in', puts the resulting bytes in 'out'. |
174 | // Returns the amount of bytes read from 'in' | |
175 | size_t parseRFC1035CharString(const std::string &in, std::string &val) { | |
176 | ||
177 | val.clear(); | |
178 | val.reserve(in.size()); | |
179 | const char *p = in.c_str(); | |
ca749ae1 | 180 | const char *pe = p + in.size(); |
b1a048a9 PL |
181 | int cs = 0; |
182 | uint8_t escaped_octet = 0; | |
183 | // Keeps track of how many chars we read from the source string | |
184 | size_t counter=0; | |
185 | ||
186 | /* This parses an RFC 1035 char-string. | |
187 | * It was created from the ABNF in draft-ietf-dnsop-svcb-https-02 with | |
188 | * https://github.com/zinid/abnfc and modified to put all the characters in the | |
189 | * right place. | |
190 | */ | |
191 | %%{ | |
192 | machine dns_text_to_string; | |
193 | ||
194 | action doEscapedNumber { | |
195 | escaped_octet *= 10; | |
196 | escaped_octet += fc-'0'; | |
197 | counter++; | |
198 | } | |
199 | ||
200 | action doneEscapedNumber { | |
201 | val += escaped_octet; | |
202 | escaped_octet = 0; | |
203 | } | |
204 | ||
205 | action addToVal { | |
206 | val += fc; | |
207 | counter++; | |
208 | } | |
209 | ||
210 | action incrementCounter { | |
211 | counter++; | |
212 | } | |
213 | ||
214 | # generated rules, define required actions | |
215 | DIGIT = 0x30..0x39; | |
216 | DQUOTE = "\""; | |
217 | HTAB = "\t"; | |
218 | SP = " "; | |
219 | WSP = (SP | HTAB)@addToVal; | |
220 | non_special = "!" | 0x23..0x27 | 0x2a..0x3a | 0x3c..0x5b | 0x5d..0x7e; | |
221 | non_digit = 0x21..0x2f | 0x3a..0x7e; | |
222 | dec_octet = ( ( "0" | "1" ) DIGIT{2} ) | ( "2" ( ( 0x30..0x34 DIGIT ) | ( "5" 0x30..0x35 ) ) ); | |
223 | escaped = '\\'@incrementCounter ( non_digit$addToVal | dec_octet$doEscapedNumber@doneEscapedNumber ); | |
224 | contiguous = ( non_special$addToVal | escaped )+; | |
225 | quoted = DQUOTE@incrementCounter ( contiguous | ( '\\'? WSP ) )* DQUOTE@incrementCounter; | |
226 | char_string = (contiguous | quoted); | |
227 | ||
228 | # instantiate machine rules | |
229 | main := char_string; | |
230 | write data; | |
231 | write init; | |
232 | }%% | |
233 | ||
234 | // silence warnings | |
235 | (void) dns_text_to_string_first_final; | |
236 | (void) dns_text_to_string_error; | |
237 | (void) dns_text_to_string_en_main; | |
238 | %% write exec; | |
239 | ||
240 | return counter; | |
241 | } | |
242 | ||
e701f9d4 | 243 | size_t parseSVCBValueListFromParsedRFC1035CharString(const std::string &in, std::vector<std::string> &val) { |
78788348 PL |
244 | val.clear(); |
245 | const char *p = in.c_str(); | |
246 | const char *pe = p + in.size(); | |
247 | int cs = 0; | |
e701f9d4 | 248 | const char* eof = pe; |
78788348 PL |
249 | // Keeps track of how many chars we read from the source string |
250 | size_t counter=0; | |
251 | ||
252 | // Here we store the parsed value until we hit a comma or are done | |
253 | std::string tmp; | |
254 | ||
255 | %%{ | |
256 | machine dns_text_to_value_list; | |
e701f9d4 | 257 | alphtype unsigned char; |
78788348 | 258 | |
e701f9d4 PL |
259 | action addToVal { |
260 | tmp += fc; | |
78788348 PL |
261 | counter++; |
262 | } | |
263 | ||
e701f9d4 | 264 | action addToValNoIncrement { |
78788348 | 265 | tmp += fc; |
78788348 PL |
266 | } |
267 | ||
e701f9d4 | 268 | action addToVector { |
78788348 PL |
269 | val.push_back(tmp); |
270 | tmp.clear(); | |
271 | counter++; | |
272 | } | |
273 | ||
274 | action incrementCounter { | |
275 | counter++; | |
276 | } | |
277 | ||
278 | # generated rules, define required actions | |
e701f9d4 PL |
279 | OCTET = 0x00..0xff; |
280 | item_allowed = 0x00..0x2b | 0x2d..0x5b | 0x5d..0xff; | |
281 | escaped_item = ( item_allowed$addToVal | '\\,'$incrementCounter@addToValNoIncrement | '\\\\'$incrementCounter@addToValNoIncrement )+; | |
282 | comma_separated = ( escaped_item%addToVector ( ","@incrementCounter escaped_item%addToVector )* )?; | |
78788348 PL |
283 | |
284 | # instantiate machine rules | |
e701f9d4 | 285 | main := comma_separated; |
78788348 PL |
286 | write data; |
287 | write init; | |
288 | }%% | |
289 | ||
290 | // silence warnings | |
291 | (void) dns_text_to_value_list_first_final; | |
292 | (void) dns_text_to_value_list_error; | |
293 | (void) dns_text_to_value_list_en_main; | |
294 | %% write exec; | |
295 | ||
dda5e234 PD |
296 | if ( cs < dns_text_to_value_list_first_final ) { |
297 | throw runtime_error("Unable to parse DNS SVCB value list '"+in+"'"); | |
298 | } | |
299 | ||
78788348 PL |
300 | return counter; |
301 | } | |
3c115e0f | 302 | |
bae1b0a2 | 303 | |
bac8f21b BH |
304 | #if 0 |
305 | int main() | |
306 | { | |
307 | //char blah[]="\"blah\" \"bleh\" \"bloeh\\\"bleh\" \"\\97enzo\""; | |
308 | 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\""; | |
309 | //char blah[]="\"abc \\097\\098 def\""; | |
310 | printf("Input: '%s'\n", blah); | |
311 | vector<string> res=dnstext(blah); | |
312 | cerr<<res.size()<<" segments"<<endl; | |
313 | cerr<<res[0]<<endl; | |
314 | } | |
315 | #endif |