join 1.0
lightweight network framework library
Loading...
Searching...
No Matches
dnsmessage.hpp
Go to the documentation of this file.
1
25#ifndef JOIN_FABRIC_DNSMESSAGE_HPP
26#define JOIN_FABRIC_DNSMESSAGE_HPP
27
28// libjoin.
29#include <join/ipaddress.hpp>
30#include <join/utils.hpp>
31#include <join/error.hpp>
32
33// C++.
34#include <unordered_set>
35#include <system_error>
36#include <sstream>
37#include <vector>
38#include <string>
39
40// C.
41#include <cstdint>
42
43namespace join
44{
46 using AliasList = std::unordered_set<std::string>;
47
49 using ServerList = std::unordered_set<std::string>;
50
52 using ExchangerList = std::unordered_set<std::string>;
53
58 {
59 std::string host;
60 uint16_t type = 0;
61 uint16_t dnsclass = 0;
62 };
63
68 {
69 uint32_t ttl = 0;
71 std::string name;
72 uint16_t priority = 0;
73 uint16_t weight = 0;
74 uint16_t port = 0;
75 std::vector<std::string> txts;
76 std::string mail;
77 uint32_t serial = 0;
78 uint32_t refresh = 0;
79 uint32_t retry = 0;
80 uint32_t expire = 0;
81 uint32_t minimum = 0;
82 uint16_t mxpref = 0;
83 };
84
88 struct DnsPacket
89 {
90 uint16_t id = 0;
91 uint16_t flags = 0;
94 uint16_t port = 0;
95 std::vector<QuestionRecord> questions;
96 std::vector<ResourceRecord> answers;
97 std::vector<ResourceRecord> authorities;
98 std::vector<ResourceRecord> additionals;
99 };
100
105 {
106 public:
110 enum RecordType : uint16_t
111 {
112 A = 1,
113 NS = 2,
114 CNAME = 5,
115 SOA = 6,
116 PTR = 12,
117 MX = 15,
118 TXT = 16,
119 AAAA = 28,
120 SRV = 33,
121 ANY = 255,
122 };
123
127 enum RecordClass : uint16_t
128 {
129 IN = 1,
130 };
131
135 DnsMessage () noexcept = default;
136
141 DnsMessage (const DnsMessage& other) = delete;
142
148 DnsMessage& operator= (const DnsMessage& other) = delete;
149
154 DnsMessage (DnsMessage&& other) = delete;
155
161 DnsMessage& operator= (DnsMessage&& other) = delete;
162
166 ~DnsMessage () noexcept = default;
167
174 int serialize (const DnsPacket& packet, std::stringstream& data) const
175 {
176 uint16_t id = htons (packet.id);
177 data.write (reinterpret_cast<const char*> (&id), sizeof (id));
178
179 uint16_t flags = htons (packet.flags);
180 data.write (reinterpret_cast<const char*> (&flags), sizeof (flags));
181
182 uint16_t qcount = htons (static_cast<uint16_t> (packet.questions.size ()));
183 data.write (reinterpret_cast<const char*> (&qcount), sizeof (qcount));
184
185 uint16_t ancount = htons (static_cast<uint16_t> (packet.answers.size ()));
186 data.write (reinterpret_cast<const char*> (&ancount), sizeof (ancount));
187
188 uint16_t nscount = htons (static_cast<uint16_t> (packet.authorities.size ()));
189 data.write (reinterpret_cast<const char*> (&nscount), sizeof (nscount));
190
191 uint16_t arcount = htons (static_cast<uint16_t> (packet.additionals.size ()));
192 data.write (reinterpret_cast<const char*> (&arcount), sizeof (arcount));
193
194 for (auto const& question : packet.questions)
195 {
196 if (encodeQuestion (question, data) == -1)
197 {
198 return -1; // LCOV_EXCL_LINE
199 }
200 }
201
202 for (auto const& answer : packet.answers)
203 {
204 if (encodeResource (answer, data) == -1)
205 {
206 return -1; // LCOV_EXCL_LINE
207 }
208 }
209
210 for (auto const& authority : packet.authorities)
211 {
212 if (encodeResource (authority, data) == -1)
213 {
214 return -1; // LCOV_EXCL_LINE
215 }
216 }
217
218 for (auto const& additional : packet.additionals)
219 {
220 if (encodeResource (additional, data) == -1)
221 {
222 return -1; // LCOV_EXCL_LINE
223 }
224 }
225
226 return 0;
227 }
228
235 int deserialize (DnsPacket& packet, std::stringstream& data) const
236 {
237 data.read (reinterpret_cast<char*> (&packet.id), sizeof (packet.id));
238 packet.id = ntohs (packet.id);
239
240 data.read (reinterpret_cast<char*> (&packet.flags), sizeof (packet.flags));
241 packet.flags = ntohs (packet.flags);
242
243 uint16_t qcount = 0;
244 data.read (reinterpret_cast<char*> (&qcount), sizeof (qcount));
245 qcount = ntohs (qcount);
246
247 uint16_t ancount = 0;
248 data.read (reinterpret_cast<char*> (&ancount), sizeof (ancount));
249 ancount = ntohs (ancount);
250
251 uint16_t nscount = 0;
252 data.read (reinterpret_cast<char*> (&nscount), sizeof (nscount));
253 nscount = ntohs (nscount);
254
255 uint16_t arcount = 0;
256 data.read (reinterpret_cast<char*> (&arcount), sizeof (arcount));
257 arcount = ntohs (arcount);
258
259 packet.questions.clear ();
260 for (uint16_t i = 0; i < qcount; ++i)
261 {
262 QuestionRecord question;
263 if (decodeQuestion (question, data) == -1)
264 {
265 return -1;
266 }
267 packet.questions.emplace_back (std::move (question));
268 }
269
270 packet.answers.clear ();
271 for (uint16_t i = 0; i < ancount; ++i)
272 {
273 ResourceRecord answer;
274 if (decodeResource (answer, data) == -1)
275 {
276 return -1; // LCOV_EXCL_LINE
277 }
278 packet.answers.emplace_back (std::move (answer));
279 }
280
281 packet.authorities.clear ();
282 for (uint16_t i = 0; i < nscount; ++i)
283 {
284 ResourceRecord authority;
285 if (decodeResource (authority, data) == -1)
286 {
287 return -1; // LCOV_EXCL_LINE
288 }
289 packet.authorities.emplace_back (std::move (authority));
290 }
291
292 packet.additionals.clear ();
293 for (uint16_t i = 0; i < arcount; ++i)
294 {
295 ResourceRecord additional;
296 if (decodeResource (additional, data) == -1)
297 {
298 return -1; // LCOV_EXCL_LINE
299 }
300 packet.additionals.emplace_back (std::move (additional));
301 }
302
303 return 0;
304 }
305
311 static std::error_code decodeError (uint16_t error) noexcept
312 {
313 switch (error)
314 {
315 case 0:
316 return {};
317 case 1:
318 case 4:
320 case 2:
322 case 3:
324 case 5:
326 default:
328 }
329 }
330
336 static std::string typeName (uint16_t recordType)
337 {
338 switch (recordType)
339 {
340 OUT_ENUM (A);
341 OUT_ENUM (NS);
342 OUT_ENUM (CNAME);
343 OUT_ENUM (SOA);
344 OUT_ENUM (PTR);
345 OUT_ENUM (MX);
346 OUT_ENUM (TXT);
347 OUT_ENUM (AAAA);
348 OUT_ENUM (SRV);
349 OUT_ENUM (ANY);
350 }
351
352 return "UNKNOWN";
353 }
354
360 static std::string className (uint16_t recordClass)
361 {
362 switch (recordClass & 0x7FFF)
363 {
364 OUT_ENUM (IN);
365 }
366
367 return "UNKNOWN";
368 }
369
370 private:
377 int encodeName (const std::string& name, std::stringstream& data) const
378 {
379 std::istringstream iss (name);
380
381 for (std::string token; std::getline (iss, token, '.');)
382 {
383 uint8_t len = static_cast<uint8_t> (token.size ());
384 data.write (reinterpret_cast<const char*> (&len), 1);
385 data.write (token.data (), len);
386 }
387
388 data << '\0';
389
390 return 0;
391 }
392
400 int decodeName (std::string& name, std::stringstream& data, int depth = 0) const
401 {
402 if (depth > 10)
403 {
404 return -1;
405 }
406
407 for (;;)
408 {
409 uint8_t first = 0;
410 if (!data.read (reinterpret_cast<char*> (&first), sizeof (first)))
411 {
412 return -1;
413 }
414
415 if ((first & 0xC0) == 0xC0)
416 {
417 uint8_t second = 0;
418 if (!data.read (reinterpret_cast<char*> (&second), sizeof (second)))
419 {
420 return -1;
421 }
422
423 uint16_t ptr = ((first & 0x3F) << 8) | second;
424 auto saved = data.tellg ();
425
426 data.seekg (ptr);
427 if (decodeName (name, data, depth + 1) == -1)
428 {
429 return -1;
430 }
431 data.seekg (saved);
432 break;
433 }
434
435 if (first == 0)
436 {
437 if (!name.empty () && name.back () == '.')
438 {
439 name.pop_back ();
440 }
441 break;
442 }
443
444 name.reserve (name.size () + first + 1);
445 name.resize (name.size () + first);
446 if (!data.read (&name[name.size () - first], first))
447 {
448 return -1; // LCOV_EXCL_LINE
449 }
450 name += '.';
451 }
452
453 return 0;
454 }
455
462 int encodeMail (const std::string& mail, std::stringstream& data) const
463 {
464 std::string encodedMail = mail;
465 size_t atPos = encodedMail.find ('@');
466
467 if (atPos != std::string::npos)
468 {
469 encodedMail.replace (atPos, 1, ".");
470 }
471
472 encodeName (encodedMail, data);
473
474 return 0;
475 }
476
483 int decodeMail (std::string& mail, std::stringstream& data) const
484 {
485 if (decodeName (mail, data) == -1)
486 {
487 return -1; // LCOV_EXCL_LINE
488 }
489
490 auto pos = mail.find ('.');
491 if (pos != std::string::npos)
492 {
493 mail[pos] = '@';
494 }
495
496 return 0;
497 }
498
505 int encodeQuestion (const QuestionRecord& question, std::stringstream& data) const
506 {
507 encodeName (question.host, data);
508
509 uint16_t type = htons (question.type);
510 data.write (reinterpret_cast<const char*> (&type), sizeof (type));
511
512 uint16_t dnsclass = htons (question.dnsclass);
513 data.write (reinterpret_cast<const char*> (&dnsclass), sizeof (dnsclass));
514
515 return 0;
516 }
517
524 int decodeQuestion (QuestionRecord& question, std::stringstream& data) const
525 {
526 if (decodeName (question.host, data) == -1)
527 {
528 return -1;
529 }
530
531 data.read (reinterpret_cast<char*> (&question.type), sizeof (question.type));
532 question.type = ntohs (question.type);
533
534 data.read (reinterpret_cast<char*> (&question.dnsclass), sizeof (question.dnsclass));
535 question.dnsclass = ntohs (question.dnsclass);
536
537 return 0;
538 }
539
546 int encodeResource (const ResourceRecord& resource, std::stringstream& data) const
547 {
548 encodeName (resource.host, data);
549
550 uint16_t type = htons (resource.type);
551 data.write (reinterpret_cast<const char*> (&type), sizeof (type));
552
553 uint16_t dnsclass = htons (resource.dnsclass);
554 data.write (reinterpret_cast<const char*> (&dnsclass), sizeof (dnsclass));
555
556 uint32_t ttl = htonl (resource.ttl);
557 data.write (reinterpret_cast<const char*> (&ttl), sizeof (ttl));
558
559 uint16_t dataLen = 0;
560 auto dataLenPos = data.tellp ();
561 data.write (reinterpret_cast<const char*> (&dataLen), sizeof (dataLen));
562
563 auto dataBegPos = data.tellp ();
564
565 if (resource.type == RecordType::A)
566 {
567 data.write (reinterpret_cast<const char*> (resource.addr.addr ()), sizeof (in_addr));
568 }
569 else if (resource.type == RecordType::AAAA)
570 {
571 data.write (reinterpret_cast<const char*> (resource.addr.addr ()), sizeof (in6_addr));
572 }
573 else if (resource.type == RecordType::NS)
574 {
575 encodeName (resource.name, data);
576 }
577 else if (resource.type == RecordType::CNAME)
578 {
579 encodeName (resource.name, data);
580 }
581 else if (resource.type == RecordType::PTR)
582 {
583 encodeName (resource.name, data);
584 }
585 else if (resource.type == RecordType::MX)
586 {
587 uint16_t mxpref = htons (resource.mxpref);
588 data.write (reinterpret_cast<const char*> (&mxpref), sizeof (mxpref));
589 encodeName (resource.name, data);
590 }
591 else if (resource.type == RecordType::SOA)
592 {
593 encodeName (resource.name, data);
594 encodeMail (resource.mail, data);
595
596 uint32_t serial = htonl (resource.serial);
597 data.write (reinterpret_cast<const char*> (&serial), sizeof (serial));
598
599 uint32_t refresh = htonl (resource.refresh);
600 data.write (reinterpret_cast<const char*> (&refresh), sizeof (refresh));
601
602 uint32_t retry = htonl (resource.retry);
603 data.write (reinterpret_cast<const char*> (&retry), sizeof (retry));
604
605 uint32_t expire = htonl (resource.expire);
606 data.write (reinterpret_cast<const char*> (&expire), sizeof (expire));
607
608 uint32_t minimum = htonl (resource.minimum);
609 data.write (reinterpret_cast<const char*> (&minimum), sizeof (minimum));
610 }
611 else if (resource.type == RecordType::TXT)
612 {
613 for (auto const& txt : resource.txts)
614 {
615 uint8_t size = static_cast<uint8_t> (txt.size ());
616 data.write (reinterpret_cast<const char*> (&size), sizeof (size));
617 data.write (txt.data (), size);
618 }
619 }
620 else if (resource.type == RecordType::SRV)
621 {
622 uint16_t priority = htons (resource.priority);
623 data.write (reinterpret_cast<const char*> (&priority), sizeof (priority));
624
625 uint16_t weight = htons (resource.weight);
626 data.write (reinterpret_cast<const char*> (&weight), sizeof (weight));
627
628 uint16_t port = htons (resource.port);
629 data.write (reinterpret_cast<const char*> (&port), sizeof (port));
630
631 encodeName (resource.name, data);
632 }
633
634 auto dataEndPos = data.tellp ();
635 dataLen = static_cast<uint16_t> (dataEndPos - dataBegPos);
636
637 data.seekp (dataLenPos);
638 dataLen = htons (dataLen);
639 data.write (reinterpret_cast<const char*> (&dataLen), sizeof (dataLen));
640 data.seekp (dataEndPos);
641
642 return 0;
643 }
644
651 int decodeResource (ResourceRecord& resource, std::stringstream& data) const
652 {
653 if (decodeName (resource.host, data) == -1)
654 {
655 return -1; // LCOV_EXCL_LINE
656 }
657
658 data.read (reinterpret_cast<char*> (&resource.type), sizeof (resource.type));
659 resource.type = ntohs (resource.type);
660
661 data.read (reinterpret_cast<char*> (&resource.dnsclass), sizeof (resource.dnsclass));
662 resource.dnsclass = ntohs (resource.dnsclass);
663
664 data.read (reinterpret_cast<char*> (&resource.ttl), sizeof (resource.ttl));
665 resource.ttl = ntohl (resource.ttl);
666
667 uint16_t dataLen = 0;
668 data.read (reinterpret_cast<char*> (&dataLen), sizeof (dataLen));
669 dataLen = ntohs (dataLen);
670
671 auto dataBegPos = data.tellg ();
672
673 if (resource.type == RecordType::A)
674 {
675 struct in_addr addr;
676 data.read (reinterpret_cast<char*> (&addr), sizeof (addr));
677 resource.addr = IpAddress (&addr, sizeof (struct in_addr));
678 }
679 else if (resource.type == RecordType::AAAA)
680 {
681 struct in6_addr addr;
682 data.read (reinterpret_cast<char*> (&addr), sizeof (addr));
683 resource.addr = IpAddress (&addr, sizeof (struct in6_addr));
684 }
685 else if (resource.type == RecordType::NS)
686 {
687 if (decodeName (resource.name, data) == -1)
688 {
689 return -1; // LCOV_EXCL_LINE
690 }
691 }
692 else if (resource.type == RecordType::CNAME)
693 {
694 if (decodeName (resource.name, data) == -1)
695 {
696 return -1; // LCOV_EXCL_LINE
697 }
698 }
699 else if (resource.type == RecordType::PTR)
700 {
701 if (decodeName (resource.name, data) == -1)
702 {
703 return -1; // LCOV_EXCL_LINE
704 }
705 }
706 else if (resource.type == RecordType::MX)
707 {
708 data.read (reinterpret_cast<char*> (&resource.mxpref), sizeof (resource.mxpref));
709 resource.mxpref = ntohs (resource.mxpref);
710
711 if (decodeName (resource.name, data) == -1)
712 {
713 return -1; // LCOV_EXCL_LINE
714 }
715 }
716 else if (resource.type == RecordType::SOA)
717 {
718 if (decodeName (resource.name, data) == -1)
719 {
720 return -1; // LCOV_EXCL_LINE
721 }
722
723 decodeMail (resource.mail, data);
724
725 data.read (reinterpret_cast<char*> (&resource.serial), sizeof (resource.serial));
726 resource.serial = ntohl (resource.serial);
727
728 data.read (reinterpret_cast<char*> (&resource.refresh), sizeof (resource.refresh));
729 resource.refresh = ntohl (resource.refresh);
730
731 data.read (reinterpret_cast<char*> (&resource.retry), sizeof (resource.retry));
732 resource.retry = ntohl (resource.retry);
733
734 data.read (reinterpret_cast<char*> (&resource.expire), sizeof (resource.expire));
735 resource.expire = ntohl (resource.expire);
736
737 data.read (reinterpret_cast<char*> (&resource.minimum), sizeof (resource.minimum));
738 resource.minimum = ntohl (resource.minimum);
739 }
740 else if (resource.type == RecordType::TXT)
741 {
742 while (data.tellg () != -1 && (data.tellg () - dataBegPos < dataLen))
743 {
744 uint8_t size = 0;
745 if (!data.read (reinterpret_cast<char*> (&size), sizeof (size)))
746 {
747 return -1; // LCOV_EXCL_LINE
748 }
749
750 std::string txt;
751 txt.resize (size);
752 if (!data.read (&txt[0], size))
753 {
754 return -1; // LCOV_EXCL_LINE
755 }
756
757 resource.txts.emplace_back (std::move (txt));
758 }
759 }
760 else if (resource.type == RecordType::SRV)
761 {
762 data.read (reinterpret_cast<char*> (&resource.priority), sizeof (resource.priority));
763 resource.priority = ntohs (resource.priority);
764
765 data.read (reinterpret_cast<char*> (&resource.weight), sizeof (resource.weight));
766 resource.weight = ntohs (resource.weight);
767
768 data.read (reinterpret_cast<char*> (&resource.port), sizeof (resource.port));
769 resource.port = ntohs (resource.port);
770
771 if (decodeName (resource.name, data) == -1)
772 {
773 return -1; // LCOV_EXCL_LINE
774 }
775 }
776 else
777 {
778 data.seekg (dataBegPos + static_cast<std::streamoff> (dataLen));
779 }
780
781 return 0;
782 }
783 };
784}
785
786#endif
DNS message codec.
Definition dnsmessage.hpp:105
int serialize(const DnsPacket &packet, std::stringstream &data) const
serialize a DNS packet into a byte stream.
Definition dnsmessage.hpp:174
static std::string className(uint16_t recordClass)
get record class name.
Definition dnsmessage.hpp:360
RecordType
DNS record types.
Definition dnsmessage.hpp:111
@ A
Definition dnsmessage.hpp:112
@ PTR
Definition dnsmessage.hpp:116
@ TXT
Definition dnsmessage.hpp:118
@ SOA
Definition dnsmessage.hpp:115
@ NS
Definition dnsmessage.hpp:113
@ MX
Definition dnsmessage.hpp:117
@ ANY
Definition dnsmessage.hpp:121
@ CNAME
Definition dnsmessage.hpp:114
@ SRV
Definition dnsmessage.hpp:120
@ AAAA
Definition dnsmessage.hpp:119
int deserialize(DnsPacket &packet, std::stringstream &data) const
deserialize a DNS packet from a byte stream.
Definition dnsmessage.hpp:235
static std::error_code decodeError(uint16_t error) noexcept
convert DNS error to system error code.
Definition dnsmessage.hpp:311
RecordClass
DNS record classes.
Definition dnsmessage.hpp:128
@ IN
Definition dnsmessage.hpp:129
DnsMessage() noexcept=default
create the DnsMessage instance.
static std::string typeName(uint16_t recordType)
get record type name.
Definition dnsmessage.hpp:336
IPv6, IPv4 address class.
Definition ipaddress.hpp:51
Definition acceptor.hpp:32
std::unordered_set< std::string > ExchangerList
list of mail exchangers.
Definition dnsmessage.hpp:52
std::unordered_set< std::string > ServerList
list of name servers.
Definition dnsmessage.hpp:49
std::unordered_set< std::string > AliasList
list of aliases.
Definition dnsmessage.hpp:46
std::error_code make_error_code(join::Errc code) noexcept
Create an std::error_code object.
Definition error.cpp:150
Definition error.hpp:137
DNS packet.
Definition dnsmessage.hpp:89
std::vector< ResourceRecord > additionals
Definition dnsmessage.hpp:98
uint16_t id
Definition dnsmessage.hpp:90
uint16_t flags
Definition dnsmessage.hpp:91
uint16_t port
Definition dnsmessage.hpp:94
std::vector< QuestionRecord > questions
Definition dnsmessage.hpp:95
std::vector< ResourceRecord > authorities
Definition dnsmessage.hpp:97
IpAddress src
Definition dnsmessage.hpp:92
IpAddress dest
Definition dnsmessage.hpp:93
std::vector< ResourceRecord > answers
Definition dnsmessage.hpp:96
question record.
Definition dnsmessage.hpp:58
std::string host
Definition dnsmessage.hpp:59
uint16_t type
Definition dnsmessage.hpp:60
uint16_t dnsclass
Definition dnsmessage.hpp:61
resource record.
Definition dnsmessage.hpp:68
std::vector< std::string > txts
Definition dnsmessage.hpp:75
IpAddress addr
Definition dnsmessage.hpp:70
uint32_t serial
Definition dnsmessage.hpp:77
uint32_t refresh
Definition dnsmessage.hpp:78
uint16_t mxpref
Definition dnsmessage.hpp:82
uint16_t weight
Definition dnsmessage.hpp:73
uint16_t port
Definition dnsmessage.hpp:74
std::string name
Definition dnsmessage.hpp:71
uint32_t expire
Definition dnsmessage.hpp:80
uint32_t ttl
Definition dnsmessage.hpp:69
uint32_t minimum
Definition dnsmessage.hpp:81
uint16_t priority
Definition dnsmessage.hpp:72
uint32_t retry
Definition dnsmessage.hpp:79
std::string mail
Definition dnsmessage.hpp:76
uint16_t port
Definition tcpacceptor_test.cpp:36
#define OUT_ENUM(a)
Definition utils.hpp:49