]>
Commit | Line | Data |
---|---|---|
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 | 18 | StatBag S; |
ff6a1e7b | 19 | |
6e4c4a26 PD |
20 | bool hidettl=false; |
21 | ||
22 | string ttl(uint32_t ttl) | |
23 | { | |
24 | if(hidettl) | |
25 | return "[ttl]"; | |
26 | else | |
27 | return std::to_string(ttl); | |
28 | } | |
29 | ||
13f61288 PL |
30 | void 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 | 35 | const 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 |
48 | int main(int argc, char** argv) |
49 | try | |
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 | 286 | catch(std::exception &e) |
ff6a1e7b BH |
287 | { |
288 | cerr<<"Fatal: "<<e.what()<<endl; | |
289 | } | |
1a4843e0 | 290 | catch(PDNSException &e) |
291 | { | |
292 | cerr<<"Fatal: "<<e.reason<<endl; | |
293 | } |