2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include "arguments.hh"
27 #include "zoneparser-tng.hh"
29 #include "dnsrecords.hh"
30 #include "root-addresses.hh"
38 const vState validationState
= Insecure
;
39 vector
<DNSRecord
> nsset
;
41 t_RC
= std::unique_ptr
<MemRecursorCache
>(new MemRecursorCache());
43 if(::arg()["hint-file"].empty()) {
44 DNSRecord arr
, aaaarr
, nsrr
;
45 nsrr
.d_name
=g_rootdnsname
;
47 aaaarr
.d_type
=QType::AAAA
;
48 nsrr
.d_type
=QType::NS
;
49 arr
.d_ttl
=aaaarr
.d_ttl
=nsrr
.d_ttl
=time(0)+3600000;
51 for(char c
='a';c
<='m';++c
) {
53 strncpy(templ
,"a.root-servers.net.", sizeof(templ
) - 1);
54 templ
[sizeof(templ
)-1] = '\0';
56 aaaarr
.d_name
=arr
.d_name
=DNSName(templ
);
57 nsrr
.d_content
=std::make_shared
<NSRecordContent
>(DNSName(templ
));
58 arr
.d_content
=std::make_shared
<ARecordContent
>(ComboAddress(rootIps4
[c
-'a']));
59 vector
<DNSRecord
> aset
;
61 t_RC
->replace(time(0), DNSName(templ
), QType(QType::A
), aset
, vector
<std::shared_ptr
<RRSIGRecordContent
>>(), vector
<std::shared_ptr
<DNSRecord
>>(), true, boost::none
, validationState
); // auth, nuke it all
62 if (rootIps6
[c
-'a'] != NULL
) {
63 aaaarr
.d_content
=std::make_shared
<AAAARecordContent
>(ComboAddress(rootIps6
[c
-'a']));
65 vector
<DNSRecord
> aaaaset
;
66 aaaaset
.push_back(aaaarr
);
67 t_RC
->replace(time(0), DNSName(templ
), QType(QType::AAAA
), aaaaset
, vector
<std::shared_ptr
<RRSIGRecordContent
>>(), vector
<std::shared_ptr
<DNSRecord
>>(), true, boost::none
, validationState
);
70 nsset
.push_back(nsrr
);
74 ZoneParserTNG
zpt(::arg()["hint-file"]);
79 if(rr
.qtype
.getCode()==QType::A
) {
80 vector
<DNSRecord
> aset
;
81 aset
.push_back(DNSRecord(rr
));
82 t_RC
->replace(time(0), rr
.qname
, QType(QType::A
), aset
, vector
<std::shared_ptr
<RRSIGRecordContent
>>(), vector
<std::shared_ptr
<DNSRecord
>>(), true, boost::none
, validationState
); // auth, etc see above
83 } else if(rr
.qtype
.getCode()==QType::AAAA
) {
84 vector
<DNSRecord
> aaaaset
;
85 aaaaset
.push_back(DNSRecord(rr
));
86 t_RC
->replace(time(0), rr
.qname
, QType(QType::AAAA
), aaaaset
, vector
<std::shared_ptr
<RRSIGRecordContent
>>(), vector
<std::shared_ptr
<DNSRecord
>>(), true, boost::none
, validationState
);
87 } else if(rr
.qtype
.getCode()==QType::NS
) {
88 rr
.content
=toLower(rr
.content
);
89 nsset
.push_back(DNSRecord(rr
));
93 t_RC
->doWipeCache(g_rootdnsname
, false, QType::NS
);
94 t_RC
->replace(time(0), g_rootdnsname
, QType(QType::NS
), nsset
, vector
<std::shared_ptr
<RRSIGRecordContent
>>(), vector
<std::shared_ptr
<DNSRecord
>>(), false, boost::none
, validationState
); // and stuff in the cache
97 static void makeNameToIPZone(std::shared_ptr
<SyncRes::domainmap_t
> newMap
, const DNSName
& hostname
, const string
& ip
)
99 SyncRes::AuthDomain ad
;
100 ad
.d_rdForward
=false;
104 dr
.d_place
=DNSResourceRecord::ANSWER
;
106 dr
.d_type
=QType::SOA
;
108 dr
.d_content
= DNSRecordContent::mastermake(QType::SOA
, 1, "localhost. root 1 604800 86400 2419200 604800");
110 ad
.d_records
.insert(dr
);
113 dr
.d_content
=std::make_shared
<NSRecordContent
>("localhost.");
115 ad
.d_records
.insert(dr
);
118 dr
.d_content
= DNSRecordContent::mastermake(QType::A
, 1, ip
);
119 ad
.d_records
.insert(dr
);
121 if(newMap
->count(dr
.d_name
)) {
122 g_log
<<Logger::Warning
<<"Hosts file will not overwrite zone '"<<dr
.d_name
<<"' already loaded"<<endl
;
125 g_log
<<Logger::Warning
<<"Inserting forward zone '"<<dr
.d_name
<<"' based on hosts file"<<endl
;
127 (*newMap
)[ad
.d_name
]=ad
;
131 //! parts[0] must be an IP address, the rest must be host names
132 static void makeIPToNamesZone(std::shared_ptr
<SyncRes::domainmap_t
> newMap
, const vector
<string
>& parts
)
134 string address
=parts
[0];
135 vector
<string
> ipparts
;
136 stringtok(ipparts
, address
,".");
138 SyncRes::AuthDomain ad
;
139 ad
.d_rdForward
=false;
142 for(int n
=ipparts
.size()-1; n
>=0 ; --n
) {
143 dr
.d_name
.appendRawLabel(ipparts
[n
]);
145 dr
.d_name
.appendRawLabel("in-addr");
146 dr
.d_name
.appendRawLabel("arpa");
148 dr
.d_place
=DNSResourceRecord::ANSWER
;
150 dr
.d_type
=QType::SOA
;
151 dr
.d_content
=DNSRecordContent::mastermake(QType::SOA
, 1, "localhost. root 1 604800 86400 2419200 604800");
153 ad
.d_records
.insert(dr
);
156 dr
.d_content
=std::make_shared
<NSRecordContent
>(DNSName("localhost."));
158 ad
.d_records
.insert(dr
);
159 dr
.d_type
=QType::PTR
;
161 if(ipparts
.size()==4) // otherwise this is a partial zone
162 for(unsigned int n
=1; n
< parts
.size(); ++n
) {
163 dr
.d_content
=DNSRecordContent::mastermake(QType::PTR
, 1, DNSName(parts
[n
]).toString()); // XXX FIXME DNSNAME PAIN CAN THIS BE RIGHT?
164 ad
.d_records
.insert(dr
);
167 if(newMap
->count(dr
.d_name
)) {
168 g_log
<<Logger::Warning
<<"Will not overwrite zone '"<<dr
.d_name
<<"' already loaded"<<endl
;
171 if(ipparts
.size()==4)
172 g_log
<<Logger::Warning
<<"Inserting reverse zone '"<<dr
.d_name
<<"' based on hosts file"<<endl
;
173 ad
.d_name
= dr
.d_name
;
174 (*newMap
)[ad
.d_name
]=ad
;
180 /* mission in life: parse three cases
187 ComboAddress
parseIPAndPort(const std::string
& input
, uint16_t port
)
189 if(input
.find(':') == string::npos
|| input
.empty()) // common case
190 return ComboAddress(input
, port
);
192 pair
<string
,string
> both
;
195 both
=splitField(input
,':');
196 uint16_t newport
=static_cast<uint16_t>(pdns_stou(both
.second
));
197 return ComboAddress(both
.first
, newport
);
201 if(input
[0]=='[') { // case 4
202 both
=splitField(input
.substr(1),']');
203 return ComboAddress(both
.first
, both
.second
.empty() ? port
: static_cast<uint16_t>(pdns_stou(both
.second
.substr(1))));
206 return ComboAddress(input
, port
); // case 3
210 void convertServersForAD(const std::string
& input
, SyncRes::AuthDomain
& ad
, const char* sepa
, bool verbose
=true)
212 vector
<string
> servers
;
213 stringtok(servers
, input
, sepa
);
214 ad
.d_servers
.clear();
216 for(vector
<string
>::const_iterator iter
= servers
.begin(); iter
!= servers
.end(); ++iter
) {
217 if(verbose
&& iter
!= servers
.begin())
220 ComboAddress addr
=parseIPAndPort(*iter
, 53);
222 g_log
<<addr
.toStringWithPort();
223 ad
.d_servers
.push_back(addr
);
229 void* pleaseWipeNegCache()
231 SyncRes::clearNegCache();
235 void* pleaseUseNewSDomainsMap(std::shared_ptr
<SyncRes::domainmap_t
> newmap
)
237 SyncRes::setDomainMap(newmap
);
241 string
reloadAuthAndForwards()
243 std::shared_ptr
<SyncRes::domainmap_t
> original
=SyncRes::getDomainMap();
246 g_log
<<Logger::Warning
<<"Reloading zones, purging data from cache"<<endl
;
248 string configname
=::arg()["config-dir"]+"/recursor.conf";
249 if(::arg()["config-name"]!="") {
250 configname
=::arg()["config-dir"]+"/recursor-"+::arg()["config-name"]+".conf";
252 cleanSlashes(configname
);
254 if(!::arg().preParseFile(configname
.c_str(), "forward-zones"))
255 throw runtime_error("Unable to re-parse configuration file '"+configname
+"'");
256 ::arg().preParseFile(configname
.c_str(), "forward-zones-file");
257 ::arg().preParseFile(configname
.c_str(), "forward-zones-recurse");
258 ::arg().preParseFile(configname
.c_str(), "auth-zones");
259 ::arg().preParseFile(configname
.c_str(), "export-etc-hosts", "off");
260 ::arg().preParseFile(configname
.c_str(), "serve-rfc1918");
261 ::arg().preParseFile(configname
.c_str(), "include-dir");
262 ::arg().preParse(g_argc
, g_argv
, "include-dir");
264 // then process includes
265 std::vector
<std::string
> extraConfigs
;
266 ::arg().gatherIncludes(extraConfigs
);
268 for(const std::string
& fn
: extraConfigs
) {
269 if(!::arg().preParseFile(fn
.c_str(), "forward-zones", ::arg()["forward-zones"]))
270 throw runtime_error("Unable to re-parse configuration file include '"+fn
+"'");
271 ::arg().preParseFile(fn
.c_str(), "forward-zones-file", ::arg()["forward-zones-file"]);
272 ::arg().preParseFile(fn
.c_str(), "forward-zones-recurse", ::arg()["forward-zones-recurse"]);
273 ::arg().preParseFile(fn
.c_str(), "auth-zones",::arg()["auth-zones"]);
274 ::arg().preParseFile(fn
.c_str(), "export-etc-hosts",::arg()["export-etc-hosts"]);
275 ::arg().preParseFile(fn
.c_str(), "serve-rfc1918",::arg()["serve-rfc1918"]);
278 ::arg().preParse(g_argc
, g_argv
, "forward-zones");
279 ::arg().preParse(g_argc
, g_argv
, "forward-zones-file");
280 ::arg().preParse(g_argc
, g_argv
, "forward-zones-recurse");
281 ::arg().preParse(g_argc
, g_argv
, "auth-zones");
282 ::arg().preParse(g_argc
, g_argv
, "export-etc-hosts");
283 ::arg().preParse(g_argc
, g_argv
, "serve-rfc1918");
285 std::shared_ptr
<SyncRes::domainmap_t
> newDomainMap
= parseAuthAndForwards();
287 // purge both original and new names
288 std::set
<DNSName
> oldAndNewDomains
;
289 for(const auto& i
: *newDomainMap
) {
290 oldAndNewDomains
.insert(i
.first
);
294 for(const auto& i
: *original
) {
295 oldAndNewDomains
.insert(i
.first
);
299 for(const auto i
: oldAndNewDomains
) {
300 broadcastAccFunction
<uint64_t>(boost::bind(pleaseWipeCache
, i
, true));
301 broadcastAccFunction
<uint64_t>(boost::bind(pleaseWipePacketCache
, i
, true));
302 broadcastAccFunction
<uint64_t>(boost::bind(pleaseWipeAndCountNegCache
, i
, true));
305 broadcastFunction(boost::bind(pleaseUseNewSDomainsMap
, newDomainMap
));
308 catch(std::exception
& e
) {
309 g_log
<<Logger::Error
<<"Encountered error reloading zones, keeping original data: "<<e
.what()<<endl
;
311 catch(PDNSException
& ae
) {
312 g_log
<<Logger::Error
<<"Encountered error reloading zones, keeping original data: "<<ae
.reason
<<endl
;
315 g_log
<<Logger::Error
<<"Encountered unknown error reloading zones, keeping original data"<<endl
;
317 return "reloading failed, see log\n";
320 std::shared_ptr
<SyncRes::domainmap_t
> parseAuthAndForwards()
322 TXTRecordContent::report();
323 OPTRecordContent::report();
325 auto newMap
= std::make_shared
<SyncRes::domainmap_t
>();
327 typedef vector
<string
> parts_t
;
329 const char *option_names
[3]={"auth-zones", "forward-zones", "forward-zones-recurse"};
330 for(int n
=0; n
< 3 ; ++n
) {
332 stringtok(parts
, ::arg()[option_names
[n
]], " ,\t\n\r");
333 for(parts_t::const_iterator iter
= parts
.begin(); iter
!= parts
.end(); ++iter
) {
334 SyncRes::AuthDomain ad
;
335 if ((*iter
).find('=') == string::npos
)
336 throw PDNSException("Error parsing '" + *iter
+ "', missing =");
337 pair
<string
,string
> headers
=splitField(*iter
, '=');
339 trim(headers
.second
);
340 // headers.first=toCanonic("", headers.first);
342 ad
.d_rdForward
= false;
343 g_log
<<Logger::Error
<<"Parsing authoritative data for zone '"<<headers
.first
<<"' from file '"<<headers
.second
<<"'"<<endl
;
344 ZoneParserTNG
zpt(headers
.second
, DNSName(headers
.first
));
345 DNSResourceRecord rr
;
350 dr
.d_place
=DNSResourceRecord::ANSWER
;
352 catch(std::exception
&e
) {
353 throw PDNSException("Error parsing record '"+rr
.qname
.toLogString()+"' of type "+rr
.qtype
.getName()+" in zone '"+headers
.first
+"' from file '"+headers
.second
+"': "+e
.what());
356 throw PDNSException("Error parsing record '"+rr
.qname
.toLogString()+"' of type "+rr
.qtype
.getName()+" in zone '"+headers
.first
+"' from file '"+headers
.second
+"'");
359 ad
.d_records
.insert(dr
);
363 g_log
<<Logger::Error
<<"Redirecting queries for zone '"<<headers
.first
<<"' ";
365 g_log
<<"with recursion ";
366 ad
.d_rdForward
= true;
368 else ad
.d_rdForward
= false;
371 convertServersForAD(headers
.second
, ad
, ";");
373 ad
.d_rdForward
= true;
377 ad
.d_name
= DNSName(headers
.first
);
378 (*newMap
)[ad
.d_name
]=ad
;
382 if(!::arg()["forward-zones-file"].empty()) {
383 g_log
<<Logger::Warning
<<"Reading zone forwarding information from '"<<::arg()["forward-zones-file"]<<"'"<<endl
;
384 SyncRes::AuthDomain ad
;
385 auto fp
= std::unique_ptr
<FILE, int(*)(FILE*)>(fopen(::arg()["forward-zones-file"].c_str(), "r"), fclose
);
387 throw PDNSException("Error opening forward-zones-file '"+::arg()["forward-zones-file"]+"': "+stringerror());
392 uint64_t before
= newMap
->size();
393 while(linenum
++, stringfgets(fp
.get(), line
)) {
395 if (line
[0] == '#') // Comment line, skip to the next line
397 string domain
, instructions
;
398 tie(domain
, instructions
)=splitField(line
, '=');
399 instructions
= splitField(instructions
, '#').first
; // Remove EOL comments
402 if(domain
.empty() && instructions
.empty()) { // empty line
405 if(boost::starts_with(domain
,"+")) {
406 domain
=domain
.c_str()+1;
407 ad
.d_rdForward
= true;
410 ad
.d_rdForward
= false;
412 throw PDNSException("Error parsing line "+std::to_string(linenum
)+" of " +::arg()["forward-zones-file"]);
416 convertServersForAD(instructions
, ad
, ",; ", false);
419 throw PDNSException("Conversion error parsing line "+std::to_string(linenum
)+" of " +::arg()["forward-zones-file"]);
422 ad
.d_name
= DNSName(domain
);
423 (*newMap
)[ad
.d_name
]=ad
;
425 g_log
<<Logger::Warning
<<"Done parsing " << newMap
->size() - before
<<" forwarding instructions from file '"<<::arg()["forward-zones-file"]<<"'"<<endl
;
428 if(::arg().mustDo("export-etc-hosts")) {
430 string fname
=::arg()["etc-hosts-file"];
432 ifstream
ifs(fname
.c_str());
434 g_log
<<Logger::Warning
<<"Could not open "<<fname
<<" for reading"<<endl
;
437 string searchSuffix
= ::arg()["export-etc-hosts-search-suffix"];
438 string::size_type pos
;
439 while(getline(ifs
,line
)) {
441 if(pos
!=string::npos
)
447 stringtok(parts
, line
, "\t\r\n ");
448 if(parts
[0].find(':')!=string::npos
)
451 for(unsigned int n
=1; n
< parts
.size(); ++n
) {
452 if(searchSuffix
.empty() || parts
[n
].find('.') != string::npos
)
453 makeNameToIPZone(newMap
, DNSName(parts
[n
]), parts
[0]);
455 DNSName canonic
=toCanonic(DNSName(searchSuffix
), parts
[n
]); /// XXXX DNSName pain
456 if(canonic
!= DNSName(parts
[n
])) { // XXX further DNSName pain
457 makeNameToIPZone(newMap
, canonic
, parts
[0]);
461 makeIPToNamesZone(newMap
, parts
);
465 if(::arg().mustDo("serve-rfc1918")) {
466 g_log
<<Logger::Warning
<<"Inserting rfc 1918 private space zones"<<endl
;
468 parts
.push_back("127");
469 makeIPToNamesZone(newMap
, parts
);
471 makeIPToNamesZone(newMap
, parts
);
474 makeIPToNamesZone(newMap
, parts
);
475 for(int n
=16; n
< 32; n
++) {
476 parts
[0]="172."+std::to_string(n
);
477 makeIPToNamesZone(newMap
,parts
);