root/trunk/pdns/pdns/reczones.cc @ 1533

Revision 1533, 13.2 KB (checked in by ahu, 3 years ago)

no longer get confused by empty lines in the forward-zones-file. again discovered by Imre Gergely, this closes ticket 273.

Line 
1/*
2    PowerDNS Versatile Database Driven Nameserver
3    Copyright (C) 2003 - 2010  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 "syncres.hh"
20#include "arguments.hh"
21#include "zoneparser-tng.hh"
22#include "logger.hh"
23#include "dnsrecords.hh"
24
25void primeHints(void)
26{
27  // prime root cache
28  set<DNSResourceRecord>nsset;
29  if(!t_RC)
30    t_RC = new MemRecursorCache();
31
32  if(::arg()["hint-file"].empty()) {
33    static const char*ips[]={"198.41.0.4", "192.228.79.201", "192.33.4.12", "128.8.10.90", "192.203.230.10", "192.5.5.241", 
34                             "192.112.36.4", "128.63.2.53",
35                             "192.36.148.17","192.58.128.30", "193.0.14.129", "199.7.83.42", "202.12.27.33"};
36    static const char *ip6s[]={
37      "2001:503:ba3e::2:30", NULL, NULL, NULL, NULL,
38      "2001:500:2f::f", NULL, "2001:500:1::803f:235", NULL,
39      "2001:503:c27::2:30", NULL, NULL, NULL
40    };
41    DNSResourceRecord arr, aaaarr, nsrr;
42    arr.qtype=QType::A;
43    aaaarr.qtype=QType::AAAA;
44    nsrr.qtype=QType::NS;
45    arr.ttl=aaaarr.ttl=nsrr.ttl=time(0)+3600000;
46   
47    for(char c='a';c<='m';++c) {
48      static char templ[40];
49      strncpy(templ,"a.root-servers.net.", sizeof(templ) - 1);
50      *templ=c;
51      aaaarr.qname=arr.qname=nsrr.content=templ;
52      arr.content=ips[c-'a'];
53      set<DNSResourceRecord> aset;
54      aset.insert(arr);
55      t_RC->replace(time(0), string(templ), QType(QType::A), aset, true); // auth, nuke it all
56      if (ip6s[c-'a'] != NULL) {
57        aaaarr.content=ip6s[c-'a'];
58
59        set<DNSResourceRecord> aaaaset;
60        aaaaset.insert(aaaarr);
61        t_RC->replace(time(0), string(templ), QType(QType::AAAA), aaaaset, true);
62      }
63     
64      nsset.insert(nsrr);
65    }
66  }
67  else {
68    ZoneParserTNG zpt(::arg()["hint-file"]);
69    DNSResourceRecord rr;
70
71    while(zpt.get(rr)) {
72      rr.ttl+=time(0);
73      if(rr.qtype.getCode()==QType::A) {
74        set<DNSResourceRecord> aset;
75        aset.insert(rr);
76        t_RC->replace(time(0), rr.qname, QType(QType::A), aset, true); // auth, etc see above
77      } else if(rr.qtype.getCode()==QType::AAAA) {
78        set<DNSResourceRecord> aaaaset;
79        aaaaset.insert(rr);
80        t_RC->replace(time(0), rr.qname, QType(QType::AAAA), aaaaset, true);
81      } else if(rr.qtype.getCode()==QType::NS) {
82        rr.content=toLower(rr.content);
83        nsset.insert(rr);
84      }
85    }
86  }
87  t_RC->replace(time(0),".", QType(QType::NS), nsset, true); // and stuff in the cache (auth)
88}
89
90static void makeNameToIPZone(SyncRes::domainmap_t* newMap, const string& hostname, const string& ip)
91{
92  SyncRes::AuthDomain ad;
93  DNSResourceRecord rr;
94  rr.qname=toCanonic("", hostname);
95  rr.d_place=DNSResourceRecord::ANSWER;
96  rr.ttl=86400;
97  rr.qtype=QType::SOA;
98  rr.content="localhost. root 1 604800 86400 2419200 604800";
99 
100  ad.d_records.insert(rr);
101
102  rr.qtype=QType::NS;
103  rr.content="localhost.";
104
105  ad.d_records.insert(rr);
106 
107  rr.qtype=QType::A;
108  rr.content=ip;
109  ad.d_records.insert(rr);
110 
111  if(newMap->count(rr.qname)) { 
112    L<<Logger::Warning<<"Hosts file will not overwrite zone '"<<rr.qname<<"' already loaded"<<endl;
113  }
114  else {
115    L<<Logger::Warning<<"Inserting forward zone '"<<rr.qname<<"' based on hosts file"<<endl;
116    (*newMap)[rr.qname]=ad;
117  }
118}
119
120//! parts[0] must be an IP address, the rest must be host names
121static void makeIPToNamesZone(SyncRes::domainmap_t* newMap, const vector<string>& parts) 
122{
123  string address=parts[0];
124  vector<string> ipparts;
125  stringtok(ipparts, address,".");
126 
127  SyncRes::AuthDomain ad;
128  DNSResourceRecord rr;
129  for(int n=ipparts.size()-1; n>=0 ; --n) {
130    rr.qname.append(ipparts[n]);
131    rr.qname.append(1,'.');
132  }
133  rr.qname.append("in-addr.arpa.");
134
135  rr.d_place=DNSResourceRecord::ANSWER;
136  rr.ttl=86400;
137  rr.qtype=QType::SOA;
138  rr.content="localhost. root. 1 604800 86400 2419200 604800";
139 
140  ad.d_records.insert(rr);
141
142  rr.qtype=QType::NS;
143  rr.content="localhost.";
144
145  ad.d_records.insert(rr);
146  rr.qtype=QType::PTR;
147
148  if(ipparts.size()==4)  // otherwise this is a partial zone
149    for(unsigned int n=1; n < parts.size(); ++n) {
150      rr.content=toCanonic("", parts[n]);
151      ad.d_records.insert(rr);
152    }
153
154  if(newMap->count(rr.qname)) { 
155    L<<Logger::Warning<<"Will not overwrite zone '"<<rr.qname<<"' already loaded"<<endl;
156  }
157  else {
158    if(ipparts.size()==4)
159      L<<Logger::Warning<<"Inserting reverse zone '"<<rr.qname<<"' based on hosts file"<<endl;
160    (*newMap)[rr.qname]=ad;
161  }
162}
163
164
165
166/* mission in life: parse three cases
167   1) 1.2.3.4
168   2) 1.2.3.4:5300
169   3) 2001::1
170   4) [2002::1]:53
171*/
172
173ComboAddress parseIPAndPort(const std::string& input, uint16_t port)
174{
175  if(input.find(':') == string::npos || input.empty()) // common case
176    return ComboAddress(input, port);
177
178  pair<string,string> both;
179
180  try { // case 2
181    both=splitField(input,':');
182    uint16_t newport=boost::lexical_cast<uint16_t>(both.second);
183    return ComboAddress(both.first, newport);
184  } 
185  catch(...){}
186
187  if(input[0]=='[') { // case 4
188    both=splitField(input.substr(1),']');
189    return ComboAddress(both.first, both.second.empty() ? port : boost::lexical_cast<uint16_t>(both.second.substr(1)));
190  }
191
192  return ComboAddress(input, port); // case 3
193}
194
195
196void convertServersForAD(const std::string& input, SyncRes::AuthDomain& ad, const char* sepa, bool verbose=true)
197{
198  vector<string> servers;
199  stringtok(servers, input, sepa);
200  ad.d_servers.clear();
201
202  for(vector<string>::const_iterator iter = servers.begin(); iter != servers.end(); ++iter) {
203    if(verbose && iter != servers.begin()) 
204      L<<", ";
205
206    ComboAddress addr=parseIPAndPort(*iter, 53);
207    if(verbose)
208      L<<addr.toStringWithPort();
209    ad.d_servers.push_back(addr);
210  }
211  if(verbose)
212    L<<endl;
213}
214
215void* pleaseWipeCache(const std::string& qname)
216{
217  t_RC->doWipeCache(qname); 
218  return 0;
219}
220
221void* pleaseWipeNegCache()
222{
223  t_sstorage->negcache.clear();   
224  return 0;
225}
226
227void* pleaseUseNewSDomainsMap(SyncRes::domainmap_t* newmap)
228{
229  t_sstorage->domainmap = newmap;
230  return 0;
231}
232
233string reloadAuthAndForwards()
234{
235  SyncRes::domainmap_t* original=t_sstorage->domainmap; 
236 
237  try {
238    L<<Logger::Warning<<"Reloading zones, purging data from cache"<<endl;
239 
240    for(SyncRes::domainmap_t::const_iterator i = t_sstorage->domainmap->begin(); i != t_sstorage->domainmap->end(); ++i) {
241      for(SyncRes::AuthDomain::records_t::const_iterator j = i->second.d_records.begin(); j != i->second.d_records.end(); ++j) 
242        broadcastFunction(boost::bind(pleaseWipeCache, j->qname));
243    }
244
245    string configname=::arg()["config-dir"]+"/recursor.conf";
246    cleanSlashes(configname);
247   
248    if(!::arg().preParseFile(configname.c_str(), "forward-zones")) 
249      L<<Logger::Warning<<"Unable to re-parse configuration file '"<<configname<<"'"<<endl;
250   
251    ::arg().preParseFile(configname.c_str(), "forward-zones-file");
252    ::arg().preParseFile(configname.c_str(), "auth-zones");
253    ::arg().preParseFile(configname.c_str(), "export-etc-hosts", "off");
254    ::arg().preParseFile(configname.c_str(), "serve-rfc1918");
255
256    SyncRes::domainmap_t* newDomainMap = parseAuthAndForwards();
257   
258    // purge again - new zones need to blank out the cache
259    for(SyncRes::domainmap_t::const_iterator i = newDomainMap->begin(); i != newDomainMap->end(); ++i) {
260      for(SyncRes::AuthDomain::records_t::const_iterator j = i->second.d_records.begin(); j != i->second.d_records.end(); ++j) 
261        broadcastFunction(boost::bind(pleaseWipeCache, j->qname));
262    }
263
264    // this is pretty blunt
265    broadcastFunction(pleaseWipeNegCache);
266    broadcastFunction(boost::bind(pleaseUseNewSDomainsMap, newDomainMap)); 
267    delete original;
268    return "ok\n";
269  }
270  catch(std::exception& e) {
271    L<<Logger::Error<<"Had error reloading zones, keeping original data: "<<e.what()<<endl;
272  }
273  catch(AhuException& ae) {
274    L<<Logger::Error<<"Encountered error reloading zones, keeping original data: "<<ae.reason<<endl;
275  }
276  catch(...) {
277    L<<Logger::Error<<"Encountered unknown error reloading zones, keeping original data"<<endl;
278  }
279  return "reloading failed, see log\n";
280}
281
282SyncRes::domainmap_t* parseAuthAndForwards()
283{
284  TXTRecordContent::report();
285  OPTRecordContent::report();
286
287  SyncRes::domainmap_t* newMap = new SyncRes::domainmap_t();
288
289  typedef vector<string> parts_t;
290  parts_t parts; 
291  const char *option_names[3]={"auth-zones", "forward-zones", "forward-zones-recurse"};
292  for(int n=0; n < 3 ; ++n ) {
293    parts.clear();
294    stringtok(parts, ::arg()[option_names[n]], ",\t\n\r");
295    for(parts_t::const_iterator iter = parts.begin(); iter != parts.end(); ++iter) {
296      SyncRes::AuthDomain ad;
297      pair<string,string> headers=splitField(*iter, '=');
298      trim(headers.first);
299      trim(headers.second);
300      headers.first=toCanonic("", headers.first);
301      if(n==0) {
302        L<<Logger::Error<<"Parsing authoritative data for zone '"<<headers.first<<"' from file '"<<headers.second<<"'"<<endl;
303        ZoneParserTNG zpt(headers.second, headers.first);
304        DNSResourceRecord rr;
305        while(zpt.get(rr)) {
306          try {
307            string tmp=DNSRR2String(rr);
308            rr=String2DNSRR(rr.qname, rr.qtype, tmp, rr.ttl);
309          }
310          catch(std::exception &e) {
311            delete newMap;
312            throw AhuException("Error parsing record '"+rr.qname+"' of type "+rr.qtype.getName()+" in zone '"+headers.first+"' from file '"+headers.second+"': "+e.what());
313          }
314          catch(...) {
315            delete newMap;
316            throw AhuException("Error parsing record '"+rr.qname+"' of type "+rr.qtype.getName()+" in zone '"+headers.first+"' from file '"+headers.second+"'");
317          }
318
319          ad.d_records.insert(rr);
320        }
321      }
322      else {
323        L<<Logger::Error<<"Redirecting queries for zone '"<<headers.first<<"' ";
324        if(n == 2) {
325          L<<"with recursion ";
326          ad.d_rdForward = 1;
327        }
328        else ad.d_rdForward = 0;
329        L<<"to: ";
330       
331        convertServersForAD(headers.second, ad, ";");
332        if(n == 2) {
333          ad.d_rdForward = 1;
334        }
335      }
336     
337      (*newMap)[headers.first]=ad; 
338    }
339  }
340 
341  if(!::arg()["forward-zones-file"].empty()) {
342    L<<Logger::Warning<<"Reading zone forwarding information from '"<<::arg()["forward-zones-file"]<<"'"<<endl;
343    SyncRes::AuthDomain ad;
344    FILE *rfp=fopen(::arg()["forward-zones-file"].c_str(), "r");
345
346    if(!rfp) {
347      delete newMap;
348      throw AhuException("Error opening forward-zones-file '"+::arg()["forward-zones-file"]+"': "+stringerror());
349    }
350
351    shared_ptr<FILE> fp=shared_ptr<FILE>(rfp, fclose);
352   
353    char line[1024];
354    int linenum=0;
355    uint64_t before = newMap->size();
356    while(linenum++, fgets(line, sizeof(line)-1, fp.get())) {
357      string domain, instructions;
358      tie(domain, instructions)=splitField(line, '=');
359      trim(domain);
360      trim(instructions);
361      if(domain.empty() && instructions.empty()) { // empty line
362        continue;
363      }
364      if(boost::starts_with(domain,"+")) {
365        domain=domain.c_str()+1;
366        ad.d_rdForward = true;
367      }
368      else
369        ad.d_rdForward = false;
370      if(domain.empty()) {
371        delete newMap;
372        throw AhuException("Error parsing line "+lexical_cast<string>(linenum)+" of " +::arg()["forward-zones-file"]);
373      }
374
375      try {
376        convertServersForAD(instructions, ad, ",; ", false);
377      }
378      catch(...) {
379        delete newMap;
380        throw AhuException("Conversion error parsing line "+lexical_cast<string>(linenum)+" of " +::arg()["forward-zones-file"]);
381      }
382
383      (*newMap)[toCanonic("", domain)]=ad;
384    }
385    L<<Logger::Warning<<"Done parsing " << newMap->size() - before<<" forwarding instructions from file '"<<::arg()["forward-zones-file"]<<"'"<<endl;
386  }
387
388  if(::arg().mustDo("export-etc-hosts")) {
389    string line;
390    string fname=::arg()["etc-hosts-file"];
391   
392    ifstream ifs(fname.c_str());
393    if(!ifs) {
394      L<<Logger::Warning<<"Could not open /etc/hosts for reading"<<endl;
395    }
396    else {
397      string::size_type pos;
398      while(getline(ifs,line)) {
399        pos=line.find('#');
400        if(pos!=string::npos)
401          line.resize(pos);
402        trim(line);
403        if(line.empty())
404          continue;
405        parts.clear();
406        stringtok(parts, line, "\t\r\n ");
407        if(parts[0].find(':')!=string::npos)
408          continue;
409       
410        for(unsigned int n=1; n < parts.size(); ++n)
411          makeNameToIPZone(newMap, parts[n], parts[0]);
412        makeIPToNamesZone(newMap, parts);
413      }
414    }
415  }
416  if(::arg().mustDo("serve-rfc1918")) {
417    L<<Logger::Warning<<"Inserting rfc 1918 private space zones"<<endl;
418    parts.clear();
419    parts.push_back("127");
420    makeIPToNamesZone(newMap, parts);
421    parts[0]="10";
422    makeIPToNamesZone(newMap, parts);
423
424    parts[0]="192.168";
425    makeIPToNamesZone(newMap, parts);
426    for(int n=16; n < 32; n++) {
427      parts[0]="172."+lexical_cast<string>(n);
428      makeIPToNamesZone(newMap,parts);
429    }
430  }
431  return newMap;
432}
433
Note: See TracBrowser for help on using the browser.