]>
Commit | Line | Data |
---|---|---|
4192ca66 BH |
1 | /* |
2 | PowerDNS Versatile Database Driven Nameserver | |
38e655b6 | 3 | Copyright (C) 2005 - 2007 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 | ||
9 | This program is distributed in the hope that it will be useful, | |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | GNU General Public License for more details. | |
13 | ||
14 | You should have received a copy of the GNU General Public License | |
15 | along with this program; if not, write to the Free Software | |
06bd9ccf | 16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
4192ca66 BH |
17 | */ |
18 | ||
19 | #include "rcpgenerator.hh" | |
20133c59 | 20 | #include "dnsparser.hh" |
cbf0e7f3 | 21 | #include "misc.hh" |
4192ca66 | 22 | #include <boost/lexical_cast.hpp> |
12b33ac2 | 23 | #include <boost/algorithm/string.hpp> |
4192ca66 | 24 | #include <iostream> |
8c1c9170 | 25 | #include "base64.hh" |
d2d6cf61 | 26 | #include "namespaces.hh" |
4192ca66 BH |
27 | |
28 | RecordTextReader::RecordTextReader(const string& str, const string& zone) : d_string(str), d_zone(zone), d_pos(0), d_end(str.size()) | |
29 | { | |
30 | } | |
31 | ||
341930bb BH |
32 | void RecordTextReader::xfr48BitInt(uint64_t &val) |
33 | { | |
34 | xfr64BitInt(val); | |
35 | } | |
36 | ||
37 | void RecordTextReader::xfr64BitInt(uint64_t &val) | |
38 | { | |
39 | skipSpaces(); | |
40 | ||
41 | if(!isdigit(d_string.at(d_pos))) | |
42 | throw RecordTextException("expected digits at position "+lexical_cast<string>(d_pos)+" in '"+d_string+"'"); | |
43 | ||
44 | char *endptr; | |
45 | unsigned long ret=strtoull(d_string.c_str() + d_pos, &endptr, 10); | |
46 | val=ret; | |
47 | ||
48 | d_pos = endptr - d_string.c_str(); | |
49 | } | |
50 | ||
51 | ||
cbf0e7f3 | 52 | void RecordTextReader::xfr32BitInt(uint32_t &val) |
4192ca66 BH |
53 | { |
54 | skipSpaces(); | |
55 | ||
56 | if(!isdigit(d_string.at(d_pos))) | |
57 | throw RecordTextException("expected digits at position "+lexical_cast<string>(d_pos)+" in '"+d_string+"'"); | |
58 | ||
59 | char *endptr; | |
60 | unsigned long ret=strtoul(d_string.c_str() + d_pos, &endptr, 10); | |
61 | val=ret; | |
62 | ||
63 | d_pos = endptr - d_string.c_str(); | |
64 | } | |
65 | ||
8bf26468 BH |
66 | void RecordTextReader::xfrTime(uint32_t &val) |
67 | { | |
68 | struct tm tm; | |
69 | memset(&tm, 0, sizeof(tm)); | |
70 | ||
71 | string tmp; | |
72 | xfrLabel(tmp); // ends on number, so this works | |
73 | ||
74 | sscanf(tmp.c_str(), "%04d%02d%02d" "%02d%02d%02d", | |
4957a608 BH |
75 | &tm.tm_year, &tm.tm_mon, &tm.tm_mday, |
76 | &tm.tm_hour, &tm.tm_min, &tm.tm_sec); | |
8bf26468 BH |
77 | |
78 | tm.tm_year-=1900; | |
12b33ac2 | 79 | tm.tm_mon-=1; |
12b33ac2 | 80 | val=(uint32_t)timegm(&tm); |
8bf26468 BH |
81 | } |
82 | ||
cbf0e7f3 BH |
83 | void RecordTextReader::xfrIP(uint32_t &val) |
84 | { | |
85 | skipSpaces(); | |
86 | ||
87 | if(!isdigit(d_string.at(d_pos))) | |
aab4adb0 | 88 | throw RecordTextException("while parsing IP address, expected digits at position "+lexical_cast<string>(d_pos)+" in '"+d_string+"'"); |
1149fa0b BH |
89 | |
90 | uint32_t octet=0; | |
91 | val=0; | |
92 | char count=0; | |
cbf0e7f3 | 93 | |
1149fa0b BH |
94 | for(;;) { |
95 | if(d_string.at(d_pos)=='.') { | |
96 | val<<=8; | |
97 | val+=octet; | |
98 | octet=0; | |
99 | count++; | |
100 | if(count > 3) | |
4957a608 | 101 | break; |
1149fa0b BH |
102 | } |
103 | else if(isdigit(d_string.at(d_pos))) { | |
104 | octet*=10; | |
105 | octet+=d_string.at(d_pos) - '0'; | |
106 | if(octet > 255) | |
4957a608 | 107 | throw RecordTextException("unable to parse IP address"); |
1149fa0b BH |
108 | } |
109 | else if(dns_isspace(d_string.at(d_pos))) | |
110 | break; | |
87c837a3 BH |
111 | else { |
112 | throw RecordTextException(string("unable to parse IP address, strange character: ")+d_string.at(d_pos)); | |
113 | } | |
1149fa0b BH |
114 | d_pos++; |
115 | if(d_pos == d_string.length()) | |
116 | break; | |
117 | } | |
118 | if(count<=3) { | |
119 | val<<=8; | |
120 | val+=octet; | |
121 | } | |
122 | val=ntohl(val); | |
cbf0e7f3 BH |
123 | } |
124 | ||
125 | ||
20133c59 BH |
126 | bool RecordTextReader::eof() |
127 | { | |
128 | return d_pos==d_end; | |
129 | } | |
130 | ||
cbf0e7f3 BH |
131 | void RecordTextReader::xfr16BitInt(uint16_t &val) |
132 | { | |
133 | uint32_t tmp; | |
134 | xfr32BitInt(tmp); | |
135 | val=tmp; | |
136 | if(val!=tmp) | |
137 | throw RecordTextException("Overflow reading 16 bit integer from record content"); // fixme improve | |
138 | } | |
139 | ||
8c1c9170 BH |
140 | void RecordTextReader::xfr8BitInt(uint8_t &val) |
141 | { | |
142 | uint32_t tmp; | |
143 | xfr32BitInt(tmp); | |
144 | val=tmp; | |
145 | if(val!=tmp) | |
146 | throw RecordTextException("Overflow reading 8 bit integer from record content"); // fixme improve | |
147 | } | |
148 | ||
35ce8576 | 149 | void RecordTextReader::xfrLabel(string& val, bool) |
4192ca66 BH |
150 | { |
151 | skipSpaces(); | |
29a14b24 BH |
152 | val.clear(); |
153 | val.reserve(d_end - d_pos); | |
154 | ||
d39704c0 | 155 | const char* strptr=d_string.c_str(); |
83b77f90 | 156 | string::size_type begin_pos = d_pos; |
29a14b24 | 157 | while(d_pos < d_end) { |
0a1825d6 | 158 | if(strptr[d_pos]!='\r' && dns_isspace(strptr[d_pos])) |
29a14b24 BH |
159 | break; |
160 | ||
38e655b6 | 161 | if(strptr[d_pos]=='\\' && d_pos < d_end - 1 && strptr[d_pos+1]!='.') // leave the \. escape around |
29a14b24 BH |
162 | d_pos++; |
163 | ||
4192ca66 | 164 | d_pos++; |
29a14b24 | 165 | } |
4192ca66 | 166 | |
83b77f90 BH |
167 | val.append(strptr+begin_pos, strptr+d_pos); |
168 | ||
4192ca66 BH |
169 | if(val.empty()) |
170 | val=d_zone; | |
bca6643b | 171 | else if(!d_zone.empty()) { |
cbf0e7f3 BH |
172 | char last=val[val.size()-1]; |
173 | ||
b0d4fb45 BH |
174 | if(last =='.') |
175 | val.resize(val.size()-1); | |
176 | else if(last != '.' && !isdigit(last)) // don't add zone to IP address | |
cbf0e7f3 BH |
177 | val+="."+d_zone; |
178 | } | |
4192ca66 BH |
179 | } |
180 | ||
12b33ac2 BH |
181 | static bool isbase64(char c) |
182 | { | |
183 | if(dns_isspace(c)) | |
184 | return true; | |
185 | if(c >= '0' && c <= '9') | |
186 | return true; | |
187 | if(c >= 'a' && c <= 'z') | |
188 | return true; | |
189 | if(c >= 'A' && c <= 'Z') | |
190 | return true; | |
191 | if(c=='+' || c=='/' || c=='=') | |
192 | return true; | |
193 | return false; | |
194 | } | |
195 | ||
06ffdc52 | 196 | void RecordTextReader::xfrBlob(string& val, int) |
8c1c9170 BH |
197 | { |
198 | skipSpaces(); | |
705f31ae | 199 | int pos=(int)d_pos; |
d39704c0 | 200 | const char* strptr=d_string.c_str(); |
12b33ac2 | 201 | while(d_pos < d_end && isbase64(strptr[d_pos])) |
8c1c9170 | 202 | d_pos++; |
12b33ac2 | 203 | |
20133c59 BH |
204 | string tmp; |
205 | tmp.assign(d_string.c_str()+pos, d_string.c_str() + d_pos); | |
12b33ac2 | 206 | boost::erase_all(tmp," "); |
20133c59 BH |
207 | val.clear(); |
208 | B64Decode(tmp, val); | |
8c1c9170 BH |
209 | } |
210 | ||
59a0f653 BH |
211 | |
212 | static inline uint8_t hextodec(uint8_t val) | |
213 | { | |
214 | if(val >= '0' && val<='9') | |
215 | return val-'0'; | |
216 | else if(val >= 'A' && val<='F') | |
217 | return 10+(val-'A'); | |
218 | else if(val >= 'a' && val<='f') | |
219 | return 10+(val-'a'); | |
220 | else | |
221 | throw RecordTextException("Unknown hexadecimal character '"+lexical_cast<string>(val)+"'"); | |
222 | } | |
223 | ||
224 | ||
225 | void HEXDecode(const char* begin, const char* end, string& val) | |
226 | { | |
227 | if((end - begin)%2) | |
228 | throw RecordTextException("Hexadecimal blob with odd number of characters"); | |
229 | ||
705f31ae | 230 | int limit=(int)(end-begin)/2; |
59a0f653 BH |
231 | val.resize(limit); |
232 | for(int n=0; n < limit; ++n) { | |
233 | val[n] = hextodec(begin[2*n])*16 + hextodec(begin[2*n+1]); | |
234 | } | |
235 | } | |
236 | ||
237 | void RecordTextReader::xfrHexBlob(string& val) | |
238 | { | |
239 | skipSpaces(); | |
705f31ae | 240 | int pos=(int)d_pos; |
59a0f653 BH |
241 | while(d_pos < d_end && !dns_isspace(d_string[d_pos])) |
242 | d_pos++; | |
243 | ||
244 | HEXDecode(d_string.c_str()+pos, d_string.c_str() + d_pos, val); | |
245 | } | |
246 | ||
ef6a78d5 | 247 | void RecordTextReader::xfrText(string& val, bool multi) |
4192ca66 | 248 | { |
4192ca66 BH |
249 | val.clear(); |
250 | val.reserve(d_end - d_pos); | |
ef6a78d5 BH |
251 | |
252 | while(d_pos != d_end) { | |
253 | if(!val.empty()) | |
254 | val.append(1, ' '); | |
255 | ||
256 | skipSpaces(); | |
257 | if(d_string[d_pos]!='"') | |
258 | throw RecordTextException("Data field in DNS should start with quote (\") at position "+lexical_cast<string>(d_pos)+" of '"+d_string+"'"); | |
259 | ||
260 | val.append(1, '"'); | |
261 | while(++d_pos < d_end && d_string[d_pos]!='"') { | |
262 | if(d_string[d_pos]=='\\' && d_pos+1!=d_end) { | |
4957a608 | 263 | val.append(1, d_string[d_pos++]); |
ef6a78d5 BH |
264 | } |
265 | val.append(1, d_string[d_pos]); | |
4192ca66 | 266 | } |
ef6a78d5 BH |
267 | val.append(1,'"'); |
268 | if(d_pos == d_end) | |
269 | throw RecordTextException("Data field in DNS should end on a quote (\") in '"+d_string+"'"); | |
270 | d_pos++; | |
271 | if(!multi) | |
272 | break; | |
4192ca66 | 273 | } |
4192ca66 BH |
274 | } |
275 | ||
20133c59 BH |
276 | void RecordTextReader::xfrType(uint16_t& val) |
277 | { | |
278 | skipSpaces(); | |
705f31ae | 279 | int pos=(int)d_pos; |
ec486449 | 280 | while(d_pos < d_end && !dns_isspace(d_string[d_pos])) |
20133c59 BH |
281 | d_pos++; |
282 | ||
283 | string tmp; | |
284 | tmp.assign(d_string.c_str()+pos, d_string.c_str() + d_pos); | |
285 | ||
286 | val=DNSRecordContent::TypeToNumber(tmp); | |
287 | } | |
4192ca66 | 288 | |
8c1c9170 | 289 | |
4192ca66 BH |
290 | void RecordTextReader::skipSpaces() |
291 | { | |
d39704c0 BH |
292 | const char* strptr = d_string.c_str(); |
293 | while(d_pos < d_end && dns_isspace(strptr[d_pos])) | |
4192ca66 | 294 | d_pos++; |
4192ca66 BH |
295 | if(d_pos == d_end) |
296 | throw RecordTextException("missing field at the end of record content '"+d_string+"'"); | |
297 | } | |
298 | ||
299 | ||
300 | RecordTextWriter::RecordTextWriter(string& str) : d_string(str) | |
301 | { | |
302 | d_string.clear(); | |
303 | } | |
304 | ||
341930bb | 305 | void RecordTextWriter::xfr48BitInt(const uint64_t& val) |
4192ca66 BH |
306 | { |
307 | if(!d_string.empty()) | |
308 | d_string.append(1,' '); | |
309 | d_string+=lexical_cast<string>(val); | |
310 | } | |
311 | ||
20133c59 | 312 | |
341930bb BH |
313 | void RecordTextWriter::xfr32BitInt(const uint32_t& val) |
314 | { | |
315 | if(!d_string.empty()) | |
316 | d_string.append(1,' '); | |
317 | d_string+=lexical_cast<string>(val); | |
318 | } | |
20133c59 BH |
319 | |
320 | void RecordTextWriter::xfrType(const uint16_t& val) | |
321 | { | |
322 | if(!d_string.empty()) | |
323 | d_string.append(1,' '); | |
324 | d_string+=DNSRecordContent::NumberToType(val); | |
325 | } | |
326 | ||
ec486449 | 327 | // this function is on the fast path for the pdns_recursor |
cbf0e7f3 BH |
328 | void RecordTextWriter::xfrIP(const uint32_t& val) |
329 | { | |
330 | if(!d_string.empty()) | |
331 | d_string.append(1,' '); | |
332 | ||
ec486449 | 333 | char tmp[17]; |
79a9e9ad | 334 | uint32_t ip=val; |
1149fa0b BH |
335 | uint8_t vals[4]; |
336 | ||
337 | memcpy(&vals[0], &ip, sizeof(ip)); | |
338 | ||
339 | char *pos=tmp; | |
340 | ||
341 | for(int n=0; n < 4; ++n) { | |
342 | if(vals[n]<10) { | |
343 | *(pos++)=vals[n]+'0'; | |
344 | } else if(vals[n] < 100) { | |
345 | *(pos++)=(vals[n]/10) +'0'; | |
346 | *(pos++)=(vals[n]%10) +'0'; | |
347 | } else { | |
348 | *(pos++)=(vals[n]/100) +'0'; | |
349 | vals[n]%=100; | |
350 | *(pos++)=(vals[n]/10) +'0'; | |
351 | *(pos++)=(vals[n]%10) +'0'; | |
352 | } | |
353 | if(n!=3) | |
354 | *(pos++)='.'; | |
355 | } | |
356 | *pos=0; | |
357 | d_string.append(tmp, pos); | |
cbf0e7f3 BH |
358 | } |
359 | ||
360 | ||
8bf26468 BH |
361 | void RecordTextWriter::xfrTime(const uint32_t& val) |
362 | { | |
363 | if(!d_string.empty()) | |
364 | d_string.append(1,' '); | |
365 | ||
366 | struct tm tm; | |
367 | time_t time=val; // Y2038 bug! | |
d3b729e8 | 368 | #ifndef WIN32 |
8bf26468 | 369 | gmtime_r(&time, &tm); |
d3b729e8 BH |
370 | #else |
371 | struct tm* tmptr; | |
372 | tmptr=gmtime(&time); | |
373 | if(!tmptr) | |
374 | throw RecordTextException("Unable to convert timestamp into pretty printable time"); | |
375 | tm=*tmptr; | |
376 | #endif | |
8bf26468 BH |
377 | |
378 | char tmp[16]; | |
379 | snprintf(tmp,sizeof(tmp)-1, "%04d%02d%02d" "%02d%02d%02d", | |
4957a608 BH |
380 | tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, |
381 | tm.tm_hour, tm.tm_min, tm.tm_sec); | |
8bf26468 BH |
382 | |
383 | d_string += tmp; | |
384 | } | |
385 | ||
386 | ||
cbf0e7f3 BH |
387 | void RecordTextWriter::xfr16BitInt(const uint16_t& val) |
388 | { | |
389 | xfr32BitInt(val); | |
390 | } | |
391 | ||
8c1c9170 BH |
392 | void RecordTextWriter::xfr8BitInt(const uint8_t& val) |
393 | { | |
394 | xfr32BitInt(val); | |
395 | } | |
396 | ||
cbf0e7f3 | 397 | |
bca6643b | 398 | void RecordTextWriter::xfrLabel(const string& val, bool) |
4192ca66 BH |
399 | { |
400 | if(!d_string.empty()) | |
401 | d_string.append(1,' '); | |
29a14b24 BH |
402 | if(val.find(' ')==string::npos) |
403 | d_string+=val; | |
404 | else { | |
405 | d_string.reserve(d_string.size()+val.size()+3); | |
406 | for(string::size_type pos=0; pos < val.size() ; ++pos) | |
ec486449 | 407 | if(dns_isspace(val[pos])) |
4957a608 | 408 | d_string+="\\ "; |
29a14b24 | 409 | else if(val[pos]=='\\') |
4957a608 | 410 | d_string.append(1,'\\'); |
29a14b24 | 411 | else |
4957a608 | 412 | d_string.append(1,val[pos]); |
29a14b24 | 413 | } |
7738a23f | 414 | // d_string.append(1,'.'); |
4192ca66 BH |
415 | } |
416 | ||
06ffdc52 | 417 | void RecordTextWriter::xfrBlob(const string& val, int) |
8c1c9170 BH |
418 | { |
419 | if(!d_string.empty()) | |
420 | d_string.append(1,' '); | |
421 | ||
422 | d_string+=Base64Encode(val); | |
423 | } | |
424 | ||
59a0f653 BH |
425 | void RecordTextWriter::xfrHexBlob(const string& val) |
426 | { | |
427 | if(!d_string.empty()) | |
428 | d_string.append(1,' '); | |
429 | ||
430 | string::size_type limit=val.size(); | |
431 | char tmp[5]; | |
432 | for(string::size_type n = 0; n < limit; ++n) { | |
433 | snprintf(tmp, sizeof(tmp)-1, "%02x", (unsigned char)val[n]); | |
434 | d_string+=tmp; | |
435 | } | |
436 | } | |
437 | ||
ef6a78d5 | 438 | void RecordTextWriter::xfrText(const string& val, bool multi) |
4192ca66 BH |
439 | { |
440 | if(!d_string.empty()) | |
441 | d_string.append(1,' '); | |
4192ca66 | 442 | |
ef6a78d5 | 443 | d_string.append(val); |
4192ca66 BH |
444 | } |
445 | ||
446 | ||
447 | #ifdef TESTING | |
448 | ||
449 | int main(int argc, char**argv) | |
450 | try | |
451 | { | |
452 | RecordTextReader rtr(argv[1], argv[2]); | |
453 | ||
454 | unsigned int order, pref; | |
455 | string flags, services, regexp, replacement; | |
456 | string mx; | |
457 | ||
458 | rtr.xfrInt(order); | |
459 | rtr.xfrInt(pref); | |
cbf0e7f3 BH |
460 | rtr.xfrText(flags); |
461 | rtr.xfrText(services); | |
462 | rtr.xfrText(regexp); | |
4192ca66 BH |
463 | rtr.xfrLabel(replacement); |
464 | ||
465 | cout<<"order: "<<order<<", pref: "<<pref<<"\n"; | |
466 | cout<<"flags: \""<<flags<<"\", services: \""<<services<<"\", regexp: \""<<regexp<<"\", replacement: "<<replacement<<"\n"; | |
467 | ||
468 | string out; | |
469 | RecordTextWriter rtw(out); | |
470 | ||
471 | rtw.xfrInt(order); | |
472 | rtw.xfrInt(pref); | |
cbf0e7f3 BH |
473 | rtw.xfrText(flags); |
474 | rtw.xfrText(services); | |
475 | rtw.xfrText(regexp); | |
4192ca66 BH |
476 | rtw.xfrLabel(replacement); |
477 | ||
478 | cout<<"Regenerated: '"<<out<<"'\n"; | |
479 | ||
480 | } | |
adc10f99 | 481 | catch(std::exception& e) |
4192ca66 BH |
482 | { |
483 | cerr<<"Fatal: "<<e.what()<<endl; | |
484 | } | |
485 | ||
486 | #endif |