| 1 | /* |
|---|
| 2 | PowerDNS Versatile Database Driven Nameserver |
|---|
| 3 | Copyright (C) 2005 - 2007 PowerDNS.COM BV |
|---|
| 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 | |
|---|
| 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 |
|---|
| 16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|---|
| 17 | */ |
|---|
| 18 | |
|---|
| 19 | #include "dnsparser.hh" |
|---|
| 20 | #include "sstuff.hh" |
|---|
| 21 | #include "misc.hh" |
|---|
| 22 | #include "dnswriter.hh" |
|---|
| 23 | #include "dnsrecords.hh" |
|---|
| 24 | #include "misc.hh" |
|---|
| 25 | #include <fstream> |
|---|
| 26 | #include "dns.hh" |
|---|
| 27 | #include "zoneparser-tng.hh" |
|---|
| 28 | #include <deque> |
|---|
| 29 | #include <boost/algorithm/string.hpp> |
|---|
| 30 | #include <boost/lexical_cast.hpp> |
|---|
| 31 | |
|---|
| 32 | ZoneParserTNG::ZoneParserTNG(const string& fname, const string& zname, const string& reldir) : d_reldir(reldir), d_zonename(zname), d_defaultttl(3600) |
|---|
| 33 | { |
|---|
| 34 | d_zonename = toCanonic("", d_zonename); |
|---|
| 35 | stackFile(fname); |
|---|
| 36 | } |
|---|
| 37 | |
|---|
| 38 | void ZoneParserTNG::stackFile(const std::string& fname) |
|---|
| 39 | { |
|---|
| 40 | FILE *fp=fopen(fname.c_str(), "r"); |
|---|
| 41 | if(!fp) |
|---|
| 42 | throw runtime_error("Unable to open file '"+fname+"': "+stringerror()); |
|---|
| 43 | d_fps.push(fp); |
|---|
| 44 | } |
|---|
| 45 | |
|---|
| 46 | ZoneParserTNG::~ZoneParserTNG() |
|---|
| 47 | { |
|---|
| 48 | while(!d_fps.empty()) { |
|---|
| 49 | fclose(d_fps.top()); |
|---|
| 50 | d_fps.pop(); |
|---|
| 51 | } |
|---|
| 52 | } |
|---|
| 53 | |
|---|
| 54 | static string makeString(const string& line, const pair<string::size_type, string::size_type>& range) |
|---|
| 55 | { |
|---|
| 56 | return string(line.c_str() + range.first, range.second - range.first); |
|---|
| 57 | } |
|---|
| 58 | |
|---|
| 59 | static unsigned int makeTTLFromZone(const string& str) |
|---|
| 60 | { |
|---|
| 61 | if(str.empty()) |
|---|
| 62 | return 0; |
|---|
| 63 | |
|---|
| 64 | unsigned int val=atoi(str.c_str()); |
|---|
| 65 | char lc=toupper(str[str.length()-1]); |
|---|
| 66 | if(!isdigit(lc)) |
|---|
| 67 | switch(lc) { |
|---|
| 68 | case 'H': |
|---|
| 69 | val*=3600; |
|---|
| 70 | break; |
|---|
| 71 | case 'D': |
|---|
| 72 | val*=3600*24; |
|---|
| 73 | break; |
|---|
| 74 | case 'W': |
|---|
| 75 | val*=3600*24*7; |
|---|
| 76 | break; |
|---|
| 77 | case 'M': |
|---|
| 78 | val*=3600*24*7*4; |
|---|
| 79 | break; |
|---|
| 80 | case 'Y': // ? :-) |
|---|
| 81 | val*=3600*24*365; |
|---|
| 82 | break; |
|---|
| 83 | default: |
|---|
| 84 | throw ZoneParserTNG::exception("Unable to parse time specification '"+str+"'"); |
|---|
| 85 | } |
|---|
| 86 | return val; |
|---|
| 87 | } |
|---|
| 88 | |
|---|
| 89 | bool ZoneParserTNG::getTemplateLine() |
|---|
| 90 | { |
|---|
| 91 | if(d_templateparts.empty() || d_templatecounter > d_templatestop) // no template, or done with |
|---|
| 92 | return false; |
|---|
| 93 | |
|---|
| 94 | string retline; |
|---|
| 95 | for(parts_t::const_iterator iter = d_templateparts.begin() ; iter != d_templateparts.end(); ++iter) { |
|---|
| 96 | if(iter != d_templateparts.begin()) |
|---|
| 97 | retline+=" "; |
|---|
| 98 | |
|---|
| 99 | string part=makeString(d_templateline, *iter); |
|---|
| 100 | |
|---|
| 101 | /* a part can contain a 'naked' $, an escaped $ (\$), or ${offset,width,radix}, with width defaulting to 0, |
|---|
| 102 | and radix beging 'd', 'o', 'x' or 'X', defaulting to 'd'. |
|---|
| 103 | |
|---|
| 104 | The width is zero-padded, so if the counter is at 1, the offset is 15, with is 3, and the radix is 'x', |
|---|
| 105 | output will be '010', from the input of ${15,3,x} |
|---|
| 106 | */ |
|---|
| 107 | |
|---|
| 108 | string outpart; |
|---|
| 109 | outpart.reserve(part.size()+5); |
|---|
| 110 | bool inescape=false; |
|---|
| 111 | |
|---|
| 112 | for(string::size_type pos = 0; pos < part.size() ; ++pos) { |
|---|
| 113 | char c=part[pos]; |
|---|
| 114 | if(inescape) { |
|---|
| 115 | outpart.append(1, c); |
|---|
| 116 | inescape=false; |
|---|
| 117 | continue; |
|---|
| 118 | } |
|---|
| 119 | |
|---|
| 120 | if(part[pos]=='\\') { |
|---|
| 121 | inescape=true; |
|---|
| 122 | continue; |
|---|
| 123 | } |
|---|
| 124 | if(c=='$') { |
|---|
| 125 | if(pos + 1 == part.size() || part[pos+1]!='{') { // a trailing $, or not followed by { |
|---|
| 126 | outpart.append(lexical_cast<string>(d_templatecounter)); |
|---|
| 127 | continue; |
|---|
| 128 | } |
|---|
| 129 | |
|---|
| 130 | // need to deal with { case |
|---|
| 131 | |
|---|
| 132 | pos+=2; |
|---|
| 133 | string::size_type startPos=pos; |
|---|
| 134 | for(; pos < part.size() && part[pos]!='}' ; ++pos) |
|---|
| 135 | ; |
|---|
| 136 | |
|---|
| 137 | if(pos == part.size()) // partial spec |
|---|
| 138 | break; |
|---|
| 139 | |
|---|
| 140 | // we are on the '}' |
|---|
| 141 | |
|---|
| 142 | string spec(part.c_str() + startPos, part.c_str() + pos); |
|---|
| 143 | int offset=0, width=0; |
|---|
| 144 | char radix='d'; |
|---|
| 145 | sscanf(spec.c_str(), "%d,%d,%c", &offset, &width, &radix); // parse format specifier |
|---|
| 146 | |
|---|
| 147 | char format[12]; |
|---|
| 148 | snprintf(format, sizeof(format) - 1, "%%0%d%c", width, radix); // make into printf-style format |
|---|
| 149 | |
|---|
| 150 | char tmp[80]; |
|---|
| 151 | snprintf(tmp, sizeof(tmp)-1, format, d_templatecounter + offset); // and do the actual printing |
|---|
| 152 | outpart+=tmp; |
|---|
| 153 | } |
|---|
| 154 | else |
|---|
| 155 | outpart.append(1, c); |
|---|
| 156 | } |
|---|
| 157 | retline+=outpart; |
|---|
| 158 | } |
|---|
| 159 | d_templatecounter+=d_templatestep; |
|---|
| 160 | |
|---|
| 161 | d_line = retline; |
|---|
| 162 | return true; |
|---|
| 163 | } |
|---|
| 164 | |
|---|
| 165 | bool ZoneParserTNG::get(DNSResourceRecord& rr) |
|---|
| 166 | { |
|---|
| 167 | retry:; |
|---|
| 168 | if(!getTemplateLine() && !getLine()) |
|---|
| 169 | return false; |
|---|
| 170 | |
|---|
| 171 | chomp(d_line, " \r\n\x1a"); |
|---|
| 172 | |
|---|
| 173 | parts_t parts; |
|---|
| 174 | vstringtok(parts, d_line); |
|---|
| 175 | |
|---|
| 176 | if(parts.empty()) |
|---|
| 177 | goto retry; |
|---|
| 178 | |
|---|
| 179 | if(d_line[0]=='$') { |
|---|
| 180 | string command=makeString(d_line, parts[0]); |
|---|
| 181 | if(command=="$TTL" && parts.size() > 1) |
|---|
| 182 | d_defaultttl=makeTTLFromZone(makeString(d_line,parts[1])); |
|---|
| 183 | else if(iequals(command,"$INCLUDE") && parts.size() > 1) { |
|---|
| 184 | string fname=unquotify(makeString(d_line, parts[1])); |
|---|
| 185 | if(!fname.empty() && fname[0]!='/' && !d_reldir.empty()) |
|---|
| 186 | fname=d_reldir+"/"+fname; |
|---|
| 187 | stackFile(fname); |
|---|
| 188 | } |
|---|
| 189 | else if(iequals(command, "$ORIGIN") && parts.size() > 1) { |
|---|
| 190 | d_zonename = toCanonic("", makeString(d_line, parts[1])); |
|---|
| 191 | } |
|---|
| 192 | else if(iequals(command, "$GENERATE") && parts.size() > 2) { |
|---|
| 193 | // $GENERATE 1-127 $ CNAME $.0 |
|---|
| 194 | string range=makeString(d_line, parts[1]); |
|---|
| 195 | d_templatestep=1; |
|---|
| 196 | d_templatestop=0; |
|---|
| 197 | sscanf(range.c_str(),"%d-%d/%d", &d_templatecounter, &d_templatestop, &d_templatestep); |
|---|
| 198 | d_templateline=d_line; |
|---|
| 199 | parts.pop_front(); |
|---|
| 200 | parts.pop_front(); |
|---|
| 201 | |
|---|
| 202 | d_templateparts=parts; |
|---|
| 203 | goto retry; |
|---|
| 204 | } |
|---|
| 205 | else |
|---|
| 206 | throw exception("Can't parse zone line '"+d_line+"'"); |
|---|
| 207 | goto retry; |
|---|
| 208 | } |
|---|
| 209 | |
|---|
| 210 | if(isspace(d_line[0])) |
|---|
| 211 | rr.qname=d_prevqname; |
|---|
| 212 | else { |
|---|
| 213 | rr.qname=makeString(d_line, parts[0]); |
|---|
| 214 | parts.pop_front(); |
|---|
| 215 | if(rr.qname.empty() || rr.qname[0]==';') |
|---|
| 216 | goto retry; |
|---|
| 217 | } |
|---|
| 218 | if(rr.qname=="@") |
|---|
| 219 | rr.qname=d_zonename; |
|---|
| 220 | else if(!isCanonical(rr.qname)) { |
|---|
| 221 | rr.qname.append(1,'.'); |
|---|
| 222 | rr.qname.append(d_zonename); |
|---|
| 223 | } |
|---|
| 224 | d_prevqname=rr.qname; |
|---|
| 225 | |
|---|
| 226 | if(parts.empty()) |
|---|
| 227 | throw exception("Line with too little parts"); |
|---|
| 228 | |
|---|
| 229 | string nextpart; |
|---|
| 230 | |
|---|
| 231 | rr.ttl=d_defaultttl; |
|---|
| 232 | bool haveTTL=0, haveQTYPE=0; |
|---|
| 233 | pair<string::size_type, string::size_type> range; |
|---|
| 234 | |
|---|
| 235 | while(!parts.empty()) { |
|---|
| 236 | range=parts.front(); |
|---|
| 237 | parts.pop_front(); |
|---|
| 238 | nextpart=makeString(d_line, range); |
|---|
| 239 | if(nextpart.empty()) |
|---|
| 240 | break; |
|---|
| 241 | |
|---|
| 242 | if(nextpart.find(';')!=string::npos) |
|---|
| 243 | break; |
|---|
| 244 | |
|---|
| 245 | // cout<<"Next part: '"<<nextpart<<"'"<<endl; |
|---|
| 246 | |
|---|
| 247 | if(!Utility::strcasecmp(nextpart.c_str(), "IN")) { |
|---|
| 248 | // cout<<"Ignoring 'IN'\n"; |
|---|
| 249 | continue; |
|---|
| 250 | } |
|---|
| 251 | if(!haveTTL && !haveQTYPE && all(nextpart, is_digit())) { |
|---|
| 252 | rr.ttl=makeTTLFromZone(nextpart); |
|---|
| 253 | haveTTL=true; |
|---|
| 254 | // cout<<"ttl is probably: "<<rr.ttl<<endl; |
|---|
| 255 | continue; |
|---|
| 256 | } |
|---|
| 257 | if(haveQTYPE) |
|---|
| 258 | break; |
|---|
| 259 | |
|---|
| 260 | try { |
|---|
| 261 | rr.qtype=DNSRecordContent::TypeToNumber(nextpart); |
|---|
| 262 | // cout<<"Got qtype ("<<rr.qtype.getCode()<<")\n"; |
|---|
| 263 | haveQTYPE=1; |
|---|
| 264 | continue; |
|---|
| 265 | } |
|---|
| 266 | catch(...) { |
|---|
| 267 | cerr<<"Oops, this doesn't look like a qtype, stopping loop\n"; |
|---|
| 268 | break; |
|---|
| 269 | } |
|---|
| 270 | } |
|---|
| 271 | if(!haveQTYPE) |
|---|
| 272 | throw exception("Malformed line '"+d_line+"'"); |
|---|
| 273 | |
|---|
| 274 | rr.content=d_line.substr(range.first); |
|---|
| 275 | |
|---|
| 276 | string::size_type pos=rr.content.rfind(';'); |
|---|
| 277 | if(pos!=string::npos) |
|---|
| 278 | rr.content.resize(pos); |
|---|
| 279 | |
|---|
| 280 | if(rr.qtype.getCode()!=QType::TXT && (pos=rr.content.find('('))!=string::npos) { |
|---|
| 281 | rr.content.resize(pos); // chop off ( |
|---|
| 282 | trim(rr.content); |
|---|
| 283 | while(getLine()) { |
|---|
| 284 | chomp(d_line,"\r\n "); |
|---|
| 285 | pos=d_line.rfind(';'); |
|---|
| 286 | if(pos!=string::npos) |
|---|
| 287 | d_line.resize(pos); |
|---|
| 288 | |
|---|
| 289 | trim(d_line); |
|---|
| 290 | |
|---|
| 291 | pos=d_line.find(')'); |
|---|
| 292 | if(pos!=string::npos) { |
|---|
| 293 | d_line.resize(pos); |
|---|
| 294 | trim(d_line); |
|---|
| 295 | rr.content+=" "+d_line; |
|---|
| 296 | break; |
|---|
| 297 | } |
|---|
| 298 | rr.content+=" "+d_line; |
|---|
| 299 | } |
|---|
| 300 | } |
|---|
| 301 | vector<string> soaparts; |
|---|
| 302 | switch(rr.qtype.getCode()) { |
|---|
| 303 | case QType::MX: |
|---|
| 304 | case QType::NS: |
|---|
| 305 | case QType::CNAME: |
|---|
| 306 | case QType::PTR: |
|---|
| 307 | case QType::SRV: |
|---|
| 308 | case QType::AFSDB: |
|---|
| 309 | rr.content=toCanonic(d_zonename, rr.content); |
|---|
| 310 | break; |
|---|
| 311 | |
|---|
| 312 | case QType::SOA: |
|---|
| 313 | stringtok(soaparts, rr.content); |
|---|
| 314 | if(soaparts.size() > 1) { |
|---|
| 315 | soaparts[0]=toCanonic(d_zonename, soaparts[0]); |
|---|
| 316 | soaparts[1]=toCanonic(d_zonename, soaparts[1]); |
|---|
| 317 | } |
|---|
| 318 | rr.content.clear(); |
|---|
| 319 | for(string::size_type n = 0; n < soaparts.size(); ++n) { |
|---|
| 320 | if(n) |
|---|
| 321 | rr.content.append(1,' '); |
|---|
| 322 | if(n > 1) |
|---|
| 323 | rr.content+=lexical_cast<string>(makeTTLFromZone(soaparts[n])); |
|---|
| 324 | else |
|---|
| 325 | rr.content+=soaparts[n]; |
|---|
| 326 | } |
|---|
| 327 | default:; |
|---|
| 328 | } |
|---|
| 329 | |
|---|
| 330 | rr.d_place=DNSResourceRecord::ANSWER; |
|---|
| 331 | return true; |
|---|
| 332 | } |
|---|
| 333 | |
|---|
| 334 | bool ZoneParserTNG::getLine() |
|---|
| 335 | { |
|---|
| 336 | while(!d_fps.empty()) { |
|---|
| 337 | char buffer[1024]; |
|---|
| 338 | if(fgets(buffer, 1024, d_fps.top())) { |
|---|
| 339 | d_line=buffer; |
|---|
| 340 | return true; |
|---|
| 341 | } |
|---|
| 342 | fclose(d_fps.top()); |
|---|
| 343 | d_fps.pop(); |
|---|
| 344 | } |
|---|
| 345 | return false; |
|---|
| 346 | } |
|---|
| 347 | |
|---|
| 348 | |
|---|
| 349 | #if 0 |
|---|
| 350 | int main(int argc, char** argv) |
|---|
| 351 | try |
|---|
| 352 | { |
|---|
| 353 | reportAllTypes(); |
|---|
| 354 | ZoneParserTNG zpt(argv[1]); |
|---|
| 355 | DNSResourceRecord rr; |
|---|
| 356 | while(zpt.get(rr)) { |
|---|
| 357 | } |
|---|
| 358 | |
|---|
| 359 | |
|---|
| 360 | } |
|---|
| 361 | catch(...) |
|---|
| 362 | {} |
|---|
| 363 | #endif |
|---|