join 1.0
lightweight network framework library
Loading...
Searching...
No Matches
dns_message.hpp
Go to the documentation of this file.
1
25#ifndef JOIN_FABRIC_DNS_MESSAGE_HPP
26#define JOIN_FABRIC_DNS_MESSAGE_HPP
27
28// libjoin.
29#include <join/ip_address.hpp>
30#include <join/utils.hpp>
31#include <join/error.hpp>
32
33// C++.
34#include <unordered_set>
35#include <system_error>
36#include <vector>
37#include <string>
38
39// C.
40#include <cstdint>
41
42namespace join
43{
45 using AliasList = std::unordered_set<std::string>;
46
48 using ServerList = std::unordered_set<std::string>;
49
51 using ExchangerList = std::unordered_set<std::string>;
52
57 {
58 std::string host;
59 uint16_t type = 0;
60 uint16_t dnsclass = 0;
61 };
62
67 {
68 uint32_t ttl = 0;
70 std::string name;
71 uint16_t priority = 0;
72 uint16_t weight = 0;
73 uint16_t port = 0;
74 std::vector<std::string> txts;
75 std::string mail;
76 uint32_t serial = 0;
77 uint32_t refresh = 0;
78 uint32_t retry = 0;
79 uint32_t expire = 0;
80 uint32_t minimum = 0;
81 uint16_t mxpref = 0;
82 };
83
87 struct DnsPacket
88 {
89 uint16_t id = 0;
90 uint16_t flags = 0;
93 uint16_t port = 0;
94 std::vector<QuestionRecord> questions;
95 std::vector<ResourceRecord> answers;
96 std::vector<ResourceRecord> authorities;
97 std::vector<ResourceRecord> additionals;
98 };
99
104 {
105 public:
109 enum RecordType : uint16_t
110 {
111 A = 1,
112 NS = 2,
113 CNAME = 5,
114 SOA = 6,
115 PTR = 12,
116 MX = 15,
117 TXT = 16,
118 AAAA = 28,
119 SRV = 33,
120 ANY = 255,
121 };
122
126 enum RecordClass : uint16_t
127 {
128 IN = 1,
129 };
130
134 DnsMessage () noexcept = default;
135
140 DnsMessage (const DnsMessage& other) = delete;
141
147 DnsMessage& operator= (const DnsMessage& other) = delete;
148
153 DnsMessage (DnsMessage&& other) = delete;
154
160 DnsMessage& operator= (DnsMessage&& other) = delete;
161
165 ~DnsMessage () noexcept = default;
166
174 ssize_t serialize (const DnsPacket& packet, char* data, size_t maxSize) const
175 {
176 char* cur = data;
177 const char* end = data + maxSize;
178 bool ok = true;
179
180 uint16_t id = htons (packet.id);
181 ok &= writeBytes (cur, end, &id, sizeof (id));
182
183 uint16_t flags = htons (packet.flags);
184 ok &= writeBytes (cur, end, &flags, sizeof (flags));
185
186 uint16_t qcount = htons (static_cast<uint16_t> (packet.questions.size ()));
187 ok &= writeBytes (cur, end, &qcount, sizeof (qcount));
188
189 uint16_t ancount = htons (static_cast<uint16_t> (packet.answers.size ()));
190 ok &= writeBytes (cur, end, &ancount, sizeof (ancount));
191
192 uint16_t nscount = htons (static_cast<uint16_t> (packet.authorities.size ()));
193 ok &= writeBytes (cur, end, &nscount, sizeof (nscount));
194
195 uint16_t arcount = htons (static_cast<uint16_t> (packet.additionals.size ()));
196 ok &= writeBytes (cur, end, &arcount, sizeof (arcount));
197
198 for (auto const& question : packet.questions)
199 {
200 ok &= encodeQuestion (question, cur, end);
201 }
202
203 for (auto const& answer : packet.answers)
204 {
205 ok &= encodeResource (answer, cur, end);
206 }
207
208 for (auto const& authority : packet.authorities)
209 {
210 ok &= encodeResource (authority, cur, end);
211 }
212
213 for (auto const& additional : packet.additionals)
214 {
215 ok &= encodeResource (additional, cur, end);
216 }
217
218 return ok ? cur - data : -1;
219 }
220
228 int deserialize (DnsPacket& packet, const char* data, size_t size) const
229 {
230 const char* cur = data;
231 const char* end = data + size;
232 bool ok = true;
233
234 uint16_t qcount = 0, ancount = 0, nscount = 0, arcount = 0;
235
236 ok &= readBytes (cur, end, &packet.id, sizeof (packet.id));
237 ok &= readBytes (cur, end, &packet.flags, sizeof (packet.flags));
238 ok &= readBytes (cur, end, &qcount, sizeof (qcount));
239 ok &= readBytes (cur, end, &ancount, sizeof (ancount));
240 ok &= readBytes (cur, end, &nscount, sizeof (nscount));
241 ok &= readBytes (cur, end, &arcount, sizeof (arcount));
242
243 if (!ok)
244 {
245 return -1;
246 }
247
248 packet.id = ntohs (packet.id);
249 packet.flags = ntohs (packet.flags);
250 qcount = ntohs (qcount);
251 ancount = ntohs (ancount);
252 nscount = ntohs (nscount);
253 arcount = ntohs (arcount);
254
255 packet.questions.clear ();
256 for (uint16_t i = 0; i < qcount; ++i)
257 {
258 QuestionRecord question;
259 if (decodeQuestion (question, cur, data, end) == -1)
260 {
261 return -1;
262 }
263 packet.questions.emplace_back (std::move (question));
264 }
265
266 packet.answers.clear ();
267 for (uint16_t i = 0; i < ancount; ++i)
268 {
269 ResourceRecord answer;
270 if (decodeResource (answer, cur, data, end) == -1)
271 {
272 return -1;
273 }
274 packet.answers.emplace_back (std::move (answer));
275 }
276
277 packet.authorities.clear ();
278 for (uint16_t i = 0; i < nscount; ++i)
279 {
280 ResourceRecord authority;
281 if (decodeResource (authority, cur, data, end) == -1)
282 {
283 return -1;
284 }
285 packet.authorities.emplace_back (std::move (authority));
286 }
287
288 packet.additionals.clear ();
289 for (uint16_t i = 0; i < arcount; ++i)
290 {
291 ResourceRecord additional;
292 if (decodeResource (additional, cur, data, end) == -1)
293 {
294 return -1;
295 }
296 packet.additionals.emplace_back (std::move (additional));
297 }
298
299 return 0;
300 }
301
307 static std::error_code decodeError (uint16_t error) noexcept
308 {
309 switch (error)
310 {
311 case 0:
312 return {};
313 case 1:
314 case 4:
316 case 2:
318 case 3:
320 case 5:
322 default:
324 }
325 }
326
332 static std::string typeName (uint16_t recordType)
333 {
334 switch (recordType)
335 {
336 OUT_ENUM (A);
337 OUT_ENUM (NS);
338 OUT_ENUM (CNAME);
339 OUT_ENUM (SOA);
340 OUT_ENUM (PTR);
341 OUT_ENUM (MX);
342 OUT_ENUM (TXT);
343 OUT_ENUM (AAAA);
344 OUT_ENUM (SRV);
345 OUT_ENUM (ANY);
346 }
347
348 return "UNKNOWN";
349 }
350
356 static std::string className (uint16_t recordClass)
357 {
358 switch (recordClass & 0x7FFF)
359 {
360 OUT_ENUM (IN);
361 }
362
363 return "UNKNOWN";
364 }
365
366 private:
374 bool encodeName (const std::string& name, char*& cur, const char* end) const
375 {
376 bool ok = true;
377
378 for (size_t pos = 0; pos < name.size ();)
379 {
380 size_t dot = name.find ('.', pos);
381 if (dot == std::string::npos)
382 {
383 dot = name.size ();
384 }
385
386 uint8_t len = static_cast<uint8_t> (dot - pos);
387 ok &= writeBytes (cur, end, &len, sizeof (len));
388 ok &= writeBytes (cur, end, name.data () + pos, len);
389
390 pos = dot + 1;
391 }
392
393 uint8_t root = 0;
394 ok &= writeBytes (cur, end, &root, sizeof (root));
395
396 return ok;
397 }
398
408 int decodeName (std::string& name, const char*& cur, const char* begin, const char* end, int depth = 0) const
409 {
410 if (depth > 10)
411 {
412 return -1;
413 }
414
415 for (;;)
416 {
417 uint8_t first = 0;
418 if (!readBytes (cur, end, &first, sizeof (first)))
419 {
420 return -1;
421 }
422
423 if ((first & 0xC0) == 0xC0)
424 {
425 uint8_t second = 0;
426 if (!readBytes (cur, end, &second, sizeof (second)))
427 {
428 return -1;
429 }
430
431 const size_t offset = ((first & 0x3F) << 8) | second;
432 if (offset >= static_cast<size_t> (end - begin))
433 {
435 return -1;
436 }
437
438 const char* target = begin + offset;
439 if (decodeName (name, target, begin, end, depth + 1) == -1)
440 {
441 return -1;
442 }
443 break;
444 }
445
446 if (first == 0)
447 {
448 if (!name.empty () && name.back () == '.')
449 {
450 name.pop_back ();
451 }
452 break;
453 }
454
455 name.reserve (name.size () + first + 1);
456 name.resize (name.size () + first);
457 if (!readBytes (cur, end, &name[name.size () - first], first))
458 {
459 return -1;
460 }
461 name += '.';
462 }
463
464 return 0;
465 }
466
474 bool encodeMail (const std::string& mail, char*& cur, const char* end) const
475 {
476 std::string encodedMail = mail;
477 size_t atPos = encodedMail.find ('@');
478
479 if (atPos != std::string::npos)
480 {
481 encodedMail.replace (atPos, 1, ".");
482 }
483
484 return encodeName (encodedMail, cur, end);
485 }
486
495 int decodeMail (std::string& mail, const char*& cur, const char* begin, const char* end) const
496 {
497 if (decodeName (mail, cur, begin, end) == -1)
498 {
499 return -1;
500 }
501
502 auto pos = mail.find ('.');
503 if (pos != std::string::npos)
504 {
505 mail[pos] = '@';
506 }
507
508 return 0;
509 }
510
518 bool encodeQuestion (const QuestionRecord& question, char*& cur, const char* end) const
519 {
520 bool ok = encodeName (question.host, cur, end);
521
522 uint16_t type = htons (question.type);
523 ok &= writeBytes (cur, end, &type, sizeof (type));
524
525 uint16_t dnsclass = htons (question.dnsclass);
526 ok &= writeBytes (cur, end, &dnsclass, sizeof (dnsclass));
527
528 return ok;
529 }
530
539 int decodeQuestion (QuestionRecord& question, const char*& cur, const char* begin, const char* end) const
540 {
541 if (decodeName (question.host, cur, begin, end) == -1)
542 {
543 return -1;
544 }
545
546 bool ok = true;
547 ok &= readBytes (cur, end, &question.type, sizeof (question.type));
548 ok &= readBytes (cur, end, &question.dnsclass, sizeof (question.dnsclass));
549
550 if (!ok)
551 {
552 return -1;
553 }
554
555 question.type = ntohs (question.type);
556 question.dnsclass = ntohs (question.dnsclass);
557
558 return 0;
559 }
560
568 bool encodeResource (const ResourceRecord& resource, char*& cur, const char* end) const
569 {
570 bool ok = encodeName (resource.host, cur, end);
571
572 uint16_t type = htons (resource.type);
573 ok &= writeBytes (cur, end, &type, sizeof (type));
574
575 uint16_t dnsclass = htons (resource.dnsclass);
576 ok &= writeBytes (cur, end, &dnsclass, sizeof (dnsclass));
577
578 uint32_t ttl = htonl (resource.ttl);
579 ok &= writeBytes (cur, end, &ttl, sizeof (ttl));
580
581 uint16_t dataLen = 0;
582 char* length = cur;
583 ok &= writeBytes (cur, end, &dataLen, sizeof (dataLen));
584
585 char* rdata = cur;
586
587 if (resource.type == RecordType::A)
588 {
589 ok &= writeBytes (cur, end, resource.addr.addr (), sizeof (in_addr));
590 }
591 else if (resource.type == RecordType::AAAA)
592 {
593 ok &= writeBytes (cur, end, resource.addr.addr (), sizeof (in6_addr));
594 }
595 else if (resource.type == RecordType::NS)
596 {
597 ok &= encodeName (resource.name, cur, end);
598 }
599 else if (resource.type == RecordType::CNAME)
600 {
601 ok &= encodeName (resource.name, cur, end);
602 }
603 else if (resource.type == RecordType::PTR)
604 {
605 ok &= encodeName (resource.name, cur, end);
606 }
607 else if (resource.type == RecordType::MX)
608 {
609 uint16_t mxpref = htons (resource.mxpref);
610 ok &= writeBytes (cur, end, &mxpref, sizeof (mxpref));
611 ok &= encodeName (resource.name, cur, end);
612 }
613 else if (resource.type == RecordType::SOA)
614 {
615 ok &= encodeName (resource.name, cur, end);
616 ok &= encodeMail (resource.mail, cur, end);
617
618 uint32_t serial = htonl (resource.serial);
619 ok &= writeBytes (cur, end, &serial, sizeof (serial));
620
621 uint32_t refresh = htonl (resource.refresh);
622 ok &= writeBytes (cur, end, &refresh, sizeof (refresh));
623
624 uint32_t retry = htonl (resource.retry);
625 ok &= writeBytes (cur, end, &retry, sizeof (retry));
626
627 uint32_t expire = htonl (resource.expire);
628 ok &= writeBytes (cur, end, &expire, sizeof (expire));
629
630 uint32_t minimum = htonl (resource.minimum);
631 ok &= writeBytes (cur, end, &minimum, sizeof (minimum));
632 }
633 else if (resource.type == RecordType::TXT)
634 {
635 for (auto const& txt : resource.txts)
636 {
637 uint8_t size = static_cast<uint8_t> (txt.size ());
638 ok &= writeBytes (cur, end, &size, sizeof (size));
639 ok &= writeBytes (cur, end, txt.data (), size);
640 }
641 }
642 else if (resource.type == RecordType::SRV)
643 {
644 uint16_t priority = htons (resource.priority);
645 ok &= writeBytes (cur, end, &priority, sizeof (priority));
646
647 uint16_t weight = htons (resource.weight);
648 ok &= writeBytes (cur, end, &weight, sizeof (weight));
649
650 uint16_t port = htons (resource.port);
651 ok &= writeBytes (cur, end, &port, sizeof (port));
652
653 ok &= encodeName (resource.name, cur, end);
654 }
655
656 if (ok)
657 {
658 dataLen = htons (static_cast<uint16_t> (cur - rdata));
659 ::memcpy (length, &dataLen, sizeof (dataLen));
660 }
661
662 return ok;
663 }
664
673 int decodeResource (ResourceRecord& resource, const char*& cur, const char* begin, const char* end) const
674 {
675 if (decodeName (resource.host, cur, begin, end) == -1)
676 {
677 return -1;
678 }
679
680 uint16_t dataLen = 0;
681
682 bool ok = true;
683 ok &= readBytes (cur, end, &resource.type, sizeof (resource.type));
684 ok &= readBytes (cur, end, &resource.dnsclass, sizeof (resource.dnsclass));
685 ok &= readBytes (cur, end, &resource.ttl, sizeof (resource.ttl));
686 ok &= readBytes (cur, end, &dataLen, sizeof (dataLen));
687
688 if (!ok)
689 {
690 return -1;
691 }
692
693 resource.type = ntohs (resource.type);
694 resource.dnsclass = ntohs (resource.dnsclass);
695 resource.ttl = ntohl (resource.ttl);
696 dataLen = ntohs (dataLen);
697
698 if (dataLen > static_cast<size_t> (end - cur))
699 {
701 return -1;
702 }
703
704 const char* rdataEnd = cur + dataLen;
705
706 if (resource.type == RecordType::A)
707 {
708 struct in_addr addr;
709 if (!readBytes (cur, end, &addr, sizeof (addr)))
710 {
711 return -1;
712 }
713 resource.addr = IpAddress (&addr, sizeof (struct in_addr));
714 }
715 else if (resource.type == RecordType::AAAA)
716 {
717 struct in6_addr addr;
718 if (!readBytes (cur, end, &addr, sizeof (addr)))
719 {
720 return -1;
721 }
722 resource.addr = IpAddress (&addr, sizeof (struct in6_addr));
723 }
724 else if (resource.type == RecordType::NS)
725 {
726 if (decodeName (resource.name, cur, begin, end) == -1)
727 {
728 return -1;
729 }
730 }
731 else if (resource.type == RecordType::CNAME)
732 {
733 if (decodeName (resource.name, cur, begin, end) == -1)
734 {
735 return -1;
736 }
737 }
738 else if (resource.type == RecordType::PTR)
739 {
740 if (decodeName (resource.name, cur, begin, end) == -1)
741 {
742 return -1;
743 }
744 }
745 else if (resource.type == RecordType::MX)
746 {
747 if (!readBytes (cur, end, &resource.mxpref, sizeof (resource.mxpref)) ||
748 (decodeName (resource.name, cur, begin, end) == -1))
749 {
750 return -1;
751 }
752
753 resource.mxpref = ntohs (resource.mxpref);
754 }
755 else if (resource.type == RecordType::SOA)
756 {
757 if ((decodeName (resource.name, cur, begin, end) == -1) ||
758 (decodeMail (resource.mail, cur, begin, end) == -1))
759 {
760 return -1;
761 }
762
763 ok &= readBytes (cur, end, &resource.serial, sizeof (resource.serial));
764 ok &= readBytes (cur, end, &resource.refresh, sizeof (resource.refresh));
765 ok &= readBytes (cur, end, &resource.retry, sizeof (resource.retry));
766 ok &= readBytes (cur, end, &resource.expire, sizeof (resource.expire));
767 ok &= readBytes (cur, end, &resource.minimum, sizeof (resource.minimum));
768
769 if (!ok)
770 {
771 return -1;
772 }
773
774 resource.serial = ntohl (resource.serial);
775 resource.refresh = ntohl (resource.refresh);
776 resource.retry = ntohl (resource.retry);
777 resource.expire = ntohl (resource.expire);
778 resource.minimum = ntohl (resource.minimum);
779 }
780 else if (resource.type == RecordType::TXT)
781 {
782 while (cur < rdataEnd)
783 {
784 uint8_t size = 0;
785 if (!readBytes (cur, end, &size, sizeof (size)))
786 {
787 return -1; // LCOV_EXCL_LINE
788 }
789
790 std::string txt;
791 txt.resize (size);
792 if (!readBytes (cur, end, &txt[0], size))
793 {
794 return -1;
795 }
796
797 resource.txts.emplace_back (std::move (txt));
798 }
799 }
800 else if (resource.type == RecordType::SRV)
801 {
802 ok &= readBytes (cur, end, &resource.priority, sizeof (resource.priority));
803 ok &= readBytes (cur, end, &resource.weight, sizeof (resource.weight));
804 ok &= readBytes (cur, end, &resource.port, sizeof (resource.port));
805
806 if (!ok || (decodeName (resource.name, cur, begin, end) == -1))
807 {
808 return -1;
809 }
810
811 resource.priority = ntohs (resource.priority);
812 resource.weight = ntohs (resource.weight);
813 resource.port = ntohs (resource.port);
814 }
815 else
816 {
817 cur = rdataEnd;
818 }
819
820 return 0;
821 }
822 };
823}
824
825#endif
DNS message codec.
Definition dns_message.hpp:104
int deserialize(DnsPacket &packet, const char *data, size_t size) const
deserialize a DNS packet from a buffer.
Definition dns_message.hpp:228
static std::string className(uint16_t recordClass)
get record class name.
Definition dns_message.hpp:356
RecordType
DNS record types.
Definition dns_message.hpp:110
@ A
Definition dns_message.hpp:111
@ PTR
Definition dns_message.hpp:115
@ TXT
Definition dns_message.hpp:117
@ SOA
Definition dns_message.hpp:114
@ NS
Definition dns_message.hpp:112
@ MX
Definition dns_message.hpp:116
@ ANY
Definition dns_message.hpp:120
@ CNAME
Definition dns_message.hpp:113
@ SRV
Definition dns_message.hpp:119
@ AAAA
Definition dns_message.hpp:118
ssize_t serialize(const DnsPacket &packet, char *data, size_t maxSize) const
serialize a DNS packet into a buffer.
Definition dns_message.hpp:174
static std::error_code decodeError(uint16_t error) noexcept
convert DNS error to system error code.
Definition dns_message.hpp:307
RecordClass
DNS record classes.
Definition dns_message.hpp:127
@ IN
Definition dns_message.hpp:128
DnsMessage() noexcept=default
create the DnsMessage instance.
static std::string typeName(uint16_t recordType)
get record type name.
Definition dns_message.hpp:332
IPv6, IPv4 address class.
Definition ip_address.hpp:52
Definition acceptor.hpp:32
std::unordered_set< std::string > ExchangerList
list of mail exchangers.
Definition dns_message.hpp:51
std::unordered_set< std::string > ServerList
list of name servers.
Definition dns_message.hpp:48
bool readBytes(const char *&cur, const char *end, void *out, size_t size) noexcept
read bytes from a buffer.
Definition utils.hpp:276
std::unordered_set< std::string > AliasList
list of aliases.
Definition dns_message.hpp:45
std::error_code make_error_code(join::Errc code) noexcept
Create an std::error_code object.
Definition error.cpp:195
bool writeBytes(char *&cur, const char *end, const void *in, size_t size) noexcept
write bytes to a buffer.
Definition utils.hpp:298
DNS packet.
Definition dns_message.hpp:88
std::vector< ResourceRecord > additionals
Definition dns_message.hpp:97
uint16_t id
Definition dns_message.hpp:89
uint16_t flags
Definition dns_message.hpp:90
uint16_t port
Definition dns_message.hpp:93
std::vector< QuestionRecord > questions
Definition dns_message.hpp:94
std::vector< ResourceRecord > authorities
Definition dns_message.hpp:96
IpAddress src
Definition dns_message.hpp:91
IpAddress dest
Definition dns_message.hpp:92
std::vector< ResourceRecord > answers
Definition dns_message.hpp:95
question record.
Definition dns_message.hpp:57
std::string host
Definition dns_message.hpp:58
uint16_t type
Definition dns_message.hpp:59
uint16_t dnsclass
Definition dns_message.hpp:60
resource record.
Definition dns_message.hpp:67
std::vector< std::string > txts
Definition dns_message.hpp:74
IpAddress addr
Definition dns_message.hpp:69
uint32_t serial
Definition dns_message.hpp:76
uint32_t refresh
Definition dns_message.hpp:77
uint16_t mxpref
Definition dns_message.hpp:81
uint16_t weight
Definition dns_message.hpp:72
uint16_t port
Definition dns_message.hpp:73
std::string name
Definition dns_message.hpp:70
uint32_t expire
Definition dns_message.hpp:79
uint32_t ttl
Definition dns_message.hpp:68
uint32_t minimum
Definition dns_message.hpp:80
uint16_t priority
Definition dns_message.hpp:71
uint32_t retry
Definition dns_message.hpp:78
std::string mail
Definition dns_message.hpp:75
uint16_t port
Definition tcp_acceptor_test.cpp:36
#define OUT_ENUM(a)
Definition utils.hpp:53