]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/rcpgenerator.cc
implement LocalState/GlobalState
[thirdparty/pdns.git] / pdns / rcpgenerator.cc
CommitLineData
4192ca66
BH
1/*
2 PowerDNS Versatile Database Driven Nameserver
e4090157 3 Copyright (C) 2005 - 2011 PowerDNS.COM BV
4192ca66
BH
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2 as
7 published by the Free Software Foundation
8
f782fe38
MH
9 Additionally, the license of this program contains a special
10 exception which allows to distribute the program in binary form when
11 it is linked against OpenSSL.
12
4192ca66
BH
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
06bd9ccf 20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
4192ca66
BH
21*/
22
23#include "rcpgenerator.hh"
20133c59 24#include "dnsparser.hh"
cbf0e7f3 25#include "misc.hh"
4192ca66 26#include <boost/lexical_cast.hpp>
12b33ac2 27#include <boost/algorithm/string.hpp>
4192ca66 28#include <iostream>
1c4d88c5 29#include "base32.hh"
8c1c9170 30#include "base64.hh"
d2d6cf61 31#include "namespaces.hh"
4192ca66
BH
32
33RecordTextReader::RecordTextReader(const string& str, const string& zone) : d_string(str), d_zone(zone), d_pos(0), d_end(str.size())
34{
35}
36
341930bb
BH
37void RecordTextReader::xfr48BitInt(uint64_t &val)
38{
39 xfr64BitInt(val);
40}
41
42void RecordTextReader::xfr64BitInt(uint64_t &val)
43{
44 skipSpaces();
45
46 if(!isdigit(d_string.at(d_pos)))
47 throw RecordTextException("expected digits at position "+lexical_cast<string>(d_pos)+" in '"+d_string+"'");
48
49 char *endptr;
50 unsigned long ret=strtoull(d_string.c_str() + d_pos, &endptr, 10);
51 val=ret;
52
53 d_pos = endptr - d_string.c_str();
54}
55
56
cbf0e7f3 57void RecordTextReader::xfr32BitInt(uint32_t &val)
4192ca66
BH
58{
59 skipSpaces();
60
61 if(!isdigit(d_string.at(d_pos)))
62 throw RecordTextException("expected digits at position "+lexical_cast<string>(d_pos)+" in '"+d_string+"'");
63
64 char *endptr;
f55691d1 65 unsigned long ret=pdns_strtoui(d_string.c_str() + d_pos, &endptr, 10);
58044407 66 if (ret == UINT_MAX && errno == ERANGE) throw RecordTextException("serial number too large in '"+d_string+"'");
4192ca66
BH
67 val=ret;
68
69 d_pos = endptr - d_string.c_str();
70}
71
8bf26468
BH
72void RecordTextReader::xfrTime(uint32_t &val)
73{
74 struct tm tm;
75 memset(&tm, 0, sizeof(tm));
76
77 string tmp;
78 xfrLabel(tmp); // ends on number, so this works
79
80 sscanf(tmp.c_str(), "%04d%02d%02d" "%02d%02d%02d",
4957a608
BH
81 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
82 &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
8bf26468
BH
83
84 tm.tm_year-=1900;
12b33ac2 85 tm.tm_mon-=1;
49f72da1 86 val=(uint32_t)Utility::timegm(&tm);
8bf26468
BH
87}
88
cbf0e7f3
BH
89void RecordTextReader::xfrIP(uint32_t &val)
90{
91 skipSpaces();
92
93 if(!isdigit(d_string.at(d_pos)))
aab4adb0 94 throw RecordTextException("while parsing IP address, expected digits at position "+lexical_cast<string>(d_pos)+" in '"+d_string+"'");
1149fa0b
BH
95
96 uint32_t octet=0;
97 val=0;
98 char count=0;
cbf0e7f3 99
1149fa0b
BH
100 for(;;) {
101 if(d_string.at(d_pos)=='.') {
102 val<<=8;
103 val+=octet;
104 octet=0;
105 count++;
106 if(count > 3)
4957a608 107 break;
1149fa0b
BH
108 }
109 else if(isdigit(d_string.at(d_pos))) {
110 octet*=10;
111 octet+=d_string.at(d_pos) - '0';
112 if(octet > 255)
4957a608 113 throw RecordTextException("unable to parse IP address");
1149fa0b
BH
114 }
115 else if(dns_isspace(d_string.at(d_pos)))
116 break;
87c837a3
BH
117 else {
118 throw RecordTextException(string("unable to parse IP address, strange character: ")+d_string.at(d_pos));
119 }
1149fa0b
BH
120 d_pos++;
121 if(d_pos == d_string.length())
122 break;
123 }
124 if(count<=3) {
125 val<<=8;
126 val+=octet;
127 }
128 val=ntohl(val);
cbf0e7f3
BH
129}
130
131
b9b28916
AT
132void RecordTextReader::xfrIP6(std::string &val)
133{
b9b28916
AT
134 struct in6_addr tmpbuf;
135
136 skipSpaces();
b9b28916
AT
137
138 size_t len;
cf90f8e1 139 // lookup end of value - think of ::ffff encoding too, has dots in it!
8d388c48 140 for(len=0;
cf90f8e1 141 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)=='.');
8d388c48 142 len++);
b9b28916 143
8d388c48 144 if(!len)
145 throw RecordTextException("while parsing IPv6 address, expected xdigits at position "+lexical_cast<string>(d_pos)+" in '"+d_string+"'");
b9b28916 146
8d388c48 147 // end of value is here, try parse as IPv6
148 string address=d_string.substr(d_pos, len);
149
150 if (inet_pton(AF_INET6, address.c_str(), &tmpbuf) != 1) {
151 throw RecordTextException("while parsing IPv6 address: '" + address + "' is invalid");
b9b28916
AT
152 }
153
154 val = std::string((char*)tmpbuf.s6_addr, 16);
155
156 d_pos += len;
157}
158
20133c59
BH
159bool RecordTextReader::eof()
160{
161 return d_pos==d_end;
162}
163
cbf0e7f3
BH
164void RecordTextReader::xfr16BitInt(uint16_t &val)
165{
166 uint32_t tmp;
167 xfr32BitInt(tmp);
168 val=tmp;
169 if(val!=tmp)
170 throw RecordTextException("Overflow reading 16 bit integer from record content"); // fixme improve
171}
172
8c1c9170
BH
173void RecordTextReader::xfr8BitInt(uint8_t &val)
174{
175 uint32_t tmp;
176 xfr32BitInt(tmp);
177 val=tmp;
178 if(val!=tmp)
179 throw RecordTextException("Overflow reading 8 bit integer from record content"); // fixme improve
180}
181
7ecd3576 182// this code should leave all the escapes around
35ce8576 183void RecordTextReader::xfrLabel(string& val, bool)
4192ca66
BH
184{
185 skipSpaces();
29a14b24
BH
186 val.clear();
187 val.reserve(d_end - d_pos);
188
d39704c0 189 const char* strptr=d_string.c_str();
83b77f90 190 string::size_type begin_pos = d_pos;
29a14b24 191 while(d_pos < d_end) {
0a1825d6 192 if(strptr[d_pos]!='\r' && dns_isspace(strptr[d_pos]))
29a14b24 193 break;
7ecd3576 194
4192ca66 195 d_pos++;
29a14b24 196 }
83b77f90
BH
197 val.append(strptr+begin_pos, strptr+d_pos);
198
4192ca66
BH
199 if(val.empty())
200 val=d_zone;
bca6643b 201 else if(!d_zone.empty()) {
cbf0e7f3
BH
202 char last=val[val.size()-1];
203
b0d4fb45
BH
204 if(last =='.')
205 val.resize(val.size()-1);
206 else if(last != '.' && !isdigit(last)) // don't add zone to IP address
cbf0e7f3
BH
207 val+="."+d_zone;
208 }
4192ca66
BH
209}
210
2fe9d6f7 211static bool isbase64(char c, bool acceptspace)
12b33ac2
BH
212{
213 if(dns_isspace(c))
2fe9d6f7 214 return acceptspace;
12b33ac2
BH
215 if(c >= '0' && c <= '9')
216 return true;
217 if(c >= 'a' && c <= 'z')
218 return true;
219 if(c >= 'A' && c <= 'Z')
220 return true;
221 if(c=='+' || c=='/' || c=='=')
222 return true;
223 return false;
224}
225
2fe9d6f7
AT
226void RecordTextReader::xfrBlobNoSpaces(string& val, int len) {
227 skipSpaces();
228 int pos=(int)d_pos;
229 const char* strptr=d_string.c_str();
230 while(d_pos < d_end && isbase64(strptr[d_pos], false))
231 d_pos++;
232
233 string tmp;
234 tmp.assign(d_string.c_str()+pos, d_string.c_str() + d_pos);
235 boost::erase_all(tmp," ");
236 val.clear();
237 B64Decode(tmp, val);
238
239 if (len>-1 && val.size() != static_cast<size_t>(len))
240 throw RecordTextException("Record length "+lexical_cast<string>(val.size()) + " does not match expected length '"+lexical_cast<string>(len));
241}
242
06ffdc52 243void RecordTextReader::xfrBlob(string& val, int)
8c1c9170
BH
244{
245 skipSpaces();
705f31ae 246 int pos=(int)d_pos;
d39704c0 247 const char* strptr=d_string.c_str();
2fe9d6f7 248 while(d_pos < d_end && isbase64(strptr[d_pos], true))
8c1c9170 249 d_pos++;
12b33ac2 250
20133c59
BH
251 string tmp;
252 tmp.assign(d_string.c_str()+pos, d_string.c_str() + d_pos);
12b33ac2 253 boost::erase_all(tmp," ");
20133c59
BH
254 val.clear();
255 B64Decode(tmp, val);
8c1c9170
BH
256}
257
59a0f653
BH
258
259static inline uint8_t hextodec(uint8_t val)
260{
261 if(val >= '0' && val<='9')
262 return val-'0';
263 else if(val >= 'A' && val<='F')
264 return 10+(val-'A');
265 else if(val >= 'a' && val<='f')
266 return 10+(val-'a');
267 else
268 throw RecordTextException("Unknown hexadecimal character '"+lexical_cast<string>(val)+"'");
269}
270
271
4e75f84a 272void HEXDecode(const char* begin, const char* end, string& out)
59a0f653 273{
3c873e66 274 if(end - begin == 1 && *begin=='-') {
4e75f84a 275 out.clear();
3c873e66
BH
276 return;
277 }
4e75f84a
BH
278 out.clear();
279 out.reserve((end-begin)/2);
280 uint8_t mode=0, val=0;
281 for(; begin != end; ++begin) {
282 if(!isalnum(*begin))
283 continue;
284 if(mode==0) {
285 val = 16*hextodec(*begin);
286 mode=1;
287 } else {
288 val += hextodec(*begin);
289 out.append(1, (char) val);
290 mode = 0;
291 val = 0;
292 }
59a0f653 293 }
4e75f84a
BH
294 if(mode)
295 out.append(1, (char) val);
296
59a0f653
BH
297}
298
e4090157 299void RecordTextReader::xfrHexBlob(string& val, bool keepReading)
59a0f653
BH
300{
301 skipSpaces();
705f31ae 302 int pos=(int)d_pos;
e4090157 303 while(d_pos < d_end && (keepReading || !dns_isspace(d_string[d_pos])))
59a0f653
BH
304 d_pos++;
305
306 HEXDecode(d_string.c_str()+pos, d_string.c_str() + d_pos, val);
307}
308
8b606f6e
BH
309void RecordTextReader::xfrBase32HexBlob(string& val)
310{
311 skipSpaces();
312 int pos=(int)d_pos;
313 while(d_pos < d_end && !dns_isspace(d_string[d_pos]))
314 d_pos++;
315
316 val=fromBase32Hex(string(d_string.c_str()+pos, d_pos-pos));
317}
318
319
1c4d88c5
BH
320void RecordTextWriter::xfrBase32HexBlob(const string& val)
321{
322 if(!d_string.empty())
323 d_string.append(1,' ');
324
1bad4190 325 d_string.append(toUpper(toBase32Hex(val)));
1c4d88c5
BH
326}
327
328
ef6a78d5 329void RecordTextReader::xfrText(string& val, bool multi)
4192ca66 330{
4192ca66
BH
331 val.clear();
332 val.reserve(d_end - d_pos);
ef6a78d5
BH
333
334 while(d_pos != d_end) {
335 if(!val.empty())
336 val.append(1, ' ');
337
338 skipSpaces();
2220e01f
BH
339 if(d_string[d_pos]!='"') { // special case 'plenus' - without quotes
340 string::size_type pos = d_pos;
341 while(pos != d_end && isalnum(d_string[pos]))
342 pos++;
343 if(pos == d_end) {
344 val.append(1, '"');
345 val.append(d_string.c_str() + d_pos, d_end - d_pos);
346 val.append(1, '"');
347 d_pos = d_end;
348 break;
349 }
ef6a78d5 350 throw RecordTextException("Data field in DNS should start with quote (\") at position "+lexical_cast<string>(d_pos)+" of '"+d_string+"'");
2220e01f 351 }
ef6a78d5
BH
352 val.append(1, '"');
353 while(++d_pos < d_end && d_string[d_pos]!='"') {
354 if(d_string[d_pos]=='\\' && d_pos+1!=d_end) {
4957a608 355 val.append(1, d_string[d_pos++]);
ef6a78d5
BH
356 }
357 val.append(1, d_string[d_pos]);
4192ca66 358 }
ef6a78d5
BH
359 val.append(1,'"');
360 if(d_pos == d_end)
361 throw RecordTextException("Data field in DNS should end on a quote (\") in '"+d_string+"'");
362 d_pos++;
363 if(!multi)
364 break;
4192ca66 365 }
4192ca66
BH
366}
367
20133c59
BH
368void RecordTextReader::xfrType(uint16_t& val)
369{
370 skipSpaces();
705f31ae 371 int pos=(int)d_pos;
ec486449 372 while(d_pos < d_end && !dns_isspace(d_string[d_pos]))
20133c59
BH
373 d_pos++;
374
375 string tmp;
376 tmp.assign(d_string.c_str()+pos, d_string.c_str() + d_pos);
377
378 val=DNSRecordContent::TypeToNumber(tmp);
379}
4192ca66 380
8c1c9170 381
4192ca66
BH
382void RecordTextReader::skipSpaces()
383{
d39704c0
BH
384 const char* strptr = d_string.c_str();
385 while(d_pos < d_end && dns_isspace(strptr[d_pos]))
4192ca66 386 d_pos++;
4192ca66
BH
387 if(d_pos == d_end)
388 throw RecordTextException("missing field at the end of record content '"+d_string+"'");
389}
390
391
392RecordTextWriter::RecordTextWriter(string& str) : d_string(str)
393{
394 d_string.clear();
395}
396
341930bb 397void RecordTextWriter::xfr48BitInt(const uint64_t& val)
4192ca66
BH
398{
399 if(!d_string.empty())
400 d_string.append(1,' ');
401 d_string+=lexical_cast<string>(val);
402}
403
20133c59 404
341930bb
BH
405void RecordTextWriter::xfr32BitInt(const uint32_t& val)
406{
407 if(!d_string.empty())
408 d_string.append(1,' ');
409 d_string+=lexical_cast<string>(val);
410}
20133c59
BH
411
412void RecordTextWriter::xfrType(const uint16_t& val)
413{
414 if(!d_string.empty())
415 d_string.append(1,' ');
416 d_string+=DNSRecordContent::NumberToType(val);
417}
418
ec486449 419// this function is on the fast path for the pdns_recursor
cbf0e7f3
BH
420void RecordTextWriter::xfrIP(const uint32_t& val)
421{
422 if(!d_string.empty())
423 d_string.append(1,' ');
424
ec486449 425 char tmp[17];
79a9e9ad 426 uint32_t ip=val;
1149fa0b
BH
427 uint8_t vals[4];
428
429 memcpy(&vals[0], &ip, sizeof(ip));
430
431 char *pos=tmp;
432
433 for(int n=0; n < 4; ++n) {
434 if(vals[n]<10) {
435 *(pos++)=vals[n]+'0';
436 } else if(vals[n] < 100) {
437 *(pos++)=(vals[n]/10) +'0';
438 *(pos++)=(vals[n]%10) +'0';
439 } else {
440 *(pos++)=(vals[n]/100) +'0';
441 vals[n]%=100;
442 *(pos++)=(vals[n]/10) +'0';
443 *(pos++)=(vals[n]%10) +'0';
444 }
445 if(n!=3)
446 *(pos++)='.';
447 }
448 *pos=0;
449 d_string.append(tmp, pos);
cbf0e7f3
BH
450}
451
b9b28916
AT
452void RecordTextWriter::xfrIP6(const std::string& val)
453{
454 char tmpbuf[16];
455 char addrbuf[40];
456
457 if(!d_string.empty())
458 d_string.append(1,' ');
459
460 val.copy(tmpbuf,16);
461
462 if (inet_ntop(AF_INET6, tmpbuf, addrbuf, sizeof addrbuf) == NULL)
463 throw RecordTextException("Unable to convert to ipv6 address");
464
465 d_string += std::string(addrbuf);
466}
cbf0e7f3 467
8bf26468
BH
468void RecordTextWriter::xfrTime(const uint32_t& val)
469{
470 if(!d_string.empty())
471 d_string.append(1,' ');
472
473 struct tm tm;
474 time_t time=val; // Y2038 bug!
ab13b07b 475 Utility::gmtime_r(&time, &tm);
76473b92 476
8bf26468
BH
477 char tmp[16];
478 snprintf(tmp,sizeof(tmp)-1, "%04d%02d%02d" "%02d%02d%02d",
4957a608
BH
479 tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
480 tm.tm_hour, tm.tm_min, tm.tm_sec);
8bf26468
BH
481
482 d_string += tmp;
483}
484
485
cbf0e7f3
BH
486void RecordTextWriter::xfr16BitInt(const uint16_t& val)
487{
488 xfr32BitInt(val);
489}
490
8c1c9170
BH
491void RecordTextWriter::xfr8BitInt(const uint8_t& val)
492{
493 xfr32BitInt(val);
494}
495
7ecd3576 496// should not mess with the escapes
bca6643b 497void RecordTextWriter::xfrLabel(const string& val, bool)
4192ca66
BH
498{
499 if(!d_string.empty())
500 d_string.append(1,' ');
7ecd3576
BH
501
502 d_string+=val;
4192ca66
BH
503}
504
2fe9d6f7
AT
505void RecordTextWriter::xfrBlobNoSpaces(const string& val, int size)
506{
507 xfrBlob(val, size);
508}
509
06ffdc52 510void RecordTextWriter::xfrBlob(const string& val, int)
8c1c9170
BH
511{
512 if(!d_string.empty())
513 d_string.append(1,' ');
514
515 d_string+=Base64Encode(val);
516}
517
e4090157 518void RecordTextWriter::xfrHexBlob(const string& val, bool)
59a0f653
BH
519{
520 if(!d_string.empty())
521 d_string.append(1,' ');
522
3c873e66
BH
523 if(val.empty()) {
524 d_string.append(1,'-');
525 return;
526 }
527
59a0f653
BH
528 string::size_type limit=val.size();
529 char tmp[5];
530 for(string::size_type n = 0; n < limit; ++n) {
531 snprintf(tmp, sizeof(tmp)-1, "%02x", (unsigned char)val[n]);
532 d_string+=tmp;
533 }
534}
535
ef6a78d5 536void RecordTextWriter::xfrText(const string& val, bool multi)
4192ca66
BH
537{
538 if(!d_string.empty())
539 d_string.append(1,' ');
4192ca66 540
ef6a78d5 541 d_string.append(val);
4192ca66
BH
542}
543
544
545#ifdef TESTING
546
547int main(int argc, char**argv)
548try
549{
550 RecordTextReader rtr(argv[1], argv[2]);
551
552 unsigned int order, pref;
553 string flags, services, regexp, replacement;
554 string mx;
555
556 rtr.xfrInt(order);
557 rtr.xfrInt(pref);
cbf0e7f3
BH
558 rtr.xfrText(flags);
559 rtr.xfrText(services);
560 rtr.xfrText(regexp);
4192ca66
BH
561 rtr.xfrLabel(replacement);
562
563 cout<<"order: "<<order<<", pref: "<<pref<<"\n";
564 cout<<"flags: \""<<flags<<"\", services: \""<<services<<"\", regexp: \""<<regexp<<"\", replacement: "<<replacement<<"\n";
565
566 string out;
567 RecordTextWriter rtw(out);
568
569 rtw.xfrInt(order);
570 rtw.xfrInt(pref);
cbf0e7f3
BH
571 rtw.xfrText(flags);
572 rtw.xfrText(services);
573 rtw.xfrText(regexp);
4192ca66
BH
574 rtw.xfrLabel(replacement);
575
576 cout<<"Regenerated: '"<<out<<"'\n";
577
578}
adc10f99 579catch(std::exception& e)
4192ca66
BH
580{
581 cerr<<"Fatal: "<<e.what()<<endl;
582}
583
584#endif