]>
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 BH |
22 | #include <boost/lexical_cast.hpp> |
23 | #include <iostream> | |
8c1c9170 | 24 | #include "base64.hh" |
4192ca66 BH |
25 | using namespace boost; |
26 | ||
27 | RecordTextReader::RecordTextReader(const string& str, const string& zone) : d_string(str), d_zone(zone), d_pos(0), d_end(str.size()) | |
28 | { | |
29 | } | |
30 | ||
341930bb BH |
31 | void RecordTextReader::xfr48BitInt(uint64_t &val) |
32 | { | |
33 | xfr64BitInt(val); | |
34 | } | |
35 | ||
36 | void RecordTextReader::xfr64BitInt(uint64_t &val) | |
37 | { | |
38 | skipSpaces(); | |
39 | ||
40 | if(!isdigit(d_string.at(d_pos))) | |
41 | throw RecordTextException("expected digits at position "+lexical_cast<string>(d_pos)+" in '"+d_string+"'"); | |
42 | ||
43 | char *endptr; | |
44 | unsigned long ret=strtoull(d_string.c_str() + d_pos, &endptr, 10); | |
45 | val=ret; | |
46 | ||
47 | d_pos = endptr - d_string.c_str(); | |
48 | } | |
49 | ||
50 | ||
cbf0e7f3 | 51 | void RecordTextReader::xfr32BitInt(uint32_t &val) |
4192ca66 BH |
52 | { |
53 | skipSpaces(); | |
54 | ||
55 | if(!isdigit(d_string.at(d_pos))) | |
56 | throw RecordTextException("expected digits at position "+lexical_cast<string>(d_pos)+" in '"+d_string+"'"); | |
57 | ||
58 | char *endptr; | |
59 | unsigned long ret=strtoul(d_string.c_str() + d_pos, &endptr, 10); | |
60 | val=ret; | |
61 | ||
62 | d_pos = endptr - d_string.c_str(); | |
63 | } | |
64 | ||
8bf26468 BH |
65 | void RecordTextReader::xfrTime(uint32_t &val) |
66 | { | |
67 | struct tm tm; | |
68 | memset(&tm, 0, sizeof(tm)); | |
69 | ||
70 | string tmp; | |
71 | xfrLabel(tmp); // ends on number, so this works | |
72 | ||
73 | sscanf(tmp.c_str(), "%04d%02d%02d" "%02d%02d%02d", | |
74 | &tm.tm_year, &tm.tm_mon, &tm.tm_mday, | |
75 | &tm.tm_hour, &tm.tm_min, &tm.tm_sec); | |
76 | ||
77 | tm.tm_year-=1900; | |
78 | tm.tm_min-=1; | |
79 | ||
705f31ae | 80 | val=(uint32_t)mktime(&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) | |
101 | break; | |
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) | |
107 | throw RecordTextException("unable to parse IP address"); | |
108 | } | |
109 | else if(dns_isspace(d_string.at(d_pos))) | |
110 | break; | |
111 | else | |
112 | throw RecordTextException("unable to parse IP address, strange character: "+d_string.at(d_pos)); | |
113 | ||
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(); |
29a14b24 | 156 | while(d_pos < d_end) { |
0a1825d6 | 157 | if(strptr[d_pos]!='\r' && dns_isspace(strptr[d_pos])) |
29a14b24 BH |
158 | break; |
159 | ||
38e655b6 | 160 | if(strptr[d_pos]=='\\' && d_pos < d_end - 1 && strptr[d_pos+1]!='.') // leave the \. escape around |
29a14b24 BH |
161 | d_pos++; |
162 | ||
d39704c0 | 163 | val.append(1, strptr[d_pos]); |
4192ca66 | 164 | d_pos++; |
29a14b24 | 165 | } |
4192ca66 | 166 | |
4192ca66 BH |
167 | if(val.empty()) |
168 | val=d_zone; | |
bca6643b | 169 | else if(!d_zone.empty()) { |
cbf0e7f3 BH |
170 | char last=val[val.size()-1]; |
171 | ||
b0d4fb45 BH |
172 | if(last =='.') |
173 | val.resize(val.size()-1); | |
174 | else if(last != '.' && !isdigit(last)) // don't add zone to IP address | |
cbf0e7f3 BH |
175 | val+="."+d_zone; |
176 | } | |
4192ca66 BH |
177 | } |
178 | ||
06ffdc52 | 179 | void RecordTextReader::xfrBlob(string& val, int) |
8c1c9170 BH |
180 | { |
181 | skipSpaces(); | |
705f31ae | 182 | int pos=(int)d_pos; |
d39704c0 BH |
183 | const char* strptr=d_string.c_str(); |
184 | while(d_pos < d_end && !dns_isspace(strptr[d_pos])) | |
8c1c9170 BH |
185 | d_pos++; |
186 | ||
20133c59 BH |
187 | string tmp; |
188 | tmp.assign(d_string.c_str()+pos, d_string.c_str() + d_pos); | |
189 | val.clear(); | |
190 | B64Decode(tmp, val); | |
8c1c9170 BH |
191 | } |
192 | ||
59a0f653 BH |
193 | |
194 | static inline uint8_t hextodec(uint8_t val) | |
195 | { | |
196 | if(val >= '0' && val<='9') | |
197 | return val-'0'; | |
198 | else if(val >= 'A' && val<='F') | |
199 | return 10+(val-'A'); | |
200 | else if(val >= 'a' && val<='f') | |
201 | return 10+(val-'a'); | |
202 | else | |
203 | throw RecordTextException("Unknown hexadecimal character '"+lexical_cast<string>(val)+"'"); | |
204 | } | |
205 | ||
206 | ||
207 | void HEXDecode(const char* begin, const char* end, string& val) | |
208 | { | |
209 | if((end - begin)%2) | |
210 | throw RecordTextException("Hexadecimal blob with odd number of characters"); | |
211 | ||
705f31ae | 212 | int limit=(int)(end-begin)/2; |
59a0f653 BH |
213 | val.resize(limit); |
214 | for(int n=0; n < limit; ++n) { | |
215 | val[n] = hextodec(begin[2*n])*16 + hextodec(begin[2*n+1]); | |
216 | } | |
217 | } | |
218 | ||
219 | void RecordTextReader::xfrHexBlob(string& val) | |
220 | { | |
221 | skipSpaces(); | |
705f31ae | 222 | int pos=(int)d_pos; |
59a0f653 BH |
223 | while(d_pos < d_end && !dns_isspace(d_string[d_pos])) |
224 | d_pos++; | |
225 | ||
226 | HEXDecode(d_string.c_str()+pos, d_string.c_str() + d_pos, val); | |
227 | } | |
228 | ||
ef6a78d5 | 229 | void RecordTextReader::xfrText(string& val, bool multi) |
4192ca66 | 230 | { |
4192ca66 BH |
231 | val.clear(); |
232 | val.reserve(d_end - d_pos); | |
ef6a78d5 BH |
233 | |
234 | while(d_pos != d_end) { | |
235 | if(!val.empty()) | |
236 | val.append(1, ' '); | |
237 | ||
238 | skipSpaces(); | |
239 | if(d_string[d_pos]!='"') | |
240 | throw RecordTextException("Data field in DNS should start with quote (\") at position "+lexical_cast<string>(d_pos)+" of '"+d_string+"'"); | |
241 | ||
242 | val.append(1, '"'); | |
243 | while(++d_pos < d_end && d_string[d_pos]!='"') { | |
244 | if(d_string[d_pos]=='\\' && d_pos+1!=d_end) { | |
245 | val.append(1, d_string[d_pos++]); | |
246 | } | |
247 | val.append(1, d_string[d_pos]); | |
4192ca66 | 248 | } |
ef6a78d5 BH |
249 | val.append(1,'"'); |
250 | if(d_pos == d_end) | |
251 | throw RecordTextException("Data field in DNS should end on a quote (\") in '"+d_string+"'"); | |
252 | d_pos++; | |
253 | if(!multi) | |
254 | break; | |
4192ca66 | 255 | } |
4192ca66 BH |
256 | } |
257 | ||
20133c59 BH |
258 | void RecordTextReader::xfrType(uint16_t& val) |
259 | { | |
260 | skipSpaces(); | |
705f31ae | 261 | int pos=(int)d_pos; |
ec486449 | 262 | while(d_pos < d_end && !dns_isspace(d_string[d_pos])) |
20133c59 BH |
263 | d_pos++; |
264 | ||
265 | string tmp; | |
266 | tmp.assign(d_string.c_str()+pos, d_string.c_str() + d_pos); | |
267 | ||
268 | val=DNSRecordContent::TypeToNumber(tmp); | |
269 | } | |
4192ca66 | 270 | |
8c1c9170 | 271 | |
4192ca66 BH |
272 | void RecordTextReader::skipSpaces() |
273 | { | |
d39704c0 BH |
274 | const char* strptr = d_string.c_str(); |
275 | while(d_pos < d_end && dns_isspace(strptr[d_pos])) | |
4192ca66 | 276 | d_pos++; |
4192ca66 BH |
277 | if(d_pos == d_end) |
278 | throw RecordTextException("missing field at the end of record content '"+d_string+"'"); | |
279 | } | |
280 | ||
281 | ||
282 | RecordTextWriter::RecordTextWriter(string& str) : d_string(str) | |
283 | { | |
284 | d_string.clear(); | |
285 | } | |
286 | ||
341930bb | 287 | void RecordTextWriter::xfr48BitInt(const uint64_t& val) |
4192ca66 BH |
288 | { |
289 | if(!d_string.empty()) | |
290 | d_string.append(1,' '); | |
291 | d_string+=lexical_cast<string>(val); | |
292 | } | |
293 | ||
20133c59 | 294 | |
341930bb BH |
295 | void RecordTextWriter::xfr32BitInt(const uint32_t& val) |
296 | { | |
297 | if(!d_string.empty()) | |
298 | d_string.append(1,' '); | |
299 | d_string+=lexical_cast<string>(val); | |
300 | } | |
20133c59 BH |
301 | |
302 | void RecordTextWriter::xfrType(const uint16_t& val) | |
303 | { | |
304 | if(!d_string.empty()) | |
305 | d_string.append(1,' '); | |
306 | d_string+=DNSRecordContent::NumberToType(val); | |
307 | } | |
308 | ||
ec486449 | 309 | // this function is on the fast path for the pdns_recursor |
cbf0e7f3 BH |
310 | void RecordTextWriter::xfrIP(const uint32_t& val) |
311 | { | |
312 | if(!d_string.empty()) | |
313 | d_string.append(1,' '); | |
314 | ||
ec486449 | 315 | char tmp[17]; |
1149fa0b BH |
316 | uint32_t ip=htonl(val); |
317 | uint8_t vals[4]; | |
318 | ||
319 | memcpy(&vals[0], &ip, sizeof(ip)); | |
320 | ||
321 | char *pos=tmp; | |
322 | ||
323 | for(int n=0; n < 4; ++n) { | |
324 | if(vals[n]<10) { | |
325 | *(pos++)=vals[n]+'0'; | |
326 | } else if(vals[n] < 100) { | |
327 | *(pos++)=(vals[n]/10) +'0'; | |
328 | *(pos++)=(vals[n]%10) +'0'; | |
329 | } else { | |
330 | *(pos++)=(vals[n]/100) +'0'; | |
331 | vals[n]%=100; | |
332 | *(pos++)=(vals[n]/10) +'0'; | |
333 | *(pos++)=(vals[n]%10) +'0'; | |
334 | } | |
335 | if(n!=3) | |
336 | *(pos++)='.'; | |
337 | } | |
338 | *pos=0; | |
339 | d_string.append(tmp, pos); | |
cbf0e7f3 BH |
340 | } |
341 | ||
342 | ||
8bf26468 BH |
343 | void RecordTextWriter::xfrTime(const uint32_t& val) |
344 | { | |
345 | if(!d_string.empty()) | |
346 | d_string.append(1,' '); | |
347 | ||
348 | struct tm tm; | |
349 | time_t time=val; // Y2038 bug! | |
d3b729e8 | 350 | #ifndef WIN32 |
8bf26468 | 351 | gmtime_r(&time, &tm); |
d3b729e8 BH |
352 | #else |
353 | struct tm* tmptr; | |
354 | tmptr=gmtime(&time); | |
355 | if(!tmptr) | |
356 | throw RecordTextException("Unable to convert timestamp into pretty printable time"); | |
357 | tm=*tmptr; | |
358 | #endif | |
8bf26468 BH |
359 | |
360 | char tmp[16]; | |
361 | snprintf(tmp,sizeof(tmp)-1, "%04d%02d%02d" "%02d%02d%02d", | |
362 | tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, | |
363 | tm.tm_hour, tm.tm_min, tm.tm_sec); | |
364 | ||
365 | d_string += tmp; | |
366 | } | |
367 | ||
368 | ||
cbf0e7f3 BH |
369 | void RecordTextWriter::xfr16BitInt(const uint16_t& val) |
370 | { | |
371 | xfr32BitInt(val); | |
372 | } | |
373 | ||
8c1c9170 BH |
374 | void RecordTextWriter::xfr8BitInt(const uint8_t& val) |
375 | { | |
376 | xfr32BitInt(val); | |
377 | } | |
378 | ||
cbf0e7f3 | 379 | |
bca6643b | 380 | void RecordTextWriter::xfrLabel(const string& val, bool) |
4192ca66 BH |
381 | { |
382 | if(!d_string.empty()) | |
383 | d_string.append(1,' '); | |
29a14b24 BH |
384 | if(val.find(' ')==string::npos) |
385 | d_string+=val; | |
386 | else { | |
387 | d_string.reserve(d_string.size()+val.size()+3); | |
388 | for(string::size_type pos=0; pos < val.size() ; ++pos) | |
ec486449 | 389 | if(dns_isspace(val[pos])) |
29a14b24 BH |
390 | d_string+="\\ "; |
391 | else if(val[pos]=='\\') | |
392 | d_string.append(1,'\\'); | |
393 | else | |
394 | d_string.append(1,val[pos]); | |
395 | } | |
7738a23f | 396 | // d_string.append(1,'.'); |
4192ca66 BH |
397 | } |
398 | ||
06ffdc52 | 399 | void RecordTextWriter::xfrBlob(const string& val, int) |
8c1c9170 BH |
400 | { |
401 | if(!d_string.empty()) | |
402 | d_string.append(1,' '); | |
403 | ||
404 | d_string+=Base64Encode(val); | |
405 | } | |
406 | ||
59a0f653 BH |
407 | void RecordTextWriter::xfrHexBlob(const string& val) |
408 | { | |
409 | if(!d_string.empty()) | |
410 | d_string.append(1,' '); | |
411 | ||
412 | string::size_type limit=val.size(); | |
413 | char tmp[5]; | |
414 | for(string::size_type n = 0; n < limit; ++n) { | |
415 | snprintf(tmp, sizeof(tmp)-1, "%02x", (unsigned char)val[n]); | |
416 | d_string+=tmp; | |
417 | } | |
418 | } | |
419 | ||
ef6a78d5 | 420 | void RecordTextWriter::xfrText(const string& val, bool multi) |
4192ca66 BH |
421 | { |
422 | if(!d_string.empty()) | |
423 | d_string.append(1,' '); | |
4192ca66 | 424 | |
ef6a78d5 | 425 | d_string.append(val); |
4192ca66 BH |
426 | } |
427 | ||
428 | ||
429 | #ifdef TESTING | |
430 | ||
431 | int main(int argc, char**argv) | |
432 | try | |
433 | { | |
434 | RecordTextReader rtr(argv[1], argv[2]); | |
435 | ||
436 | unsigned int order, pref; | |
437 | string flags, services, regexp, replacement; | |
438 | string mx; | |
439 | ||
440 | rtr.xfrInt(order); | |
441 | rtr.xfrInt(pref); | |
cbf0e7f3 BH |
442 | rtr.xfrText(flags); |
443 | rtr.xfrText(services); | |
444 | rtr.xfrText(regexp); | |
4192ca66 BH |
445 | rtr.xfrLabel(replacement); |
446 | ||
447 | cout<<"order: "<<order<<", pref: "<<pref<<"\n"; | |
448 | cout<<"flags: \""<<flags<<"\", services: \""<<services<<"\", regexp: \""<<regexp<<"\", replacement: "<<replacement<<"\n"; | |
449 | ||
450 | string out; | |
451 | RecordTextWriter rtw(out); | |
452 | ||
453 | rtw.xfrInt(order); | |
454 | rtw.xfrInt(pref); | |
cbf0e7f3 BH |
455 | rtw.xfrText(flags); |
456 | rtw.xfrText(services); | |
457 | rtw.xfrText(regexp); | |
4192ca66 BH |
458 | rtw.xfrLabel(replacement); |
459 | ||
460 | cout<<"Regenerated: '"<<out<<"'\n"; | |
461 | ||
462 | } | |
463 | catch(exception& e) | |
464 | { | |
465 | cerr<<"Fatal: "<<e.what()<<endl; | |
466 | } | |
467 | ||
468 | #endif |