~ubuntu-branches/ubuntu/maverick/pdns/maverick-updates

« back to all changes in this revision

Viewing changes to pdns/backends/bind/zoneparser2.cc

  • Committer: Bazaar Package Importer
  • Author(s): Matthijs Mohlmann, Matthijs Mohlmann, Christoph Haas
  • Date: 2007-04-15 23:23:39 UTC
  • mfrom: (1.1.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20070415232339-5x3scc8gx04e50um
Tags: 2.9.21-1
[ Matthijs Mohlmann ]
* New upstream release. (Closes: #420294)
* Remove meta pdns package.
* Added new sqlite3 backend package.
* Months and minutes where mixed up. (Closes: #406462)
* Case sensitivity in bind backend caused PowerDNS to not serve a certain
  zone. (Closes: #406461)
* Bind backend forgot about zones on a notify. (Closes: #398213)

[ Christoph Haas ]
* Documented incorporated backend bind. (Closes: #415471)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
    PowerDNS Versatile Database Driven Nameserver
3
 
    Copyright (C) 2002 - 2005  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 as 
7
 
    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
 
*/
18
 
 
19
 
#ifdef WIN32
20
 
# pragma warning ( disable: 4786 )
21
 
#endif // WIN32
22
 
 
23
 
#include <stdio.h>
24
 
#include <unistd.h>
25
 
#include <string.h>
26
 
#include <stdlib.h>
27
 
#include <string>
28
 
#include <vector>
29
 
#include <iostream>
30
 
#include <utility>
31
 
#include <ctype.h>
32
 
#include <zlib.h>
33
 
#include <errno.h>
34
 
#include <stack>
35
 
#include "utility.hh"
36
 
#include "misc.hh"
37
 
#include "ahuexception.hh"
38
 
#include "qtype.hh"
39
 
#include <algorithm>
40
 
using namespace std;
41
 
 
42
 
#include "zoneparser.hh"
43
 
 
44
 
extern const char *bind_directory;
45
 
void ZoneParser::setDirectory(const string &dir)
46
 
{
47
 
  d_dir=dir;
48
 
 
49
 
}
50
 
 
51
 
void ZoneParser::parse(const string &fname, const string &origin, unsigned int domain_id)
52
 
{       
53
 
  d_filename=fname.c_str();
54
 
  
55
 
  gzFile zonein;
56
 
  if(fname!="-")
57
 
    zonein=gzopen(fname.c_str(),"r");
58
 
  else
59
 
    zonein=gzdopen(STDIN_FILENO,"r");
60
 
 
61
 
  if(!zonein)
62
 
    throw AhuException("Unable to open zonefile '"+fname+"': "+stringerror());
63
 
 
64
 
  d_origin=origin;
65
 
  
66
 
  char cline[2048];
67
 
  string line;
68
 
  d_lineno=0;
69
 
  vector<Record> rec;
70
 
  stack<gzFile> fds;
71
 
  fds.push(zonein);
72
 
 
73
 
 
74
 
  while(!fds.empty()) {
75
 
    while(gzgets(fds.top(), cline,sizeof(cline)-1)) {
76
 
      line=cline;
77
 
      chomp(line," \x1a\r\n");
78
 
      cutOff(line,";");
79
 
 
80
 
      d_lineno++;
81
 
      if(line.empty())
82
 
        continue;
83
 
 
84
 
      if(line[0]=='$' && (!line.find("$INCLUDE ") || !line.find("$include "))) {
85
 
        vector<string> parts;
86
 
        stringtok(parts,line," \t\n"); 
87
 
        if(parts.size()!=2)
88
 
          throw AhuException("Invalid $INCLUDE statement in zonefile '"+fname+"'");
89
 
        
90
 
        string filename=unquotify(parts[1]);
91
 
        if(filename[0]!='/')
92
 
          filename=d_dir+"/"+filename;
93
 
 
94
 
 
95
 
        gzFile fp=gzopen(filename.c_str(),"r");
96
 
        if(!fp)
97
 
          throw AhuException("Unable to open zonefile '"+filename+"' included from '"+fname+"': "+stringerror());
98
 
        fds.push(fp);
99
 
        continue;
100
 
      }
101
 
      if(eatLine(line,rec))
102
 
        for(vector<Record>::const_iterator i=rec.begin();i!=rec.end();++i)
103
 
          d_callback(domain_id, i->name, i->qtype, i->content, i->ttl, i->prio);
104
 
    }
105
 
 
106
 
    //    if(ferror(fds.top())) {
107
 
    //      fclose(fds.top());
108
 
    //      fds.pop();
109
 
    //      throw AhuException("Error reading from file '"+fname+"': "+stringerror());
110
 
    //    }
111
 
    gzclose(fds.top());
112
 
    fds.pop();
113
 
  }
114
 
}
115
 
 
116
 
void ZoneParser::parse(const string &fname, const string &origin, vector<Record>&records)
117
 
{       
118
 
  d_filename=fname.c_str();
119
 
 
120
 
  gzFile zonein=gzopen(fname.c_str(),"r");
121
 
 
122
 
  if(!zonein)
123
 
    throw AhuException("Unable to open zonefile '"+fname+"': "+stringerror());
124
 
 
125
 
  d_origin=origin;
126
 
  
127
 
  char cline[2048];
128
 
  string line;
129
 
  d_lineno=0;
130
 
  vector<Record> rec;
131
 
  stack<gzFile>fds;
132
 
  fds.push(zonein);
133
 
 
134
 
  while(!fds.empty()) {
135
 
    while(gzgets(fds.top(),cline, sizeof(cline)-1)) {
136
 
      line=cline;
137
 
      chomp(line," \x1a\r\n");
138
 
      cutOff(line,";");
139
 
 
140
 
      d_lineno++;
141
 
      if(line.empty())
142
 
        continue;
143
 
 
144
 
      if(line[0]=='$' && (!line.find("$INCLUDE ") || !line.find("$include "))) {
145
 
        vector<string> parts;
146
 
        stringtok(parts,line," \t\r\n");
147
 
        if(parts.size()!=2)
148
 
          throw AhuException("Invalid $INCLUDE statement in zonefile '"+fname+"'");
149
 
 
150
 
        string filename=unquotify(parts[1]);
151
 
        gzFile fp=gzopen(filename.c_str(),"r");
152
 
        if(!fp)
153
 
          throw AhuException("Unable to open zonefile '"+filename+"' included from '"+fname+"': "+stringerror());
154
 
        fds.push(fp);
155
 
        continue;
156
 
      }
157
 
      if(eatLine(line,rec))
158
 
        for(vector<Record>::const_iterator i=rec.begin();i!=rec.end();++i)
159
 
          records.push_back(*i);
160
 
    }
161
 
 
162
 
    //    if(ferror(fds.top())) {
163
 
    //      fclose(fds.top());
164
 
    //      fds.pop();
165
 
    //      throw AhuException("Error reading from file '"+fname+"': "+stringerror());
166
 
    //    }
167
 
    gzclose(fds.top());
168
 
    fds.pop();
169
 
  }
170
 
}
171
 
 
172
 
 
173
 
void ZoneParser::fillRec(const string &qname, const string &qtype, const string &content, int ttl, int prio, vector<Record>&recs)
174
 
{
175
 
  Record rec;
176
 
  rec.name=qname;
177
 
  rec.qtype=qtype;
178
 
 
179
 
  if(!QType::chartocode(qtype.c_str()))
180
 
    throw AhuException("Unknown qtype '"+qtype+"' on line "+itoa(d_lineno)+" of file '"+d_filename+"'");
181
 
  rec.content=content;
182
 
  rec.ttl=ttl;
183
 
  rec.prio=prio;
184
 
 
185
 
  //  cerr<<"filled with type: "<<rec.qtype<<", "<<QType::chartocode(qtype.c_str())<<": "<<content<<endl;
186
 
  recs.push_back(rec);
187
 
}
188
 
 
189
 
 
190
 
void ZoneParser::cutOff(string &line, const string &delim)
191
 
{
192
 
  string::size_type pos=line.find_first_of(delim);
193
 
  if(pos==string::npos)
194
 
    return;
195
 
  line.resize(pos);
196
 
}
197
 
 
198
 
bool ZoneParser::eatLine(const string& line, vector<Record> &rec)
199
 
{
200
 
  rec.clear();
201
 
  static string tline;
202
 
  static string lastfirstword;
203
 
  string::size_type pos=string::npos;
204
 
 
205
 
  if(tline.empty()) {
206
 
    pos=line.find_first_of("(");
207
 
    if(pos!=string::npos) { // this is a line that continues
208
 
      tline=line.substr(0,pos);
209
 
      return false;
210
 
    }
211
 
    else 
212
 
      tline=line; // complete & boring line
213
 
  }
214
 
  else { // continuation
215
 
    pos=line.find(")");
216
 
    if(pos==string::npos) { // middle part
217
 
      tline.append(line);
218
 
      return false;
219
 
    }
220
 
    else {
221
 
      tline.append(line.substr(0,pos)); // end part, we have a complete line!
222
 
    }
223
 
  }
224
 
  
225
 
  // full & unparenthesised line now in tline!
226
 
  //  cout<<"line: '"<<tline<<"'"<<endl;
227
 
  if(tline.empty() || tline.find_first_not_of(" \t\n")==string::npos) {
228
 
 
229
 
    tline="";
230
 
    return false;
231
 
  }
232
 
 
233
 
  if(isspace(tline[0]))
234
 
    tline=lastfirstword+"\t"+tline;
235
 
 
236
 
  vector<string> parts;
237
 
  stringtok(parts,tline," \t");  // we used to strip " here
238
 
  if(parts[0][0]!='$' && !isspace(parts[0][0]))
239
 
    lastfirstword=parts[0];
240
 
 
241
 
  //  for_each(parts.begin(),parts.end(),print);
242
 
  tline="";
243
 
  return parseLine(parts,rec);
244
 
}
245
 
 
246
 
ZoneParser::~ZoneParser()
247
 
{
248
 
 
249
 
}
250
 
 
251
 
void ZoneParser::setCallback(callback_t *callback)
252
 
{
253
 
  d_callback=callback;
254
 
}
255
 
 
256
 
bool ZoneParser::isNumber(const string &s)
257
 
{
258
 
  for(string::const_iterator i=s.begin();
259
 
      i!=s.end();
260
 
      ++i) {
261
 
    if(i+1==s.end())
262
 
      if(*i=='M' || *i=='D' || *i=='H' || *i=='W' || *i=='m' || *i=='d' || *i=='h' || *i=='w') // last character
263
 
        continue;
264
 
    if(!isdigit(*i))
265
 
      return false;
266
 
  }
267
 
  return true;
268
 
}
269
 
 
270
 
bool ZoneParser::isType(const string &s)
271
 
{
272
 
  if(isNumber(s))
273
 
    return false;
274
 
 
275
 
  if(isClass(s))
276
 
    return false;
277
 
 
278
 
 
279
 
  return true;
280
 
}
281
 
 
282
 
bool ZoneParser::isClass(const string &s)
283
 
{
284
 
  return (s.size()==2 && (s=="IN" || s=="CH" || s=="HS" || s=="in" || s=="ch" || s=="hs"));
285
 
}
286
 
 
287
 
unsigned int ZoneParser::zoneNumber(const string &str)
288
 
{
289
 
  unsigned int val=atoi(str.c_str());
290
 
  char lc=toupper(str[str.length()-1]);
291
 
  if(!isdigit(lc))
292
 
    switch(lc) {
293
 
    case 'H':
294
 
      val*=3600;
295
 
      break;
296
 
    case 'D':
297
 
      val*=3600*24;
298
 
      break;
299
 
    case 'W':
300
 
      val*=3600*24*7;
301
 
      break;
302
 
    case 'M':
303
 
      val*=3600*24*7*4;
304
 
      break;
305
 
    case 'Y': // ? :-)
306
 
      val*=3600*24*365;
307
 
      break;
308
 
    default:
309
 
      throw AhuException("Unable to parse "+d_origin+" time specification '"+str+"' at line "+itoa(d_lineno));
310
 
    }
311
 
  return val;
312
 
 
313
 
}
314
 
 
315
 
/** this parser handles 10 cases (sigh)
316
 
    1) qname TTL CLASS QTYPE *
317
 
    2) qname CLASS TTL QTYPE *
318
 
    3) qname CLASS QTYPE *
319
 
    4) qname TTL QTYPE *
320
 
    5) qname QTYPE *
321
 
 
322
 
    And then everything again with a space first character, which implies 'same as last name'
323
 
*/
324
 
 
325
 
void ZoneParser::soaCanonic(string &content)
326
 
{
327
 
  vector<string>parts;
328
 
  stringtok(parts,content," \t");
329
 
  int pos=0;
330
 
 
331
 
  // 'ns.naamserver.net. hostmaster.naamserver.net 2001102501 8H 2H 1W 1D'
332
 
  // FIXME: what about 'ns hostmaster.naamserver.net 2001102501 8H 2H 1W 1D'?
333
 
 
334
 
  string newcontent;
335
 
  for(vector<string>::const_iterator i=parts.begin();i!=parts.end();++i,++pos) {
336
 
    if(pos<3) {
337
 
      if(pos)
338
 
        newcontent.append(1,' ');
339
 
      newcontent.append( canonic( *i ) );
340
 
    }
341
 
    else {
342
 
      unsigned int val=zoneNumber(*i);
343
 
 
344
 
      newcontent.append(1,' ');
345
 
      newcontent.append(itoa(val));
346
 
    }
347
 
  }
348
 
  content=newcontent;
349
 
}
350
 
 
351
 
string ZoneParser::expandWord(const string &line, int value)
352
 
{
353
 
  string newline;
354
 
  bool escape=false;
355
 
  for(string::const_iterator i=line.begin();i!=line.end();++i) {
356
 
    if(*i=='\\')
357
 
      escape=true;
358
 
    else{
359
 
      if(!escape && *i=='$') {
360
 
        if(i+2<line.end() && *(i+1)=='{') { // shit
361
 
          string::const_iterator k=(i+=2);
362
 
          while(k++!=line.end() && *k!='}')
363
 
            ;
364
 
          if(k==line.end())
365
 
            throw AhuException("Malformed $GENERATE statement");
366
 
 
367
 
          string spec;
368
 
          
369
 
          //copy(i,k,back_inserter(spec));
370
 
    for ( string::const_iterator a = i; a != k; ++a )
371
 
      spec += *a;
372
 
 
373
 
          vector<string> partjes;
374
 
          stringtok(partjes,spec,",");
375
 
          if(partjes.empty())
376
 
            throw AhuException("Malformed $GENERATE statement: '"+spec+"'");
377
 
          
378
 
          value+=atoi(partjes[0].c_str());
379
 
          int width=0;
380
 
          char radix='d';
381
 
          if(partjes.size()>=2)
382
 
            width=atoi(partjes[1].c_str());
383
 
          if(partjes.size()>=3)
384
 
            radix=partjes[2][0];
385
 
 
386
 
          char tmp[20];
387
 
          string format;
388
 
          format="%0";
389
 
          format+=itoa(width);
390
 
          format.append(1,radix);
391
 
          
392
 
          snprintf(tmp,19,format.c_str(),value);
393
 
 
394
 
          newline.append(tmp);
395
 
          i=k;
396
 
        }
397
 
        else
398
 
          newline.append(itoa(value));
399
 
      }
400
 
      else
401
 
        newline.append(1,*i);
402
 
      escape=false;
403
 
    }
404
 
  }
405
 
  return newline;
406
 
}
407
 
 
408
 
string ZoneParser::canonic(const string& dom)
409
 
{
410
 
  if(dom[dom.size()-1]!='.')
411
 
    return dom;
412
 
 
413
 
  return dom.substr(0,dom.size()-1);
414
 
 
415
 
}
416
 
 
417
 
 
418
 
bool ZoneParser::parseLine(const vector<string>&words, vector<Record>&rec)
419
 
{
420
 
  int cpos=0;
421
 
  if(!words.size())
422
 
    return false;
423
 
 
424
 
  if(words[0][0]=='$')
425
 
    {
426
 
    if(!Utility::strcasecmp(words[0].c_str(),"$ORIGIN") && words.size()>1) {
427
 
        d_origin=canonic(words[1]);
428
 
      }
429
 
      else if(!Utility::strcasecmp(words[0].c_str(),"$TTL") && words.size()>1) {
430
 
        d_ttl=zoneNumber(words[1]);
431
 
      }
432
 
      else if(!Utility::strcasecmp(words[0].c_str(),"$GENERATE") && words.size()>1) {
433
 
        // $GENERATE 1-127 $ CNAME $.0
434
 
        string range=words[1];  // 1-127 means 1...127 (including 127). 1-127/2 is 1..3..5..
435
 
        vector<string>parts;
436
 
        stringtok(parts,range,"-/");
437
 
        if(parts.size()<2 || parts.size()>3)
438
 
          throw AhuException("Malformed $GENERATE on line "+itoa(d_lineno)+" of "+d_filename);
439
 
 
440
 
        int start, stop, step=1;
441
 
        start=atoi(parts[0].c_str());
442
 
        stop=atoi(parts[1].c_str());
443
 
        if(parts.size()==3)
444
 
          step=atoi(parts[2].c_str());
445
 
        vector<string>newwords;
446
 
 
447
 
        for(int i=start;i<=stop;++i) {
448
 
          newwords.clear();
449
 
          for(unsigned int j=2;j<words.size();++j) {
450
 
            newwords.push_back(expandWord(words[j],i));
451
 
          }
452
 
          parseLine(newwords, rec);
453
 
        }
454
 
        return true;
455
 
      }
456
 
      else {
457
 
        throw AhuException("Unhandled command '"+words[0]+"' on line "+itoa(d_lineno)+" of '"+d_filename+"'");
458
 
      }
459
 
      
460
 
      return false;
461
 
 
462
 
    }
463
 
  if(words.size()<3) {
464
 
    if(words.size()==1 && words[0]==";")
465
 
      return false;
466
 
    throw AhuException("Short line "+itoa(d_lineno)+" in '"+d_filename+"': "+itoa(words.size())+ " words. Probably due to repeated record without domainname");
467
 
  }
468
 
 
469
 
  string qname=words[0];
470
 
  string qclass="IN";
471
 
  int ttl=d_ttl;
472
 
  string qtype="NONE";
473
 
  if(isNumber(words[1])) // 1 || 4
474
 
    {
475
 
      ttl=zoneNumber(words[1]);
476
 
      if(isClass(words[2])) 
477
 
        {
478
 
//        cout<<1<<endl;
479
 
          qclass=words[2];
480
 
          qtype=words[3];
481
 
          cpos=4;
482
 
          // 1
483
 
        }
484
 
      else
485
 
        {
486
 
//        cout<<4<<endl;
487
 
 
488
 
          qtype=words[2];
489
 
          cpos=3;
490
 
          // 4
491
 
        }
492
 
    }
493
 
  else /* 2 || 3 || 5 */
494
 
    {
495
 
      if(!isClass(words[1]))
496
 
        {
497
 
 
498
 
          qtype=words[1];
499
 
          cpos=2;
500
 
//        cout<<5<<endl;
501
 
          // 5
502
 
        }
503
 
      else // 2 || 3
504
 
        {
505
 
          qclass=words[1];
506
 
          if(isNumber(words[2])) 
507
 
            {
508
 
              ttl=zoneNumber(words[2]);
509
 
              qtype=words[3];
510
 
//            cout<<2<<endl;
511
 
              cpos=4;
512
 
              // 2
513
 
            }
514
 
          else if(isType(words[2]))
515
 
            {
516
 
              qtype=words[2];
517
 
//            cout<<4<<endl;
518
 
              cpos=3;
519
 
              // 4
520
 
            }
521
 
        }
522
 
      
523
 
    }
524
 
  if(!cpos) {
525
 
    throw AhuException("Funky parse case on line  "+itoa(d_lineno));
526
 
  }
527
 
 
528
 
  if(qname=="@")
529
 
    qname=d_origin;
530
 
  else
531
 
    if(qname[qname.size()-1]!='.')
532
 
      qname+="."+d_origin;
533
 
 
534
 
 
535
 
  //  cerr<<qname<<", "<<qclass<<", "<<qtype<<", "<<ttl<<", rest from field "<<cpos<<endl;
536
 
          
537
 
  int left=words.size()-cpos;
538
 
  string content;
539
 
 
540
 
  if((qtype=="MX" && left==2) || (qtype=="SRV" && left==4)){
541
 
    int prio=atoi(words[cpos++].c_str());left--;
542
 
    content=words[cpos++];left--;
543
 
    
544
 
    while(left--)
545
 
      content+=" "+words[cpos++];
546
 
    
547
 
    if(content=="@")
548
 
      content=d_origin;
549
 
    else
550
 
      if(content[content.size()-1]!='.')
551
 
        content+="."+d_origin;
552
 
    
553
 
    fillRec(qname, qtype, content, ttl, prio,rec);
554
 
    return true;
555
 
  }
556
 
  else if(left) {
557
 
    content=words[cpos++];left--;
558
 
    
559
 
    while(left--)
560
 
      content+=" "+words[cpos++];
561
 
    
562
 
    if(qtype=="MX" || qtype=="CNAME" || qtype=="NS") {
563
 
      if(content=="@")
564
 
        content=d_origin;
565
 
      else
566
 
        if(content[content.size()-1]!='.')
567
 
          content+="."+d_origin;
568
 
    }
569
 
    if(qtype=="SOA")
570
 
      soaCanonic(content);
571
 
    if(qtype=="TXT" && content.size() > 2) {  // strip quotes from TXT
572
 
      if(content[0]=='"')
573
 
        content=content.substr(1);
574
 
      if(content[content.size()-1]=='"')
575
 
        content.resize(content.size()-1);
576
 
    }
577
 
      
578
 
    fillRec(qname, qtype, content,ttl, 0, rec);
579
 
    return true;
580
 
  }
581
 
  else {
582
 
    throw AhuException("No content on line  "+itoa(d_lineno));
583
 
  }
584
 
  return false;
585
 
}
586
 
 
587