root/trunk/pdns/pdns/zoneparser-tng.cc @ 979

Revision 979, 8.8 KB (checked in by ahu, 6 years ago)

silence (correct) valgrind warning, plus add ComboAddress? 'toStringWithPort'

Line 
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
32ZoneParserTNG::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
38void 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
46ZoneParserTNG::~ZoneParserTNG()
47{
48  while(!d_fps.empty()) {
49    fclose(d_fps.top());
50    d_fps.pop();
51  }
52}
53
54static 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
59static 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
89bool 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
165bool 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
334bool 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
350int main(int argc, char** argv)
351try
352{
353  reportAllTypes();
354  ZoneParserTNG zpt(argv[1]);
355  DNSResourceRecord rr;
356  while(zpt.get(rr)) {
357  }
358 
359
360}
361catch(...)
362{}
363#endif
Note: See TracBrowser for help on using the browser.