]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/rcpgenerator.cc
Merge pull request #8223 from PowerDNS/omoerbeek-patch-1
[thirdparty/pdns.git] / pdns / rcpgenerator.cc
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "rcpgenerator.hh"
26 #include "ascii.hh"
27 #include "dnsparser.hh"
28 #include "misc.hh"
29 #include "utility.hh"
30 #include <boost/algorithm/string.hpp>
31 #include <boost/algorithm/string/classification.hpp>
32
33 #include <iostream>
34 #include "base32.hh"
35 #include "base64.hh"
36 #include "namespaces.hh"
37
38 RecordTextReader::RecordTextReader(const string& str, const DNSName& zone) : d_string(str), d_zone(zone), d_pos(0)
39 {
40 /* remove whitespace */
41 if(!d_string.empty() && ( dns_isspace(*d_string.begin()) || dns_isspace(*d_string.rbegin()) ))
42 boost::trim_if(d_string, dns_isspace);
43 d_end = d_string.size();
44 }
45
46 void RecordTextReader::xfr48BitInt(uint64_t &val)
47 {
48 xfr64BitInt(val);
49 if (val > 281474976710655LL)
50 throw RecordTextException("Overflow reading 48 bit integer from record content"); // fixme improve
51 }
52
53 void RecordTextReader::xfr64BitInt(uint64_t &val)
54 {
55 skipSpaces();
56
57 if(!isdigit(d_string.at(d_pos)))
58 throw RecordTextException("expected digits at position "+std::to_string(d_pos)+" in '"+d_string+"'");
59
60 size_t pos;
61 val=std::stoull(d_string.substr(d_pos), &pos);
62
63 d_pos += pos;
64 }
65
66
67 void RecordTextReader::xfr32BitInt(uint32_t &val)
68 {
69 skipSpaces();
70
71 if(!isdigit(d_string.at(d_pos)))
72 throw RecordTextException("expected digits at position "+std::to_string(d_pos)+" in '"+d_string+"'");
73
74 size_t pos;
75 val=pdns_stou(d_string.c_str()+d_pos, &pos);
76
77 d_pos += pos;
78 }
79
80 void RecordTextReader::xfrTime(uint32_t &val)
81 {
82 struct tm tm;
83 memset(&tm, 0, sizeof(tm));
84
85 uint64_t itmp;
86 xfr64BitInt(itmp);
87
88 if (itmp <= (uint32_t)~0) {
89 // formatted as seconds since epoch, not as YYYYMMDDHHmmSS:
90 val = (uint32_t) itmp;
91 return;
92 }
93
94 ostringstream tmp;
95
96 tmp<<itmp;
97
98 sscanf(tmp.str().c_str(), "%04d%02d%02d" "%02d%02d%02d",
99 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
100 &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
101
102 tm.tm_year-=1900;
103 tm.tm_mon-=1;
104 val=(uint32_t)Utility::timegm(&tm);
105 }
106
107 void RecordTextReader::xfrIP(uint32_t &val)
108 {
109 skipSpaces();
110
111 if(!isdigit(d_string.at(d_pos)))
112 throw RecordTextException("while parsing IP address, expected digits at position "+std::to_string(d_pos)+" in '"+d_string+"'");
113
114 uint32_t octet=0;
115 val=0;
116 char count=0;
117 bool last_was_digit = false;
118
119 for(;;) {
120 if(d_string.at(d_pos)=='.') {
121 if (!last_was_digit)
122 throw RecordTextException(string("unable to parse IP address, dot without previous digit"));
123 last_was_digit = false;
124 val<<=8;
125 val+=octet;
126 octet=0;
127 count++;
128 if(count > 3)
129 throw RecordTextException(string("unable to parse IP address, too many dots"));
130 }
131 else if(isdigit(d_string.at(d_pos))) {
132 last_was_digit = true;
133 octet*=10;
134 octet+=d_string.at(d_pos) - '0';
135 if(octet > 255)
136 throw RecordTextException("unable to parse IP address");
137 }
138 else if(dns_isspace(d_string.at(d_pos)))
139 break;
140 else {
141 throw RecordTextException(string("unable to parse IP address, strange character: ")+d_string.at(d_pos));
142 }
143 d_pos++;
144 if(d_pos == d_string.length())
145 break;
146 }
147 if (count != 3)
148 throw RecordTextException(string("unable to parse IP address, not enough dots"));
149 if (!last_was_digit)
150 throw RecordTextException(string("unable to parse IP address, trailing dot"));
151 val<<=8;
152 val+=octet;
153 val=ntohl(val);
154 }
155
156
157 void RecordTextReader::xfrIP6(std::string &val)
158 {
159 struct in6_addr tmpbuf;
160
161 skipSpaces();
162
163 size_t len;
164 // lookup end of value - think of ::ffff encoding too, has dots in it!
165 for(len=0;
166 d_pos+len < d_string.length() && (isxdigit(d_string.at(d_pos+len)) || d_string.at(d_pos+len) == ':' || d_string.at(d_pos+len)=='.');
167 len++);
168
169 if(!len)
170 throw RecordTextException("while parsing IPv6 address, expected xdigits at position "+std::to_string(d_pos)+" in '"+d_string+"'");
171
172 // end of value is here, try parse as IPv6
173 string address=d_string.substr(d_pos, len);
174
175 if (inet_pton(AF_INET6, address.c_str(), &tmpbuf) != 1) {
176 throw RecordTextException("while parsing IPv6 address: '" + address + "' is invalid");
177 }
178
179 val = std::string((char*)tmpbuf.s6_addr, 16);
180
181 d_pos += len;
182 }
183
184 void RecordTextReader::xfrCAWithoutPort(uint8_t version, ComboAddress &val)
185 {
186 if (version == 4) {
187 uint32_t ip;
188 xfrIP(ip);
189 val = makeComboAddressFromRaw(4, string((const char*) &ip, 4));
190 }
191 else if (version == 6) {
192 string ip;
193 xfrIP6(ip);
194 val = makeComboAddressFromRaw(6, ip);
195 }
196 else throw RecordTextException("invalid address family");
197 }
198
199 void RecordTextReader::xfrCAPort(ComboAddress &val)
200 {
201 uint16_t port;
202 xfr16BitInt(port);
203 val.sin4.sin_port = port;
204 }
205
206 bool RecordTextReader::eof()
207 {
208 return d_pos==d_end;
209 }
210
211 void RecordTextReader::xfr16BitInt(uint16_t &val)
212 {
213 uint32_t tmp;
214 xfr32BitInt(tmp);
215 val=tmp;
216 if(val!=tmp)
217 throw RecordTextException("Overflow reading 16 bit integer from record content"); // fixme improve
218 }
219
220 void RecordTextReader::xfr8BitInt(uint8_t &val)
221 {
222 uint32_t tmp;
223 xfr32BitInt(tmp);
224 val=tmp;
225 if(val!=tmp)
226 throw RecordTextException("Overflow reading 8 bit integer from record content"); // fixme improve
227 }
228
229 // this code should leave all the escapes around
230 void RecordTextReader::xfrName(DNSName& val, bool, bool)
231 {
232 skipSpaces();
233 DNSName sval;
234
235 const char* strptr=d_string.c_str();
236 string::size_type begin_pos = d_pos;
237 while(d_pos < d_end) {
238 if(strptr[d_pos]!='\r' && dns_isspace(strptr[d_pos]))
239 break;
240
241 d_pos++;
242 }
243 sval = DNSName(std::string(strptr+begin_pos, strptr+d_pos));
244
245 if(sval.empty())
246 sval=d_zone;
247 else if(!d_zone.empty())
248 sval+=d_zone;
249 val = sval;
250 }
251
252 static bool isbase64(char c, bool acceptspace)
253 {
254 if(dns_isspace(c))
255 return acceptspace;
256 if(c >= '0' && c <= '9')
257 return true;
258 if(c >= 'a' && c <= 'z')
259 return true;
260 if(c >= 'A' && c <= 'Z')
261 return true;
262 if(c=='+' || c=='/' || c=='=')
263 return true;
264 return false;
265 }
266
267 void RecordTextReader::xfrBlobNoSpaces(string& val, int len) {
268 skipSpaces();
269 int pos=(int)d_pos;
270 const char* strptr=d_string.c_str();
271 while(d_pos < d_end && isbase64(strptr[d_pos], false))
272 d_pos++;
273
274 string tmp;
275 tmp.assign(d_string.c_str()+pos, d_string.c_str() + d_pos);
276 boost::erase_all(tmp," ");
277 val.clear();
278 B64Decode(tmp, val);
279
280 if (len>-1 && val.size() != static_cast<size_t>(len))
281 throw RecordTextException("Record length "+std::to_string(val.size()) + " does not match expected length '"+std::to_string(len));
282 }
283
284 void RecordTextReader::xfrBlob(string& val, int)
285 {
286 skipSpaces();
287 int pos=(int)d_pos;
288 const char* strptr=d_string.c_str();
289 while(d_pos < d_end && isbase64(strptr[d_pos], true))
290 d_pos++;
291
292 string tmp;
293 tmp.assign(d_string.c_str()+pos, d_string.c_str() + d_pos);
294 boost::erase_all(tmp," ");
295 val.clear();
296 B64Decode(tmp, val);
297 }
298
299
300 static inline uint8_t hextodec(uint8_t val)
301 {
302 if(val >= '0' && val<='9')
303 return val-'0';
304 else if(val >= 'A' && val<='F')
305 return 10+(val-'A');
306 else if(val >= 'a' && val<='f')
307 return 10+(val-'a');
308 else
309 throw RecordTextException("Unknown hexadecimal character '"+std::to_string(val)+"'");
310 }
311
312
313 void HEXDecode(const char* begin, const char* end, string& out)
314 {
315 if(end - begin == 1 && *begin=='-') {
316 out.clear();
317 return;
318 }
319 out.clear();
320 out.reserve((end-begin)/2);
321 uint8_t mode=0, val=0;
322 for(; begin != end; ++begin) {
323 if(!isalnum(*begin))
324 continue;
325 if(mode==0) {
326 val = 16*hextodec(*begin);
327 mode=1;
328 } else {
329 val += hextodec(*begin);
330 out.append(1, (char) val);
331 mode = 0;
332 val = 0;
333 }
334 }
335 if(mode)
336 out.append(1, (char) val);
337
338 }
339
340 void RecordTextReader::xfrHexBlob(string& val, bool keepReading)
341 {
342 skipSpaces();
343 int pos=(int)d_pos;
344 while(d_pos < d_end && (keepReading || !dns_isspace(d_string[d_pos])))
345 d_pos++;
346
347 HEXDecode(d_string.c_str()+pos, d_string.c_str() + d_pos, val);
348 }
349
350 void RecordTextReader::xfrBase32HexBlob(string& val)
351 {
352 skipSpaces();
353 int pos=(int)d_pos;
354 while(d_pos < d_end && !dns_isspace(d_string[d_pos]))
355 d_pos++;
356
357 val=fromBase32Hex(string(d_string.c_str()+pos, d_pos-pos));
358 }
359
360
361 void RecordTextWriter::xfrBase32HexBlob(const string& val)
362 {
363 if(!d_string.empty())
364 d_string.append(1,' ');
365
366 d_string.append(toUpper(toBase32Hex(val)));
367 }
368
369
370 void RecordTextReader::xfrText(string& val, bool multi, bool lenField)
371 {
372 val.clear();
373 val.reserve(d_end - d_pos);
374
375 while(d_pos != d_end) {
376 if(!val.empty())
377 val.append(1, ' ');
378
379 skipSpaces();
380 if(d_string[d_pos]!='"') { // special case 'plenus' - without quotes
381 string::size_type pos = d_pos;
382 while(pos != d_end && isalnum(d_string[pos]))
383 pos++;
384 if(pos == d_end) {
385 val.append(1, '"');
386 val.append(d_string.c_str() + d_pos, d_end - d_pos);
387 val.append(1, '"');
388 d_pos = d_end;
389 break;
390 }
391 throw RecordTextException("Data field in DNS should start with quote (\") at position "+std::to_string(d_pos)+" of '"+d_string+"'");
392 }
393 val.append(1, '"');
394 while(++d_pos < d_end && d_string[d_pos]!='"') {
395 if(d_string[d_pos]=='\\' && d_pos+1!=d_end) {
396 val.append(1, d_string[d_pos++]);
397 }
398 val.append(1, d_string[d_pos]);
399 }
400 val.append(1,'"');
401 if(d_pos == d_end)
402 throw RecordTextException("Data field in DNS should end on a quote (\") in '"+d_string+"'");
403 d_pos++;
404 if(!multi)
405 break;
406 }
407 }
408
409 void RecordTextReader::xfrUnquotedText(string& val, bool lenField)
410 {
411 val.clear();
412 val.reserve(d_end - d_pos);
413
414 if(!val.empty())
415 val.append(1, ' ');
416
417 skipSpaces();
418 val.append(1, d_string[d_pos]);
419 while(++d_pos < d_end && d_string[d_pos] != ' '){
420 val.append(1, d_string[d_pos]);
421 }
422 }
423
424 void RecordTextReader::xfrType(uint16_t& val)
425 {
426 skipSpaces();
427 int pos=(int)d_pos;
428 while(d_pos < d_end && !dns_isspace(d_string[d_pos]))
429 d_pos++;
430
431 string tmp;
432 tmp.assign(d_string.c_str()+pos, d_string.c_str() + d_pos);
433
434 val=DNSRecordContent::TypeToNumber(tmp);
435 }
436
437
438 void RecordTextReader::skipSpaces()
439 {
440 const char* strptr = d_string.c_str();
441 while(d_pos < d_end && dns_isspace(strptr[d_pos]))
442 d_pos++;
443 if(d_pos == d_end)
444 throw RecordTextException("missing field at the end of record content '"+d_string+"'");
445 }
446
447
448 RecordTextWriter::RecordTextWriter(string& str, bool noDot) : d_string(str)
449 {
450 d_string.clear();
451 d_nodot=noDot;
452 }
453
454 void RecordTextWriter::xfr48BitInt(const uint64_t& val)
455 {
456 if(!d_string.empty())
457 d_string.append(1,' ');
458 d_string+=std::to_string(val);
459 }
460
461
462 void RecordTextWriter::xfr32BitInt(const uint32_t& val)
463 {
464 if(!d_string.empty())
465 d_string.append(1,' ');
466 d_string+=std::to_string(val);
467 }
468
469 void RecordTextWriter::xfrType(const uint16_t& val)
470 {
471 if(!d_string.empty())
472 d_string.append(1,' ');
473 d_string+=DNSRecordContent::NumberToType(val);
474 }
475
476 // this function is on the fast path for the pdns_recursor
477 void RecordTextWriter::xfrIP(const uint32_t& val)
478 {
479 if(!d_string.empty())
480 d_string.append(1,' ');
481
482 char tmp[17];
483 uint32_t ip=val;
484 uint8_t vals[4];
485
486 memcpy(&vals[0], &ip, sizeof(ip));
487
488 char *pos=tmp;
489
490 for(int n=0; n < 4; ++n) {
491 if(vals[n]<10) {
492 *(pos++)=vals[n]+'0';
493 } else if(vals[n] < 100) {
494 *(pos++)=(vals[n]/10) +'0';
495 *(pos++)=(vals[n]%10) +'0';
496 } else {
497 *(pos++)=(vals[n]/100) +'0';
498 vals[n]%=100;
499 *(pos++)=(vals[n]/10) +'0';
500 *(pos++)=(vals[n]%10) +'0';
501 }
502 if(n!=3)
503 *(pos++)='.';
504 }
505 *pos=0;
506 d_string.append(tmp, pos);
507 }
508
509 void RecordTextWriter::xfrIP6(const std::string& val)
510 {
511 char tmpbuf[16];
512 char addrbuf[40];
513
514 if(!d_string.empty())
515 d_string.append(1,' ');
516
517 val.copy(tmpbuf,16);
518
519 if (inet_ntop(AF_INET6, tmpbuf, addrbuf, sizeof addrbuf) == NULL)
520 throw RecordTextException("Unable to convert to ipv6 address");
521
522 d_string += std::string(addrbuf);
523 }
524
525 void RecordTextWriter::xfrCAWithoutPort(uint8_t version, ComboAddress &val)
526 {
527 string ip = val.toString();
528
529 if(!d_string.empty())
530 d_string.append(1,' ');
531
532 d_string += ip;
533 }
534
535 void RecordTextWriter::xfrCAPort(ComboAddress &val)
536 {
537 xfr16BitInt(val.sin4.sin_port);
538 }
539
540 void RecordTextWriter::xfrTime(const uint32_t& val)
541 {
542 if(!d_string.empty())
543 d_string.append(1,' ');
544
545 struct tm tm;
546 time_t time=val; // Y2038 bug!
547 gmtime_r(&time, &tm);
548
549 static const boost::format fmt("%04d%02d%02d" "%02d%02d%02d");
550 d_string += boost::str(boost::format(fmt) % (tm.tm_year+1900) % (tm.tm_mon+1) % tm.tm_mday % tm.tm_hour % tm.tm_min % tm.tm_sec);
551 }
552
553
554 void RecordTextWriter::xfr16BitInt(const uint16_t& val)
555 {
556 xfr32BitInt(val);
557 }
558
559 void RecordTextWriter::xfr8BitInt(const uint8_t& val)
560 {
561 xfr32BitInt(val);
562 }
563
564 // should not mess with the escapes
565 void RecordTextWriter::xfrName(const DNSName& val, bool, bool noDot)
566 {
567 if(!d_string.empty())
568 d_string.append(1,' ');
569
570 if(d_nodot) {
571 d_string+=val.toStringRootDot();
572 }
573 else
574 {
575 d_string+=val.toString();
576 }
577 }
578
579 void RecordTextWriter::xfrBlobNoSpaces(const string& val, int size)
580 {
581 xfrBlob(val, size);
582 }
583
584 void RecordTextWriter::xfrBlob(const string& val, int)
585 {
586 if(!d_string.empty())
587 d_string.append(1,' ');
588
589 d_string+=Base64Encode(val);
590 }
591
592 void RecordTextWriter::xfrHexBlob(const string& val, bool)
593 {
594 if(!d_string.empty())
595 d_string.append(1,' ');
596
597 if(val.empty()) {
598 d_string.append(1,'-');
599 return;
600 }
601
602 string::size_type limit=val.size();
603 char tmp[5];
604 for(string::size_type n = 0; n < limit; ++n) {
605 snprintf(tmp, sizeof(tmp), "%02x", (unsigned char)val[n]);
606 d_string+=tmp;
607 }
608 }
609
610 void RecordTextWriter::xfrText(const string& val, bool multi, bool lenField)
611 {
612 if(!d_string.empty())
613 d_string.append(1,' ');
614
615 d_string.append(val);
616 }
617
618 void RecordTextWriter::xfrUnquotedText(const string& val, bool lenField)
619 {
620 if(!d_string.empty())
621 d_string.append(1,' ');
622 d_string.append(val);
623 }
624
625 #ifdef TESTING
626
627 int main(int argc, char**argv)
628 try
629 {
630 RecordTextReader rtr(argv[1], argv[2]);
631
632 unsigned int order, pref;
633 string flags, services, regexp, replacement;
634 string mx;
635
636 rtr.xfrInt(order);
637 rtr.xfrInt(pref);
638 rtr.xfrText(flags);
639 rtr.xfrText(services);
640 rtr.xfrText(regexp);
641 rtr.xfrName(replacement);
642
643 cout<<"order: "<<order<<", pref: "<<pref<<"\n";
644 cout<<"flags: \""<<flags<<"\", services: \""<<services<<"\", regexp: \""<<regexp<<"\", replacement: "<<replacement<<"\n";
645
646 string out;
647 RecordTextWriter rtw(out);
648
649 rtw.xfrInt(order);
650 rtw.xfrInt(pref);
651 rtw.xfrText(flags);
652 rtw.xfrText(services);
653 rtw.xfrText(regexp);
654 rtw.xfrName(replacement);
655
656 cout<<"Regenerated: '"<<out<<"'\n";
657
658 }
659 catch(std::exception& e)
660 {
661 cerr<<"Fatal: "<<e.what()<<endl;
662 }
663
664 #endif