Line data Source code
1 : /*
2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3 : */
4 :
5 : #include <boost/algorithm/string/join.hpp>
6 : #include <boost/assign/list_of.hpp>
7 : #include <boost/regex.hpp>
8 : #include <bind/bind_util.h>
9 :
10 : using namespace boost::assign;
11 : SandeshTraceBufferPtr DnsBindTraceBuf(SandeshTraceBufferCreate("DnsBind", 2000));
12 :
13 : DnsTypeMap g_dns_type_map = map_list_of<std::string, uint16_t>
14 : ("A", 1)
15 : ("NS", 2)
16 : ("CNAME", 5)
17 : ("SOA", 6)
18 : ("PTR", 0x0C)
19 : ("MX", 0x0F)
20 : ("TXT", 0x10)
21 : ("AAAA", 0x1C)
22 : ("SRV", 0x21)
23 : ("ANY", 0xFF);
24 :
25 : DnsTypeNumMap g_dns_type_num_map = map_list_of<uint16_t, std::string>
26 : (DNS_A_RECORD, "A")
27 : (DNS_NS_RECORD, "NS")
28 : (DNS_CNAME_RECORD, "CNAME")
29 : (DNS_TYPE_SOA, "SOA")
30 : (DNS_PTR_RECORD, "PTR")
31 : (DNS_MX_RECORD, "MX")
32 : (DNS_TXT_RECORD, "TXT")
33 : (DNS_AAAA_RECORD, "AAAA")
34 : (DNS_SRV_RECORD, "SRV")
35 : (DNS_TYPE_ANY, "ANY");
36 :
37 : DnsTypeNumMap g_dns_class_num_map = map_list_of<uint16_t, std::string>
38 : (DNS_CLASS_IN, "IN")
39 : (DNS_CLASS_NONE, "None")
40 : (DNS_CLASS_ANY, "Any");
41 :
42 : DnsResponseMap g_dns_response_map = map_list_of<uint16_t, std::string>
43 : (0, "No error")
44 : (1, "Format error")
45 : (2, "Server failure")
46 : (3, "Non-existent domain")
47 : (4, "Not implemented")
48 : (5, "Query refused")
49 : (6, "Name exists when it should not")
50 : (7, "RR Set Exists when it should not")
51 : (8, "RR Set that should exist does not")
52 : (9, "Not Authorized")
53 : (10, "Name not contained in zone")
54 : (16, "Bad OPT Version")
55 : (17, "Key not recognized")
56 : (18, "Signature out of time window")
57 : (19, "Bad TKEY Mode")
58 : (20, "Duplicate key name")
59 : (21, "Algorithm not supported")
60 : (22, "Bad truncation")
61 : (4095, "Invalid response code");
62 :
63 0 : std::string DnsItem::ToString() const {
64 0 : return BindUtil::DnsClass(eclass) + "/" +
65 0 : BindUtil::DnsType(type) + "/" + name + "/" + data + ";";
66 : }
67 :
68 64 : bool Subnet::operator< (const Subnet &rhs) const {
69 64 : if (prefix.is_v4()) {
70 62 : if (rhs.prefix.is_v4()) {
71 62 : Ip4Address me = prefix.to_v4(),
72 62 : they = rhs.prefix.to_v4();
73 62 : if (me != they) {
74 26 : return me < they;
75 : }
76 36 : return plen < rhs.plen;
77 : } else {
78 : // IPv4 goes before IPv6 and garbage
79 0 : return true;
80 : }
81 2 : } else if (prefix.is_v6()) {
82 2 : if (rhs.prefix.is_v6()) {
83 0 : Ip6Address me = prefix.to_v6(),
84 0 : they = rhs.prefix.to_v6();
85 0 : if (me != they)
86 0 : return me < they;
87 0 : return plen < rhs.plen;
88 : } else {
89 : // IPv4 goes before IPv6, IPv6 goes before garbage
90 2 : return !rhs.prefix.is_v4();
91 : }
92 : } else {
93 : // Garbage always sorts towards the end
94 0 : return false;
95 : }
96 : }
97 :
98 12 : bool Subnet::Contains(const IpAddress &addr) const {
99 12 : if (prefix.is_v4() && addr.is_v4()) {
100 4 : return IsIp4SubnetMember(addr.to_v4(), prefix.to_v4(), plen);
101 8 : } else if (prefix.is_v6() && addr.is_v6()) {
102 4 : return IsIp6SubnetMember(addr.to_v6(), prefix.to_v6(), plen);
103 : } else {
104 4 : return false;
105 : }
106 : }
107 :
108 354 : void Subnet::GetReverseZones(ZoneList &zones) const {
109 354 : BindUtil::GetReverseZoneList(prefix, plen, zones);
110 354 : }
111 :
112 41 : uint16_t BindUtil::DnsClass(const std::string &cl) {
113 41 : if (cl == "IN")
114 41 : return DNS_CLASS_IN;
115 0 : return DNS_CLASS_ANY;
116 : }
117 :
118 1 : std::string BindUtil::DnsClass(uint16_t cl) {
119 1 : DnsTypeNumIter iter = g_dns_class_num_map.find(cl);
120 1 : if (iter == g_dns_class_num_map.end())
121 1 : return integerToString(cl);
122 0 : return iter->second;
123 : }
124 :
125 41 : uint16_t BindUtil::DnsType(const std::string &tp) {
126 41 : DnsTypeIter iter = g_dns_type_map.find(tp);
127 41 : if (iter == g_dns_type_map.end())
128 0 : return -1;
129 41 : return iter->second;
130 : }
131 :
132 48 : std::string BindUtil::DnsType(uint16_t tp) {
133 48 : DnsTypeNumIter iter = g_dns_type_num_map.find(tp);
134 48 : if (iter == g_dns_type_num_map.end())
135 0 : return integerToString(tp);
136 48 : return iter->second;
137 : }
138 :
139 0 : const std::string &BindUtil::DnsResponseCode(uint16_t code) {
140 0 : DnsResponseIter iter = g_dns_response_map.find(code);
141 0 : if (iter == g_dns_response_map.end())
142 0 : return DnsResponseCode(4095);
143 0 : return iter->second;
144 : }
145 :
146 38 : uint8_t *BindUtil::AddName(uint8_t *ptr, const std::string &addr,
147 : uint16_t plen, uint16_t offset, uint16_t &length) {
148 38 : std::size_t size = addr.size();
149 38 : std::size_t cur_pos = 0;
150 38 : std::size_t prev_pos = 0;
151 151 : while (cur_pos < size && cur_pos != std::string::npos) {
152 113 : if (offset && !plen) {
153 0 : ptr = WriteShort(ptr, offset);
154 0 : length += 2;
155 0 : return ptr;
156 : }
157 : std::size_t len;
158 113 : cur_pos = addr.find('.', prev_pos);
159 113 : if (cur_pos == std::string::npos)
160 37 : len = size - prev_pos;
161 : else
162 76 : len = cur_pos - prev_pos;
163 :
164 113 : *ptr = len;
165 113 : memcpy(ptr + 1, addr.substr(prev_pos, len).data(), len);
166 113 : ptr += len + 1;
167 113 : plen = (plen > len) ? plen - len - 1 : 0;
168 :
169 113 : prev_pos = cur_pos + 1;
170 113 : length += len + 1;
171 : }
172 38 : ptr = WriteByte(ptr, 0);
173 38 : length++;
174 :
175 38 : return ptr;
176 : }
177 :
178 20 : uint8_t *BindUtil::AddQuestionSection(uint8_t *ptr, const std::string &name,
179 : uint16_t type, uint16_t cl,
180 : uint16_t &length) {
181 20 : ptr = AddName(ptr, name, 0, 0, length);
182 20 : ptr = WriteShort(ptr, type);
183 20 : ptr = WriteShort(ptr, cl);
184 20 : length += 4;
185 :
186 20 : return ptr;
187 : }
188 :
189 16 : uint8_t *BindUtil::AddData(uint8_t *ptr, const DnsItem &item,
190 : uint16_t &length) {
191 16 : boost::system::error_code ec;
192 :
193 16 : if (item.type == DNS_A_RECORD) {
194 10 : ptr = WriteShort(ptr, 4);
195 10 : ptr = WriteWord(ptr, boost::asio::ip::address_v4::from_string(
196 10 : item.data, ec).to_ulong());
197 10 : length += 2 + 4;
198 6 : } else if (item.type == DNS_AAAA_RECORD) {
199 0 : ptr = WriteShort(ptr, 16);
200 : boost::asio::ip::address_v6 addr =
201 0 : boost::asio::ip::address_v6::from_string(item.data, ec);
202 0 : if (ec.value()) {
203 0 : memset(ptr, 0, 16);
204 : } else {
205 0 : memcpy(ptr, addr.to_bytes().data(), 16);
206 : }
207 0 : ptr += 16;
208 0 : length += 2 + 16;
209 6 : } else if(item.type == DNS_TYPE_SOA) {
210 1 : uint16_t data_len = DataLength(item.soa.ns_plen, item.soa.ns_offset,
211 1 : item.soa.primary_ns.size()) +
212 1 : DataLength(item.soa.mailbox_plen,
213 1 : item.soa.mailbox_offset,
214 1 : item.soa.mailbox.size()) + 20;
215 1 : ptr = WriteShort(ptr, data_len);
216 2 : ptr = AddName(ptr, item.soa.primary_ns, item.soa.ns_plen,
217 1 : item.soa.ns_offset, length);
218 2 : ptr = AddName(ptr, item.soa.mailbox, item.soa.mailbox_plen,
219 1 : item.soa.mailbox_offset, length);
220 1 : ptr = WriteWord(ptr, item.soa.serial);
221 1 : ptr = WriteWord(ptr, item.soa.refresh);
222 1 : ptr = WriteWord(ptr, item.soa.retry);
223 1 : ptr = WriteWord(ptr, item.soa.expiry);
224 1 : ptr = WriteWord(ptr, item.soa.ttl);
225 1 : length += 2 + 20;
226 5 : } else if(item.type == DNS_PTR_RECORD ||
227 5 : item.type == DNS_CNAME_RECORD ||
228 5 : item.type == DNS_NS_RECORD) {
229 3 : uint16_t data_len = DataLength(item.data_plen, item.data_offset,
230 3 : item.data.size());
231 3 : ptr = WriteShort(ptr, data_len);
232 3 : ptr = AddName(ptr, item.data, item.data_plen, item.data_offset, length);
233 3 : length += 2;
234 5 : } else if (item.type == DNS_MX_RECORD) {
235 0 : uint16_t data_len = 2 + DataLength(item.data_plen, item.data_offset,
236 0 : item.data.size());
237 0 : ptr = WriteShort(ptr, data_len);
238 : // An MX record has 16 bit preference followed by host name
239 0 : ptr = WriteShort(ptr, item.priority);
240 0 : ptr = AddName(ptr, item.data, item.data_plen, item.data_offset, length);
241 0 : length += 2 + 2;
242 2 : } else if (item.type == DNS_SRV_RECORD) {
243 1 : uint16_t data_len = 6 + DataLength(item.srv.hn_plen, item.srv.hn_offset,
244 1 : item.srv.hostname.size());
245 1 : ptr = WriteShort(ptr, data_len);
246 1 : ptr = WriteShort(ptr, item.srv.priority);
247 1 : ptr = WriteShort(ptr, item.srv.weight);
248 1 : ptr = WriteShort(ptr, item.srv.port);
249 2 : ptr = AddName(ptr, item.srv.hostname, item.srv.hn_plen,
250 1 : item.srv.hn_offset, length);
251 1 : length += 2 + 6;
252 : } else {
253 : // TXT and other record types are handled here.
254 : // TODO: In case a record type has domain name and name is compressed
255 : // in the message that is being read, it may lead to problem if the
256 : // offset in the message we send doesnt match with the offset in the
257 : // message that is received. Each such message has to be handled
258 : // as a different case here.
259 1 : uint16_t size = item.data.size();
260 1 : ptr = WriteShort(ptr, size);
261 1 : if (size) {
262 1 : memcpy(ptr, item.data.c_str(), size);
263 1 : ptr += size;
264 : }
265 1 : length += 2 + size;
266 : }
267 :
268 16 : return ptr;
269 : }
270 :
271 12 : uint8_t *BindUtil::AddAnswerSection(uint8_t *ptr, const DnsItem &item,
272 : uint16_t &length) {
273 12 : ptr = AddName(ptr, item.name, item.name_plen, item.name_offset, length);
274 12 : ptr = WriteShort(ptr, item.type);
275 12 : ptr = WriteShort(ptr, item.eclass);
276 12 : ptr = WriteWord(ptr, item.ttl);
277 12 : length += 2 + 2 + 4;
278 12 : ptr = AddData(ptr, item, length);
279 :
280 12 : return ptr;
281 : }
282 :
283 0 : uint8_t *BindUtil::AddAdditionalSection(uint8_t *ptr, const std::string name,
284 : uint16_t type, uint16_t cl,
285 : uint32_t ttl, const std::string &data,
286 : uint16_t &length) {
287 0 : ptr = AddQuestionSection(ptr, name, type, cl, length);
288 0 : ptr = WriteWord(ptr, ttl);
289 0 : ptr = WriteShort(ptr, data.size() + 1);
290 :
291 : // adding the domain name as it is, without replacing '.' with length
292 0 : ptr = WriteByte(ptr, data.size());
293 0 : memcpy(ptr, data.data(), data.size());
294 0 : ptr += data.size();
295 0 : length += 4 + 2 + 1 + data.size();
296 :
297 0 : return ptr;
298 : }
299 :
300 4 : uint8_t *BindUtil::AddUpdate(uint8_t *ptr, const DnsItem &item,
301 : uint16_t cl, uint32_t ttl, uint16_t &length) {
302 4 : ptr = AddQuestionSection(ptr, item.name, item.type, cl, length);
303 4 : ptr = WriteWord(ptr, ttl);
304 4 : length += 4;
305 4 : ptr = AddData(ptr, item, length);
306 :
307 4 : return ptr;
308 : }
309 :
310 41 : bool BindUtil::ReadName(uint8_t *dns, uint16_t dnslen, int *remlen,
311 : std::string &name, uint16_t &plen, uint16_t &offset) {
312 41 : if (*remlen <= 0) {
313 3 : return false;
314 : }
315 :
316 38 : uint8_t *ptr = dns + (dnslen - *remlen);
317 38 : std::size_t len = *ptr;
318 151 : while (len) {
319 113 : if ((len & 0xC0) == 0xC0) {
320 0 : plen = name.size() ? name.size() - 1 : 0;
321 0 : if (ReadShort(dns, dnslen, remlen, offset) == false)
322 0 : return false;
323 0 : int offset_remlen = dnslen - (offset & ~0xC000);
324 : uint16_t dummy;
325 0 : return ReadName(dns, dnslen, &offset_remlen, name, dummy, dummy);
326 : } else {
327 113 : *remlen -= (len + 1);
328 113 : if (*remlen < 0) {
329 0 : return false;
330 : }
331 113 : name.append((char *)ptr+1, len);
332 113 : ptr += len + 1;
333 113 : len = *ptr;
334 113 : if (len)
335 76 : name.append(".");
336 : }
337 : }
338 :
339 38 : *remlen -= 1;
340 38 : return true;
341 : }
342 :
343 16 : bool BindUtil::ReadData(uint8_t *dns, uint16_t dnslen, int *remlen,
344 : DnsItem &item) {
345 : uint16_t length;
346 16 : if (ReadShort(dns, dnslen, remlen, length) == false)
347 0 : return false;
348 :
349 16 : boost::system::error_code ec;
350 16 : if (item.type == DNS_A_RECORD) {
351 : uint32_t ip;
352 10 : if (ReadWord(dns, dnslen, remlen, ip) == false)
353 0 : return false;
354 10 : boost::asio::ip::address_v4 addr(ip);
355 10 : item.data = addr.to_string(ec);
356 10 : return true;
357 6 : } else if(item.type == DNS_AAAA_RECORD) {
358 0 : if (*remlen < 16) {
359 0 : return false;
360 : }
361 0 : uint8_t *ptr = dns + (dnslen - *remlen);
362 : boost::asio::ip::address_v6::bytes_type ip;
363 0 : memcpy(&ip[0], ptr, 16);
364 0 : boost::asio::ip::address_v6 addr(ip);
365 0 : item.data = addr.to_string(ec);
366 0 : *remlen -= 16;
367 0 : return true;
368 6 : } else if(item.type == DNS_TYPE_SOA) {
369 2 : if (ReadName(dns, dnslen, remlen, item.soa.primary_ns,
370 1 : item.soa.ns_plen, item.soa.ns_offset) == false)
371 0 : return false;
372 2 : if (ReadName(dns, dnslen, remlen, item.soa.mailbox,
373 1 : item.soa.mailbox_plen, item.soa.mailbox_offset) == false)
374 0 : return false;
375 1 : if (ReadWord(dns, dnslen, remlen, item.soa.serial) == false)
376 0 : return false;
377 1 : if (ReadWord(dns, dnslen, remlen, item.soa.refresh) == false)
378 0 : return false;
379 1 : if (ReadWord(dns, dnslen, remlen, item.soa.retry) == false)
380 0 : return false;
381 1 : if (ReadWord(dns, dnslen, remlen, item.soa.expiry) == false)
382 0 : return false;
383 1 : return ReadWord(dns, dnslen, remlen, item.soa.ttl);
384 5 : } else if(item.type == DNS_PTR_RECORD ||
385 5 : item.type == DNS_CNAME_RECORD ||
386 5 : item.type == DNS_NS_RECORD) {
387 3 : return ReadName(dns, dnslen, remlen, item.data, item.data_plen, item.data_offset);
388 2 : } else if(item.type == DNS_MX_RECORD) {
389 0 : if (ReadShort(dns, dnslen, remlen, item.priority) == false)
390 0 : return false;
391 0 : return ReadName(dns, dnslen, remlen, item.data, item.data_plen, item.data_offset);
392 2 : } else if (item.type == DNS_SRV_RECORD) {
393 1 : if (ReadShort(dns, dnslen, remlen, item.srv.priority) == false)
394 0 : return false;
395 1 : if (ReadShort(dns, dnslen, remlen, item.srv.weight) == false)
396 0 : return false;
397 1 : if (ReadShort(dns, dnslen, remlen, item.srv.port) == false)
398 0 : return false;
399 2 : return ReadName(dns, dnslen, remlen, item.srv.hostname,
400 1 : item.srv.hn_plen, item.srv.hn_offset);
401 : } else {
402 : // TXT and other record types are handled here.
403 : // TODO: In case a record type has domain name and name is compressed
404 : // in the message that is being read, it may lead to problem if the
405 : // offset in the message we send doesnt match with the offset in the
406 : // message that is received. Each such message has to be handled
407 : // as a different case here.
408 1 : if (*remlen < length) {
409 0 : return false;
410 : }
411 1 : uint8_t *ptr = dns + (dnslen - *remlen);
412 1 : item.data.assign((const char *)ptr, length);
413 1 : *remlen -= length;
414 1 : return true;
415 : }
416 :
417 : DNS_BIND_TRACE(DnsBindError,
418 : "Unsupported data type in DNS response : " << item.type);
419 : return false;
420 : }
421 :
422 28 : bool BindUtil::ReadQuestionEntry(uint8_t *dns, uint16_t dnslen, int *remlen,
423 : DnsItem &item) {
424 56 : if (ReadName(dns, dnslen, remlen, item.name, item.name_plen,
425 28 : item.name_offset) == false)
426 2 : return false;
427 :
428 26 : if (ReadShort(dns, dnslen, remlen, item.type) == false)
429 0 : return false;
430 :
431 26 : return ReadShort(dns, dnslen, remlen, item.eclass);
432 : }
433 :
434 13 : bool BindUtil::ReadAnswerEntry(uint8_t *dns, uint16_t dnslen, int *remlen,
435 : DnsItem &item) {
436 13 : if (ReadQuestionEntry(dns, dnslen, remlen, item) == false)
437 1 : return false;
438 :
439 12 : if (ReadWord(dns, dnslen, remlen, item.ttl) == false)
440 0 : return false;
441 :
442 12 : return ReadData(dns, dnslen, remlen, item);
443 : }
444 :
445 3 : bool BindUtil::ParseDnsQuery(uint8_t *dns, uint16_t dnslen, uint16_t *parsed_length,
446 : DnsItems &items) {
447 3 : uint16_t xid = 0;
448 3 : *parsed_length = 0;
449 3 : if (dnslen <= sizeof(dnshdr)) {
450 1 : DNS_BIND_TRACE(DnsBindError,
451 : "Invalid DNS Query with header missing - dropping it");
452 1 : return false;
453 : }
454 :
455 2 : dnshdr *hdr = (dnshdr *) dns;
456 2 : xid = ntohs(hdr->xid);
457 2 : uint16_t ques_rrcount = ntohs(hdr->ques_rrcount);
458 :
459 2 : int remlen = dnslen - sizeof(dnshdr);
460 8 : for (unsigned int i = 0; i < ques_rrcount; ++i) {
461 7 : DnsItem item;
462 7 : item.offset = (dnslen - remlen) | 0xC000;
463 7 : if (ReadQuestionEntry(dns, dnslen, &remlen, item) == false) {
464 1 : DNS_BIND_TRACE(DnsBindError, "DNS Query Parse error in question "
465 : "section - xid : " << xid << " - dropping it");
466 1 : return false;
467 : }
468 6 : items.push_back(item);
469 7 : }
470 :
471 1 : *parsed_length = dnslen - remlen;
472 1 : return true;
473 :
474 : }
475 :
476 4 : bool BindUtil::ParseDnsResponse(uint8_t *dns, uint16_t dnslen, uint16_t &xid,
477 : dns_flags &flags, DnsItems &ques, DnsItems &ans,
478 : DnsItems &auth, DnsItems &add) {
479 4 : if (dnslen < sizeof(dnshdr)) {
480 0 : DNS_BIND_TRACE(DnsBindError,
481 : "Invalid DNS Response with header missing - dropping it");
482 0 : return false;
483 : }
484 :
485 4 : dnshdr *hdr = (dnshdr *) dns;
486 4 : xid = ntohs(hdr->xid);
487 4 : flags = hdr->flags;
488 :
489 4 : uint16_t ques_rrcount = ntohs(hdr->ques_rrcount);
490 4 : uint16_t ans_rrcount = ntohs(hdr->ans_rrcount);
491 4 : uint16_t auth_rrcount = ntohs(hdr->auth_rrcount);
492 4 : uint16_t add_rrcount = ntohs(hdr->add_rrcount);
493 :
494 4 : int remlen = dnslen - sizeof(dnshdr);
495 4 : std::string errmsg;
496 :
497 : // question section
498 12 : for (unsigned int i = 0; i < ques_rrcount; ++i) {
499 8 : DnsItem item;
500 8 : if (ReadQuestionEntry(dns, dnslen, &remlen, item) == false) {
501 0 : errmsg = "Parse error in question section";
502 0 : goto error;
503 : }
504 8 : ques.push_back(item);
505 8 : }
506 :
507 : // answer section
508 12 : for (unsigned int i = 0; i < ans_rrcount; ++i) {
509 9 : DnsItem item;
510 9 : if (ReadAnswerEntry(dns, dnslen, &remlen, item) == false) {
511 1 : errmsg = "Parse error in answer section";
512 1 : goto error;
513 : }
514 8 : ans.push_back(item);
515 9 : }
516 :
517 : // authority section
518 6 : for (unsigned int i = 0; i < auth_rrcount; ++i) {
519 3 : DnsItem item;
520 3 : if (ReadAnswerEntry(dns, dnslen, &remlen, item) == false) {
521 0 : errmsg = "Parse error in authority section";
522 0 : goto error;
523 : }
524 3 : auth.push_back(item);
525 3 : }
526 :
527 : // additional section
528 4 : for (unsigned int i = 0; i < add_rrcount; ++i) {
529 1 : DnsItem item;
530 1 : if (ReadAnswerEntry(dns, dnslen, &remlen, item) == false) {
531 0 : errmsg = "Parse error in additional section";
532 0 : goto error;
533 : }
534 1 : add.push_back(item);
535 1 : }
536 :
537 3 : return true;
538 :
539 1 : error:
540 1 : DNS_BIND_TRACE(DnsBindError,
541 : "Invalid DNS response : " << errmsg <<
542 : " xid : " << xid << " - dropping it");
543 1 : return false;
544 4 : }
545 :
546 2 : bool BindUtil::ParseDnsUpdate(uint8_t *dns, uint16_t dnslen,
547 : DnsUpdateData &data) {
548 2 : if (dnslen <= sizeof(dnshdr)) {
549 0 : DNS_BIND_TRACE(DnsBindError, "Invalid DNS Update with header missing "
550 : "- dropping it");
551 0 : return false;
552 : }
553 :
554 2 : dnshdr *hdr = (dnshdr *) dns;
555 2 : uint16_t zone_count = ntohs(hdr->ques_rrcount);
556 2 : uint16_t prereq_count = ntohs(hdr->ans_rrcount);
557 2 : uint16_t update_count = ntohs(hdr->auth_rrcount);
558 2 : uint16_t xid = ntohs(hdr->xid);
559 :
560 2 : if (zone_count != 1) {
561 0 : DNS_BIND_TRACE(DnsBindError,
562 : "Invalid zone count in Update request : " << zone_count);
563 0 : return false;
564 : }
565 :
566 2 : if (prereq_count != 0) {
567 : // Not supporting pre-requisites now
568 0 : DNS_BIND_TRACE(DnsBindError, "Update has pre-requisites, " <<
569 : "which is not supported; dropping the request");
570 0 : return false;
571 : }
572 :
573 2 : int remlen = dnslen - sizeof(dnshdr);
574 2 : std::string errmsg;
575 :
576 : // Read zone
577 : uint16_t zone_type, zone_class, plen, offset;
578 2 : if (ReadName(dns, dnslen, &remlen, data.zone, plen, offset) == false ||
579 4 : ReadShort(dns, dnslen, &remlen, zone_type) == false ||
580 2 : ReadShort(dns, dnslen, &remlen, zone_class) == false) {
581 0 : errmsg = "Parse error in reading zone";
582 0 : goto error;
583 : }
584 :
585 : // Read Updates
586 6 : for (unsigned int i = 0; i < update_count; ++i) {
587 5 : DnsItem item;
588 5 : if (ReadName(dns, dnslen, &remlen, item.name, plen, offset) == false ||
589 4 : ReadShort(dns, dnslen, &remlen, item.type) == false ||
590 4 : ReadShort(dns, dnslen, &remlen, item.eclass) == false ||
591 13 : ReadWord(dns, dnslen, &remlen, item.ttl) == false ||
592 4 : ReadData(dns, dnslen, &remlen, item) == false) {
593 1 : errmsg = "Parse error";
594 1 : goto error;
595 : }
596 4 : data.items.push_back(item);
597 5 : }
598 :
599 1 : return true;
600 :
601 1 : error:
602 1 : DNS_BIND_TRACE(DnsBindError,
603 : "Invalid DNS Update : " << errmsg <<
604 : " xid : " << xid << " - dropping it");
605 1 : return false;
606 2 : }
607 :
608 9 : void BindUtil::BuildDnsHeader(dnshdr *dns, uint16_t xid, DnsReq req,
609 : DnsOpcode op, bool rd, bool ra, uint8_t ret,
610 : uint16_t ques_count) {
611 9 : dns->xid = htons(xid);
612 9 : dns->flags.req = req;
613 9 : dns->flags.op = op;
614 9 : dns->flags.auth = 0;
615 9 : dns->flags.trunc = 0;
616 9 : dns->flags.rd = rd;
617 9 : dns->flags.ra = ra;
618 9 : dns->flags.res = 0;
619 9 : dns->flags.ad = 0;
620 9 : dns->flags.cd = 0;
621 9 : dns->flags.ret = ret;
622 9 : dns->ques_rrcount = htons(ques_count);
623 9 : dns->ans_rrcount = 0;
624 9 : dns->auth_rrcount = 0;
625 9 : dns->add_rrcount = 0;
626 9 : }
627 :
628 0 : int BindUtil::BuildDnsQuery(uint8_t *buf, uint16_t xid,
629 : const std::string &domain,
630 : const DnsItems &items) {
631 0 : dnshdr *dns = (dnshdr *) buf;
632 0 : BuildDnsHeader(dns, xid, DNS_QUERY_REQUEST, DNS_OPCODE_QUERY,
633 0 : 1, 0, 0, items.size());
634 :
635 : // TODO : can be optimised to reuse any names using offsets
636 0 : uint16_t len = sizeof(dnshdr);
637 0 : uint8_t *ques = (uint8_t *) (dns + 1);
638 0 : for (DnsItems::const_iterator it = items.begin(); it != items.end(); ++it) {
639 0 : ques = AddQuestionSection(ques, (*it).name, (*it).type,
640 0 : (*it).eclass, len);
641 : }
642 :
643 0 : if (!domain.empty()) {
644 0 : dns->add_rrcount = htons(1);
645 0 : std::string view = "view=" + domain;
646 0 : ques = AddAdditionalSection(ques, "view", DNS_TXT_RECORD, DNS_CLASS_IN,
647 : 0, view, len);
648 0 : }
649 :
650 0 : return len;
651 : }
652 :
653 0 : int BindUtil::BuildDnsUpdate(uint8_t *buf, Operation op, uint16_t xid,
654 : const std::string &domain,
655 : const std::string &zone,
656 : const DnsItems &items) {
657 0 : dnshdr *dns = (dnshdr *) buf;
658 0 : BuildDnsHeader(dns, xid, DNS_QUERY_REQUEST, DNS_OPCODE_UPDATE, 0, 0, 0, 1);
659 : // dns->ques_rrcount = htons(1); // Number of zones
660 : // dns->ans_rrcount = 0; // Number of Prerequisites
661 0 : dns->auth_rrcount = htons(items.size()); // Number of Updates
662 0 : dns->add_rrcount = htons(1); // Number of Additional RRs
663 :
664 : // Add zone
665 0 : uint16_t len = sizeof(dnshdr);
666 0 : uint8_t *ptr = (uint8_t *) (dns + 1);
667 0 : ptr = AddQuestionSection(ptr, zone, DNS_TYPE_SOA, DNS_CLASS_IN, len);
668 :
669 : // Add Updates
670 0 : switch (op) {
671 0 : case ADD_UPDATE:
672 : case CHANGE_UPDATE:
673 0 : for (DnsItems::const_iterator it = items.begin();
674 0 : it != items.end(); ++it) {
675 0 : ptr = AddUpdate(ptr, *it, (*it).eclass, (*it).ttl, len);
676 : }
677 0 : break;
678 :
679 0 : case DELETE_UPDATE:
680 0 : for (DnsItems::const_iterator it = items.begin();
681 0 : it != items.end(); ++it) {
682 0 : ptr = AddUpdate(ptr, *it, DNS_CLASS_NONE, 0, len);
683 : }
684 0 : break;
685 :
686 0 : default:
687 0 : assert(0);
688 : }
689 :
690 : // Add Additional RRs
691 0 : std::string view = "view=" + domain;
692 0 : ptr = AddAdditionalSection(ptr, "view", DNS_TXT_RECORD, DNS_CLASS_IN,
693 : 0, view, len);
694 :
695 0 : return len;
696 : }
697 :
698 469 : bool BindUtil::IsReverseZoneV4(const std::string &name) {
699 : // According to the docs, boost::regex is thread-safe. Compile once then.
700 : static boost::regex in_addr_arpa("(?:[0-9]+\\.){1,4}in-addr\\.arpa\\.?",
701 469 : boost::regex::perl | boost::regex::icase);
702 469 : return boost::regex_match(name, in_addr_arpa);
703 : }
704 :
705 176 : bool BindUtil::IsReverseZoneV6(const std::string &name) {
706 : static boost::regex ip6_arpa("(?:[0-9a-f]\\.){1,32}ip6\\.arpa\\.?",
707 176 : boost::regex::perl | boost::regex::icase);
708 176 : return boost::regex_match(name, ip6_arpa);
709 : }
710 :
711 464 : bool BindUtil::IsReverseZone(const std::string &name) {
712 464 : return BindUtil::IsReverseZoneV4(name) || BindUtil::IsReverseZoneV6(name);
713 : }
714 :
715 10 : bool BindUtil::IsIP(const std::string &name, IpAddress &addr) {
716 10 : boost::system::error_code ec;
717 10 : addr = boost::asio::ip::address::from_string(name, ec);
718 10 : return !ec.value();
719 : }
720 :
721 180 : inline uint8_t BindUtil::GetNibble(const Ip6Address::bytes_type &addr, size_t bit)
722 : {
723 180 : assert((bit >> 3) < 16);
724 180 : uint8_t buf = addr[bit >> 3];
725 180 : return ((bit & 0x7) ? (buf & 0xF) : (buf >> 4));
726 : }
727 :
728 : // Takes nibbles up to (but not necessarily including) plen and builds
729 : // ip6.arpa zone name suffix for them. The caller is responsible for
730 : // prefixing it with remaining plen bits when plen is not a multiply of 4.
731 11 : std::string BindUtil::BuildIp6ArpaSuffix(const Ip6Address::bytes_type &addr,
732 : uint32_t plen) {
733 11 : std::vector<std::string> items;
734 : uint8_t buf;
735 :
736 187 : for (size_t i = 0; i < (plen >> 2); i++) {
737 176 : buf = GetNibble(addr, i << 2);
738 176 : std::stringstream str;
739 176 : str << std::hex << static_cast<unsigned int>(buf);
740 176 : items.push_back(str.str());
741 176 : }
742 :
743 11 : std::reverse(items.begin(), items.end());
744 11 : items.push_back("ip6.arpa");
745 22 : return boost::algorithm::join(items, ".");
746 11 : }
747 :
748 : // Get list of reverse zones, given a subnet
749 344 : void BindUtil::GetReverseZoneList(const Ip4Address &mask, uint32_t plen,
750 : ZoneList &zones) {
751 344 : uint32_t addr = mask.to_ulong();
752 344 : std::string zone_name = "in-addr.arpa.";
753 1262 : while (plen >= 8) {
754 918 : std::stringstream str;
755 918 : str << (addr >> 24);
756 918 : zone_name = str.str() + "." + zone_name;
757 918 : addr = addr << 8;
758 918 : plen -= 8;
759 918 : }
760 344 : if (plen == 0) {
761 : // when prefix len is a multiple of 8, add only one reverse zone
762 200 : zones.push_back(zone_name);
763 200 : return;
764 : }
765 608 : for (int j = 0; j <= (0xFF >> plen); j++) {
766 : // when prefix len is not a multiple of 8, add reverse zones for all
767 : // addresses upto the nearest 8 byte boundary for the prefix len. For
768 : // example, a /22 prefix results in 4 reverse zones.
769 464 : uint32_t last = (addr >> 24) + j;
770 464 : std::stringstream str;
771 464 : str << last;
772 928 : std::string rev_zone_name = str.str() + "." + zone_name;
773 464 : zones.push_back(rev_zone_name);
774 464 : }
775 344 : }
776 :
777 10 : void BindUtil::GetReverseZoneList(const Ip6Address &mask, uint32_t plen,
778 : ZoneList &zones) {
779 10 : Ip6Address::bytes_type addr = mask.to_bytes();
780 10 : std::string zone_name;
781 : uint8_t buf;
782 :
783 10 : if (!plen || plen > 128)
784 0 : return;
785 :
786 10 : zone_name = BindUtil::BuildIp6ArpaSuffix(addr, plen) + ".";
787 :
788 10 : uint32_t bits_rem = plen & 0x3;
789 : // Prefix doesn't end on nibble boundary
790 10 : if (bits_rem) {
791 4 : buf = BindUtil::GetNibble(addr, plen - bits_rem);
792 36 : for (int j = 0; j <= (0xF >> bits_rem); j++) {
793 32 : std::stringstream str;
794 32 : str << std::hex << static_cast<unsigned int>(buf + j)
795 32 : << "." << zone_name;
796 32 : zones.push_back(str.str());
797 32 : }
798 : } else {
799 6 : zones.push_back(zone_name);
800 : }
801 10 : }
802 :
803 354 : void BindUtil::GetReverseZoneList(const IpAddress &mask, uint32_t plen,
804 : ZoneList &zones) {
805 354 : if (mask.is_v4()) {
806 344 : GetReverseZoneList(mask.to_v4(), plen, zones);
807 10 : } else if (mask.is_v6()) {
808 10 : GetReverseZoneList(mask.to_v6(), plen, zones);
809 : }
810 354 : }
811 :
812 1 : void BindUtil::GetReverseZone(const Ip4Address &ip, uint32_t plen,
813 : std::string &zone) {
814 1 : uint32_t addr = ip.to_ulong();
815 1 : zone = "in-addr.arpa";
816 4 : while (plen >= 8) {
817 3 : std::stringstream str;
818 3 : str << (addr >> 24);
819 3 : zone = str.str() + "." + zone;
820 3 : addr = addr << 8;
821 3 : plen -= 8;
822 3 : }
823 1 : if (plen == 0) {
824 1 : return;
825 : }
826 : // when prefix len is not a multiple of 8, add extra byte from the addr
827 0 : std::stringstream str;
828 0 : str << (addr >> 24);
829 0 : zone = str.str() + "." + zone;
830 0 : }
831 :
832 1 : void BindUtil::GetReverseZone(const Ip6Address &ip, uint32_t plen,
833 : std::string &zone) {
834 1 : Ip6Address::bytes_type addr = ip.to_bytes();
835 : uint8_t buf;
836 :
837 1 : if (!plen || plen > 128)
838 0 : return;
839 :
840 1 : zone = BindUtil::BuildIp6ArpaSuffix(addr, plen);
841 :
842 1 : uint32_t bits_rem = plen & 0x3;
843 : // Add the trailing nibble
844 1 : if (bits_rem) {
845 0 : buf = BindUtil::GetNibble(addr, plen - bits_rem);
846 0 : std::stringstream str;
847 0 : str << std::hex << static_cast<unsigned int>(buf) << "." << zone;
848 0 : zone = str.str();
849 0 : }
850 : }
851 :
852 2 : void BindUtil::GetReverseZone(const IpAddress &addr, uint32_t plen,
853 : std::string &zone) {
854 2 : if (addr.is_v4()) {
855 1 : GetReverseZone(addr.to_v4(), plen, zone);
856 1 : } else if (addr.is_v6()) {
857 1 : GetReverseZone(addr.to_v6(), plen, zone);
858 : }
859 2 : }
860 :
861 5 : bool BindUtil::GetAddrFromPtrName(std::string &ptr_name, Ip4Address &ip) {
862 5 : uint32_t addr = 0;
863 : uint8_t dot;
864 : uint32_t num;
865 5 : if (!BindUtil::IsReverseZoneV4(ptr_name))
866 0 : return false;
867 5 : std::stringstream str(ptr_name);
868 5 : if (str.peek() == '.')
869 0 : str >> dot;
870 5 : int count = 0;
871 25 : while (count < 4) {
872 20 : if (str.peek() == 'i')
873 0 : break;
874 20 : str >> num;
875 20 : str >> dot;
876 20 : addr |= (num << (8 * count));
877 20 : count++;
878 : }
879 5 : while (count++ < 4)
880 0 : addr = addr << 8;
881 5 : ip = Ip4Address(addr);
882 5 : return true;
883 5 : }
884 :
885 5 : bool BindUtil::GetAddrFromPtrName(std::string &ptr_name, Ip6Address &ip) {
886 : Ip6Address::bytes_type addr;
887 5 : BOOST_ASSERT(addr.size() == 16);
888 :
889 : typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
890 : typedef boost::array<uint8_t, 32> nibbles;
891 :
892 5 : if (!BindUtil::IsReverseZoneV6(ptr_name))
893 0 : return false;
894 :
895 5 : tokenizer tok(ptr_name, boost::char_separator<char>("."));
896 : nibbles nib;
897 :
898 5 : tokenizer::const_iterator tok_it = tok.begin();
899 5 : nibbles::iterator nib_it = nib.begin();
900 165 : for (; tok_it != tok.end() && nib_it != nib.end(); ++tok_it, ++nib_it) {
901 160 : std::stringstream str(*tok_it);
902 : unsigned int buf;
903 :
904 : // found ip6.arpa
905 160 : if (!(str >> std::hex >> buf))
906 0 : break;
907 :
908 160 : *nib_it = static_cast<uint8_t>(buf);
909 160 : }
910 :
911 5 : std::fill(addr.begin(), addr.end(), 0);
912 :
913 5 : std::reverse_iterator<nibbles::const_iterator> r_nib_it(nib_it);
914 5 : Ip6Address::bytes_type::iterator addr_it = addr.begin();
915 :
916 : // the most significant nibble is #0 thus always even
917 5 : bool odd_nib = false;
918 : // we should never get past addr.end() as nib.size() is 2 * 16
919 165 : for (; r_nib_it != nib.rend(); ++r_nib_it) {
920 160 : if (odd_nib) {
921 80 : *addr_it |= (*r_nib_it & 0xF);
922 80 : ++addr_it;
923 : } else {
924 80 : *addr_it |= (*r_nib_it << 4);
925 : }
926 160 : odd_nib = !odd_nib;
927 : }
928 :
929 5 : ip = Ip6Address(addr);
930 5 : return true;
931 5 : }
932 :
933 10 : bool BindUtil::GetAddrFromPtrName(std::string &ptr_name,
934 : IpAddress &ip) {
935 10 : std::string name = boost::to_lower_copy(ptr_name);
936 : std::size_t pos;
937 :
938 10 : pos = name.find(".in-addr.arpa");
939 10 : if (pos != std::string::npos) {
940 5 : Ip4Address out;
941 5 : bool success = BindUtil::GetAddrFromPtrName(ptr_name, out);
942 5 : if (success)
943 5 : ip = out;
944 5 : return success;
945 : }
946 :
947 5 : pos = name.find(".ip6.arpa");
948 5 : if (pos != std::string::npos) {
949 5 : Ip6Address out;
950 5 : bool success = BindUtil::GetAddrFromPtrName(ptr_name, out);
951 5 : if (success)
952 5 : ip = out;
953 5 : return success;
954 : }
955 :
956 0 : return false;
957 10 : }
958 :
959 0 : std::string BindUtil::GetPtrNameFromAddr(const Ip4Address &ip) {
960 0 : std::stringstream str;
961 0 : for (int i = 0; i < 4; i++) {
962 0 : str << ((ip.to_ulong() >> (i * 8)) & 0xFF) << ".";
963 : }
964 0 : str << "in-addr.arpa";
965 0 : return str.str();
966 0 : }
967 :
968 0 : std::string BindUtil::GetPtrNameFromAddr(const Ip6Address &ip6) {
969 0 : std::stringstream str;
970 : // we start with nibble #31 which is always odd
971 0 : bool odd_nibble = true;
972 :
973 0 : Ip6Address::bytes_type addr = ip6.to_bytes();
974 0 : Ip6Address::bytes_type::reverse_iterator it = addr.rbegin();
975 0 : while (it != addr.rend()) {
976 : unsigned int buf;
977 0 : if (odd_nibble) {
978 0 : buf = (*it & 0xF);
979 : } else {
980 0 : buf = (*it >> 4);
981 0 : ++it;
982 : }
983 0 : str << std::hex << buf << '.';
984 0 : odd_nibble = !odd_nibble;
985 : }
986 0 : str << "ip6.arpa";
987 :
988 0 : return str.str();
989 0 : }
990 :
991 41 : std::string BindUtil::GetFQDN(const std::string &name, const std::string &domain,
992 : const std::string &match) {
993 41 : if (name.find(match, 0) == std::string::npos)
994 82 : return name + "." + domain;
995 : else
996 0 : return name;
997 : }
998 :
999 60 : bool BindUtil::HasSpecialChars(const std::string &name) {
1000 599 : for (unsigned int i = 0; i < name.size(); ++i) {
1001 539 : uint8_t c = name[i];
1002 577 : if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
1003 83 : (c >= '0' && c <= '9') || (c == '-') || (c == '.')))
1004 0 : return true;
1005 : }
1006 60 : return false;
1007 : }
1008 :
1009 930 : void BindUtil::RemoveSpecialChars(std::string &name) {
1010 8906 : for (unsigned int i = 0; i < name.size(); ++i) {
1011 7976 : uint8_t c = name[i];
1012 8191 : if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
1013 1163 : (c >= '0' && c <= '9') || (c == '-') || (c == '.')))
1014 0 : name[i] = '-';
1015 : }
1016 930 : }
1017 :
1018 0 : void DnsNameEncoder::AddName(std::string &name, uint16_t curr_msg_offset,
1019 : uint16_t &name_plen, uint16_t &name_offset) {
1020 0 : name_plen = 0;
1021 0 : name_offset = 0;
1022 0 : std::size_t pos = 0;
1023 0 : while (pos < name.size()) {
1024 0 : std::string str = name.substr(pos);
1025 0 : if(IsPresent(str, name_offset)) {
1026 0 : name_plen = pos ? pos - 1 : 0;
1027 0 : break;
1028 : }
1029 :
1030 0 : pos = name.find('.', pos + 1);
1031 0 : if (pos == std::string::npos)
1032 0 : break;
1033 0 : pos++;
1034 0 : }
1035 0 : if (pos > 0)
1036 0 : names_.push_back(Name(name, curr_msg_offset));
1037 0 : }
1038 :
1039 0 : bool DnsNameEncoder::IsPresent(std::string &name, uint16_t &name_offset) {
1040 0 : for (unsigned int i = 0; i < names_.size(); i++) {
1041 0 : if (names_[i].name == name) {
1042 0 : name_offset = names_[i].offset;
1043 0 : return true;
1044 : }
1045 : }
1046 0 : return false;
1047 : }
|