]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/sdig.cc
4 #include "dnsparser.hh"
5 #include "dnsrecords.hh"
6 #include "dnswriter.hh"
7 #include "ednsoptions.hh"
8 #include "ednssubnet.hh"
12 #include <boost/array.hpp>
15 #include "minicurl.hh"
22 string
ttl(uint32_t ttl
)
27 return std::to_string(ttl
);
32 cerr
<< "sdig" << endl
;
33 cerr
<< "Syntax: sdig IP-ADDRESS-OR-DOH-URL PORT QUESTION QUESTION-TYPE "
34 "[dnssec] [ednssubnet SUBNET/MASK] [hidesoadetails] [hidettl] "
35 "[recurse] [showflags] [tcp] [xpf XPFDATA] [class CLASSNUM]"
39 const string
nameForClass(uint16_t qclass
, uint16_t qtype
)
41 if (qtype
== QType::OPT
)
54 return string("CLASS") + std::to_string(qclass
);
58 void fillPacket(vector
<uint8_t>& packet
, const string
& q
, const string
& t
,
59 bool dnssec
, const boost::optional
<Netmask
> ednsnm
,
60 bool recurse
, uint16_t xpfcode
, uint16_t xpfversion
,
61 uint64_t xpfproto
, char* xpfsrc
, char* xpfdst
,
64 DNSPacketWriter
pw(packet
, DNSName(q
), DNSRecordContent::TypeToNumber(t
), qclass
);
66 if (dnssec
|| ednsnm
|| getenv("SDIGBUFSIZE")) {
67 char* sbuf
= getenv("SDIGBUFSIZE");
73 DNSPacketWriter::optvect_t opts
;
78 make_pair(EDNSOptionCode::ECS
, makeEDNSSubnetOptsString(eo
)));
81 pw
.addOpt(bufsize
, 0, dnssec
? EDNSOpts::DNSSECOK
: 0, opts
);
86 ComboAddress
src(xpfsrc
), dst(xpfdst
);
87 pw
.startRecord(DNSName("."), xpfcode
, 0, QClass::IN
, DNSResourceRecord::ADDITIONAL
);
89 pw
.xfr8BitInt(xpfversion
);
90 pw
.xfr8BitInt(xpfproto
);
91 pw
.xfrCAWithoutPort(xpfversion
, src
);
92 pw
.xfrCAWithoutPort(xpfversion
, dst
);
99 pw
.getHeader()->rd
= true;
103 void printReply(const string
& reply
, bool showflags
, bool hidesoadetails
)
105 MOADNSParser
mdp(false, reply
);
106 cout
<< "Reply to question for qname='" << mdp
.d_qname
.toString()
107 << "', qtype=" << DNSRecordContent::NumberToType(mdp
.d_qtype
) << endl
;
108 cout
<< "Rcode: " << mdp
.d_header
.rcode
<< " ("
109 << RCode::to_s(mdp
.d_header
.rcode
) << "), RD: " << mdp
.d_header
.rd
110 << ", QR: " << mdp
.d_header
.qr
;
111 cout
<< ", TC: " << mdp
.d_header
.tc
<< ", AA: " << mdp
.d_header
.aa
112 << ", opcode: " << mdp
.d_header
.opcode
<< endl
;
114 for (MOADNSParser::answers_t::const_iterator i
= mdp
.d_answers
.begin();
115 i
!= mdp
.d_answers
.end(); ++i
) {
116 cout
<< i
->first
.d_place
- 1 << "\t" << i
->first
.d_name
.toString() << "\t"
117 << nameForClass(i
->first
.d_class
, i
->first
.d_type
) << "\t"
118 << DNSRecordContent::NumberToType(i
->first
.d_type
);
119 if (i
->first
.d_class
== QClass::IN
) {
120 if (i
->first
.d_type
== QType::RRSIG
) {
121 string zoneRep
= i
->first
.d_content
->getZoneRepresentation();
122 vector
<string
> parts
;
123 stringtok(parts
, zoneRep
);
124 cout
<< "\t" << ttl(i
->first
.d_ttl
) << "\t" << parts
[0] << " "
125 << parts
[1] << " " << parts
[2] << " " << parts
[3]
126 << " [expiry] [inception] [keytag] " << parts
[7] << " ...\n";
129 if (!showflags
&& i
->first
.d_type
== QType::NSEC3
) {
130 string zoneRep
= i
->first
.d_content
->getZoneRepresentation();
131 vector
<string
> parts
;
132 stringtok(parts
, zoneRep
);
133 cout
<< "\t" << ttl(i
->first
.d_ttl
) << "\t" << parts
[0] << " [flags] "
134 << parts
[2] << " " << parts
[3] << " " << parts
[4];
135 for (vector
<string
>::iterator iter
= parts
.begin() + 5;
136 iter
!= parts
.end(); ++iter
)
137 cout
<< " " << *iter
;
141 if (i
->first
.d_type
== QType::DNSKEY
) {
142 string zoneRep
= i
->first
.d_content
->getZoneRepresentation();
143 vector
<string
> parts
;
144 stringtok(parts
, zoneRep
);
145 cout
<< "\t" << ttl(i
->first
.d_ttl
) << "\t" << parts
[0] << " "
146 << parts
[1] << " " << parts
[2] << " ...\n";
149 if (i
->first
.d_type
== QType::SOA
&& hidesoadetails
) {
150 string zoneRep
= i
->first
.d_content
->getZoneRepresentation();
151 vector
<string
> parts
;
152 stringtok(parts
, zoneRep
);
153 cout
<< "\t" << ttl(i
->first
.d_ttl
) << "\t" << parts
[0] << " "
154 << parts
[1] << " [serial] " << parts
[3] << " " << parts
[4] << " "
155 << parts
[5] << " " << parts
[6] << "\n";
159 cout
<< "\t" << ttl(i
->first
.d_ttl
) << "\t"
160 << i
->first
.d_content
->getZoneRepresentation() << "\n";
164 if (getEDNSOpts(mdp
, &edo
)) {
165 // cerr<<"Have "<<edo.d_options.size()<<" options!"<<endl;
166 for (vector
<pair
<uint16_t, string
>>::const_iterator iter
= edo
.d_options
.begin();
167 iter
!= edo
.d_options
.end(); ++iter
) {
168 if (iter
->first
== EDNSOptionCode::ECS
) { // 'EDNS subnet'
170 if (getEDNSSubnetOptsFromString(iter
->second
, &reso
)) {
171 cerr
<< "EDNS Subnet response: " << reso
.source
.toString()
172 << ", scope: " << reso
.scope
.toString()
173 << ", family = " << reso
.scope
.getNetwork().sin4
.sin_family
176 } else if (iter
->first
== EDNSOptionCode::PADDING
) {
177 cerr
<< "EDNS Padding size: " << (iter
->second
.size()) << endl
;
179 cerr
<< "Have unknown option " << (int)iter
->first
<< endl
;
185 int main(int argc
, char** argv
)
188 bool recurse
= false;
190 bool showflags
= false;
191 bool hidesoadetails
= false;
193 bool fromstdin
= false;
194 boost::optional
<Netmask
> ednsnm
;
195 uint16_t xpfcode
= 0, xpfversion
= 0, xpfproto
= 0;
196 char *xpfsrc
= NULL
, *xpfdst
= NULL
;
197 uint16_t qclass
= QClass::IN
;
199 for (int i
= 1; i
< argc
; i
++) {
200 if ((string
)argv
[i
] == "--help") {
205 if ((string
)argv
[i
] == "--version") {
206 cerr
<< "sdig " << VERSION
<< endl
;
219 for (int i
= 5; i
< argc
; i
++) {
220 if (strcmp(argv
[i
], "dnssec") == 0)
222 if (strcmp(argv
[i
], "recurse") == 0)
224 if (strcmp(argv
[i
], "showflags") == 0)
226 if (strcmp(argv
[i
], "hidesoadetails") == 0)
227 hidesoadetails
= true;
228 if (strcmp(argv
[i
], "hidettl") == 0)
230 if (strcmp(argv
[i
], "tcp") == 0)
232 if (strcmp(argv
[i
], "ednssubnet") == 0) {
234 cerr
<< "ednssubnet needs an argument" << endl
;
237 ednsnm
= Netmask(argv
[++i
]);
239 if (strcmp(argv
[i
], "xpf") == 0) {
241 cerr
<< "xpf needs five arguments" << endl
;
244 xpfcode
= atoi(argv
[++i
]);
245 xpfversion
= atoi(argv
[++i
]);
246 xpfproto
= atoi(argv
[++i
]);
250 if (strcmp(argv
[i
], "class") == 0) {
252 cerr
<< "class needs an argument"<<endl
;
255 qclass
= atoi(argv
[++i
]);
262 if (*argv
[1] == 'h') {
264 } else if(strcmp(argv
[1], "stdin") == 0) {
267 dest
= ComboAddress(argv
[1] + (*argv
[1] == '@'), atoi(argv
[2]));
270 string name
= string(argv
[3]);
271 string type
= string(argv
[4]);
273 vector
<pair
<string
, string
>> questions
;
274 if (name
== "-" && type
== "-") {
276 throw PDNSException("multi-query from stdin only supported for tcp");
279 while (getline(std::cin
, line
)) {
280 auto fields
= splitField(line
, ' ');
282 questions
.push_back(make_pair(fields
.first
, fields
.second
));
285 questions
.push_back(make_pair(name
, type
));
290 vector
<uint8_t> packet
;
291 fillPacket(packet
, name
, type
, dnssec
, ednsnm
, recurse
, xpfcode
, xpfversion
,
292 xpfproto
, xpfsrc
, xpfdst
, qclass
);
294 MiniCurl::MiniCurlHeaders mch
;
295 mch
.insert(std::make_pair("Content-Type", "application/dns-message"));
296 mch
.insert(std::make_pair("Accept", "application/dns-message"));
297 string
question(packet
.begin(), packet
.end());
298 reply
= mc
.postURL(argv
[1], question
, mch
);
299 printReply(reply
, showflags
, hidesoadetails
);
301 throw PDNSException("please link sdig against libcurl for DoH support");
303 } else if (fromstdin
) {
304 std::istreambuf_iterator
<char> begin(std::cin
), end
;
305 reply
= string(begin
, end
);
306 printReply(reply
, showflags
, hidesoadetails
);
308 Socket
sock(dest
.sin4
.sin_family
, SOCK_STREAM
);
310 for (const auto& it
: questions
) {
311 vector
<uint8_t> packet
;
312 fillPacket(packet
, it
.first
, it
.second
, dnssec
, ednsnm
, recurse
, xpfcode
,
313 xpfversion
, xpfproto
, xpfsrc
, xpfdst
, qclass
);
315 uint16_t len
= htons(packet
.size());
316 if (sock
.write((const char *)&len
, 2) != 2)
317 throw PDNSException("tcp write failed");
318 string
question(packet
.begin(), packet
.end());
319 sock
.writen(question
);
321 for (size_t i
= 0; i
< questions
.size(); i
++) {
323 if (sock
.read((char *)&len
, 2) != 2)
324 throw PDNSException("tcp read failed");
327 char* creply
= new char[len
];
331 numread
= sock
.read(creply
+ n
, len
- n
);
333 throw PDNSException("tcp read failed");
337 reply
= string(creply
, len
);
339 printReply(reply
, showflags
, hidesoadetails
);
343 vector
<uint8_t> packet
;
344 fillPacket(packet
, name
, type
, dnssec
, ednsnm
, recurse
, xpfcode
, xpfversion
,
345 xpfproto
, xpfsrc
, xpfdst
, qclass
);
346 string
question(packet
.begin(), packet
.end());
347 Socket
sock(dest
.sin4
.sin_family
, SOCK_DGRAM
);
348 sock
.sendTo(question
, dest
);
349 int result
= waitForData(sock
.getHandle(), 10);
351 throw std::runtime_error("Error waiting for data: " + stringerror());
353 throw std::runtime_error("Timeout waiting for data");
354 sock
.recvFrom(reply
, dest
);
355 printReply(reply
, showflags
, hidesoadetails
);
358 } catch (std::exception
& e
) {
359 cerr
<< "Fatal: " << e
.what() << endl
;
360 } catch (PDNSException
& e
) {
361 cerr
<< "Fatal: " << e
.reason
<< endl
;