]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/nsec3dig.cc
rec: ensure correct service user on debian
[thirdparty/pdns.git] / pdns / nsec3dig.cc
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 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "dnsparser.hh"
26 #include "sstuff.hh"
27 #include "misc.hh"
28 #include "dnswriter.hh"
29 #include "dnsrecords.hh"
30 #include "statbag.hh"
31 #include "base32.hh"
32 #include "dnssecinfra.hh"
33
34
35 StatBag S;
36
37 typedef std::pair<string,string> nsec3;
38 typedef set<nsec3> nsec3set;
39
40 string nsec3Hash(const DNSName &qname, const string &salt, unsigned int iters)
41 {
42 NSEC3PARAMRecordContent ns3prc;
43 ns3prc.d_iterations = iters;
44 ns3prc.d_salt = salt;
45 return toBase32Hex(hashQNameWithSalt(ns3prc, qname));
46 }
47
48 void proveOrDeny(const nsec3set &nsec3s, const DNSName &qname, const string &salt, unsigned int iters, set<DNSName> &proven, set<DNSName> &denied)
49 {
50 string hashed = nsec3Hash(qname, salt, iters);
51
52 // cerr<<"proveOrDeny(.., '"<<qname<<"', ..)"<<endl;
53 // cerr<<"hashed: "<<hashed<<endl;
54 for(nsec3set::const_iterator pos=nsec3s.begin(); pos != nsec3s.end(); ++pos) {
55 string base=(*pos).first;
56 string next=(*pos).second;
57
58 if(hashed == base)
59 {
60 proven.insert(qname);
61 cout<<qname.toString()<<" ("<<hashed<<") proven by base of "<<base<<".."<<next<<endl;
62 }
63 if(hashed == next)
64 {
65 proven.insert(qname);
66 cout<<qname.toString()<<" ("<<hashed<<") proven by next of "<<base<<".."<<next<<endl;
67 }
68 if((hashed > base && hashed < next) ||
69 (next < base && (hashed < next || hashed > base)))
70 {
71 denied.insert(qname);
72 cout<<qname.toString()<<" ("<<hashed<<") denied by "<<base<<".."<<next<<endl;
73 }
74 if (base == next && base != hashed)
75 {
76 denied.insert(qname);
77 cout<<qname.toString()<<" ("<<hashed<<") denied by "<<base<<".."<<next<<endl;
78 }
79 }
80 }
81
82 void usage() {
83 cerr<<"nsec3dig"<<endl;
84 cerr<<"Syntax: nsec3dig IP-ADDRESS PORT QUESTION QUESTION-TYPE [recurse]\n";
85 }
86
87 int main(int argc, char** argv)
88 try
89 {
90 bool recurse=false;
91
92 reportAllTypes();
93
94 for (int i = 1; i < argc; i++) {
95 if ((string) argv[i] == "--help") {
96 usage();
97 return EXIT_SUCCESS;
98 }
99
100 if ((string) argv[i] == "--version") {
101 cerr<<"nsec3dig "<<VERSION<<endl;
102 return EXIT_SUCCESS;
103 }
104 }
105
106 if(argc < 5) {
107 usage();
108 exit(EXIT_FAILURE);
109 }
110
111 // FIXME: turn recurse and dnssec into proper flags or something
112 if(argc > 5 && strcmp(argv[5], "recurse")==0)
113 {
114 recurse=true;
115 }
116
117 vector<uint8_t> packet;
118 DNSName qname(argv[3]);
119 DNSPacketWriter pw(packet, qname, DNSRecordContent::TypeToNumber(argv[4]));
120
121 if(recurse)
122 {
123 pw.getHeader()->rd=true;
124 pw.getHeader()->cd=true;
125 }
126
127 pw.addOpt(2800, 0, EDNSOpts::DNSSECOK);
128 pw.commit();
129
130
131 ComboAddress dest(argv[1] + (*argv[1]=='@'), atoi(argv[2]));
132 Socket sock(dest.sin4.sin_family, SOCK_STREAM);
133 sock.connect(dest);
134 uint16_t len;
135 len = htons(packet.size());
136 if(sock.write((char *) &len, 2) != 2)
137 throw PDNSException("tcp write failed");
138
139 sock.writen(string(packet.begin(), packet.end()));
140
141 if(sock.read((char *) &len, 2) != 2)
142 throw PDNSException("tcp read failed");
143
144 len=ntohs(len);
145 char *creply = new char[len];
146 int n=0;
147 int numread;
148 while(n<len) {
149 numread=sock.read(creply+n, len-n);
150 if(numread<0)
151 throw PDNSException("tcp read failed");
152 n+=numread;
153 }
154
155 string reply(creply, len);
156 delete[] creply;
157
158 MOADNSParser mdp(false, reply);
159 cout<<"Reply to question for qname='"<<mdp.d_qname<<"', qtype="<<DNSRecordContent::NumberToType(mdp.d_qtype)<<endl;
160 cout<<"Rcode: "<<mdp.d_header.rcode<<", RD: "<<mdp.d_header.rd<<", QR: "<<mdp.d_header.qr;
161 cout<<", TC: "<<mdp.d_header.tc<<", AA: "<<mdp.d_header.aa<<", opcode: "<<mdp.d_header.opcode<<endl;
162
163 set<DNSName> names;
164 set<DNSName> namesseen;
165 set<DNSName> namestocheck;
166 nsec3set nsec3s;
167 string nsec3salt;
168 int nsec3iters = 0;
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)
171 {
172 // cerr<<"got nsec3 ["<<i->first.d_name<<"]"<<endl;
173 // cerr<<i->first.d_content->getZoneRepresentation()<<endl;
174 const auto r = std::dynamic_pointer_cast<NSEC3RecordContent>(i->first.d_content);
175 if (!r) {
176 continue;
177 }
178 // nsec3.insert(new nsec3()
179 // cerr<<toBase32Hex(r.d_nexthash)<<endl;
180 vector<string> parts;
181 string sname=i->first.d_name.toString();
182 boost::split(parts, sname /* FIXME400 */, boost::is_any_of("."));
183 nsec3s.insert(make_pair(toLower(parts[0]), toBase32Hex(r->d_nexthash)));
184 nsec3salt = r->d_salt;
185 nsec3iters = r->d_iterations;
186 }
187 else
188 {
189 // cerr<<"namesseen.insert('"<<i->first.d_name<<"')"<<endl;
190 names.insert(i->first.d_name);
191 namesseen.insert(i->first.d_name);
192 }
193
194 if(i->first.d_type == QType::CNAME)
195 {
196 namesseen.insert(DNSName(i->first.d_content->getZoneRepresentation()));
197 }
198
199 cout<<i->first.d_place-1<<"\t"<<i->first.d_name.toString()<<"\tIN\t"<<DNSRecordContent::NumberToType(i->first.d_type);
200 cout<<"\t"<<i->first.d_ttl<<"\t"<< i->first.d_content->getZoneRepresentation()<<"\n";
201 }
202
203 #if 0
204 cerr<<"got "<<names.size()<<" names"<<endl;
205 for(set<string>::const_iterator pos=names.begin(); pos != names.end(); ++pos) {
206 cerr<<"name: "<<*pos<<endl;
207 }
208 cerr<<"got "<<nsec3s.size()<<" names"<<endl;
209 for(nsec3set::const_iterator pos=nsec3s.begin(); pos != nsec3s.end(); ++pos) {
210 cerr<<"nsec3: "<<(*pos).first<<".."<<(*pos).second<<endl;
211 }
212 #endif
213
214 cout<<"== nsec3 prove/deny report follows =="<<endl;
215 set<DNSName> proven;
216 set<DNSName> denied;
217 namesseen.insert(qname);
218 for(const auto &name: namesseen)
219 {
220 DNSName shorter(name);
221 do {
222 namestocheck.insert(shorter);
223 } while(shorter.chopOff());
224 }
225 for(const auto &name: namestocheck)
226 {
227 proveOrDeny(nsec3s, name, nsec3salt, nsec3iters, proven, denied);
228 proveOrDeny(nsec3s, g_wildcarddnsname+name, nsec3salt, nsec3iters, proven, denied);
229 }
230
231 if(names.count(qname))
232 {
233 cout<<"== qname found in names, investigating NSEC3s in case it's a wildcard"<<endl;
234 // exit(EXIT_SUCCESS);
235 }
236 // cout<<"== qname not found in names, investigating denial"<<endl;
237 if(proven.count(qname))
238 {
239 cout<<"qname found proven, NODATA response?"<<endl;
240 exit(EXIT_SUCCESS);
241 }
242 DNSName shorter=qname;
243 DNSName encloser;
244 DNSName nextcloser;
245 DNSName prev(qname);
246 while(shorter.chopOff())
247 {
248 if(proven.count(shorter))
249 {
250 encloser=shorter;
251 nextcloser=prev;
252 cout<<"found closest encloser at "<<encloser.toString()<<endl;
253 cout<<"next closer is "<<nextcloser.toString()<<endl;
254 break;
255 }
256 prev=shorter;
257 }
258 if(encloser.countLabels() && nextcloser.countLabels())
259 {
260 if(denied.count(nextcloser))
261 {
262 cout<<"next closer ("<<nextcloser.toString()<<") is denied correctly"<<endl;
263 }
264 else
265 {
266 cout<<"next closer ("<<nextcloser.toString()<<") NOT denied"<<endl;
267 }
268 DNSName wcplusencloser=g_wildcarddnsname+encloser;
269 if(denied.count(wcplusencloser))
270 {
271 cout<<"wildcard at encloser ("<<wcplusencloser.toString()<<") is denied correctly"<<endl;
272 }
273 else if(proven.count(wcplusencloser))
274 {
275 cout<<"wildcard at encloser ("<<wcplusencloser.toString()<<") is proven"<<endl;
276 }
277 else
278 {
279 cout<<"wildcard at encloser ("<<wcplusencloser.toString()<<") is NOT denied or proven"<<endl;
280 }
281 }
282 exit(EXIT_SUCCESS);
283 }
284 catch(std::exception &e)
285 {
286 cerr<<"Fatal: "<<e.what()<<endl;
287 }
288 catch(PDNSException &e)
289 {
290 cerr<<"Fatal: "<<e.reason<<endl;
291 }