/src/serenity/Userland/Libraries/LibDNS/Packet.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> |
3 | | * Copyright (c) 2021, Sergey Bugaev <bugaevc@serenityos.org> |
4 | | * |
5 | | * SPDX-License-Identifier: BSD-2-Clause |
6 | | */ |
7 | | |
8 | | #include "Packet.h" |
9 | | #include "Name.h" |
10 | | #include "PacketHeader.h" |
11 | | #include <AK/Debug.h> |
12 | | #include <AK/MemoryStream.h> |
13 | | #include <AK/StringBuilder.h> |
14 | | #include <arpa/inet.h> |
15 | | |
16 | | namespace DNS { |
17 | | |
18 | | void Packet::add_question(Question const& question) |
19 | 0 | { |
20 | 0 | m_questions.empend(question); |
21 | |
|
22 | 0 | VERIFY(m_questions.size() <= UINT16_MAX); |
23 | 0 | } |
24 | | |
25 | | void Packet::add_answer(Answer const& answer) |
26 | 0 | { |
27 | 0 | m_answers.empend(answer); |
28 | |
|
29 | 0 | VERIFY(m_answers.size() <= UINT16_MAX); |
30 | 0 | } |
31 | | |
32 | | ErrorOr<ByteBuffer> Packet::to_byte_buffer() const |
33 | 460 | { |
34 | 460 | PacketHeader header; |
35 | 460 | header.set_id(m_id); |
36 | 460 | if (is_query()) |
37 | 394 | header.set_is_query(); |
38 | 66 | else |
39 | 66 | header.set_is_response(); |
40 | 460 | header.set_authoritative_answer(m_authoritative_answer); |
41 | | // FIXME: What should this be? |
42 | 460 | header.set_opcode(0); |
43 | 460 | header.set_response_code(m_code); |
44 | 460 | header.set_truncated(false); // hopefully... |
45 | 460 | header.set_recursion_desired(m_recursion_desired); |
46 | | // FIXME: what should the be for requests? |
47 | 460 | header.set_recursion_available(m_recursion_available); |
48 | 460 | header.set_question_count(m_questions.size()); |
49 | 460 | header.set_answer_count(m_answers.size()); |
50 | | |
51 | 460 | AllocatingMemoryStream stream; |
52 | | |
53 | 460 | TRY(stream.write_value(header)); |
54 | 1.33M | for (auto& question : m_questions) { |
55 | 1.33M | TRY(stream.write_value(question.name())); |
56 | 1.33M | TRY(stream.write_value(htons((u16)question.record_type()))); |
57 | 1.33M | TRY(stream.write_value(htons(question.raw_class_code()))); |
58 | 1.33M | } |
59 | 459k | for (auto& answer : m_answers) { |
60 | 459k | TRY(stream.write_value(answer.name())); |
61 | 459k | TRY(stream.write_value(htons((u16)answer.type()))); |
62 | 459k | TRY(stream.write_value(htons(answer.raw_class_code()))); |
63 | 459k | TRY(stream.write_value(htonl(answer.ttl()))); |
64 | 459k | if (answer.type() == RecordType::PTR) { |
65 | 809 | Name name { answer.record_data() }; |
66 | 809 | TRY(stream.write_value(htons(name.serialized_size()))); |
67 | 809 | TRY(stream.write_value(name)); |
68 | 458k | } else { |
69 | 458k | TRY(stream.write_value(htons(answer.record_data().length()))); |
70 | 458k | TRY(stream.write_until_depleted(answer.record_data().bytes())); |
71 | 458k | } |
72 | 459k | } |
73 | | |
74 | 460 | auto buffer = TRY(ByteBuffer::create_uninitialized(stream.used_buffer_size())); |
75 | 460 | TRY(stream.read_until_filled(buffer)); |
76 | 0 | return buffer; |
77 | 460 | } |
78 | | |
79 | | class [[gnu::packed]] DNSRecordWithoutName { |
80 | | public: |
81 | | DNSRecordWithoutName() = default; |
82 | | |
83 | 4.57M | u16 type() const { return m_type; } |
84 | 3.05M | u16 record_class() const { return m_class; } |
85 | 1.52M | u32 ttl() const { return m_ttl; } |
86 | 3.05M | u16 data_length() const { return m_data_length; } |
87 | | |
88 | 0 | void* data() { return this + 1; } |
89 | 2.77k | void const* data() const { return this + 1; } |
90 | | |
91 | | private: |
92 | | NetworkOrdered<u16> m_type; |
93 | | NetworkOrdered<u16> m_class; |
94 | | NetworkOrdered<u32> m_ttl; |
95 | | NetworkOrdered<u16> m_data_length; |
96 | | }; |
97 | | |
98 | | static_assert(sizeof(DNSRecordWithoutName) == 10); |
99 | | |
100 | | ErrorOr<Packet> Packet::from_raw_packet(ReadonlyBytes bytes) |
101 | 1.31k | { |
102 | 1.31k | if (bytes.size() < sizeof(PacketHeader)) { |
103 | 6 | dbgln_if(LOOKUPSERVER_DEBUG, "DNS response not large enough ({} out of {}) to be a DNS packet", bytes.size(), sizeof(PacketHeader)); |
104 | 6 | return Error::from_string_literal("DNS response not large enough to be a DNS packet"); |
105 | 6 | } |
106 | | |
107 | 1.30k | auto const& header = *bit_cast<PacketHeader const*>(bytes.data()); |
108 | 1.30k | dbgln_if(LOOKUPSERVER_DEBUG, "Got packet (ID: {})", header.id()); |
109 | 1.30k | dbgln_if(LOOKUPSERVER_DEBUG, " Question count: {}", header.question_count()); |
110 | 1.30k | dbgln_if(LOOKUPSERVER_DEBUG, " Answer count: {}", header.answer_count()); |
111 | 1.30k | dbgln_if(LOOKUPSERVER_DEBUG, " Authority count: {}", header.authority_count()); |
112 | 1.30k | dbgln_if(LOOKUPSERVER_DEBUG, "Additional count: {}", header.additional_count()); |
113 | | |
114 | 1.30k | Packet packet; |
115 | 1.30k | packet.m_id = header.id(); |
116 | 1.30k | packet.m_query_or_response = header.is_response(); |
117 | 1.30k | packet.m_code = header.response_code(); |
118 | | |
119 | | // FIXME: Should we parse further in this case? |
120 | 1.30k | if (packet.code() != Code::NOERROR) |
121 | 7 | return packet; |
122 | | |
123 | 1.29k | size_t offset = sizeof(PacketHeader); |
124 | | |
125 | 6.79M | for (u16 i = 0; i < header.question_count(); i++) { |
126 | 6.79M | auto name = TRY(Name::parse(bytes, offset)); |
127 | 0 | struct RawDNSAnswerQuestion { |
128 | 6.79M | NetworkOrdered<u16> record_type; |
129 | 6.79M | NetworkOrdered<u16> class_code; |
130 | 6.79M | }; |
131 | 6.79M | if (offset >= bytes.size() || bytes.size() - offset < sizeof(RawDNSAnswerQuestion)) |
132 | 228 | return Error::from_string_literal("Unexpected EOF when parsing DNS packet"); |
133 | | |
134 | 6.79M | auto const& record_and_class = *bit_cast<RawDNSAnswerQuestion const*>(bytes.offset_pointer(offset)); |
135 | 6.79M | u16 class_code = record_and_class.class_code & ~MDNS_WANTS_UNICAST_RESPONSE; |
136 | 6.79M | bool mdns_wants_unicast_response = record_and_class.class_code & MDNS_WANTS_UNICAST_RESPONSE; |
137 | 6.79M | packet.m_questions.empend(name, (RecordType)(u16)record_and_class.record_type, (RecordClass)class_code, mdns_wants_unicast_response); |
138 | 6.79M | offset += 4; |
139 | 6.79M | auto& question = packet.m_questions.last(); |
140 | 6.79M | dbgln_if(LOOKUPSERVER_DEBUG, "Question #{}: name=_{}_, type={}, class={}", i, question.name(), question.record_type(), question.class_code()); |
141 | 6.79M | } |
142 | | |
143 | 1.52M | for (u16 i = 0; i < header.answer_count(); ++i) { |
144 | 1.52M | auto name = TRY(Name::parse(bytes, offset)); |
145 | 1.52M | if (offset >= bytes.size() || bytes.size() - offset < sizeof(DNSRecordWithoutName)) |
146 | 153 | return Error::from_string_literal("Unexpected EOF when parsing DNS packet"); |
147 | | |
148 | 1.52M | auto const& record = *bit_cast<DNSRecordWithoutName const*>(bytes.offset_pointer(offset)); |
149 | 1.52M | offset += sizeof(DNSRecordWithoutName); |
150 | 1.52M | if (record.data_length() > bytes.size() - offset) |
151 | 58 | return Error::from_string_literal("Unexpected EOF when parsing DNS packet"); |
152 | | |
153 | 1.52M | ByteString data; |
154 | | |
155 | 1.52M | switch ((RecordType)record.type()) { |
156 | 1.60k | case RecordType::PTR: { |
157 | 1.60k | size_t dummy_offset = offset; |
158 | 1.60k | data = TRY(Name::parse(bytes, dummy_offset)).as_string(); |
159 | 0 | break; |
160 | 1.60k | } |
161 | 268 | case RecordType::CNAME: |
162 | | // Fall through |
163 | 1.65k | case RecordType::A: |
164 | | // Fall through |
165 | 2.19k | case RecordType::TXT: |
166 | | // Fall through |
167 | 2.49k | case RecordType::AAAA: |
168 | | // Fall through |
169 | 2.77k | case RecordType::SRV: |
170 | 2.77k | data = ReadonlyBytes { record.data(), record.data_length() }; |
171 | 2.77k | break; |
172 | 1.52M | default: |
173 | | // FIXME: Parse some other record types perhaps? |
174 | 1.52M | dbgln("data=(unimplemented record type {})", (u16)record.type()); |
175 | 1.52M | } |
176 | | |
177 | 1.52M | dbgln_if(LOOKUPSERVER_DEBUG, "Answer #{}: name=_{}_, type={}, ttl={}, length={}, data=_{}_", i, name, record.type(), record.ttl(), record.data_length(), data); |
178 | 1.52M | u16 class_code = record.record_class() & ~MDNS_CACHE_FLUSH; |
179 | 1.52M | bool mdns_cache_flush = record.record_class() & MDNS_CACHE_FLUSH; |
180 | 1.52M | packet.m_answers.empend(name, (RecordType)record.type(), (RecordClass)class_code, record.ttl(), data, mdns_cache_flush); |
181 | 1.52M | offset += record.data_length(); |
182 | 1.52M | } |
183 | | |
184 | 453 | return packet; |
185 | 855 | } |
186 | | |
187 | | } |