]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/sdig.cc
dnsdist: Add HTTPStatusAction to return a specific HTTP response
[thirdparty/pdns.git] / pdns / sdig.cc
CommitLineData
870a0fe4
AT
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
ff6a1e7b 4#include "dnsparser.hh"
ea892f70 5#include "ednsoptions.hh"
ff6a1e7b
BH
6#include "sstuff.hh"
7#include "misc.hh"
a0a276c2
BH
8#include "dnswriter.hh"
9#include "dnsrecords.hh"
b0d4fb45 10#include "statbag.hh"
c5c4fbdc 11#include <boost/array.hpp>
a7d9ffb3 12#include "ednssubnet.hh"
f33f80ba
PD
13
14#ifdef HAVE_LIBCURL
ebcc91b9 15#include "minicurl.hh"
f33f80ba
PD
16#endif
17
b0d4fb45 18StatBag S;
ff6a1e7b 19
6e4c4a26
PD
20bool hidettl=false;
21
22string ttl(uint32_t ttl)
23{
24 if(hidettl)
25 return "[ttl]";
26 else
27 return std::to_string(ttl);
28}
29
13f61288
PL
30void usage() {
31 cerr<<"sdig"<<endl;
0b8cdaf1 32 cerr<<"Syntax: sdig IP-ADDRESS-OR-DOH-URL PORT QUESTION QUESTION-TYPE [dnssec] [ednssubnet SUBNET/MASK] [hidesoadetails] [hidettl] [recurse] [showflags] [tcp] [xpf XPFDATA]"<<endl;
13f61288
PL
33}
34
a052b7eb 35const string nameForClass(uint16_t qclass, uint16_t qtype)
2f406790 36{
a052b7eb
PD
37 if (qtype == QType::OPT) return "IN";
38
2f406790
PD
39 switch(qclass) {
40 case QClass::IN: return "IN";
41 case QClass::CHAOS: return "CHAOS";
42 case QClass::NONE: return "NONE";
43 case QClass::ANY: return "ANY";
44 default: return string("CLASS")+std::to_string(qclass);
45 }
46}
47
ff6a1e7b
BH
48int main(int argc, char** argv)
49try
50{
ee6c3a6b 51 bool dnssec=false;
b97043ee 52 bool recurse=false;
c5c4fbdc 53 bool tcp=false;
b8adb30d 54 bool showflags=false;
b19ad29b 55 bool hidesoadetails=false;
ebcc91b9 56 bool doh=false;
a7d9ffb3 57 boost::optional<Netmask> ednsnm;
4bc27517
PD
58 uint16_t xpfcode = 0, xpfversion = 0, xpfproto = 0;
59 char *xpfsrc = NULL, *xpfdst = NULL;
13f61288
PL
60
61 for(int i=1; i<argc; i++) {
62 if ((string) argv[i] == "--help") {
63 usage();
64 exit(EXIT_SUCCESS);
65 }
66
67 if ((string) argv[i] == "--version") {
68 cerr<<"sdig "<<VERSION<<endl;
69 exit(EXIT_SUCCESS);
70 }
71 }
a0a276c2 72
ee6c3a6b 73 if(argc < 5) {
13f61288 74 usage();
a8ad4624
BH
75 exit(EXIT_FAILURE);
76 }
a0a276c2 77
13f61288
PL
78 reportAllTypes();
79
b19ad29b
RA
80 if (argc > 5) {
81 for(int i=5; i<argc; i++) {
82 if (strcmp(argv[i], "dnssec") == 0)
83 dnssec=true;
84 if (strcmp(argv[i], "recurse") == 0)
85 recurse=true;
b0d6eced
KM
86 if (strcmp(argv[i], "showflags") == 0)
87 showflags=true;
b19ad29b
RA
88 if (strcmp(argv[i], "hidesoadetails") == 0)
89 hidesoadetails=true;
6e4c4a26
PD
90 if (strcmp(argv[i], "hidettl") == 0)
91 hidettl=true;
54baf8be 92 if (strcmp(argv[i], "tcp") == 0)
93 tcp=true;
a7d9ffb3 94 if (strcmp(argv[i], "ednssubnet") == 0) {
4bc27517
PD
95 if(argc < i+2) {
96 cerr<<"ednssubnet needs an argument"<<endl;
f359e10f
CH
97 exit(EXIT_FAILURE);
98 }
4bc27517
PD
99 ednsnm=Netmask(argv[++i]);
100 }
101 if (strcmp(argv[i], "xpf") == 0) {
102 if(argc < i+6) {
103 cerr<<"xpf needs five arguments"<<endl;
104 exit(EXIT_FAILURE);
105 }
106 xpfcode = atoi(argv[++i]);
107 xpfversion = atoi(argv[++i]);
108 xpfproto = atoi(argv[++i]);
109 xpfsrc = argv[++i];
110 xpfdst = argv[++i];
b19ad29b
RA
111 }
112 }
b97043ee
PD
113 }
114
a0a276c2 115 vector<uint8_t> packet;
bca6643b 116
eaedd091 117 DNSPacketWriter pw(packet, DNSName(argv[3]), DNSRecordContent::TypeToNumber(argv[4]));
a0a276c2 118
a7d9ffb3 119 if(dnssec || ednsnm || getenv("SDIGBUFSIZE"))
ee6c3a6b 120 {
794c2f92
PD
121 char *sbuf=getenv("SDIGBUFSIZE");
122 int bufsize;
123 if(sbuf)
124 bufsize=atoi(sbuf);
125 else
126 bufsize=2800;
a7d9ffb3 127 DNSPacketWriter::optvect_t opts;
128 if(ednsnm) {
a7d9ffb3 129 EDNSSubnetOpts eo;
130 eo.source = *ednsnm;
ea892f70 131 opts.push_back(make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(eo)));
a7d9ffb3 132 }
133
134 pw.addOpt(bufsize, 0, dnssec ? EDNSOpts::DNSSECOK : 0, opts);
ee6c3a6b
PD
135 pw.commit();
136 }
b97043ee 137
4bc27517 138 if(xpfcode)
2023616e 139 {
4bc27517
PD
140 ComboAddress src(xpfsrc), dst(xpfdst);
141 pw.startRecord(DNSName("."), xpfcode, 0, 1, DNSResourceRecord::ADDITIONAL);
142 // xpf->toPacket(pw);
143 pw.xfr8BitInt(xpfversion);
144 pw.xfr8BitInt(xpfproto);
145 pw.xfrCAWithoutPort(xpfversion, src);
146 pw.xfrCAWithoutPort(xpfversion, dst);
147 pw.xfrCAPort(src);
148 pw.xfrCAPort(dst);
2023616e
PD
149 pw.commit();
150 }
151
b97043ee
PD
152 if(recurse)
153 {
154 pw.getHeader()->rd=true;
155 }
fdf05fd4 156
ff6a1e7b 157 string reply;
ebcc91b9
PD
158 string question(packet.begin(), packet.end());
159 ComboAddress dest;
f33f80ba 160 if(*argv[1]=='h') {
ebcc91b9 161 doh = true;
f33f80ba
PD
162 }
163 else {
ebcc91b9 164 dest = ComboAddress(argv[1] + (*argv[1]=='@'), atoi(argv[2]));
f33f80ba 165 }
ebcc91b9
PD
166
167 if(doh) {
f33f80ba 168#ifdef HAVE_LIBCURL
ebcc91b9
PD
169 MiniCurl mc;
170 MiniCurl::MiniCurlHeaders mch;
171 mch.insert(std::make_pair("Content-Type", "application/dns-message"));
172 mch.insert(std::make_pair("Accept", "application/dns-message"));
173 reply = mc.postURL(argv[1], question, mch);
f33f80ba
PD
174#else
175 throw PDNSException("please link sdig against libcurl for DoH support");
176#endif
ebcc91b9
PD
177 }
178 else if(tcp) {
4a549837 179 Socket sock(dest.sin4.sin_family, SOCK_STREAM);
c5c4fbdc
PD
180 sock.connect(dest);
181 uint16_t len;
182 len = htons(packet.size());
183 if(sock.write((char *) &len, 2) != 2)
3f81d239 184 throw PDNSException("tcp write failed");
c5c4fbdc 185
ebcc91b9 186 sock.writen(question);
c5c4fbdc
PD
187
188 if(sock.read((char *) &len, 2) != 2)
3f81d239 189 throw PDNSException("tcp read failed");
c5c4fbdc
PD
190
191 len=ntohs(len);
192 char *creply = new char[len];
193 int n=0;
194 int numread;
195 while(n<len) {
196 numread=sock.read(creply+n, len-n);
197 if(numread<0)
3f81d239 198 throw PDNSException("tcp read failed");
c5c4fbdc
PD
199 n+=numread;
200 }
201
202 reply=string(creply, len);
203 delete[] creply;
204 }
205 else //udp
206 {
4a549837 207 Socket sock(dest.sin4.sin_family, SOCK_DGRAM);
ebcc91b9 208 sock.sendTo(question, dest);
fbf18d75 209 int result=waitForData(sock.getHandle(), 10);
210 if(result < 0)
211 throw std::runtime_error("Error waiting for data: "+string(strerror(errno)));
212 if(!result)
213 throw std::runtime_error("Timeout waiting for data");
c5c4fbdc
PD
214 sock.recvFrom(reply, dest);
215 }
27c0050c 216 MOADNSParser mdp(false, reply);
9d7fa327 217 cout<<"Reply to question for qname='"<<mdp.d_qname.toString()<<"', qtype="<<DNSRecordContent::NumberToType(mdp.d_qtype)<<endl;
a7d9ffb3 218 cout<<"Rcode: "<<mdp.d_header.rcode<<" ("<<RCode::to_s(mdp.d_header.rcode)<<"), RD: "<<mdp.d_header.rd<<", QR: "<<mdp.d_header.qr;
7fc69fd0 219 cout<<", TC: "<<mdp.d_header.tc<<", AA: "<<mdp.d_header.aa<<", opcode: "<<mdp.d_header.opcode<<endl;
945a9ad4 220
ff6a1e7b 221 for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i!=mdp.d_answers.end(); ++i) {
a052b7eb
PD
222 cout<<i->first.d_place-1<<"\t"<<i->first.d_name.toString()<<"\t"<<nameForClass(i->first.d_class, i->first.d_type)<<"\t"<<DNSRecordContent::NumberToType(i->first.d_type);
223 if(i->first.d_class == QClass::IN)
b19ad29b 224 {
a052b7eb
PD
225 if(i->first.d_type == QType::RRSIG)
226 {
227 string zoneRep = i->first.d_content->getZoneRepresentation();
228 vector<string> parts;
229 stringtok(parts, zoneRep);
230 cout<<"\t"<<ttl(i->first.d_ttl)<<"\t"<< parts[0]<<" "<<parts[1]<<" "<<parts[2]<<" "<<parts[3]<<" [expiry] [inception] [keytag] "<<parts[7]<<" ...\n";
231 continue;
232 }
233 if(!showflags && i->first.d_type == QType::NSEC3)
234 {
235 string zoneRep = i->first.d_content->getZoneRepresentation();
236 vector<string> parts;
237 stringtok(parts, zoneRep);
238 cout<<"\t"<<ttl(i->first.d_ttl)<<"\t"<< parts[0]<<" [flags] "<<parts[2]<<" "<<parts[3]<<" "<<parts[4];
239 for(vector<string>::iterator iter = parts.begin()+5; iter != parts.end(); ++iter)
240 cout<<" "<<*iter;
241 cout<<"\n";
242 continue;
243 }
244 if(i->first.d_type == QType::DNSKEY)
245 {
246 string zoneRep = i->first.d_content->getZoneRepresentation();
247 vector<string> parts;
248 stringtok(parts, zoneRep);
249 cout<<"\t"<<ttl(i->first.d_ttl)<<"\t"<< parts[0]<<" "<<parts[1]<<" "<<parts[2]<<" ...\n";
250 continue;
251 }
252 if (i->first.d_type == QType::SOA && hidesoadetails)
253 {
254 string zoneRep = i->first.d_content->getZoneRepresentation();
255 vector<string> parts;
256 stringtok(parts, zoneRep);
257 cout<<"\t"<<ttl(i->first.d_ttl)<<"\t"<<parts[0]<<" "<<parts[1]<<" [serial] "<<parts[3]<<" "<<parts[4]<<" "<<parts[5]<<" "<<parts[6]<<"\n";
258 continue;
259 }
794c2f92 260 }
a052b7eb 261 cout<<"\t"<<ttl(i->first.d_ttl)<<"\t"<< i->first.d_content->getZoneRepresentation()<<"\n";
ff6a1e7b 262 }
fdf05fd4 263
0c70797e
BH
264 EDNSOpts edo;
265 if(getEDNSOpts(mdp, &edo)) {
a5d9f5f9 266// cerr<<"Have "<<edo.d_options.size()<<" options!"<<endl;
0c70797e 267 for(vector<pair<uint16_t, string> >::const_iterator iter = edo.d_options.begin();
4957a608
BH
268 iter != edo.d_options.end();
269 ++iter) {
ea892f70 270 if(iter->first == EDNSOptionCode::ECS) {// 'EDNS subnet'
a7d9ffb3 271 EDNSSubnetOpts reso;
272 if(getEDNSSubnetOptsFromString(iter->second, &reso)) {
273 cerr<<"EDNS Subnet response: "<<reso.source.toString()<<", scope: "<<reso.scope.toString()<<", family = "<<reso.scope.getNetwork().sin4.sin_family<<endl;
274 }
275 }
667f39c1
PD
276 else if(iter->first == EDNSOptionCode::PADDING) {
277 cerr<<"EDNS Padding size: "<<(iter->second.size())<<endl;
278 }
fdf05fd4 279 else {
4957a608 280 cerr<<"Have unknown option "<<(int)iter->first<<endl;
fdf05fd4 281 }
0c70797e
BH
282 }
283
284 }
ff6a1e7b 285}
0c70797e 286catch(std::exception &e)
ff6a1e7b
BH
287{
288 cerr<<"Fatal: "<<e.what()<<endl;
289}
1a4843e0 290catch(PDNSException &e)
291{
292 cerr<<"Fatal: "<<e.reason<<endl;
293}