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