]>
Commit | Line | Data |
---|---|---|
870a0fe4 AT |
1 | #ifdef HAVE_CONFIG_H |
2 | #include "config.h" | |
3 | #endif | |
0b122e8f | 4 | #include "base64.hh" |
df82962d KM |
5 | #include "dnsparser.hh" |
6 | #include "sstuff.hh" | |
7 | #include "misc.hh" | |
8 | #include "dnswriter.hh" | |
9 | #include "dnsrecords.hh" | |
10 | #include "statbag.hh" | |
3f6fa55d KM |
11 | #include "base32.hh" |
12 | #include "dnssecinfra.hh" | |
fa8fd4d2 | 13 | |
0b122e8f | 14 | #include "dns_random.hh" |
b08f1315 | 15 | #include "gss_context.hh" |
3f6fa55d | 16 | |
df82962d KM |
17 | StatBag S; |
18 | ||
19 | int main(int argc, char** argv) | |
20 | try | |
21 | { | |
22 | if(argc < 4) { | |
b08f1315 | 23 | cerr<<"Syntax: saxfr IP-address port zone [showdetails] [showflags] [unhash] [gss:remote-principal] [tsig:keyname:algo:secret]"<<endl; |
df82962d KM |
24 | exit(EXIT_FAILURE); |
25 | } | |
26 | ||
75b16c22 | 27 | bool showdetails=false; |
df82962d | 28 | bool showflags=false; |
3f6fa55d | 29 | bool unhash=false; |
b08f1315 | 30 | bool gss=false; |
0b122e8f AT |
31 | bool tsig=false; |
32 | TSIGHashEnum tsig_algo; | |
eaedd091 | 33 | DNSName tsig_key; |
0b122e8f AT |
34 | string tsig_secret; |
35 | string tsigprevious; | |
36 | string remote_principal; | |
df82962d KM |
37 | |
38 | if (argc > 4) { | |
39 | for(int i=4; i<argc; i++) { | |
75b16c22 KM |
40 | if (strcmp(argv[i], "showdetails") == 0) |
41 | showdetails=true; | |
df82962d KM |
42 | if (strcmp(argv[i], "showflags") == 0) |
43 | showflags=true; | |
3f6fa55d KM |
44 | if (strcmp(argv[i], "unhash") == 0) |
45 | unhash=true; | |
b08f1315 OM |
46 | if (strncmp(argv[i], "gss:",4) == 0) { |
47 | gss=true; | |
48 | tsig=true; | |
49 | tsig_algo=TSIG_GSS; | |
50 | remote_principal = string(argv[i]+4); | |
51 | if (remote_principal.empty()) { | |
52 | cerr<<"Remote principal is required"<<endl; | |
53 | exit(EXIT_FAILURE); | |
54 | } | |
55 | } | |
0b122e8f AT |
56 | if (strncmp(argv[i], "tsig:",5) == 0) { |
57 | vector<string> parts; | |
58 | tsig=true; | |
59 | stringtok(parts, argv[i], ":"); | |
60 | if (parts.size()!=4) { | |
61 | cerr<<"Invalid syntax for tsig"<<endl; | |
62 | exit(EXIT_FAILURE); | |
63 | } | |
eaedd091 | 64 | if (!getTSIGHashEnum(DNSName(parts[2]), tsig_algo)) { |
0b122e8f AT |
65 | cerr<<"Cannot understand TSIG algorithm '"<<parts[1]<<"'"<<endl; |
66 | exit(EXIT_FAILURE); | |
67 | } | |
eaedd091 | 68 | tsig_key = DNSName(parts[1]); |
69 | if (tsig_key == DNSName()) { | |
0b122e8f AT |
70 | cerr<<"Key name must be set for tsig"<<endl; |
71 | exit(EXIT_FAILURE); | |
72 | } | |
73 | if (B64Decode(parts[3], tsig_secret)) { | |
74 | cerr<<"Secret must be base64 encoded"<<endl; | |
75 | exit(EXIT_FAILURE); | |
76 | } | |
77 | if (tsig_secret.size()==0) { | |
78 | cerr<<"Secret must be set for tsig"<<endl; | |
79 | exit(EXIT_FAILURE); | |
80 | } | |
81 | } | |
df82962d KM |
82 | } |
83 | } | |
84 | ||
85 | reportAllTypes(); | |
86 | ||
87 | vector<uint8_t> packet; | |
0b122e8f AT |
88 | uint16_t len; |
89 | ComboAddress dest(argv[1] + (*argv[1]=='@'), atoi(argv[2])); | |
90 | Socket sock(dest.sin4.sin_family, SOCK_STREAM); | |
91 | sock.connect(dest); | |
92 | ||
b08f1315 OM |
93 | if (gss) { |
94 | #ifndef ENABLE_GSS_TSIG | |
95 | cerr<<"No GSS support compiled in"<<endl; | |
96 | exit(EXIT_FAILURE); | |
97 | #else | |
98 | string input,output; | |
99 | GssContext gssctx; | |
100 | gssctx.generateLabel(argv[3]); | |
101 | gssctx.setPeerPrincipal(remote_principal); | |
102 | ||
103 | while(gssctx.init(input, output) && gssctx.valid() == false) { | |
104 | input=""; | |
105 | DNSPacketWriter pwtkey(packet, gssctx.getLabel(), QType::TKEY, QClass::ANY); | |
106 | TKEYRecordContent tkrc; | |
107 | tkrc.d_algo = DNSName("gss-tsig."); | |
5a7a3b67 | 108 | // coverity[store_truncates_time_t] |
b08f1315 OM |
109 | tkrc.d_inception = time((time_t*)NULL); |
110 | tkrc.d_expiration = tkrc.d_inception+15; | |
111 | tkrc.d_mode = 3; | |
112 | tkrc.d_error = 0; | |
113 | tkrc.d_keysize = output.size(); | |
114 | tkrc.d_key = output; | |
115 | tkrc.d_othersize = 0; | |
116 | pwtkey.getHeader()->id = dns_random_uint16(); | |
117 | pwtkey.startRecord(gssctx.getLabel(), QType::TKEY, 3600, QClass::ANY, DNSResourceRecord::ADDITIONAL, false); | |
118 | tkrc.toPacket(pwtkey); | |
119 | pwtkey.commit(); | |
120 | for(const string& msg : gssctx.getErrorStrings()) { | |
121 | cerr<<msg<<endl; | |
122 | } | |
123 | ||
124 | len = htons(packet.size()); | |
125 | if(sock.write((char *) &len, 2) != 2) | |
126 | throw PDNSException("tcp write failed"); | |
127 | sock.writen(string((char*)&packet[0], packet.size())); | |
128 | if(sock.read((char *) &len, 2) != 2) | |
129 | throw PDNSException("tcp read failed"); | |
130 | ||
131 | len=ntohs(len); | |
2a104e81 | 132 | auto creply = std::make_unique<char[]>(len); |
b08f1315 OM |
133 | int n=0; |
134 | int numread; | |
135 | while(n<len) { | |
136 | numread=sock.read(creply.get()+n, len-n); | |
137 | if(numread<0) | |
138 | throw PDNSException("tcp read failed"); | |
139 | n+=numread; | |
140 | } | |
141 | ||
142 | MOADNSParser mdp(false, string(creply.get(), len)); | |
143 | if (mdp.d_header.rcode != 0) { | |
144 | throw PDNSException(string("Remote server refused: ") + std::to_string(mdp.d_header.rcode)); | |
145 | } | |
146 | for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i!=mdp.d_answers.end(); ++i) { | |
147 | if(i->first.d_type != QType::TKEY) continue; | |
148 | // recover TKEY record | |
d06dcda4 | 149 | tkrc = TKEYRecordContent(i->first.getContent()->getZoneRepresentation()); |
b08f1315 OM |
150 | input = tkrc.d_key; |
151 | } | |
152 | } | |
153 | ||
154 | if (gssctx.valid() == false) { | |
155 | cerr<<"Could not create GSS context"<<endl; | |
156 | exit(EXIT_FAILURE); | |
157 | } | |
158 | ||
159 | tsig_key = DNSName(gssctx.getLabel()); | |
160 | #endif | |
161 | } | |
162 | ||
eaedd091 | 163 | DNSPacketWriter pw(packet, DNSName(argv[3]), 252); |
df82962d | 164 | |
a410b176 | 165 | pw.getHeader()->id = dns_random_uint16(); |
0b122e8f AT |
166 | |
167 | if (tsig) { | |
168 | TSIGRecordContent trc; | |
169 | trc.d_algoName = getTSIGAlgoName(tsig_algo); | |
170 | trc.d_time = time((time_t*)NULL); | |
171 | trc.d_fudge = 300; | |
172 | trc.d_origID=ntohs(pw.getHeader()->id); | |
173 | trc.d_eRcode=0; | |
ea3816cf | 174 | addTSIG(pw, trc, tsig_key, tsig_secret, "", false); |
0b122e8f | 175 | } |
0131659f | 176 | |
df82962d KM |
177 | len = htons(packet.size()); |
178 | if(sock.write((char *) &len, 2) != 2) | |
179 | throw PDNSException("tcp write failed"); | |
180 | ||
16657041 | 181 | sock.writen(string(packet.begin(), packet.end())); |
df82962d | 182 | |
3f6fa55d | 183 | bool isNSEC3 = false; |
df82962d | 184 | int soacount=0; |
9d7fa327 PD |
185 | vector<pair<DNSName,string> > records; |
186 | set<DNSName> labels; | |
187 | map<string,DNSName> hashes; | |
3f6fa55d KM |
188 | NSEC3PARAMRecordContent ns3pr; |
189 | ||
df82962d | 190 | while(soacount<2) { |
0b122e8f AT |
191 | TSIGRecordContent trc; |
192 | ||
df82962d KM |
193 | if(sock.read((char *) &len, 2) != 2) |
194 | throw PDNSException("tcp read failed"); | |
195 | ||
196 | len=ntohs(len); | |
2bbc9eb0 | 197 | auto creply = std::make_unique<char[]>(len); |
df82962d KM |
198 | int n=0; |
199 | int numread; | |
200 | while(n<len) { | |
c2826d2e | 201 | numread=sock.read(creply.get()+n, len-n); |
df82962d KM |
202 | if(numread<0) |
203 | throw PDNSException("tcp read failed"); | |
204 | n+=numread; | |
205 | } | |
206 | ||
c2826d2e | 207 | MOADNSParser mdp(false, string(creply.get(), len)); |
0b122e8f | 208 | if (mdp.d_header.rcode != 0) { |
335da0ba | 209 | throw PDNSException(string("Remote server refused: ") + std::to_string(mdp.d_header.rcode)); |
0b122e8f | 210 | } |
df82962d | 211 | for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i!=mdp.d_answers.end(); ++i) { |
0b122e8f AT |
212 | if (i->first.d_type == QType::TSIG) { |
213 | string message; | |
214 | if (!tsig) { | |
215 | std::cerr<<"Unexpected TSIG signature in data"<<endl; | |
216 | } | |
d06dcda4 | 217 | trc = TSIGRecordContent(i->first.getContent()->getZoneRepresentation()); |
0b122e8f AT |
218 | continue; |
219 | } | |
df82962d KM |
220 | if(i->first.d_type == QType::SOA) |
221 | { | |
222 | ++soacount; | |
223 | } | |
3f6fa55d | 224 | else if (i->first.d_type == QType::NSEC3PARAM) { |
d06dcda4 RG |
225 | ns3pr = NSEC3PARAMRecordContent(i->first.getContent()->getZoneRepresentation()); |
226 | isNSEC3 = true; | |
3f6fa55d | 227 | } |
df82962d | 228 | |
3f6fa55d | 229 | ostringstream o; |
ac6f97d8 | 230 | o<<"\t"<<i->first.d_ttl<<"\tIN\t"<<DNSRecordContent::NumberToType(i->first.d_type); |
75b16c22 KM |
231 | if(showdetails) |
232 | { | |
d06dcda4 | 233 | o<<"\t"<<i->first.getContent()->getZoneRepresentation(); |
75b16c22 KM |
234 | } |
235 | else if(i->first.d_type == QType::RRSIG) | |
df82962d | 236 | { |
d06dcda4 | 237 | string zoneRep = i->first.getContent()->getZoneRepresentation(); |
df82962d KM |
238 | vector<string> parts; |
239 | stringtok(parts, zoneRep); | |
ac6f97d8 | 240 | o<<"\t"<<parts[0]<<" "<<parts[1]<<" "<<parts[2]<<" "<<parts[3]<<" [expiry] [inception] [keytag] "<<parts[7]<<" ..."; |
df82962d | 241 | } |
702581e4 | 242 | else if(i->first.d_type == QType::NSEC3) |
df82962d | 243 | { |
d06dcda4 | 244 | string zoneRep = i->first.getContent()->getZoneRepresentation(); |
df82962d KM |
245 | vector<string> parts; |
246 | stringtok(parts, zoneRep); | |
ac6f97d8 | 247 | o<<"\t"<<parts[0]<<" "; |
702581e4 KM |
248 | if (showflags) |
249 | o<<parts[1]; | |
250 | else | |
251 | o<<"[flags]"; | |
252 | o<<" "<<parts[2]<<" "<<parts[3]<<" "<<"[next owner]"; | |
df82962d | 253 | for(vector<string>::iterator iter = parts.begin()+5; iter != parts.end(); ++iter) |
3f6fa55d | 254 | o<<" "<<*iter; |
df82962d KM |
255 | } |
256 | else if(i->first.d_type == QType::DNSKEY) | |
257 | { | |
d06dcda4 | 258 | string zoneRep = i->first.getContent()->getZoneRepresentation(); |
df82962d KM |
259 | vector<string> parts; |
260 | stringtok(parts, zoneRep); | |
ac6f97d8 | 261 | o<<"\t"<<parts[0]<<" "<<parts[1]<<" "<<parts[2]<<" ..."; |
df82962d | 262 | } |
df82962d KM |
263 | else |
264 | { | |
d06dcda4 | 265 | o<<"\t"<<i->first.getContent()->getZoneRepresentation(); |
df82962d | 266 | } |
3f6fa55d | 267 | |
e32a8d46 | 268 | records.emplace_back(i->first.d_name, o.str()); |
3f6fa55d | 269 | |
f809c028 | 270 | DNSName shorter(i->first.d_name); |
3f6fa55d KM |
271 | do { |
272 | labels.insert(shorter); | |
eaedd091 | 273 | if (shorter == DNSName(argv[3])) |
3f6fa55d | 274 | break; |
9d7fa327 | 275 | }while(shorter.chopOff()); |
3f6fa55d | 276 | |
df82962d | 277 | } |
df82962d | 278 | } |
3f6fa55d KM |
279 | |
280 | if (isNSEC3 && unhash) | |
281 | { | |
282 | string hashed; | |
9d7fa327 | 283 | for(const auto &label: labels) { |
28e2e78e | 284 | hashed=toBase32Hex(hashQNameWithSalt(ns3pr, label)); |
9d7fa327 | 285 | hashes.insert(pair<string,DNSName>(hashed, label)); |
3f6fa55d KM |
286 | } |
287 | } | |
288 | ||
9d7fa327 | 289 | for(auto &record: records) { |
3343ad1f | 290 | DNSName label /* FIXME400 rename */=record.first; |
3f6fa55d KM |
291 | if (isNSEC3 && unhash) |
292 | { | |
eaedd091 | 293 | auto i = hashes.find(label.makeRelative(DNSName(argv[3])).toStringNoDot()); |
3f6fa55d KM |
294 | if (i != hashes.end()) |
295 | label=i->second; | |
296 | } | |
d96717f7 | 297 | cout<<label.toString()<<record.second<<endl; |
3f6fa55d KM |
298 | } |
299 | ||
df82962d | 300 | } |
0b122e8f AT |
301 | catch(PDNSException &e2) { |
302 | cerr<<"Fatal: "<<e2.reason<<endl; | |
303 | } | |
df82962d KM |
304 | catch(std::exception &e) |
305 | { | |
306 | cerr<<"Fatal: "<<e.what()<<endl; | |
307 | } |