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 data << static_cast<uint8_t> (token.size ());
384 data << token;
385 }
386
387 data << '\0';
388
389 return 0;
390 }
391
399 int decodeName (std::string& name, std::stringstream& data, int depth = 0) const
400 {
401 if (depth > 10)
402 {
403 return -1;
404 }
405
406 for (;;)
407 {
408 auto pos = data.tellg ();
409
410 uint16_t offset = 0;
411 data.read (reinterpret_cast<char*> (&offset), sizeof (offset));
412 offset = ntohs (offset);
413
414 if (offset & 0xC000)
415 {
416 pos = data.tellg ();
417 data.seekg (offset & 0x3FFF);
418 if (decodeName (name, data, depth + 1) == -1)
419 {
420 return -1;
421 }
422 data.seekg (pos);
423 break;
424 }
425 else
426 {
427 data.seekg (pos);
428
429 uint8_t size = 0;
430 data.read (reinterpret_cast<char*> (&size), sizeof (size));
431
432 if (size == 0)
433 {
434 if (!name.empty () && name.back () == '.')
435 {
436 name.pop_back ();
437 }
438 break;
439 }
440
441 name.resize (name.size () + size);
442 data.read (&name[name.size () - size], size);
443 name += '.';
444 }
445 }
446
447 return 0;
448 }
449
456 int encodeMail (const std::string& mail, std::stringstream& data) const
457 {
458 std::string encodedMail = mail;
459 size_t atPos = encodedMail.find ('@');
460
461 if (atPos != std::string::npos)
462 {
463 encodedMail.replace (atPos, 1, ".");
464 }
465
466 encodeName (encodedMail, data);
467
468 return 0;
469 }
470
477 int decodeMail (std::string& mail, std::stringstream& data) const
478 {
479 if (decodeName (mail, data) == -1)
480 {
481 return -1; // LCOV_EXCL_LINE
482 }
483
484 auto pos = mail.find ('.');
485 if (pos != std::string::npos)
486 {
487 mail[pos] = '@';
488 }
489
490 return 0;
491 }
492
499 int encodeQuestion (const QuestionRecord& question, std::stringstream& data) const
500 {
501 encodeName (question.host, data);
502
503 uint16_t type = htons (question.type);
504 data.write (reinterpret_cast<const char*> (&type), sizeof (type));
505
506 uint16_t dnsclass = htons (question.dnsclass);
507 data.write (reinterpret_cast<const char*> (&dnsclass), sizeof (dnsclass));
508
509 return 0;
510 }
511
518 int decodeQuestion (QuestionRecord& question, std::stringstream& data) const
519 {
520 if (decodeName (question.host, data) == -1)
521 {
522 return -1;
523 }
524
525 data.read (reinterpret_cast<char*> (&question.type), sizeof (question.type));
526 question.type = ntohs (question.type);
527
528 data.read (reinterpret_cast<char*> (&question.dnsclass), sizeof (question.dnsclass));
529 question.dnsclass = ntohs (question.dnsclass);
530
531 return 0;
532 }
533
540 int encodeResource (const ResourceRecord& resource, std::stringstream& data) const
541 {
542 encodeName (resource.host, data);
543
544 uint16_t type = htons (resource.type);
545 data.write (reinterpret_cast<const char*> (&type), sizeof (type));
546
547 uint16_t dnsclass = htons (resource.dnsclass);
548 data.write (reinterpret_cast<const char*> (&dnsclass), sizeof (dnsclass));
549
550 uint32_t ttl = htonl (resource.ttl);
551 data.write (reinterpret_cast<const char*> (&ttl), sizeof (ttl));
552
553 uint16_t dataLen = 0;
554 auto dataLenPos = data.tellp ();
555 data.write (reinterpret_cast<const char*> (&dataLen), sizeof (dataLen));
556
557 auto dataBegPos = data.tellp ();
558
559 if (resource.type == RecordType::A)
560 {
561 data.write (reinterpret_cast<const char*> (resource.addr.addr ()), sizeof (in_addr));
562 }
563 else if (resource.type == RecordType::AAAA)
564 {
565 data.write (reinterpret_cast<const char*> (resource.addr.addr ()), sizeof (in6_addr));
566 }
567 else if (resource.type == RecordType::NS)
568 {
569 encodeName (resource.name, data);
570 }
571 else if (resource.type == RecordType::CNAME)
572 {
573 encodeName (resource.name, data);
574 }
575 else if (resource.type == RecordType::PTR)
576 {
577 encodeName (resource.name, data);
578 }
579 else if (resource.type == RecordType::MX)
580 {
581 uint16_t mxpref = htons (resource.mxpref);
582 data.write (reinterpret_cast<const char*> (&mxpref), sizeof (mxpref));
583 encodeName (resource.name, data);
584 }
585 else if (resource.type == RecordType::SOA)
586 {
587 encodeName (resource.name, data);
588 encodeMail (resource.mail, data);
589
590 uint32_t serial = htonl (resource.serial);
591 data.write (reinterpret_cast<const char*> (&serial), sizeof (serial));
592
593 uint32_t refresh = htonl (resource.refresh);
594 data.write (reinterpret_cast<const char*> (&refresh), sizeof (refresh));
595
596 uint32_t retry = htonl (resource.retry);
597 data.write (reinterpret_cast<const char*> (&retry), sizeof (retry));
598
599 uint32_t expire = htonl (resource.expire);
600 data.write (reinterpret_cast<const char*> (&expire), sizeof (expire));
601
602 uint32_t minimum = htonl (resource.minimum);
603 data.write (reinterpret_cast<const char*> (&minimum), sizeof (minimum));
604 }
605 else if (resource.type == RecordType::TXT)
606 {
607 for (auto const& txt : resource.txts)
608 {
609 uint8_t size = static_cast<uint8_t> (txt.size ());
610 data.write (reinterpret_cast<const char*> (&size), sizeof (size));
611 data.write (txt.data (), size);
612 }
613 }
614 else if (resource.type == RecordType::SRV)
615 {
616 uint16_t priority = htons (resource.priority);
617 data.write (reinterpret_cast<const char*> (&priority), sizeof (priority));
618
619 uint16_t weight = htons (resource.weight);
620 data.write (reinterpret_cast<const char*> (&weight), sizeof (weight));
621
622 uint16_t port = htons (resource.port);
623 data.write (reinterpret_cast<const char*> (&port), sizeof (port));
624
625 encodeName (resource.name, data);
626 }
627
628 auto dataEndPos = data.tellp ();
629 dataLen = static_cast<uint16_t> (dataEndPos - dataBegPos);
630
631 data.seekp (dataLenPos);
632 dataLen = htons (dataLen);
633 data.write (reinterpret_cast<const char*> (&dataLen), sizeof (dataLen));
634 data.seekp (dataEndPos);
635
636 return 0;
637 }
638
645 int decodeResource (ResourceRecord& resource, std::stringstream& data) const
646 {
647 if (decodeName (resource.host, data) == -1)
648 {
649 return -1; // LCOV_EXCL_LINE
650 }
651
652 data.read (reinterpret_cast<char*> (&resource.type), sizeof (resource.type));
653 resource.type = ntohs (resource.type);
654
655 data.read (reinterpret_cast<char*> (&resource.dnsclass), sizeof (resource.dnsclass));
656 resource.dnsclass = ntohs (resource.dnsclass);
657
658 data.read (reinterpret_cast<char*> (&resource.ttl), sizeof (resource.ttl));
659 resource.ttl = ntohl (resource.ttl);
660
661 uint16_t dataLen = 0;
662 data.read (reinterpret_cast<char*> (&dataLen), sizeof (dataLen));
663 dataLen = ntohs (dataLen);
664
665 auto dataBegPos = data.tellg ();
666
667 if (resource.type == RecordType::A)
668 {
669 struct in_addr addr;
670 data.read (reinterpret_cast<char*> (&addr), sizeof (addr));
671 resource.addr = IpAddress (&addr, sizeof (struct in_addr));
672 }
673 else if (resource.type == RecordType::AAAA)
674 {
675 struct in6_addr addr;
676 data.read (reinterpret_cast<char*> (&addr), sizeof (addr));
677 resource.addr = IpAddress (&addr, sizeof (struct in6_addr));
678 }
679 else if (resource.type == RecordType::NS)
680 {
681 if (decodeName (resource.name, data) == -1)
682 {
683 return -1; // LCOV_EXCL_LINE
684 }
685 }
686 else if (resource.type == RecordType::CNAME)
687 {
688 if (decodeName (resource.name, data) == -1)
689 {
690 return -1; // LCOV_EXCL_LINE
691 }
692 }
693 else if (resource.type == RecordType::PTR)
694 {
695 if (decodeName (resource.name, data) == -1)
696 {
697 return -1; // LCOV_EXCL_LINE
698 }
699 }
700 else if (resource.type == RecordType::MX)
701 {
702 data.read (reinterpret_cast<char*> (&resource.mxpref), sizeof (resource.mxpref));
703 resource.mxpref = ntohs (resource.mxpref);
704
705 if (decodeName (resource.name, data) == -1)
706 {
707 return -1; // LCOV_EXCL_LINE
708 }
709 }
710 else if (resource.type == RecordType::SOA)
711 {
712 if (decodeName (resource.name, data) == -1)
713 {
714 return -1; // LCOV_EXCL_LINE
715 }
716
717 decodeMail (resource.mail, data);
718
719 data.read (reinterpret_cast<char*> (&resource.serial), sizeof (resource.serial));
720 resource.serial = ntohl (resource.serial);
721
722 data.read (reinterpret_cast<char*> (&resource.refresh), sizeof (resource.refresh));
723 resource.refresh = ntohl (resource.refresh);
724
725 data.read (reinterpret_cast<char*> (&resource.retry), sizeof (resource.retry));
726 resource.retry = ntohl (resource.retry);
727
728 data.read (reinterpret_cast<char*> (&resource.expire), sizeof (resource.expire));
729 resource.expire = ntohl (resource.expire);
730
731 data.read (reinterpret_cast<char*> (&resource.minimum), sizeof (resource.minimum));
732 resource.minimum = ntohl (resource.minimum);
733 }
734 else if (resource.type == RecordType::TXT)
735 {
736 while (data.tellg () != -1 && (data.tellg () - dataBegPos < dataLen))
737 {
738 uint8_t size = 0;
739 if (!data.read (reinterpret_cast<char*> (&size), sizeof (size)))
740 {
741 return -1; // LCOV_EXCL_LINE
742 }
743
744 std::string txt;
745 txt.resize (size);
746 if (!data.read (&txt[0], size))
747 {
748 return -1; // LCOV_EXCL_LINE
749 }
750
751 resource.txts.emplace_back (std::move (txt));
752 }
753 }
754 else if (resource.type == RecordType::SRV)
755 {
756 data.read (reinterpret_cast<char*> (&resource.priority), sizeof (resource.priority));
757 resource.priority = ntohs (resource.priority);
758
759 data.read (reinterpret_cast<char*> (&resource.weight), sizeof (resource.weight));
760 resource.weight = ntohs (resource.weight);
761
762 data.read (reinterpret_cast<char*> (&resource.port), sizeof (resource.port));
763 resource.port = ntohs (resource.port);
764
765 if (decodeName (resource.name, data) == -1)
766 {
767 return -1; // LCOV_EXCL_LINE
768 }
769 }
770 else
771 {
772 data.seekg (dataBegPos + static_cast<std::streamoff> (dataLen));
773 }
774
775 return 0;
776 }
777 };
778}
779
780#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