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