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.
25 #include "dnsparser.hh"
28 #include "dnswriter.hh"
29 #include "dnsrecords.hh"
32 #include "dnssecinfra.hh"
37 typedef std::pair
<string
,string
> nsec3
;
38 typedef set
<nsec3
> nsec3set
;
39 typedef map
<string
, int> nsec3types
;
41 static string
nsec3Hash(const DNSName
&qname
, const string
&salt
, unsigned int iters
)
43 NSEC3PARAMRecordContent ns3prc
;
44 ns3prc
.d_iterations
= iters
;
46 return toBase32Hex(hashQNameWithSalt(ns3prc
, qname
));
49 static void proveOrDeny(const nsec3set
&nsec3s
, const nsec3types
&nsec3t
, const DNSName
&qname
, const string
&salt
, unsigned int iters
, set
<DNSName
> &proven
, set
<DNSName
> &denied
)
51 string hashed
= nsec3Hash(qname
, salt
, iters
);
53 // cerr<<"proveOrDeny(.., '"<<qname<<"', ..)"<<endl;
54 // cerr<<"hashed: "<<hashed<<endl;
55 for(nsec3set::const_iterator pos
=nsec3s
.begin(); pos
!= nsec3s
.end(); ++pos
) {
56 string base
=(*pos
).first
;
57 string next
=(*pos
).second
;
62 cout
<<qname
.toString()<<" ("<<hashed
<<") proven by base ("<<nsec3t
.at(base
)<<" types) of "<<base
<<".."<<next
<<endl
;
67 cout
<<qname
.toString()<<" ("<<hashed
<<") proven by next of "<<base
<<".."<<next
<<endl
;
69 if((hashed
> base
&& hashed
< next
) ||
70 (next
< base
&& (hashed
< next
|| hashed
> base
)))
73 cout
<<qname
.toString()<<" ("<<hashed
<<") denied by "<<base
<<".."<<next
<<endl
;
75 if (base
== next
&& base
!= hashed
)
78 cout
<<qname
.toString()<<" ("<<hashed
<<") denied by "<<base
<<".."<<next
<<endl
;
84 cerr
<<"nsec3dig"<<endl
;
85 cerr
<<"Syntax: nsec3dig IP-ADDRESS PORT QUESTION QUESTION-TYPE [recurse]\n";
88 int main(int argc
, char** argv
)
95 for (int i
= 1; i
< argc
; i
++) {
96 if ((string
) argv
[i
] == "--help") {
101 if ((string
) argv
[i
] == "--version") {
102 cerr
<<"nsec3dig "<<VERSION
<<endl
;
112 if(argc
> 5 && strcmp(argv
[5], "recurse")==0)
117 vector
<uint8_t> packet
;
118 DNSName
qname(argv
[3]);
119 DNSPacketWriter
pw(packet
, qname
, DNSRecordContent::TypeToNumber(argv
[4]));
123 pw
.getHeader()->rd
=true;
124 pw
.getHeader()->cd
=true;
127 pw
.addOpt(2800, 0, EDNSOpts::DNSSECOK
);
131 ComboAddress
dest(argv
[1] + (*argv
[1]=='@'), atoi(argv
[2]));
132 Socket
sock(dest
.sin4
.sin_family
, SOCK_STREAM
);
135 len
= htons(packet
.size());
136 if(sock
.write((char *) &len
, 2) != 2)
137 throw PDNSException("tcp write failed");
139 sock
.writen(string(packet
.begin(), packet
.end()));
141 if(sock
.read((char *) &len
, 2) != 2)
142 throw PDNSException("tcp read failed");
145 auto creply
= std::make_unique
<char[]>(len
);
149 numread
=sock
.read(creply
.get()+n
, len
-n
);
151 throw PDNSException("tcp read failed");
155 string
reply(creply
.get(), len
);
157 MOADNSParser
mdp(false, reply
);
158 cout
<<"Reply to question for qname='"<<mdp
.d_qname
<<"', qtype="<<DNSRecordContent::NumberToType(mdp
.d_qtype
)<<endl
;
159 cout
<<"Rcode: "<<mdp
.d_header
.rcode
<<", RD: "<<mdp
.d_header
.rd
<<", QR: "<<mdp
.d_header
.qr
;
160 cout
<<", TC: "<<mdp
.d_header
.tc
<<", AA: "<<mdp
.d_header
.aa
<<", opcode: "<<mdp
.d_header
.opcode
<<endl
;
163 set
<DNSName
> namesseen
;
164 set
<DNSName
> namestocheck
;
169 for(MOADNSParser::answers_t::const_iterator i
=mdp
.d_answers
.begin(); i
!=mdp
.d_answers
.end(); ++i
) {
170 if(i
->first
.d_type
== QType::NSEC3
)
172 // cerr<<"got nsec3 ["<<i->first.d_name<<"]"<<endl;
173 // cerr<<i->first.d_content->getZoneRepresentation()<<endl;
174 const auto r
= getRR
<NSEC3RecordContent
>(i
->first
);
178 // nsec3.insert(new nsec3()
179 // cerr<<toBase32Hex(r.d_nexthash)<<endl;
180 nsec3s
.emplace(toLower(i
->first
.d_name
.getRawLabel(0)), toBase32Hex(r
->d_nexthash
));
181 nsec3salt
= r
->d_salt
;
182 nsec3iters
= r
->d_iterations
;
183 nsec3t
.emplace(toLower(i
->first
.d_name
.getRawLabel(0)), r
->numberOfTypesSet());
187 // cerr<<"namesseen.insert('"<<i->first.d_name<<"')"<<endl;
188 names
.insert(i
->first
.d_name
);
189 namesseen
.insert(i
->first
.d_name
);
192 if(i
->first
.d_type
== QType::CNAME
)
194 namesseen
.insert(DNSName(i
->first
.getContent()->getZoneRepresentation()));
197 cout
<< i
->first
.d_place
- 1 << "\t" << i
->first
.d_name
.toString() << "\t" << i
->first
.d_ttl
<< "\tIN\t" << DNSRecordContent::NumberToType(i
->first
.d_type
);
198 cout
<< "\t" << i
->first
.getContent()->getZoneRepresentation() << "\n";
202 cerr
<<"got "<<names
.size()<<" names"<<endl
;
203 for(set
<string
>::const_iterator pos
=names
.begin(); pos
!= names
.end(); ++pos
) {
204 cerr
<<"name: "<<*pos
<<endl
;
206 cerr
<<"got "<<nsec3s
.size()<<" names"<<endl
;
207 for(nsec3set::const_iterator pos
=nsec3s
.begin(); pos
!= nsec3s
.end(); ++pos
) {
208 cerr
<<"nsec3: "<<(*pos
).first
<<".."<<(*pos
).second
<<endl
;
212 cout
<<"== nsec3 prove/deny report follows =="<<endl
;
215 namesseen
.insert(qname
);
216 for(const auto &name
: namesseen
)
218 DNSName
shorter(name
);
220 namestocheck
.insert(shorter
);
221 } while(shorter
.chopOff());
223 for(const auto &name
: namestocheck
)
225 proveOrDeny(nsec3s
, nsec3t
, name
, nsec3salt
, nsec3iters
, proven
, denied
);
226 proveOrDeny(nsec3s
, nsec3t
, g_wildcarddnsname
+name
, nsec3salt
, nsec3iters
, proven
, denied
);
229 if(names
.count(qname
))
231 cout
<<"== qname found in names, investigating NSEC3s in case it's a wildcard"<<endl
;
232 // exit(EXIT_SUCCESS);
234 // cout<<"== qname not found in names, investigating denial"<<endl;
235 if(proven
.count(qname
))
237 cout
<<"qname found proven, NODATA response?"<<endl
;
240 DNSName shorter
=qname
;
244 while(shorter
.chopOff())
246 if(proven
.count(shorter
))
250 cout
<<"found closest encloser at "<<encloser
.toString()<<endl
;
251 cout
<<"next closer is "<<nextcloser
.toString()<<endl
;
256 if(encloser
.countLabels() && nextcloser
.countLabels())
258 if(denied
.count(nextcloser
))
260 cout
<<"next closer ("<<nextcloser
.toString()<<") is denied correctly"<<endl
;
264 cout
<<"next closer ("<<nextcloser
.toString()<<") NOT denied"<<endl
;
266 DNSName wcplusencloser
=g_wildcarddnsname
+encloser
;
267 if(denied
.count(wcplusencloser
))
269 cout
<<"wildcard at encloser ("<<wcplusencloser
.toString()<<") is denied correctly"<<endl
;
271 else if(proven
.count(wcplusencloser
))
273 cout
<<"wildcard at encloser ("<<wcplusencloser
.toString()<<") is proven"<<endl
;
277 cout
<<"wildcard at encloser ("<<wcplusencloser
.toString()<<") is NOT denied or proven"<<endl
;
282 catch(std::exception
&e
)
284 cerr
<<"Fatal: "<<e
.what()<<endl
;
286 catch(PDNSException
&e
)
288 cerr
<<"Fatal: "<<e
.reason
<<endl
;