]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/zoneparser-tng.cc
packetcache: use DNSName.toDNSString() as key
[thirdparty/pdns.git] / pdns / zoneparser-tng.cc
CommitLineData
f814d7c8
BH
1/*
2 PowerDNS Versatile Database Driven Nameserver
4d2c97aa 3 Copyright (C) 2005 - 2008 PowerDNS.COM BV
f814d7c8
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
7 as 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
f814d7c8
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
f814d7c8
BH
21*/
22
870a0fe4
AT
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
f814d7c8
BH
26#include "dnsparser.hh"
27#include "sstuff.hh"
28#include "misc.hh"
29#include "dnswriter.hh"
30#include "dnsrecords.hh"
f814d7c8
BH
31#include "misc.hh"
32#include <fstream>
33#include "dns.hh"
34#include "zoneparser-tng.hh"
125e4840
BH
35#include <deque>
36#include <boost/algorithm/string.hpp>
37#include <boost/lexical_cast.hpp>
f814d7c8 38
675fa24c 39ZoneParserTNG::ZoneParserTNG(const string& fname, const DNSName& zname, const string& reldir) : d_reldir(reldir),
232f0877 40 d_zonename(zname), d_defaultttl(3600),
2dfd6b65 41 d_havedollarttl(false)
f814d7c8 42{
bf503cc0
BH
43 stackFile(fname);
44}
45
675fa24c 46ZoneParserTNG::ZoneParserTNG(const vector<string> zonedata, const DNSName& zname):
0f0e73fe
MS
47 d_zonename(zname), d_defaultttl(3600),
48 d_havedollarttl(false)
49{
0f0e73fe
MS
50 d_zonedata = zonedata;
51 d_zonedataline = d_zonedata.begin();
52 d_fromfile = false;
53}
54
bf503cc0
BH
55void ZoneParserTNG::stackFile(const std::string& fname)
56{
57 FILE *fp=fopen(fname.c_str(), "r");
58 if(!fp)
f814d7c8 59 throw runtime_error("Unable to open file '"+fname+"': "+stringerror());
cfe397d5
BH
60
61 filestate fs(fp, fname);
62 d_filestates.push(fs);
0f0e73fe 63 d_fromfile = true;
f814d7c8
BH
64}
65
66ZoneParserTNG::~ZoneParserTNG()
67{
cfe397d5
BH
68 while(!d_filestates.empty()) {
69 fclose(d_filestates.top().d_fp);
70 d_filestates.pop();
bf503cc0 71 }
f814d7c8
BH
72}
73
125e4840
BH
74static string makeString(const string& line, const pair<string::size_type, string::size_type>& range)
75{
76 return string(line.c_str() + range.first, range.second - range.first);
77}
78
cfe397d5
BH
79static bool isTimeSpec(const string& nextpart)
80{
81 if(nextpart.empty())
82 return false;
83 for(string::const_iterator iter = nextpart.begin(); iter != nextpart.end(); ++iter) {
84 if(isdigit(*iter))
85 continue;
86 if(iter+1 != nextpart.end())
87 return false;
88 char c=tolower(*iter);
2326ec3f 89 return (c=='s' || c=='m' || c=='h' || c=='d' || c=='w' || c=='y');
cfe397d5
BH
90 }
91 return true;
92}
93
94
95unsigned int ZoneParserTNG::makeTTLFromZone(const string& str)
125e4840
BH
96{
97 if(str.empty())
98 return 0;
99
100 unsigned int val=atoi(str.c_str());
101 char lc=toupper(str[str.length()-1]);
102 if(!isdigit(lc))
103 switch(lc) {
2326ec3f
BH
104 case 'S':
105 break;
38e655b6
BH
106 case 'M':
107 val*=60; // minutes, not months!
108 break;
125e4840
BH
109 case 'H':
110 val*=3600;
111 break;
112 case 'D':
113 val*=3600*24;
114 break;
115 case 'W':
116 val*=3600*24*7;
117 break;
125e4840
BH
118 case 'Y': // ? :-)
119 val*=3600*24*365;
120 break;
cfe397d5 121
125e4840 122 default:
3fed7dbd 123 throw PDNSException("Unable to parse time specification '"+str+"' "+getLineOfFile());
125e4840
BH
124 }
125 return val;
126}
127
2e83ba09
BH
128bool ZoneParserTNG::getTemplateLine()
129{
b8c3ea84 130 if(d_templateparts.empty() || d_templatecounter > d_templatestop) // no template, or done with
2e83ba09
BH
131 return false;
132
133 string retline;
134 for(parts_t::const_iterator iter = d_templateparts.begin() ; iter != d_templateparts.end(); ++iter) {
135 if(iter != d_templateparts.begin())
136 retline+=" ";
137
138 string part=makeString(d_templateline, *iter);
139
140 /* a part can contain a 'naked' $, an escaped $ (\$), or ${offset,width,radix}, with width defaulting to 0,
141 and radix beging 'd', 'o', 'x' or 'X', defaulting to 'd'.
142
143 The width is zero-padded, so if the counter is at 1, the offset is 15, with is 3, and the radix is 'x',
144 output will be '010', from the input of ${15,3,x}
145 */
146
147 string outpart;
148 outpart.reserve(part.size()+5);
149 bool inescape=false;
150
151 for(string::size_type pos = 0; pos < part.size() ; ++pos) {
152 char c=part[pos];
153 if(inescape) {
4957a608
BH
154 outpart.append(1, c);
155 inescape=false;
156 continue;
2e83ba09 157 }
4957a608 158
2e83ba09 159 if(part[pos]=='\\') {
4957a608
BH
160 inescape=true;
161 continue;
2e83ba09
BH
162 }
163 if(c=='$') {
4957a608
BH
164 if(pos + 1 == part.size() || part[pos+1]!='{') { // a trailing $, or not followed by {
165 outpart.append(lexical_cast<string>(d_templatecounter));
166 continue;
167 }
168
169 // need to deal with { case
170
171 pos+=2;
172 string::size_type startPos=pos;
173 for(; pos < part.size() && part[pos]!='}' ; ++pos)
174 ;
175
176 if(pos == part.size()) // partial spec
177 break;
178
179 // we are on the '}'
180
181 string spec(part.c_str() + startPos, part.c_str() + pos);
182 int offset=0, width=0;
183 char radix='d';
184 sscanf(spec.c_str(), "%d,%d,%c", &offset, &width, &radix); // parse format specifier
185
186 char format[12];
187 snprintf(format, sizeof(format) - 1, "%%0%d%c", width, radix); // make into printf-style format
188
189 char tmp[80];
190 snprintf(tmp, sizeof(tmp)-1, format, d_templatecounter + offset); // and do the actual printing
191 outpart+=tmp;
2e83ba09
BH
192 }
193 else
4957a608 194 outpart.append(1, c);
2e83ba09
BH
195 }
196 retline+=outpart;
197 }
198 d_templatecounter+=d_templatestep;
199
200 d_line = retline;
201 return true;
202}
203
a09683af
BH
204void chopComment(string& line)
205{
206 string::size_type pos, len = line.length();
207 bool inQuote=false;
208 for(pos = 0 ; pos < len; ++pos) {
209 if(line[pos]=='\\')
210 pos++;
211 else if(line[pos]=='"')
212 inQuote=!inQuote;
213 else if(line[pos]==';' && !inQuote)
214 break;
215 }
216 if(pos != len)
217 line.resize(pos);
218}
219
9f0076d7
BH
220bool findAndElide(string& line, char c)
221{
222 string::size_type pos, len = line.length();
223 bool inQuote=false;
224 for(pos = 0 ; pos < len; ++pos) {
225 if(line[pos]=='\\')
226 pos++;
227 else if(line[pos]=='"')
228 inQuote=!inQuote;
229 else if(line[pos]==c && !inQuote)
230 break;
231 }
232 if(pos != len) {
233 line.erase(pos, 1);
234 return true;
235 }
236 return false;
237}
238
d27ea394
BH
239string ZoneParserTNG::getLineOfFile()
240{
0f0e73fe
MS
241 if (d_zonedata.size() > 0)
242 return "on line "+lexical_cast<string>(std::distance(d_zonedata.begin(), d_zonedataline))+" of given string";
243
d27ea394
BH
244 return "on line "+lexical_cast<string>(d_filestates.top().d_lineno)+" of file '"+d_filestates.top().d_filename+"'";
245}
cfe397d5 246
43f40013 247// ODD: this function never fills out the prio field! rest of pdns compensates though
a5a1f447 248bool ZoneParserTNG::get(DNSResourceRecord& rr, std::string* comment)
f814d7c8
BH
249{
250 retry:;
2e83ba09 251 if(!getTemplateLine() && !getLine())
f814d7c8 252 return false;
125e4840 253
df1d406a 254 boost::trim_right_if(d_line, is_any_of(" \r\n\x1a"));
a5a1f447 255 if(comment)
256 comment->clear();
257 if(comment && d_line.find(';') != string::npos)
258 *comment = d_line.substr(d_line.find(';'));
2e83ba09 259 parts_t parts;
125e4840
BH
260 vstringtok(parts, d_line);
261
262 if(parts.empty())
263 goto retry;
264
4d2c97aa
BH
265 if(parts[0].first != parts[0].second && makeString(d_line, parts[0])[0]==';') // line consisting of nothing but comments
266 goto retry;
267
125e4840 268 if(d_line[0]=='$') {
bf503cc0 269 string command=makeString(d_line, parts[0]);
ec6480f3 270 if(pdns_iequals(command,"$TTL") && parts.size() > 1) {
cfe397d5 271 d_defaultttl=makeTTLFromZone(trim_right_copy_if(makeString(d_line, parts[1]), is_any_of(";")));
df1d406a
BH
272 d_havedollarttl=true;
273 }
0f0e73fe 274 else if(pdns_iequals(command,"$INCLUDE") && parts.size() > 1 && d_fromfile) {
da042e6e
BH
275 string fname=unquotify(makeString(d_line, parts[1]));
276 if(!fname.empty() && fname[0]!='/' && !d_reldir.empty())
4957a608 277 fname=d_reldir+"/"+fname;
da042e6e 278 stackFile(fname);
bf503cc0 279 }
ec6480f3 280 else if(pdns_iequals(command, "$ORIGIN") && parts.size() > 1) {
675fa24c 281 d_zonename = DNSName(toCanonic(string(""), makeString(d_line, parts[1])));
2e83ba09 282 }
ec6480f3 283 else if(pdns_iequals(command, "$GENERATE") && parts.size() > 2) {
bf503cc0
BH
284 // $GENERATE 1-127 $ CNAME $.0
285 string range=makeString(d_line, parts[1]);
2e83ba09
BH
286 d_templatestep=1;
287 d_templatestop=0;
288 sscanf(range.c_str(),"%d-%d/%d", &d_templatecounter, &d_templatestop, &d_templatestep);
289 d_templateline=d_line;
290 parts.pop_front();
291 parts.pop_front();
292
293 d_templateparts=parts;
294 goto retry;
bf503cc0 295 }
125e4840 296 else
d27ea394 297 throw exception("Can't parse zone line '"+d_line+"' "+getLineOfFile());
f814d7c8 298 goto retry;
f814d7c8 299 }
125e4840 300
d66269ef
KM
301 string qname = makeString(d_line, parts[0]); // Don't use DNSName here!
302 if(isspace(d_line[0]))
125e4840
BH
303 rr.qname=d_prevqname;
304 else {
d66269ef 305 rr.qname=qname;
125e4840 306 parts.pop_front();
675fa24c 307 if(!rr.qname.countLabels() || rr.qname.toString()[0]==';')
125e4840
BH
308 goto retry;
309 }
d66269ef 310 if(qname=="@")
125e4840 311 rr.qname=d_zonename;
d66269ef 312 else if(!isCanonical(qname))
675fa24c 313 rr.qname += d_zonename;
125e4840
BH
314 d_prevqname=rr.qname;
315
316 if(parts.empty())
d27ea394 317 throw exception("Line with too little parts "+getLineOfFile());
125e4840 318
125e4840 319 string nextpart;
f814d7c8 320
125e4840
BH
321 rr.ttl=d_defaultttl;
322 bool haveTTL=0, haveQTYPE=0;
323 pair<string::size_type, string::size_type> range;
324
325 while(!parts.empty()) {
326 range=parts.front();
327 parts.pop_front();
328 nextpart=makeString(d_line, range);
329 if(nextpart.empty())
330 break;
331
a5a1f447 332 if(nextpart.find(';')!=string::npos) {
125e4840 333 break;
a5a1f447 334 }
125e4840
BH
335
336 // cout<<"Next part: '"<<nextpart<<"'"<<endl;
337
ec6480f3 338 if(pdns_iequals(nextpart, "IN")) {
125e4840
BH
339 // cout<<"Ignoring 'IN'\n";
340 continue;
341 }
cfe397d5 342 if(!haveTTL && !haveQTYPE && isTimeSpec(nextpart)) {
125e4840
BH
343 rr.ttl=makeTTLFromZone(nextpart);
344 haveTTL=true;
345 // cout<<"ttl is probably: "<<rr.ttl<<endl;
346 continue;
347 }
348 if(haveQTYPE)
349 break;
350
351 try {
352 rr.qtype=DNSRecordContent::TypeToNumber(nextpart);
353 // cout<<"Got qtype ("<<rr.qtype.getCode()<<")\n";
354 haveQTYPE=1;
355 continue;
356 }
357 catch(...) {
d27ea394 358 throw runtime_error("Parsing zone content "+getLineOfFile()+
232f0877
CH
359 ": '"+nextpart+
360 "' doesn't look like a qtype, stopping loop");
125e4840
BH
361 }
362 }
363 if(!haveQTYPE)
d27ea394 364 throw exception("Malformed line "+getLineOfFile()+": '"+d_line+"'");
125e4840
BH
365
366 rr.content=d_line.substr(range.first);
367
a09683af 368 chopComment(rr.content);
62c821cd 369 trim(rr.content);
849fde0b 370
b33702d5 371 if(equals(rr.content, "@"))
675fa24c 372 rr.content=d_zonename.toString();
b33702d5 373
9f0076d7
BH
374 if(findAndElide(rr.content, '(')) { // have found a ( and elided it
375 if(!findAndElide(rr.content, ')')) {
376 while(getLine()) {
4957a608
BH
377 trim_right(d_line);
378 chopComment(d_line);
379 trim(d_line);
380
381 bool ended = findAndElide(d_line, ')');
382 rr.content+=" "+d_line;
383 if(ended)
384 break;
125e4840 385 }
125e4840
BH
386 }
387 }
9f0076d7 388
43f40013 389 vector<string> recparts;
125e4840
BH
390 switch(rr.qtype.getCode()) {
391 case QType::MX:
43f40013
BH
392 stringtok(recparts, rr.content);
393 if(recparts.size()==2) {
4c22f7c5 394 if (recparts[1]!=".")
262536fa 395 recparts[1] = stripDot(toCanonic(d_zonename.toString(), recparts[1]));
43f40013
BH
396 rr.content=recparts[0]+" "+recparts[1];
397 }
398 break;
25e0cd7f 399
6348d406
PD
400 case QType::RP:
401 stringtok(recparts, rr.content);
402 if(recparts.size()==2) {
262536fa
KM
403 recparts[0] = stripDot(toCanonic(d_zonename.toString(), recparts[0]));
404 recparts[1] = stripDot(toCanonic(d_zonename.toString(), recparts[1]));
6348d406
PD
405 rr.content=recparts[0]+" "+recparts[1];
406 }
407 break;
408
25e0cd7f
BH
409 case QType::SRV:
410 stringtok(recparts, rr.content);
411 if(recparts.size()==4) {
3ab6b614 412 if(recparts[3]!=".")
262536fa 413 recparts[3] = stripDot(toCanonic(d_zonename.toString(), recparts[3]));
25e0cd7f
BH
414 rr.content=recparts[0]+" "+recparts[1]+" "+recparts[2]+" "+recparts[3];
415 }
416 break;
417
43f40013 418
125e4840
BH
419 case QType::NS:
420 case QType::CNAME:
8dee0750 421 case QType::DNAME:
125e4840 422 case QType::PTR:
37f47031 423 case QType::AFSDB:
262536fa 424 rr.content=stripDot(toCanonic(d_zonename.toString(), rr.content));
125e4840
BH
425 break;
426
427 case QType::SOA:
43f40013
BH
428 stringtok(recparts, rr.content);
429 if(recparts.size() > 1) {
262536fa
KM
430 recparts[0]=toCanonic(d_zonename.toString(), recparts[0]);
431 recparts[1]=toCanonic(d_zonename.toString(), recparts[1]);
125e4840
BH
432 }
433 rr.content.clear();
43f40013 434 for(string::size_type n = 0; n < recparts.size(); ++n) {
125e4840 435 if(n)
4957a608 436 rr.content.append(1,' ');
c9f935fa 437
82cc6877 438 if(n > 1)
43f40013 439 rr.content+=lexical_cast<string>(makeTTLFromZone(recparts[n]));
82cc6877 440 else
43f40013 441 rr.content+=recparts[n];
c9f935fa 442
df1d406a 443 if(n==6 && !d_havedollarttl)
43f40013 444 d_defaultttl=makeTTLFromZone(recparts[n]);
125e4840 445 }
38e655b6 446 break;
125e4840
BH
447 default:;
448 }
449
450 rr.d_place=DNSResourceRecord::ANSWER;
f814d7c8
BH
451 return true;
452}
453
834942f1 454
f814d7c8
BH
455bool ZoneParserTNG::getLine()
456{
0f0e73fe
MS
457 if (d_zonedata.size() > 0) {
458 if (d_zonedataline != d_zonedata.end()) {
459 d_line = *d_zonedataline;
460 d_zonedataline++;
461 return true;
462 }
463 return false;
464 }
cfe397d5 465 while(!d_filestates.empty()) {
834942f1 466 if(stringfgets(d_filestates.top().d_fp, d_line)) {
cfe397d5 467 d_filestates.top().d_lineno++;
bf503cc0
BH
468 return true;
469 }
cfe397d5
BH
470 fclose(d_filestates.top().d_fp);
471 d_filestates.pop();
f814d7c8
BH
472 }
473 return false;
474}