]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/zoneparser-tng.cc
DNSRecords disregarded d_ttl in < comparison. Unsure if this will break things!
[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>
f814d7c8 37
675fa24c 38ZoneParserTNG::ZoneParserTNG(const string& fname, const DNSName& zname, const string& reldir) : d_reldir(reldir),
232f0877 39 d_zonename(zname), d_defaultttl(3600),
80ad238a
AT
40 d_templatecounter(0), d_templatestop(0),
41 d_templatestep(0), d_havedollarttl(false){
bf503cc0
BH
42 stackFile(fname);
43}
44
675fa24c 45ZoneParserTNG::ZoneParserTNG(const vector<string> zonedata, const DNSName& zname):
0f0e73fe 46 d_zonename(zname), d_defaultttl(3600),
80ad238a
AT
47 d_templatecounter(0), d_templatestop(0),
48 d_templatestep(0), d_havedollarttl(false)
0f0e73fe 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
335da0ba 100 unsigned int val=pdns_stou(str);
125e4840
BH
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 164 if(pos + 1 == part.size() || part[pos+1]!='{') { // a trailing $, or not followed by {
335da0ba 165 outpart.append(std::to_string(d_templatecounter));
4957a608
BH
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
d16a2ccf
PL
239DNSName ZoneParserTNG::getZoneName()
240{
241 return d_zonename;
242}
243
d27ea394
BH
244string ZoneParserTNG::getLineOfFile()
245{
0f0e73fe 246 if (d_zonedata.size() > 0)
335da0ba 247 return "on line "+std::to_string(std::distance(d_zonedata.begin(), d_zonedataline))+" of given string";
0f0e73fe 248
335da0ba 249 return "on line "+std::to_string(d_filestates.top().d_lineno)+" of file '"+d_filestates.top().d_filename+"'";
d27ea394 250}
cfe397d5 251
43f40013 252// ODD: this function never fills out the prio field! rest of pdns compensates though
a5a1f447 253bool ZoneParserTNG::get(DNSResourceRecord& rr, std::string* comment)
f814d7c8
BH
254{
255 retry:;
2e83ba09 256 if(!getTemplateLine() && !getLine())
f814d7c8 257 return false;
125e4840 258
df1d406a 259 boost::trim_right_if(d_line, is_any_of(" \r\n\x1a"));
a5a1f447 260 if(comment)
261 comment->clear();
262 if(comment && d_line.find(';') != string::npos)
263 *comment = d_line.substr(d_line.find(';'));
2e83ba09 264 parts_t parts;
125e4840
BH
265 vstringtok(parts, d_line);
266
267 if(parts.empty())
268 goto retry;
269
4d2c97aa
BH
270 if(parts[0].first != parts[0].second && makeString(d_line, parts[0])[0]==';') // line consisting of nothing but comments
271 goto retry;
272
125e4840 273 if(d_line[0]=='$') {
bf503cc0 274 string command=makeString(d_line, parts[0]);
ec6480f3 275 if(pdns_iequals(command,"$TTL") && parts.size() > 1) {
cfe397d5 276 d_defaultttl=makeTTLFromZone(trim_right_copy_if(makeString(d_line, parts[1]), is_any_of(";")));
df1d406a
BH
277 d_havedollarttl=true;
278 }
0f0e73fe 279 else if(pdns_iequals(command,"$INCLUDE") && parts.size() > 1 && d_fromfile) {
da042e6e
BH
280 string fname=unquotify(makeString(d_line, parts[1]));
281 if(!fname.empty() && fname[0]!='/' && !d_reldir.empty())
4957a608 282 fname=d_reldir+"/"+fname;
da042e6e 283 stackFile(fname);
bf503cc0 284 }
ec6480f3 285 else if(pdns_iequals(command, "$ORIGIN") && parts.size() > 1) {
e720f311 286 d_zonename = DNSName(makeString(d_line, parts[1]));
2e83ba09 287 }
ec6480f3 288 else if(pdns_iequals(command, "$GENERATE") && parts.size() > 2) {
bf503cc0
BH
289 // $GENERATE 1-127 $ CNAME $.0
290 string range=makeString(d_line, parts[1]);
2e83ba09
BH
291 d_templatestep=1;
292 d_templatestop=0;
293 sscanf(range.c_str(),"%d-%d/%d", &d_templatecounter, &d_templatestop, &d_templatestep);
294 d_templateline=d_line;
295 parts.pop_front();
296 parts.pop_front();
297
298 d_templateparts=parts;
299 goto retry;
bf503cc0 300 }
125e4840 301 else
d27ea394 302 throw exception("Can't parse zone line '"+d_line+"' "+getLineOfFile());
f814d7c8 303 goto retry;
f814d7c8 304 }
125e4840 305
e720f311 306 bool prevqname=false;
d66269ef 307 string qname = makeString(d_line, parts[0]); // Don't use DNSName here!
e720f311 308 if(isspace(d_line[0])) {
125e4840 309 rr.qname=d_prevqname;
e720f311
KM
310 prevqname=true;
311 }else {
8171ab83 312 rr.qname=DNSName(qname);
125e4840 313 parts.pop_front();
e720f311 314 if(qname.empty() || qname[0]==';')
125e4840
BH
315 goto retry;
316 }
d66269ef 317 if(qname=="@")
125e4840 318 rr.qname=d_zonename;
e720f311 319 else if(!prevqname && !isCanonical(qname))
675fa24c 320 rr.qname += d_zonename;
125e4840
BH
321 d_prevqname=rr.qname;
322
323 if(parts.empty())
d27ea394 324 throw exception("Line with too little parts "+getLineOfFile());
125e4840 325
125e4840 326 string nextpart;
f814d7c8 327
125e4840
BH
328 rr.ttl=d_defaultttl;
329 bool haveTTL=0, haveQTYPE=0;
330 pair<string::size_type, string::size_type> range;
331
332 while(!parts.empty()) {
333 range=parts.front();
334 parts.pop_front();
335 nextpart=makeString(d_line, range);
336 if(nextpart.empty())
337 break;
338
a5a1f447 339 if(nextpart.find(';')!=string::npos) {
125e4840 340 break;
a5a1f447 341 }
125e4840
BH
342
343 // cout<<"Next part: '"<<nextpart<<"'"<<endl;
344
ec6480f3 345 if(pdns_iequals(nextpart, "IN")) {
125e4840
BH
346 // cout<<"Ignoring 'IN'\n";
347 continue;
348 }
cfe397d5 349 if(!haveTTL && !haveQTYPE && isTimeSpec(nextpart)) {
125e4840
BH
350 rr.ttl=makeTTLFromZone(nextpart);
351 haveTTL=true;
352 // cout<<"ttl is probably: "<<rr.ttl<<endl;
353 continue;
354 }
355 if(haveQTYPE)
356 break;
357
358 try {
359 rr.qtype=DNSRecordContent::TypeToNumber(nextpart);
360 // cout<<"Got qtype ("<<rr.qtype.getCode()<<")\n";
361 haveQTYPE=1;
362 continue;
363 }
364 catch(...) {
d27ea394 365 throw runtime_error("Parsing zone content "+getLineOfFile()+
232f0877
CH
366 ": '"+nextpart+
367 "' doesn't look like a qtype, stopping loop");
125e4840
BH
368 }
369 }
370 if(!haveQTYPE)
d27ea394 371 throw exception("Malformed line "+getLineOfFile()+": '"+d_line+"'");
125e4840
BH
372
373 rr.content=d_line.substr(range.first);
374
a09683af 375 chopComment(rr.content);
62c821cd 376 trim(rr.content);
849fde0b 377
b33702d5 378 if(equals(rr.content, "@"))
90c3521b 379 rr.content=d_zonename.toString();
b33702d5 380
9f0076d7
BH
381 if(findAndElide(rr.content, '(')) { // have found a ( and elided it
382 if(!findAndElide(rr.content, ')')) {
383 while(getLine()) {
4957a608
BH
384 trim_right(d_line);
385 chopComment(d_line);
386 trim(d_line);
387
388 bool ended = findAndElide(d_line, ')');
389 rr.content+=" "+d_line;
390 if(ended)
391 break;
125e4840 392 }
125e4840
BH
393 }
394 }
9f0076d7 395
43f40013 396 vector<string> recparts;
125e4840
BH
397 switch(rr.qtype.getCode()) {
398 case QType::MX:
43f40013
BH
399 stringtok(recparts, rr.content);
400 if(recparts.size()==2) {
4c22f7c5 401 if (recparts[1]!=".")
0c0e717c 402 recparts[1] = toCanonic(d_zonename, recparts[1]).toStringRootDot();
43f40013
BH
403 rr.content=recparts[0]+" "+recparts[1];
404 }
405 break;
25e0cd7f 406
6348d406
PD
407 case QType::RP:
408 stringtok(recparts, rr.content);
409 if(recparts.size()==2) {
0c0e717c
PD
410 recparts[0] = toCanonic(d_zonename, recparts[0]).toStringRootDot();
411 recparts[1] = toCanonic(d_zonename, recparts[1]).toStringRootDot();
6348d406
PD
412 rr.content=recparts[0]+" "+recparts[1];
413 }
414 break;
415
25e0cd7f
BH
416 case QType::SRV:
417 stringtok(recparts, rr.content);
418 if(recparts.size()==4) {
3ab6b614 419 if(recparts[3]!=".")
0c0e717c 420 recparts[3] = toCanonic(d_zonename, recparts[3]).toStringRootDot();
25e0cd7f
BH
421 rr.content=recparts[0]+" "+recparts[1]+" "+recparts[2]+" "+recparts[3];
422 }
423 break;
424
43f40013 425
125e4840
BH
426 case QType::NS:
427 case QType::CNAME:
8dee0750 428 case QType::DNAME:
125e4840 429 case QType::PTR:
37f47031 430 case QType::AFSDB:
0c0e717c 431 rr.content=toCanonic(d_zonename, rr.content).toStringRootDot();
125e4840
BH
432 break;
433
434 case QType::SOA:
43f40013
BH
435 stringtok(recparts, rr.content);
436 if(recparts.size() > 1) {
0c0e717c
PD
437 recparts[0]=toCanonic(d_zonename, recparts[0]).toStringRootDot();
438 recparts[1]=toCanonic(d_zonename, recparts[1]).toStringRootDot();
125e4840
BH
439 }
440 rr.content.clear();
43f40013 441 for(string::size_type n = 0; n < recparts.size(); ++n) {
125e4840 442 if(n)
4957a608 443 rr.content.append(1,' ');
c9f935fa 444
82cc6877 445 if(n > 1)
335da0ba 446 rr.content+=std::to_string(makeTTLFromZone(recparts[n]));
82cc6877 447 else
43f40013 448 rr.content+=recparts[n];
c9f935fa 449
df1d406a 450 if(n==6 && !d_havedollarttl)
43f40013 451 d_defaultttl=makeTTLFromZone(recparts[n]);
125e4840 452 }
38e655b6 453 break;
125e4840
BH
454 default:;
455 }
456
457 rr.d_place=DNSResourceRecord::ANSWER;
f814d7c8
BH
458 return true;
459}
460
834942f1 461
f814d7c8
BH
462bool ZoneParserTNG::getLine()
463{
0f0e73fe
MS
464 if (d_zonedata.size() > 0) {
465 if (d_zonedataline != d_zonedata.end()) {
466 d_line = *d_zonedataline;
467 d_zonedataline++;
468 return true;
469 }
470 return false;
471 }
cfe397d5 472 while(!d_filestates.empty()) {
834942f1 473 if(stringfgets(d_filestates.top().d_fp, d_line)) {
cfe397d5 474 d_filestates.top().d_lineno++;
bf503cc0
BH
475 return true;
476 }
cfe397d5
BH
477 fclose(d_filestates.top().d_fp);
478 d_filestates.pop();
f814d7c8
BH
479 }
480 return false;
481}