]>
Commit | Line | Data |
---|---|---|
12471842 PL |
1 | /* |
2 | * This file is part of PowerDNS or dnsdist. | |
3 | * Copyright -- PowerDNS.COM B.V. and its contributors | |
4 | * | |
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. | |
8 | * | |
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. | |
12 | * | |
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. | |
17 | * | |
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. | |
21 | */ | |
ca12836d | 22 | #include "statbag.hh" |
23 | #include "zoneparser-tng.hh" | |
24 | #include "namespaces.hh" | |
25 | #include "dnsrecords.hh" | |
26 | #include <fstream> | |
27 | #include <atomic> | |
28 | #include <thread> | |
29 | #include <unordered_set> | |
30 | #include "inflighter.cc" | |
31 | //#include "malloctrace.hh" | |
32 | StatBag S; | |
33 | bool g_quiet; | |
34 | std::unique_ptr<ofstream> g_powerdns; | |
35 | std::atomic<unsigned int> g_count; | |
36 | std::atomic<bool> g_stop; | |
37 | ||
38 | ||
39 | struct namecount { | |
40 | set<DNSName> names; | |
41 | unsigned int count; | |
42 | bool isPowerDNS{false}; | |
43 | }; | |
44 | ||
45 | struct DNSResult | |
46 | { | |
47 | string content; | |
48 | int ttl{0}; | |
49 | uint16_t qclass; | |
50 | }; | |
51 | ||
52 | struct NSQuery | |
53 | { | |
54 | ComboAddress a; | |
55 | set<DNSName> nsnames; | |
56 | DNSName qname; | |
57 | unsigned int count; | |
58 | }; | |
59 | ||
60 | struct SendReceive | |
61 | { | |
62 | typedef int Identifier; | |
63 | typedef DNSResult Answer; // ip | |
64 | int d_socket; | |
65 | deque<uint16_t> d_idqueue; | |
66 | ||
67 | SendReceive(map<ComboAddress, namecount, ComboAddress::addressOnlyLessThan>& res) : d_res(res) | |
68 | { | |
69 | d_socket = socket(AF_INET, SOCK_DGRAM, 0); | |
70 | int val=1; | |
71 | setsockopt(d_socket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); | |
72 | ||
73 | for(unsigned int id =0 ; id < std::numeric_limits<uint16_t>::max(); ++id) | |
74 | d_idqueue.push_back(id); | |
75 | } | |
76 | ||
77 | ~SendReceive() | |
78 | { | |
79 | close(d_socket); | |
80 | } | |
81 | ||
82 | Identifier send(NSQuery& domain) | |
83 | { | |
84 | //cerr<<"Sending query for '"<<domain<<"'"<<endl; | |
85 | ||
86 | // send it, copy code from 'sdig' | |
87 | vector<uint8_t> packet; | |
88 | ||
89 | DNSPacketWriter pw(packet, domain.qname, QType::TXT, 3); | |
90 | ||
91 | if(d_idqueue.empty()) { | |
92 | cerr<<"Exhausted ids!"<<endl; | |
93 | exit(1); | |
94 | } | |
95 | pw.getHeader()->id = d_idqueue.front(); | |
96 | d_idqueue.pop_front(); | |
97 | pw.getHeader()->rd = 0; | |
98 | pw.getHeader()->qr = 0; | |
99 | ||
100 | if(::sendto(d_socket, &*packet.begin(), packet.size(), 0, (struct sockaddr*)&domain.a, domain.a.getSocklen()) < 0) | |
101 | d_senderrors++; | |
102 | ||
103 | return pw.getHeader()->id; | |
104 | } | |
105 | ||
106 | bool receive(Identifier& id, DNSResult& dr) | |
107 | { | |
108 | if(waitForData(d_socket, 0, 500000) > 0) { | |
109 | char buf[512]; | |
110 | ComboAddress from; | |
111 | from.sin4.sin_family = AF_INET; | |
112 | socklen_t socklen=from.getSocklen(); | |
113 | int len = recvfrom(d_socket, buf, sizeof(buf),0, (struct sockaddr*)&from, &socklen); | |
114 | if(len < 0) { | |
115 | d_receiveerrors++; | |
116 | return 0; | |
117 | } | |
118 | else { | |
119 | d_receiveds++; | |
120 | } | |
121 | // parse packet, set 'id', fill out 'ip' | |
122 | ||
123 | MOADNSParser mdp(string(buf, len)); | |
124 | if(!g_quiet) { | |
a8d9bc94 | 125 | cout<<"Reply to question for qname='"<<mdp.d_qname<<"', qtype="<<DNSRecordContent::NumberToType(mdp.d_qtype)<<endl; |
ca12836d | 126 | cout<<"Rcode: "<<mdp.d_header.rcode<<", RD: "<<mdp.d_header.rd<<", QR: "<<mdp.d_header.qr; |
127 | cout<<", TC: "<<mdp.d_header.tc<<", AA: "<<mdp.d_header.aa<<", opcode: "<<mdp.d_header.opcode<<endl; | |
128 | } | |
129 | id = mdp.d_header.id; | |
130 | d_idqueue.push_back(id); | |
131 | dr.qclass = mdp.d_qclass; | |
132 | dr.content.clear(); | |
133 | dr.ttl=0; | |
134 | for(const auto& a : mdp.d_answers) { | |
135 | if(a.first.d_type == QType::TXT) { | |
136 | dr.content=a.first.d_content->getZoneRepresentation(); | |
137 | dr.ttl=a.first.d_ttl; | |
138 | } | |
139 | } | |
140 | if(dr.content.empty()) | |
141 | dr.content="RCode: "+RCode::to_s(mdp.d_header.rcode); | |
142 | return 1; | |
143 | } | |
144 | return 0; | |
145 | } | |
146 | ||
147 | void deliverTimeout(const Identifier& id) | |
148 | { | |
149 | if(!g_quiet) { | |
150 | cout<<"Timeout for id "<<id<<endl; | |
151 | } | |
152 | d_idqueue.push_back(id); | |
153 | } | |
154 | ||
155 | void deliverAnswer(NSQuery& domain, const DNSResult& dr, unsigned int usec) | |
156 | { | |
a8d9bc94 | 157 | cout<<domain.a.toString()<<"\t"<<domain.qname<<"\t"; |
ca12836d | 158 | for(const auto& n : domain.nsnames) |
a8d9bc94 | 159 | cout<<n<<","; |
ca12836d | 160 | cout<<"\t"<<domain.count<<"\t"<<dr.qclass<<'\t'<<dr.ttl<<": "<<dr.content<<endl; |
161 | ||
162 | if(dr.qclass==1 || toLower(dr.content).find("powerdns") != string::npos || dr.ttl==5) { | |
163 | auto f = d_res.find(domain.a); | |
164 | if(!f->second.isPowerDNS) { | |
165 | (*g_powerdns)<<domain.a.toString()<<'\t'<<domain.count<<'\t'; | |
166 | for(const auto& n : domain.nsnames) | |
a8d9bc94 | 167 | (*g_powerdns)<<n<<'\t'; |
ca12836d | 168 | (*g_powerdns)<<"\n"; |
169 | f->second.isPowerDNS=true; | |
170 | } | |
171 | ||
172 | } | |
173 | ||
174 | } | |
175 | unsigned int d_errors, d_nxdomains, d_nodatas, d_oks, d_unknowns; | |
176 | unsigned int d_receiveds, d_receiveerrors, d_senderrors; | |
177 | map<ComboAddress, namecount, ComboAddress::addressOnlyLessThan>& d_res; | |
178 | }; | |
179 | ||
180 | ||
181 | struct RESResult | |
182 | { | |
183 | vector<ComboAddress> addrs; | |
184 | uint16_t rcode; | |
185 | }; | |
186 | ||
187 | typedef DNSName RESQuery; | |
188 | ||
189 | struct SendReceiveRes | |
190 | { | |
191 | typedef int Identifier; | |
192 | typedef RESResult Answer; // ip | |
193 | int d_socket; | |
194 | deque<uint16_t> d_idqueue; | |
195 | map<DNSName, vector<ComboAddress>>& d_out; | |
196 | SendReceiveRes(const ComboAddress& remote, map<DNSName,vector<ComboAddress>>& out) : d_out(out) | |
197 | { | |
198 | d_socket = socket(AF_INET, SOCK_DGRAM, 0); | |
199 | int val=1; | |
200 | setsockopt(d_socket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); | |
201 | connect(d_socket, (struct sockaddr*)&remote, remote.getSocklen()); | |
202 | for(unsigned int id =0 ; id < std::numeric_limits<uint16_t>::max(); ++id) | |
203 | d_idqueue.push_back(id); | |
204 | } | |
205 | ||
206 | ~SendReceiveRes() | |
207 | { | |
208 | close(d_socket); | |
209 | } | |
210 | ||
211 | Identifier send(RESQuery& domain) | |
212 | { | |
213 | //cerr<<"Sending query for '"<<domain<<"'"<<endl; | |
214 | ||
215 | // send it, copy code from 'sdig' | |
216 | vector<uint8_t> packet; | |
217 | ||
218 | DNSPacketWriter pw(packet, domain, QType::A); | |
219 | ||
220 | if(d_idqueue.empty()) { | |
221 | cerr<<"Exhausted ids!"<<endl; | |
222 | exit(1); | |
223 | } | |
224 | pw.getHeader()->id = d_idqueue.front(); | |
225 | d_idqueue.pop_front(); | |
226 | pw.getHeader()->rd = 1; | |
227 | pw.getHeader()->qr = 0; | |
228 | ||
229 | if(::send(d_socket, &*packet.begin(), packet.size(), 0) < 0) { | |
230 | cout<<"Error sending: "<<strerror(errno)<<endl; | |
231 | d_senderrors++; | |
232 | } | |
233 | ||
234 | return pw.getHeader()->id; | |
235 | } | |
236 | ||
237 | bool receive(Identifier& id, RESResult& dr) | |
238 | { | |
239 | if(waitForData(d_socket, 0, 500000) > 0) { | |
240 | char buf[512]; | |
241 | ComboAddress from; | |
242 | from.sin4.sin_family = AF_INET; | |
243 | socklen_t socklen=from.getSocklen(); | |
244 | int len = recvfrom(d_socket, buf, sizeof(buf),0, (struct sockaddr*)&from, &socklen); | |
245 | if(len < 0) { | |
246 | d_receiveerrors++; | |
247 | return 0; | |
248 | } | |
249 | else { | |
250 | d_receiveds++; | |
251 | } | |
252 | // parse packet, set 'id', fill out 'ip' | |
253 | ||
254 | MOADNSParser mdp(string(buf, len)); | |
255 | if(!g_quiet) { | |
a8d9bc94 | 256 | cout<<"Reply to question for qname='"<<mdp.d_qname<<"', qtype="<<DNSRecordContent::NumberToType(mdp.d_qtype)<<endl; |
ca12836d | 257 | cout<<"Rcode: "<<mdp.d_header.rcode<<", RD: "<<mdp.d_header.rd<<", QR: "<<mdp.d_header.qr<<", answers: "<<mdp.d_answers.size(); |
258 | cout<<", TC: "<<mdp.d_header.tc<<", AA: "<<mdp.d_header.aa<<", opcode: "<<mdp.d_header.opcode<<endl; | |
259 | } | |
260 | id = mdp.d_header.id; | |
261 | d_idqueue.push_back(id); | |
262 | dr.rcode = mdp.d_header.rcode; | |
263 | dr.addrs.clear(); | |
264 | for(const auto& a : mdp.d_answers) { | |
265 | if(a.first.d_name != mdp.d_qname) | |
266 | continue; | |
267 | if(a.first.d_type == QType::A || a.first.d_type == QType::AAAA) { | |
268 | if(!g_quiet) | |
269 | cout<<a.first.d_content->getZoneRepresentation()<<endl; | |
270 | dr.addrs.push_back(getAddr(a.first)); | |
271 | } | |
272 | } | |
273 | ++g_count; | |
274 | return 1; | |
275 | } | |
276 | return 0; | |
277 | } | |
278 | ||
279 | void deliverTimeout(const Identifier& id) | |
280 | { | |
281 | if(!g_quiet) { | |
282 | cout<<"Timeout for id "<<id<<endl; | |
283 | } | |
284 | ++g_count; | |
285 | d_idqueue.push_back(id); | |
286 | } | |
287 | ||
288 | void deliverAnswer(DNSName& domain, const RESResult& dr, unsigned int usec) | |
289 | { | |
290 | d_out[domain]=dr.addrs; | |
291 | cout<<domain<<"\t"<<dr.rcode<<'\t'; | |
292 | for(const auto& a : dr.addrs) | |
293 | cout<<a.toString()<<'\t'; | |
294 | cout<<'\n'; | |
295 | } | |
296 | unsigned int d_errors, d_nxdomains, d_nodatas, d_oks, d_unknowns; | |
297 | unsigned int d_receiveds, d_receiveerrors, d_senderrors; | |
298 | }; | |
299 | ||
300 | ||
301 | void printStats() | |
302 | { | |
303 | while(!g_stop) { | |
304 | sleep(1); | |
305 | cerr<<"\r"<<g_count; | |
306 | cerr.flush(); | |
307 | } | |
308 | cerr<<"\n"; | |
309 | } | |
310 | ||
311 | int parseZone(const std::string& str, unsigned int limit) | |
312 | { | |
313 | ZoneParserTNG zpt(str); | |
314 | DNSResourceRecord rr; | |
315 | ||
316 | std::thread stats(printStats); | |
317 | ||
318 | map<DNSName,unsigned int> nsnames; | |
319 | map<DNSName,set<ComboAddress,ComboAddress::addressOnlyLessThan> > addresses; | |
320 | ||
321 | ||
322 | while(zpt.get(rr)) { | |
323 | if(rr.qtype.getCode() == QType::NS) | |
324 | nsnames[DNSName(rr.content)]++; | |
325 | else if(rr.qtype.getCode() == QType::A || rr.qtype.getCode() == QType::AAAA) { | |
326 | DNSRecord dr(rr); | |
327 | addresses[rr.qname].insert(getAddr(dr, 53)); | |
328 | } | |
329 | ++g_count; | |
330 | if(g_count == limit) | |
331 | break; | |
332 | } | |
333 | g_stop=true; | |
334 | stats.join(); | |
335 | ||
336 | cout<<"Got "<<nsnames.size()<<" different nameserver names"<<endl; | |
337 | cout<<"Got at least one address for "<<addresses.size()<<" names"<<endl; | |
338 | ||
339 | ofstream ns(str+".nameservers"); | |
340 | ofstream needres(str+".needres"); | |
341 | for(const auto& a: nsnames) { | |
342 | ns<<a.first<<"\t"<<a.second<<"\t"; | |
343 | if(auto hit=rplookup(addresses, a.first)) { | |
344 | for(const auto& b : *hit) | |
345 | ns<<b.toString()<<"\t"; | |
346 | } | |
347 | else | |
348 | needres<<a.first<<"\n"; | |
349 | ns<<"\n"; | |
350 | } | |
351 | return 0; | |
352 | } | |
353 | ||
354 | int resolveNS(const std::string& fname) | |
355 | { | |
356 | string line; | |
357 | ifstream needres(fname); | |
358 | if(!needres) | |
359 | unixDie("Unable to open file "+fname); | |
360 | vector<DNSName> tores; | |
361 | while(getline(needres,line)) { | |
362 | tores.push_back(DNSName(line)); | |
363 | } | |
364 | cerr<<"Going to resolve "<<tores.size()<<" names"<<endl; | |
365 | std::thread stats(printStats); | |
366 | map<DNSName, vector<ComboAddress>> output; | |
367 | SendReceiveRes sr(ComboAddress("192.168.1.2", 53), output); | |
368 | Inflighter<vector<DNSName>, SendReceiveRes> inflighter(tores, sr); | |
369 | inflighter.d_maxInFlight = 1000; | |
370 | inflighter.d_timeoutSeconds = 3; | |
371 | inflighter.d_burst = 100; | |
372 | for(;;) { | |
373 | try { | |
374 | inflighter.run(); | |
375 | break; | |
376 | } | |
377 | catch(std::exception& e) { | |
378 | cerr<<"Caught exception: "<<e.what()<<endl; | |
379 | } | |
380 | } | |
381 | g_stop=true; | |
382 | stats.join(); | |
383 | return EXIT_SUCCESS; | |
384 | } | |
385 | ||
386 | void readRESNames(const std::string& fname, map<DNSName, vector<ComboAddress>>& addrs) | |
387 | { | |
388 | ifstream ifs(fname); | |
389 | if(!ifs) | |
390 | unixDie("Reading resolved names from "+fname+": "+string(strerror(errno))); | |
391 | vector<string> parts; | |
392 | string line; | |
393 | addrs.clear(); | |
394 | while(getline(ifs, line)) { | |
395 | parts.clear(); | |
396 | stringtok(parts, line,"\t"); | |
397 | for(unsigned int n=2; n < parts.size(); ++n) | |
398 | addrs[DNSName(parts[0])].push_back(ComboAddress(parts[n], 53)); | |
399 | } | |
400 | //EARTH.DOMAINS.SHELTEK.CA. 0 67.15.253.219 67.15.47.188 67.15.253.252 67.15.253.251 67.15.47.189 67.15.253.220 | |
401 | cerr<<"Got "<<addrs.size()<<" resolved nameserver names from file"<<endl; | |
402 | ||
403 | } | |
404 | ||
405 | int main(int argc, char**argv) | |
406 | try | |
407 | { | |
408 | g_quiet=true; | |
409 | reportAllTypes(); | |
410 | string mode=argv[1]; | |
411 | if(mode == "parse-zone") { | |
412 | unsigned int limit = 0; | |
413 | if(argc > 3) | |
414 | limit = atoi(argv[3]); | |
415 | ||
416 | return parseZone(argv[2], limit); | |
417 | } | |
418 | else if(mode=="resolve-ns") { | |
419 | return resolveNS(string(argv[2])+".needres"); | |
420 | } | |
421 | else if(mode=="scan-ns") { | |
422 | ifstream ns(string(argv[2])+".nameservers"); | |
423 | g_powerdns = make_unique<ofstream>(string(argv[2])+".powerdns"); | |
424 | string line; | |
425 | int count=0; | |
426 | vector<string> parts; | |
427 | ||
428 | struct NSCount | |
429 | { | |
430 | unsigned int count{0}; | |
431 | vector<ComboAddress> addrs; | |
432 | }; | |
433 | map<DNSName, NSCount> stats; | |
434 | NSCount nscount; | |
435 | // NS1.IHOST2000.COM. 9 162.251.82.122 162.251.82.123 162.251.82.250 162.251.82.251 | |
436 | while(getline(ns, line)) { | |
437 | ++count; | |
438 | parts.clear(); | |
439 | stringtok(parts, line,"\t"); | |
440 | nscount.count=atoi(parts[1].c_str()); | |
441 | nscount.addrs.clear(); | |
442 | for(unsigned int n = 2; n < parts.size(); ++n) | |
443 | nscount.addrs.push_back(ComboAddress(parts[n], 53)); | |
444 | stats.insert({DNSName(parts[0]), nscount}); | |
445 | } | |
446 | cerr<<"Had "<<count<<" lines from summary"<<endl; | |
447 | ||
448 | map<DNSName, vector<ComboAddress>> lookedup; | |
449 | readRESNames(argv[2]+string(".resolved"), lookedup); | |
450 | ||
451 | map<ComboAddress, namecount, ComboAddress::addressOnlyLessThan> pure; | |
452 | ||
453 | unsigned int noaddrs=0; | |
454 | for(const auto& s : stats) { | |
455 | auto ptr = &s.second.addrs; | |
456 | if(ptr->empty()) { | |
457 | if(lookedup.count(s.first)) { | |
458 | ptr = &lookedup[s.first]; | |
459 | } | |
460 | else { | |
461 | //cout<<"Have no address for "<<s.first.toString()<<endl; | |
462 | noaddrs++; | |
463 | } | |
464 | } | |
465 | ||
466 | for(const auto& a : *ptr) { | |
467 | pure[a].count += s.second.count; | |
468 | pure[a].names.insert(s.first); | |
469 | } | |
470 | } | |
471 | ||
472 | cerr<<"Have "<<pure.size()<<" IP addresses to query, "<<noaddrs<<" names w/o address"<<endl; | |
473 | SendReceive sr(pure); | |
474 | vector<NSQuery> domains; | |
475 | ||
476 | Inflighter<vector<NSQuery>, SendReceive> inflighter(domains, sr); | |
477 | inflighter.d_maxInFlight = 1000; | |
478 | inflighter.d_timeoutSeconds = 3; | |
479 | inflighter.d_burst = 100; | |
480 | ||
481 | for(const auto& p : pure) { | |
482 | NSQuery nsq; | |
483 | nsq.a=p.first; | |
484 | nsq.nsnames = p.second.names; | |
485 | nsq.count = p.second.count; | |
486 | ||
487 | nsq.qname=DNSName("version.bind"); | |
488 | domains.push_back(nsq); | |
489 | nsq.qname=DNSName("id.server"); | |
490 | domains.push_back(nsq); | |
491 | nsq.qname=DNSName("bind.version"); | |
492 | domains.push_back(nsq); | |
493 | } | |
494 | ||
495 | sort(domains.begin(), domains.end(), [](const NSQuery& a, const NSQuery& b) { return b.count < a.count; }); | |
496 | for(;;) { | |
497 | try { | |
498 | inflighter.run(); | |
499 | break; | |
500 | } | |
501 | catch(std::exception& e) { | |
502 | cerr<<"Caught exception: "<<e.what()<<endl; | |
503 | } | |
504 | } | |
505 | } | |
506 | else if(mode=="score-ns") { | |
507 | std::unordered_set<DNSName> powerdns; | |
508 | ifstream ifs(string(argv[2])+".powerdns"); | |
509 | string line; | |
510 | vector<string> parts; | |
511 | while(getline(ifs,line)) { | |
512 | // 64.96.240.53 1234 NS1.UNIREGISTRYMARKET.LINK. NS1.INTERNETTRAFFIC.COM. BUY.INTERNETTRAFFIC.COM. NS3.SECUREDOFFERS.COM. NS3.GI.NET. NS3.IT.GI.NET. NS3.EASILY.NET. | |
513 | parts.clear(); | |
514 | stringtok(parts, line); | |
515 | for(unsigned int n=2; n < parts.size(); ++n) | |
516 | powerdns.insert(DNSName(parts[n])); | |
517 | } | |
518 | cerr<<"Have "<<powerdns.size()<<" known NS names that are PowerDNS"<<endl; | |
519 | ZoneParserTNG zpt(argv[2]); | |
520 | DNSResourceRecord rr; | |
521 | ||
522 | unsigned int pdnscount=0; | |
523 | set<DNSName> seen, pdnsdomains; | |
524 | int count=0; | |
525 | while(zpt.get(rr)) { | |
526 | if(!seen.count(rr.qname)) { | |
527 | seen.insert(rr.qname); | |
528 | } | |
529 | if(rr.qtype.getCode() == QType::NS && powerdns.count(DNSName(rr.content)) && !pdnsdomains.count(DNSName(rr.qname))) { | |
530 | pdnscount++; | |
531 | pdnsdomains.insert(DNSName(rr.qname)); | |
532 | } | |
533 | if(!(count%100000)) { | |
534 | cerr<<"\rUnique domains: "<<seen.size()<<", PowerDNS domains: "<<pdnsdomains.size()<<" ("<<(pdnsdomains.size()*100.0/seen.size())<<"%)"; | |
535 | } | |
536 | count++; | |
537 | } | |
538 | cerr<<"\n"; | |
539 | } | |
540 | else { | |
541 | cerr<<"Unknown mode "<<argv[1]<<endl; | |
542 | } | |
543 | // cout<<g_mtracer->topAllocatorsString(20)<<endl; | |
544 | } | |
545 | catch(PDNSException& pe) { | |
546 | cerr<<"Fatal error: "<<pe.reason<<endl; | |
547 | } |