]>
Commit | Line | Data |
---|---|---|
f814d7c8 | 1 | /* |
6edbf68a PL |
2 | * This file is part of PowerDNS or dnsdist. |
3 | * Copyright -- PowerDNS.COM B.V. and its contributors | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of version 2 of the GNU General Public License as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * In addition, for the avoidance of any doubt, permission is granted to | |
10 | * link this program with OpenSSL and to (re)distribute the binaries | |
11 | * produced as the result of such linking. | |
12 | * | |
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 | |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
21 | */ | |
870a0fe4 AT |
22 | #ifdef HAVE_CONFIG_H |
23 | #include "config.h" | |
24 | #endif | |
f814d7c8 BH |
25 | #include "dnsparser.hh" |
26 | #include "sstuff.hh" | |
27 | #include "misc.hh" | |
28 | #include "dnswriter.hh" | |
29 | #include "dnsrecords.hh" | |
f814d7c8 BH |
30 | #include <fstream> |
31 | #include "dns.hh" | |
32 | #include "zoneparser-tng.hh" | |
125e4840 BH |
33 | #include <deque> |
34 | #include <boost/algorithm/string.hpp> | |
e0e48a7a | 35 | #include <system_error> |
1817fef6 | 36 | #include <cinttypes> |
e3fc3ebd | 37 | #include <sys/stat.h> |
f814d7c8 | 38 | |
e3fc3ebd | 39 | const static string g_INstr("IN"); |
3108d502 | 40 | |
1290c1d2 | 41 | ZoneParserTNG::ZoneParserTNG(const string& fname, DNSName zname, string reldir, bool upgradeContent): |
1e05b07c | 42 | d_reldir(std::move(reldir)), d_zonename(std::move(zname)), d_defaultttl(3600), |
b4db4fe4 CH |
43 | d_templatecounter(0), d_templatestop(0), d_templatestep(0), |
44 | d_havedollarttl(false), d_fromfile(true), d_upgradeContent(upgradeContent) | |
45 | { | |
bf503cc0 BH |
46 | stackFile(fname); |
47 | } | |
48 | ||
1290c1d2 RP |
49 | ZoneParserTNG::ZoneParserTNG(const vector<string>& zonedata, DNSName zname, bool upgradeContent): |
50 | d_zonename(std::move(zname)), d_zonedata(zonedata), d_defaultttl(3600), | |
8a70e507 | 51 | d_templatecounter(0), d_templatestop(0), d_templatestep(0), |
b4db4fe4 | 52 | d_havedollarttl(false), d_fromfile(false), d_upgradeContent(upgradeContent) |
0f0e73fe | 53 | { |
0f0e73fe | 54 | d_zonedataline = d_zonedata.begin(); |
0f0e73fe MS |
55 | } |
56 | ||
bf503cc0 BH |
57 | void ZoneParserTNG::stackFile(const std::string& fname) |
58 | { | |
e3fc3ebd OM |
59 | if (d_filestates.size() >= d_maxIncludes) { |
60 | std::error_code ec (0, std::generic_category()); | |
61 | throw std::system_error(ec, "Include limit reached"); | |
62 | } | |
63 | int fd = open(fname.c_str(), O_RDONLY, 0); | |
64 | if (fd == -1) { | |
65 | int err = errno; | |
66 | std::error_code ec (err, std::generic_category()); | |
67 | throw std::system_error(ec, "Unable to open file '" + fname + "': " + stringerror(err)); | |
68 | } | |
2ce8165a AV |
69 | |
70 | struct stat st = {}; | |
e3fc3ebd OM |
71 | if (fstat(fd, &st) == -1) { |
72 | int err = errno; | |
73 | close(fd); | |
74 | std::error_code ec (err, std::generic_category()); | |
75 | throw std::system_error(ec, "Unable to stat file '" + fname + "': " + stringerror(err)); | |
76 | } | |
77 | if (!S_ISREG(st.st_mode)) { | |
78 | close(fd); | |
79 | std::error_code ec (0, std::generic_category()); | |
80 | throw std::system_error(ec, "File '" + fname + "': not a regular file"); | |
81 | } | |
82 | FILE *fp = fdopen(fd, "r"); | |
3700e0dd | 83 | if (fp == nullptr) { |
e3fc3ebd OM |
84 | int err = errno; |
85 | close(fd); | |
86 | std::error_code ec (err, std::generic_category()); | |
87 | throw std::system_error(ec, "Unable to open file '" + fname + "': " + stringerror(err)); | |
e0e48a7a | 88 | } |
cfe397d5 BH |
89 | |
90 | filestate fs(fp, fname); | |
91 | d_filestates.push(fs); | |
0f0e73fe | 92 | d_fromfile = true; |
f814d7c8 BH |
93 | } |
94 | ||
95 | ZoneParserTNG::~ZoneParserTNG() | |
96 | { | |
cfe397d5 BH |
97 | while(!d_filestates.empty()) { |
98 | fclose(d_filestates.top().d_fp); | |
99 | d_filestates.pop(); | |
bf503cc0 | 100 | } |
f814d7c8 BH |
101 | } |
102 | ||
125e4840 BH |
103 | static string makeString(const string& line, const pair<string::size_type, string::size_type>& range) |
104 | { | |
105 | return string(line.c_str() + range.first, range.second - range.first); | |
106 | } | |
107 | ||
cfe397d5 BH |
108 | static bool isTimeSpec(const string& nextpart) |
109 | { | |
eb914147 | 110 | if (nextpart.empty()) { |
cfe397d5 | 111 | return false; |
eb914147 AV |
112 | } |
113 | ||
78924ce1 | 114 | for (auto iter = nextpart.begin(); iter != nextpart.end(); ++iter) { |
eb914147 AV |
115 | auto current = static_cast<unsigned char>(*iter); |
116 | if (isdigit(current) != 0) { | |
cfe397d5 | 117 | continue; |
eb914147 AV |
118 | } |
119 | ||
120 | if (iter + 1 != nextpart.end()) { | |
cfe397d5 | 121 | return false; |
eb914147 AV |
122 | } |
123 | ||
124 | char c = static_cast<char>(tolower(current)); | |
2326ec3f | 125 | return (c=='s' || c=='m' || c=='h' || c=='d' || c=='w' || c=='y'); |
cfe397d5 BH |
126 | } |
127 | return true; | |
128 | } | |
129 | ||
130 | ||
131 | unsigned int ZoneParserTNG::makeTTLFromZone(const string& str) | |
125e4840 BH |
132 | { |
133 | if(str.empty()) | |
134 | return 0; | |
135 | ||
2ce8165a | 136 | unsigned int val = 0; |
97ce13be | 137 | try { |
a0383aad | 138 | pdns::checked_stoi_into(val, str); |
97ce13be RG |
139 | } |
140 | catch (const std::out_of_range& oor) { | |
141 | throw PDNSException("Unable to parse time specification '"+str+"' "+getLineOfFile()); | |
142 | } | |
143 | ||
3108d502 | 144 | char lc=dns_tolower(str[str.length()-1]); |
125e4840 BH |
145 | if(!isdigit(lc)) |
146 | switch(lc) { | |
3108d502 | 147 | case 's': |
2326ec3f | 148 | break; |
3108d502 | 149 | case 'm': |
38e655b6 BH |
150 | val*=60; // minutes, not months! |
151 | break; | |
3108d502 | 152 | case 'h': |
125e4840 BH |
153 | val*=3600; |
154 | break; | |
3108d502 | 155 | case 'd': |
125e4840 BH |
156 | val*=3600*24; |
157 | break; | |
3108d502 | 158 | case 'w': |
125e4840 BH |
159 | val*=3600*24*7; |
160 | break; | |
3108d502 | 161 | case 'y': // ? :-) |
125e4840 BH |
162 | val*=3600*24*365; |
163 | break; | |
cfe397d5 | 164 | |
125e4840 | 165 | default: |
3fed7dbd | 166 | throw PDNSException("Unable to parse time specification '"+str+"' "+getLineOfFile()); |
125e4840 BH |
167 | } |
168 | return val; | |
169 | } | |
170 | ||
2e83ba09 BH |
171 | bool ZoneParserTNG::getTemplateLine() |
172 | { | |
9da7b2be RG |
173 | if (d_templateparts.empty() || d_templateCounterWrapped || d_templatecounter > d_templatestop) { |
174 | // no template, or done with | |
2e83ba09 | 175 | return false; |
9da7b2be | 176 | } |
2e83ba09 BH |
177 | |
178 | string retline; | |
78924ce1 AV |
179 | for (auto iter = d_templateparts.begin() ; iter != d_templateparts.end(); ++iter) { |
180 | if(iter != d_templateparts.begin()) { | |
181 | retline += " "; | |
182 | } | |
2e83ba09 BH |
183 | |
184 | string part=makeString(d_templateline, *iter); | |
1e05b07c FM |
185 | |
186 | /* a part can contain a 'naked' $, an escaped $ (\$), or ${offset,width,radix}, with width defaulting to 0, | |
1817fef6 | 187 | and radix being 'd', 'o', 'x' or 'X', defaulting to 'd' (so ${offset} is valid). |
2e83ba09 | 188 | |
1817fef6 | 189 | The width is zero-padded, so if the counter is at 1, the offset is 15, width is 3, and the radix is 'x', |
2e83ba09 BH |
190 | output will be '010', from the input of ${15,3,x} |
191 | */ | |
192 | ||
193 | string outpart; | |
194 | outpart.reserve(part.size()+5); | |
195 | bool inescape=false; | |
196 | ||
197 | for(string::size_type pos = 0; pos < part.size() ; ++pos) { | |
198 | char c=part[pos]; | |
199 | if(inescape) { | |
4957a608 BH |
200 | outpart.append(1, c); |
201 | inescape=false; | |
202 | continue; | |
2e83ba09 | 203 | } |
1e05b07c | 204 | |
2e83ba09 | 205 | if(part[pos]=='\\') { |
4957a608 BH |
206 | inescape=true; |
207 | continue; | |
2e83ba09 BH |
208 | } |
209 | if(c=='$') { | |
4957a608 | 210 | if(pos + 1 == part.size() || part[pos+1]!='{') { // a trailing $, or not followed by { |
335da0ba | 211 | outpart.append(std::to_string(d_templatecounter)); |
4957a608 BH |
212 | continue; |
213 | } | |
1e05b07c FM |
214 | |
215 | // need to deal with { case | |
216 | ||
4957a608 BH |
217 | pos+=2; |
218 | string::size_type startPos=pos; | |
219 | for(; pos < part.size() && part[pos]!='}' ; ++pos) | |
220 | ; | |
1e05b07c | 221 | |
4957a608 BH |
222 | if(pos == part.size()) // partial spec |
223 | break; | |
224 | ||
225 | // we are on the '}' | |
226 | ||
227 | string spec(part.c_str() + startPos, part.c_str() + pos); | |
228 | int offset=0, width=0; | |
229 | char radix='d'; | |
1817fef6 RG |
230 | // parse format specifier |
231 | int extracted = sscanf(spec.c_str(), "%d,%d,%c", &offset, &width, &radix); | |
232 | if (extracted < 1) { | |
233 | throw PDNSException("Unable to parse offset, width and radix for $GENERATE's lhs from '"+spec+"' "+getLineOfFile()); | |
234 | } | |
2651d2f1 RG |
235 | if (width < 0) { |
236 | throw PDNSException("Invalid width ("+std::to_string(width)+") for $GENERATE's lhs from '"+spec+"' "+getLineOfFile()); | |
237 | } | |
4957a608 | 238 | |
4957a608 | 239 | char tmp[80]; |
ea50e189 RG |
240 | |
241 | /* a width larger than the output buffer does not make any sense */ | |
242 | width = std::min(width, static_cast<int>(sizeof(tmp))); | |
243 | ||
4338e69f OM |
244 | switch (radix) { |
245 | case 'o': | |
246 | snprintf(tmp, sizeof(tmp), "%0*o", width, d_templatecounter + offset); | |
247 | break; | |
248 | case 'x': | |
249 | snprintf(tmp, sizeof(tmp), "%0*x", width, d_templatecounter + offset); | |
250 | break; | |
251 | case 'X': | |
252 | snprintf(tmp, sizeof(tmp), "%0*X", width, d_templatecounter + offset); | |
253 | break; | |
254 | case 'd': | |
255 | default: | |
256 | snprintf(tmp, sizeof(tmp), "%0*d", width, d_templatecounter + offset); | |
257 | break; | |
258 | } | |
4957a608 | 259 | outpart+=tmp; |
2e83ba09 BH |
260 | } |
261 | else | |
4957a608 | 262 | outpart.append(1, c); |
2e83ba09 BH |
263 | } |
264 | retline+=outpart; | |
265 | } | |
9da7b2be RG |
266 | |
267 | if ((d_templatestop - d_templatecounter) < d_templatestep) { | |
268 | d_templateCounterWrapped = true; | |
269 | } | |
270 | else { | |
271 | d_templatecounter += d_templatestep; | |
272 | } | |
2e83ba09 BH |
273 | |
274 | d_line = retline; | |
275 | return true; | |
276 | } | |
277 | ||
050e6877 | 278 | static void chopComment(string& line) |
a09683af | 279 | { |
3108d502 | 280 | if(line.find(';')==string::npos) |
281 | return; | |
2ce8165a AV |
282 | |
283 | string::size_type pos = 0; | |
284 | auto len = line.length(); | |
285 | bool inQuote = false; | |
286 | for(; pos < len; ++pos) { | |
1e05b07c | 287 | if(line[pos]=='\\') |
a09683af | 288 | pos++; |
1e05b07c | 289 | else if(line[pos]=='"') |
a09683af BH |
290 | inQuote=!inQuote; |
291 | else if(line[pos]==';' && !inQuote) | |
292 | break; | |
293 | } | |
294 | if(pos != len) | |
295 | line.resize(pos); | |
296 | } | |
297 | ||
050e6877 | 298 | static bool findAndElide(string& line, char c) |
9f0076d7 BH |
299 | { |
300 | string::size_type pos, len = line.length(); | |
301 | bool inQuote=false; | |
302 | for(pos = 0 ; pos < len; ++pos) { | |
1e05b07c | 303 | if(line[pos]=='\\') |
9f0076d7 | 304 | pos++; |
1e05b07c | 305 | else if(line[pos]=='"') |
9f0076d7 BH |
306 | inQuote=!inQuote; |
307 | else if(line[pos]==c && !inQuote) | |
308 | break; | |
309 | } | |
310 | if(pos != len) { | |
311 | line.erase(pos, 1); | |
312 | return true; | |
313 | } | |
314 | return false; | |
315 | } | |
316 | ||
d16a2ccf PL |
317 | DNSName ZoneParserTNG::getZoneName() |
318 | { | |
319 | return d_zonename; | |
320 | } | |
321 | ||
d27ea394 BH |
322 | string ZoneParserTNG::getLineOfFile() |
323 | { | |
1d830152 | 324 | if (!d_zonedata.empty()) |
335da0ba | 325 | return "on line "+std::to_string(std::distance(d_zonedata.begin(), d_zonedataline))+" of given string"; |
0f0e73fe | 326 | |
a1239f65 RG |
327 | if (d_filestates.empty()) |
328 | return ""; | |
329 | ||
335da0ba | 330 | return "on line "+std::to_string(d_filestates.top().d_lineno)+" of file '"+d_filestates.top().d_filename+"'"; |
d27ea394 | 331 | } |
cfe397d5 | 332 | |
c91effc8 | 333 | pair<string,int> ZoneParserTNG::getLineNumAndFile() |
334 | { | |
9923fc22 PD |
335 | if (d_filestates.empty()) |
336 | return {"", 0}; | |
337 | else | |
338 | return {d_filestates.top().d_filename, d_filestates.top().d_lineno}; | |
c91effc8 | 339 | } |
340 | ||
4950b140 | 341 | bool ZoneParserTNG::get(DNSResourceRecord& rr, std::string* comment) |
f814d7c8 BH |
342 | { |
343 | retry:; | |
2e83ba09 | 344 | if(!getTemplateLine() && !getLine()) |
f814d7c8 | 345 | return false; |
125e4840 | 346 | |
dc593046 | 347 | boost::trim_right_if(d_line, boost::is_any_of(" \t\r\n\x1a")); |
a5a1f447 | 348 | if(comment) |
349 | comment->clear(); | |
350 | if(comment && d_line.find(';') != string::npos) | |
351 | *comment = d_line.substr(d_line.find(';')); | |
125e4840 | 352 | |
9bbcf03a RG |
353 | d_parts.clear(); |
354 | vstringtok(d_parts, d_line); | |
355 | ||
356 | if(d_parts.empty()) | |
125e4840 BH |
357 | goto retry; |
358 | ||
9bbcf03a | 359 | if(d_parts[0].first != d_parts[0].second && d_line[d_parts[0].first]==';') // line consisting of nothing but comments |
4d2c97aa BH |
360 | goto retry; |
361 | ||
1e05b07c | 362 | if(d_line[0]=='$') { |
9bbcf03a RG |
363 | string command=makeString(d_line, d_parts[0]); |
364 | if(pdns_iequals(command,"$TTL") && d_parts.size() > 1) { | |
dc593046 | 365 | d_defaultttl=makeTTLFromZone(trim_right_copy_if(makeString(d_line, d_parts[1]), boost::is_any_of(";"))); |
df1d406a BH |
366 | d_havedollarttl=true; |
367 | } | |
9bbcf03a RG |
368 | else if(pdns_iequals(command,"$INCLUDE") && d_parts.size() > 1 && d_fromfile) { |
369 | string fname=unquotify(makeString(d_line, d_parts[1])); | |
da042e6e | 370 | if(!fname.empty() && fname[0]!='/' && !d_reldir.empty()) |
4957a608 | 371 | fname=d_reldir+"/"+fname; |
da042e6e | 372 | stackFile(fname); |
bf503cc0 | 373 | } |
9bbcf03a RG |
374 | else if(pdns_iequals(command, "$ORIGIN") && d_parts.size() > 1) { |
375 | d_zonename = DNSName(makeString(d_line, d_parts[1])); | |
2e83ba09 | 376 | } |
9bbcf03a | 377 | else if(pdns_iequals(command, "$GENERATE") && d_parts.size() > 2) { |
91a862bf RG |
378 | if (!d_generateEnabled) { |
379 | throw exception("$GENERATE is not allowed in this zone"); | |
380 | } | |
bf503cc0 | 381 | // $GENERATE 1-127 $ CNAME $.0 |
1817fef6 RG |
382 | // The range part can be one of two forms: start-stop or start-stop/step. If the first |
383 | // form is used, then step is set to 1. start, stop and step must be positive | |
384 | // integers between 0 and (2^31)-1. start must not be larger than stop. | |
9da7b2be RG |
385 | // http://www.zytrax.com/books/dns/ch8/generate.html |
386 | string range = makeString(d_line, d_parts.at(1)); | |
387 | ||
388 | auto splitOnOnlyOneSeparator = [range](const std::string& input, std::vector<std::string>& output, char separator) { | |
389 | output.clear(); | |
390 | ||
391 | auto pos = input.find(separator); | |
392 | if (pos == string::npos) { | |
393 | output.emplace_back(input); | |
394 | return; | |
395 | } | |
396 | if (pos == (input.size()-1)) { | |
397 | /* ends on a separator!? */ | |
398 | throw std::runtime_error("Invalid range from $GENERATE parameters '" + range + "'"); | |
399 | } | |
400 | auto next = input.find(separator, pos + 1); | |
401 | if (next != string::npos) { | |
402 | /* more than one separator */ | |
403 | throw std::runtime_error("Invalid range from $GENERATE parameters '" + range + "'"); | |
404 | } | |
405 | output.emplace_back(input.substr(0, pos)); | |
406 | output.emplace_back(input.substr(pos + 1)); | |
407 | }; | |
408 | ||
409 | std::vector<std::string> fields; | |
410 | splitOnOnlyOneSeparator(range, fields, '-'); | |
411 | if (fields.size() != 2) { | |
412 | throw std::runtime_error("Invalid range from $GENERATE parameters '" + range + "'"); | |
413 | } | |
414 | ||
415 | auto parseValue = [](const std::string& parameters, const std::string& name, const std::string& str, uint32_t& value) { | |
416 | try { | |
417 | auto got = std::stoul(str); | |
418 | if (got > std::numeric_limits<uint32_t>::max()) { | |
419 | throw std::runtime_error("Invalid " + name + " value in $GENERATE parameters '" + parameters + "'"); | |
420 | } | |
421 | value = static_cast<uint32_t>(got); | |
422 | } | |
423 | catch (const std::exception& e) { | |
424 | throw std::runtime_error("Invalid " + name + " value in $GENERATE parameters '" + parameters + "': " + e.what()); | |
425 | } | |
426 | }; | |
427 | ||
428 | parseValue(range, "start", fields.at(0), d_templatecounter); | |
429 | ||
430 | /* now the remaining part(s) */ | |
431 | range = std::move(fields.at(1)); | |
432 | splitOnOnlyOneSeparator(range, fields, '/'); | |
433 | ||
434 | if (fields.size() > 2) { | |
435 | throw std::runtime_error("Invalid range from $GENERATE parameters '" + range + "'"); | |
1817fef6 | 436 | } |
9da7b2be RG |
437 | |
438 | parseValue(range, "stop", fields.at(0), d_templatestop); | |
439 | ||
440 | if (fields.size() == 2) { | |
441 | parseValue(range, "step", fields.at(1), d_templatestep); | |
442 | } | |
443 | else { | |
444 | d_templatestep = 1; | |
1817fef6 | 445 | } |
9da7b2be | 446 | |
775a673a OM |
447 | if (d_templatestep < 1 || |
448 | d_templatestop < d_templatecounter) { | |
9da7b2be | 449 | throw std::runtime_error("Invalid $GENERATE parameters"); |
775a673a | 450 | } |
ba3d53d1 RG |
451 | if (d_maxGenerateSteps != 0) { |
452 | size_t numberOfSteps = (d_templatestop - d_templatecounter) / d_templatestep; | |
453 | if (numberOfSteps > d_maxGenerateSteps) { | |
9da7b2be | 454 | throw std::runtime_error("The number of $GENERATE steps (" + std::to_string(numberOfSteps) + ") is too high, the maximum is set to " + std::to_string(d_maxGenerateSteps)); |
ba3d53d1 RG |
455 | } |
456 | } | |
9da7b2be RG |
457 | |
458 | d_templateline = d_line; | |
9bbcf03a RG |
459 | d_parts.pop_front(); |
460 | d_parts.pop_front(); | |
2e83ba09 | 461 | |
9da7b2be RG |
462 | d_templateparts = d_parts; |
463 | d_templateCounterWrapped = false; | |
464 | ||
2e83ba09 | 465 | goto retry; |
bf503cc0 | 466 | } |
125e4840 | 467 | else |
d27ea394 | 468 | throw exception("Can't parse zone line '"+d_line+"' "+getLineOfFile()); |
f814d7c8 | 469 | goto retry; |
f814d7c8 | 470 | } |
125e4840 | 471 | |
e720f311 | 472 | bool prevqname=false; |
9bbcf03a | 473 | string qname = makeString(d_line, d_parts[0]); // Don't use DNSName here! |
3108d502 | 474 | if(dns_isspace(d_line[0])) { |
125e4840 | 475 | rr.qname=d_prevqname; |
e720f311 KM |
476 | prevqname=true; |
477 | }else { | |
1e05b07c | 478 | rr.qname=DNSName(qname); |
9bbcf03a | 479 | d_parts.pop_front(); |
e720f311 | 480 | if(qname.empty() || qname[0]==';') |
125e4840 BH |
481 | goto retry; |
482 | } | |
d66269ef | 483 | if(qname=="@") |
125e4840 | 484 | rr.qname=d_zonename; |
e720f311 | 485 | else if(!prevqname && !isCanonical(qname)) |
675fa24c | 486 | rr.qname += d_zonename; |
125e4840 BH |
487 | d_prevqname=rr.qname; |
488 | ||
9bbcf03a | 489 | if(d_parts.empty()) |
d27ea394 | 490 | throw exception("Line with too little parts "+getLineOfFile()); |
125e4840 | 491 | |
125e4840 | 492 | string nextpart; |
1e05b07c | 493 | |
125e4840 | 494 | rr.ttl=d_defaultttl; |
b4db4fe4 CH |
495 | bool haveTTL{false}, haveQTYPE{false}; |
496 | string qtypeString; | |
125e4840 BH |
497 | pair<string::size_type, string::size_type> range; |
498 | ||
9bbcf03a RG |
499 | while(!d_parts.empty()) { |
500 | range=d_parts.front(); | |
501 | d_parts.pop_front(); | |
125e4840 BH |
502 | nextpart=makeString(d_line, range); |
503 | if(nextpart.empty()) | |
504 | break; | |
505 | ||
a5a1f447 | 506 | if(nextpart.find(';')!=string::npos) { |
125e4840 | 507 | break; |
a5a1f447 | 508 | } |
125e4840 BH |
509 | |
510 | // cout<<"Next part: '"<<nextpart<<"'"<<endl; | |
3108d502 | 511 | |
512 | if(pdns_iequals(nextpart, g_INstr)) { | |
125e4840 BH |
513 | // cout<<"Ignoring 'IN'\n"; |
514 | continue; | |
515 | } | |
cfe397d5 | 516 | if(!haveTTL && !haveQTYPE && isTimeSpec(nextpart)) { |
125e4840 | 517 | rr.ttl=makeTTLFromZone(nextpart); |
7fd6c67e | 518 | if(!d_havedollarttl) |
519 | d_defaultttl = rr.ttl; | |
125e4840 BH |
520 | haveTTL=true; |
521 | // cout<<"ttl is probably: "<<rr.ttl<<endl; | |
522 | continue; | |
523 | } | |
1e05b07c | 524 | if(haveQTYPE) |
125e4840 BH |
525 | break; |
526 | ||
527 | try { | |
b4db4fe4 | 528 | rr.qtype = DNSRecordContent::TypeToNumber(nextpart); |
125e4840 | 529 | // cout<<"Got qtype ("<<rr.qtype.getCode()<<")\n"; |
b4db4fe4 CH |
530 | qtypeString = nextpart; |
531 | haveQTYPE = true; | |
125e4840 BH |
532 | continue; |
533 | } | |
534 | catch(...) { | |
d27ea394 | 535 | throw runtime_error("Parsing zone content "+getLineOfFile()+ |
232f0877 CH |
536 | ": '"+nextpart+ |
537 | "' doesn't look like a qtype, stopping loop"); | |
125e4840 BH |
538 | } |
539 | } | |
1e05b07c | 540 | if(!haveQTYPE) |
d27ea394 | 541 | throw exception("Malformed line "+getLineOfFile()+": '"+d_line+"'"); |
125e4840 | 542 | |
3108d502 | 543 | // rr.content=d_line.substr(range.first); |
544 | rr.content.assign(d_line, range.first, string::npos); | |
a09683af | 545 | chopComment(rr.content); |
dc593046 | 546 | trim_if(rr.content, boost::is_any_of(" \r\n\t\x1a")); |
849fde0b | 547 | |
3108d502 | 548 | if(rr.content.size()==1 && rr.content[0]=='@') |
90c3521b | 549 | rr.content=d_zonename.toString(); |
b33702d5 | 550 | |
9f0076d7 BH |
551 | if(findAndElide(rr.content, '(')) { // have found a ( and elided it |
552 | if(!findAndElide(rr.content, ')')) { | |
553 | while(getLine()) { | |
dc593046 | 554 | boost::trim_right(d_line); |
4957a608 | 555 | chopComment(d_line); |
dc593046 | 556 | boost::trim(d_line); |
1e05b07c | 557 | |
4957a608 BH |
558 | bool ended = findAndElide(d_line, ')'); |
559 | rr.content+=" "+d_line; | |
560 | if(ended) | |
561 | break; | |
125e4840 | 562 | } |
125e4840 BH |
563 | } |
564 | } | |
dc593046 | 565 | boost::trim_if(rr.content, boost::is_any_of(" \r\n\t\x1a")); |
9f0076d7 | 566 | |
b4db4fe4 CH |
567 | if (d_upgradeContent && DNSRecordContent::isUnknownType(qtypeString)) { |
568 | rr.content = DNSRecordContent::upgradeContent(rr.qname, rr.qtype, rr.content); | |
569 | } | |
570 | ||
43f40013 | 571 | vector<string> recparts; |
125e4840 BH |
572 | switch(rr.qtype.getCode()) { |
573 | case QType::MX: | |
43f40013 BH |
574 | stringtok(recparts, rr.content); |
575 | if(recparts.size()==2) { | |
1293f91e PL |
576 | if (recparts[1]!=".") { |
577 | try { | |
578 | recparts[1] = toCanonic(d_zonename, recparts[1]).toStringRootDot(); | |
579 | } catch (std::exception &e) { | |
d5fcd583 | 580 | throw PDNSException("Error in record '" + rr.qname.toLogString() + " " + rr.qtype.toString() + "': " + e.what()); |
1293f91e PL |
581 | } |
582 | } | |
43f40013 BH |
583 | rr.content=recparts[0]+" "+recparts[1]; |
584 | } | |
585 | break; | |
1e05b07c | 586 | |
6348d406 PD |
587 | case QType::RP: |
588 | stringtok(recparts, rr.content); | |
589 | if(recparts.size()==2) { | |
0c0e717c PD |
590 | recparts[0] = toCanonic(d_zonename, recparts[0]).toStringRootDot(); |
591 | recparts[1] = toCanonic(d_zonename, recparts[1]).toStringRootDot(); | |
6348d406 PD |
592 | rr.content=recparts[0]+" "+recparts[1]; |
593 | } | |
594 | break; | |
595 | ||
25e0cd7f BH |
596 | case QType::SRV: |
597 | stringtok(recparts, rr.content); | |
598 | if(recparts.size()==4) { | |
1293f91e PL |
599 | if(recparts[3]!=".") { |
600 | try { | |
601 | recparts[3] = toCanonic(d_zonename, recparts[3]).toStringRootDot(); | |
602 | } catch (std::exception &e) { | |
d5fcd583 | 603 | throw PDNSException("Error in record '" + rr.qname.toLogString() + " " + rr.qtype.toString() + "': " + e.what()); |
1293f91e PL |
604 | } |
605 | } | |
25e0cd7f BH |
606 | rr.content=recparts[0]+" "+recparts[1]+" "+recparts[2]+" "+recparts[3]; |
607 | } | |
608 | break; | |
1e05b07c FM |
609 | |
610 | ||
125e4840 BH |
611 | case QType::NS: |
612 | case QType::CNAME: | |
8dee0750 | 613 | case QType::DNAME: |
125e4840 | 614 | case QType::PTR: |
1293f91e PL |
615 | try { |
616 | rr.content = toCanonic(d_zonename, rr.content).toStringRootDot(); | |
617 | } catch (std::exception &e) { | |
d5fcd583 | 618 | throw PDNSException("Error in record '" + rr.qname.toLogString() + " " + rr.qtype.toString() + "': " + e.what()); |
1293f91e | 619 | } |
125e4840 | 620 | break; |
319d6e47 | 621 | case QType::AFSDB: |
9f067a15 JJ |
622 | stringtok(recparts, rr.content); |
623 | if(recparts.size() == 2) { | |
624 | try { | |
319d6e47 | 625 | recparts[1]=toCanonic(d_zonename, recparts[1]).toStringRootDot(); |
9f067a15 | 626 | } catch (std::exception &e) { |
d5fcd583 | 627 | throw PDNSException("Error in record '" + rr.qname.toLogString() + " " + rr.qtype.toString() + "': " + e.what()); |
319d6e47 | 628 | } |
9f067a15 | 629 | } else { |
86f1af1c | 630 | throw PDNSException("AFSDB record for "+rr.qname.toLogString()+" invalid"); |
9f067a15 JJ |
631 | } |
632 | rr.content.clear(); | |
633 | for(string::size_type n = 0; n < recparts.size(); ++n) { | |
eb914147 | 634 | if (n != 0) { |
9f067a15 | 635 | rr.content.append(1,' '); |
eb914147 | 636 | } |
125e4840 | 637 | |
9f067a15 | 638 | rr.content+=recparts[n]; |
319d6e47 JJ |
639 | } |
640 | break; | |
125e4840 | 641 | case QType::SOA: |
43f40013 | 642 | stringtok(recparts, rr.content); |
0c306c7c | 643 | if(recparts.size() > 7) |
86f1af1c | 644 | throw PDNSException("SOA record contents for "+rr.qname.toLogString()+" contains too many parts"); |
43f40013 | 645 | if(recparts.size() > 1) { |
562c0b13 PL |
646 | try { |
647 | recparts[0]=toCanonic(d_zonename, recparts[0]).toStringRootDot(); | |
648 | recparts[1]=toCanonic(d_zonename, recparts[1]).toStringRootDot(); | |
1293f91e | 649 | } catch (std::exception &e) { |
d5fcd583 | 650 | throw PDNSException("Error in record '" + rr.qname.toLogString() + " " + rr.qtype.toString() + "': " + e.what()); |
562c0b13 | 651 | } |
125e4840 BH |
652 | } |
653 | rr.content.clear(); | |
43f40013 | 654 | for(string::size_type n = 0; n < recparts.size(); ++n) { |
eb914147 | 655 | if (n != 0) { |
4957a608 | 656 | rr.content.append(1,' '); |
eb914147 | 657 | } |
c9f935fa | 658 | |
82cc6877 | 659 | if(n > 1) |
335da0ba | 660 | rr.content+=std::to_string(makeTTLFromZone(recparts[n])); |
82cc6877 | 661 | else |
43f40013 | 662 | rr.content+=recparts[n]; |
125e4840 | 663 | } |
38e655b6 | 664 | break; |
125e4840 BH |
665 | default:; |
666 | } | |
f814d7c8 BH |
667 | return true; |
668 | } | |
669 | ||
834942f1 | 670 | |
f814d7c8 BH |
671 | bool ZoneParserTNG::getLine() |
672 | { | |
1d830152 | 673 | if (!d_zonedata.empty()) { |
0f0e73fe MS |
674 | if (d_zonedataline != d_zonedata.end()) { |
675 | d_line = *d_zonedataline; | |
cb6655e7 | 676 | ++d_zonedataline; |
0f0e73fe MS |
677 | return true; |
678 | } | |
679 | return false; | |
680 | } | |
cfe397d5 | 681 | while(!d_filestates.empty()) { |
834942f1 | 682 | if(stringfgets(d_filestates.top().d_fp, d_line)) { |
cfe397d5 | 683 | d_filestates.top().d_lineno++; |
bf503cc0 BH |
684 | return true; |
685 | } | |
cfe397d5 BH |
686 | fclose(d_filestates.top().d_fp); |
687 | d_filestates.pop(); | |
f814d7c8 BH |
688 | } |
689 | return false; | |
690 | } |