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

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

add additional IPv6 hints, closing ticket 287, plus add some threading safety net to outgoing address selection

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