1*3f982cf4SFabien Sanglard // Copyright 2019 The Chromium Authors. All rights reserved.
2*3f982cf4SFabien Sanglard // Use of this source code is governed by a BSD-style license that can be
3*3f982cf4SFabien Sanglard // found in the LICENSE file.
4*3f982cf4SFabien Sanglard
5*3f982cf4SFabien Sanglard #include "discovery/mdns/mdns_writer.h"
6*3f982cf4SFabien Sanglard
7*3f982cf4SFabien Sanglard #include <limits>
8*3f982cf4SFabien Sanglard #include <string>
9*3f982cf4SFabien Sanglard #include <utility>
10*3f982cf4SFabien Sanglard #include <vector>
11*3f982cf4SFabien Sanglard
12*3f982cf4SFabien Sanglard #include "absl/hash/hash.h"
13*3f982cf4SFabien Sanglard #include "absl/strings/ascii.h"
14*3f982cf4SFabien Sanglard #include "util/hashing.h"
15*3f982cf4SFabien Sanglard #include "util/osp_logging.h"
16*3f982cf4SFabien Sanglard
17*3f982cf4SFabien Sanglard namespace openscreen {
18*3f982cf4SFabien Sanglard namespace discovery {
19*3f982cf4SFabien Sanglard
20*3f982cf4SFabien Sanglard namespace {
21*3f982cf4SFabien Sanglard
ComputeDomainNameSubhashes(const DomainName & name)22*3f982cf4SFabien Sanglard std::vector<uint64_t> ComputeDomainNameSubhashes(const DomainName& name) {
23*3f982cf4SFabien Sanglard const std::vector<std::string>& labels = name.labels();
24*3f982cf4SFabien Sanglard // Use a large prime between 2^63 and 2^64 as a starting value.
25*3f982cf4SFabien Sanglard // This is taken from absl::Hash implementation.
26*3f982cf4SFabien Sanglard uint64_t hash_value = UINT64_C(0xc3a5c85c97cb3127);
27*3f982cf4SFabien Sanglard std::vector<uint64_t> subhashes(labels.size());
28*3f982cf4SFabien Sanglard for (size_t i = labels.size(); i-- > 0;) {
29*3f982cf4SFabien Sanglard hash_value =
30*3f982cf4SFabien Sanglard ComputeAggregateHash(hash_value, absl::AsciiStrToLower(labels[i]));
31*3f982cf4SFabien Sanglard subhashes[i] = hash_value;
32*3f982cf4SFabien Sanglard }
33*3f982cf4SFabien Sanglard return subhashes;
34*3f982cf4SFabien Sanglard }
35*3f982cf4SFabien Sanglard
36*3f982cf4SFabien Sanglard // This helper method writes the number of bytes between |begin| and |end| minus
37*3f982cf4SFabien Sanglard // the size of the uint16_t into the uint16_t length field at |begin|. The
38*3f982cf4SFabien Sanglard // method returns true if the number of bytes between |begin| and |end| fits in
39*3f982cf4SFabien Sanglard // uint16_t type, returns false otherwise.
UpdateRecordLength(const uint8_t * end,uint8_t * begin)40*3f982cf4SFabien Sanglard bool UpdateRecordLength(const uint8_t* end, uint8_t* begin) {
41*3f982cf4SFabien Sanglard OSP_DCHECK_LE(begin + sizeof(uint16_t), end);
42*3f982cf4SFabien Sanglard ptrdiff_t record_length = end - begin - sizeof(uint16_t);
43*3f982cf4SFabien Sanglard if (record_length <= std::numeric_limits<uint16_t>::max()) {
44*3f982cf4SFabien Sanglard WriteBigEndian<uint16_t>(record_length, begin);
45*3f982cf4SFabien Sanglard return true;
46*3f982cf4SFabien Sanglard }
47*3f982cf4SFabien Sanglard return false;
48*3f982cf4SFabien Sanglard }
49*3f982cf4SFabien Sanglard
50*3f982cf4SFabien Sanglard } // namespace
51*3f982cf4SFabien Sanglard
Write(absl::string_view value)52*3f982cf4SFabien Sanglard bool MdnsWriter::Write(absl::string_view value) {
53*3f982cf4SFabien Sanglard if (value.length() > std::numeric_limits<uint8_t>::max()) {
54*3f982cf4SFabien Sanglard return false;
55*3f982cf4SFabien Sanglard }
56*3f982cf4SFabien Sanglard Cursor cursor(this);
57*3f982cf4SFabien Sanglard if (Write(static_cast<uint8_t>(value.length())) &&
58*3f982cf4SFabien Sanglard Write(value.data(), value.length())) {
59*3f982cf4SFabien Sanglard cursor.Commit();
60*3f982cf4SFabien Sanglard return true;
61*3f982cf4SFabien Sanglard }
62*3f982cf4SFabien Sanglard return false;
63*3f982cf4SFabien Sanglard }
64*3f982cf4SFabien Sanglard
Write(const std::string & value)65*3f982cf4SFabien Sanglard bool MdnsWriter::Write(const std::string& value) {
66*3f982cf4SFabien Sanglard return Write(absl::string_view(value));
67*3f982cf4SFabien Sanglard }
68*3f982cf4SFabien Sanglard
69*3f982cf4SFabien Sanglard // RFC 1035: https://www.ietf.org/rfc/rfc1035.txt
70*3f982cf4SFabien Sanglard // See section 4.1.4. Message compression
Write(const DomainName & name)71*3f982cf4SFabien Sanglard bool MdnsWriter::Write(const DomainName& name) {
72*3f982cf4SFabien Sanglard if (name.empty()) {
73*3f982cf4SFabien Sanglard return false;
74*3f982cf4SFabien Sanglard }
75*3f982cf4SFabien Sanglard
76*3f982cf4SFabien Sanglard Cursor cursor(this);
77*3f982cf4SFabien Sanglard const std::vector<uint64_t> subhashes = ComputeDomainNameSubhashes(name);
78*3f982cf4SFabien Sanglard // Tentative dictionary contains label pointer entries to be added to the
79*3f982cf4SFabien Sanglard // compression dictionary after successfully writing the domain name.
80*3f982cf4SFabien Sanglard std::unordered_map<uint64_t, uint16_t> tentative_dictionary;
81*3f982cf4SFabien Sanglard const std::vector<std::string>& labels = name.labels();
82*3f982cf4SFabien Sanglard for (size_t i = 0; i < labels.size(); ++i) {
83*3f982cf4SFabien Sanglard OSP_DCHECK(IsValidDomainLabel(labels[i]));
84*3f982cf4SFabien Sanglard // We only need to do a look up in the compression dictionary and not in the
85*3f982cf4SFabien Sanglard // tentative dictionary. The tentative dictionary cannot possibly contain a
86*3f982cf4SFabien Sanglard // valid label pointer as all the entries previously added to it are for
87*3f982cf4SFabien Sanglard // names that are longer than the currently processed sub-name.
88*3f982cf4SFabien Sanglard auto find_result = dictionary_.find(subhashes[i]);
89*3f982cf4SFabien Sanglard if (find_result != dictionary_.end()) {
90*3f982cf4SFabien Sanglard if (!Write(find_result->second)) {
91*3f982cf4SFabien Sanglard return false;
92*3f982cf4SFabien Sanglard }
93*3f982cf4SFabien Sanglard dictionary_.insert(tentative_dictionary.begin(),
94*3f982cf4SFabien Sanglard tentative_dictionary.end());
95*3f982cf4SFabien Sanglard cursor.Commit();
96*3f982cf4SFabien Sanglard return true;
97*3f982cf4SFabien Sanglard }
98*3f982cf4SFabien Sanglard // Only add a pointer_label for compression if the offset into the buffer
99*3f982cf4SFabien Sanglard // fits into the bits available to store it.
100*3f982cf4SFabien Sanglard if (IsValidPointerLabelOffset(current() - begin())) {
101*3f982cf4SFabien Sanglard tentative_dictionary.insert(
102*3f982cf4SFabien Sanglard std::make_pair(subhashes[i], MakePointerLabel(current() - begin())));
103*3f982cf4SFabien Sanglard }
104*3f982cf4SFabien Sanglard if (!Write(MakeDirectLabel(labels[i].size())) ||
105*3f982cf4SFabien Sanglard !Write(labels[i].data(), labels[i].size())) {
106*3f982cf4SFabien Sanglard return false;
107*3f982cf4SFabien Sanglard }
108*3f982cf4SFabien Sanglard }
109*3f982cf4SFabien Sanglard if (!Write(kLabelTermination)) {
110*3f982cf4SFabien Sanglard return false;
111*3f982cf4SFabien Sanglard }
112*3f982cf4SFabien Sanglard // The probability of a collision is extremely low in this application, as the
113*3f982cf4SFabien Sanglard // number of domain names compressed is insignificant in comparison to the
114*3f982cf4SFabien Sanglard // hash function image.
115*3f982cf4SFabien Sanglard dictionary_.insert(tentative_dictionary.begin(), tentative_dictionary.end());
116*3f982cf4SFabien Sanglard cursor.Commit();
117*3f982cf4SFabien Sanglard return true;
118*3f982cf4SFabien Sanglard }
119*3f982cf4SFabien Sanglard
Write(const RawRecordRdata & rdata)120*3f982cf4SFabien Sanglard bool MdnsWriter::Write(const RawRecordRdata& rdata) {
121*3f982cf4SFabien Sanglard Cursor cursor(this);
122*3f982cf4SFabien Sanglard if (Write(rdata.size()) && Write(rdata.data(), rdata.size())) {
123*3f982cf4SFabien Sanglard cursor.Commit();
124*3f982cf4SFabien Sanglard return true;
125*3f982cf4SFabien Sanglard }
126*3f982cf4SFabien Sanglard return false;
127*3f982cf4SFabien Sanglard }
128*3f982cf4SFabien Sanglard
Write(const SrvRecordRdata & rdata)129*3f982cf4SFabien Sanglard bool MdnsWriter::Write(const SrvRecordRdata& rdata) {
130*3f982cf4SFabien Sanglard Cursor cursor(this);
131*3f982cf4SFabien Sanglard // Leave space at the beginning at |rollback_position| to write the record
132*3f982cf4SFabien Sanglard // length. Cannot write it upfront, since the exact space taken by the target
133*3f982cf4SFabien Sanglard // domain name is not known as it might be compressed.
134*3f982cf4SFabien Sanglard if (Skip(sizeof(uint16_t)) && Write(rdata.priority()) &&
135*3f982cf4SFabien Sanglard Write(rdata.weight()) && Write(rdata.port()) && Write(rdata.target()) &&
136*3f982cf4SFabien Sanglard UpdateRecordLength(current(), cursor.origin())) {
137*3f982cf4SFabien Sanglard cursor.Commit();
138*3f982cf4SFabien Sanglard return true;
139*3f982cf4SFabien Sanglard }
140*3f982cf4SFabien Sanglard return false;
141*3f982cf4SFabien Sanglard }
142*3f982cf4SFabien Sanglard
Write(const ARecordRdata & rdata)143*3f982cf4SFabien Sanglard bool MdnsWriter::Write(const ARecordRdata& rdata) {
144*3f982cf4SFabien Sanglard Cursor cursor(this);
145*3f982cf4SFabien Sanglard if (Write(static_cast<uint16_t>(IPAddress::kV4Size)) &&
146*3f982cf4SFabien Sanglard Write(rdata.ipv4_address())) {
147*3f982cf4SFabien Sanglard cursor.Commit();
148*3f982cf4SFabien Sanglard return true;
149*3f982cf4SFabien Sanglard }
150*3f982cf4SFabien Sanglard return false;
151*3f982cf4SFabien Sanglard }
152*3f982cf4SFabien Sanglard
Write(const AAAARecordRdata & rdata)153*3f982cf4SFabien Sanglard bool MdnsWriter::Write(const AAAARecordRdata& rdata) {
154*3f982cf4SFabien Sanglard Cursor cursor(this);
155*3f982cf4SFabien Sanglard if (Write(static_cast<uint16_t>(IPAddress::kV6Size)) &&
156*3f982cf4SFabien Sanglard Write(rdata.ipv6_address())) {
157*3f982cf4SFabien Sanglard cursor.Commit();
158*3f982cf4SFabien Sanglard return true;
159*3f982cf4SFabien Sanglard }
160*3f982cf4SFabien Sanglard return false;
161*3f982cf4SFabien Sanglard }
162*3f982cf4SFabien Sanglard
Write(const PtrRecordRdata & rdata)163*3f982cf4SFabien Sanglard bool MdnsWriter::Write(const PtrRecordRdata& rdata) {
164*3f982cf4SFabien Sanglard Cursor cursor(this);
165*3f982cf4SFabien Sanglard // Leave space at the beginning at |rollback_position| to write the record
166*3f982cf4SFabien Sanglard // length. Cannot write it upfront, since the exact space taken by the target
167*3f982cf4SFabien Sanglard // domain name is not known as it might be compressed.
168*3f982cf4SFabien Sanglard if (Skip(sizeof(uint16_t)) && Write(rdata.ptr_domain()) &&
169*3f982cf4SFabien Sanglard UpdateRecordLength(current(), cursor.origin())) {
170*3f982cf4SFabien Sanglard cursor.Commit();
171*3f982cf4SFabien Sanglard return true;
172*3f982cf4SFabien Sanglard }
173*3f982cf4SFabien Sanglard return false;
174*3f982cf4SFabien Sanglard }
175*3f982cf4SFabien Sanglard
Write(const TxtRecordRdata & rdata)176*3f982cf4SFabien Sanglard bool MdnsWriter::Write(const TxtRecordRdata& rdata) {
177*3f982cf4SFabien Sanglard Cursor cursor(this);
178*3f982cf4SFabien Sanglard // Leave space at the beginning at |rollback_position| to write the record
179*3f982cf4SFabien Sanglard // length. It's cheaper to update it at the end than precompute the length.
180*3f982cf4SFabien Sanglard if (!Skip(sizeof(uint16_t))) {
181*3f982cf4SFabien Sanglard return false;
182*3f982cf4SFabien Sanglard }
183*3f982cf4SFabien Sanglard if (rdata.texts().size() > 0) {
184*3f982cf4SFabien Sanglard if (!Write(rdata.texts())) {
185*3f982cf4SFabien Sanglard return false;
186*3f982cf4SFabien Sanglard }
187*3f982cf4SFabien Sanglard } else {
188*3f982cf4SFabien Sanglard if (!Write(kTXTEmptyRdata)) {
189*3f982cf4SFabien Sanglard return false;
190*3f982cf4SFabien Sanglard }
191*3f982cf4SFabien Sanglard }
192*3f982cf4SFabien Sanglard if (!UpdateRecordLength(current(), cursor.origin())) {
193*3f982cf4SFabien Sanglard return false;
194*3f982cf4SFabien Sanglard }
195*3f982cf4SFabien Sanglard cursor.Commit();
196*3f982cf4SFabien Sanglard return true;
197*3f982cf4SFabien Sanglard }
198*3f982cf4SFabien Sanglard
Write(const NsecRecordRdata & rdata)199*3f982cf4SFabien Sanglard bool MdnsWriter::Write(const NsecRecordRdata& rdata) {
200*3f982cf4SFabien Sanglard Cursor cursor(this);
201*3f982cf4SFabien Sanglard if (Skip(sizeof(uint16_t)) && Write(rdata.next_domain_name()) &&
202*3f982cf4SFabien Sanglard Write(rdata.encoded_types()) &&
203*3f982cf4SFabien Sanglard UpdateRecordLength(current(), cursor.origin())) {
204*3f982cf4SFabien Sanglard cursor.Commit();
205*3f982cf4SFabien Sanglard return true;
206*3f982cf4SFabien Sanglard }
207*3f982cf4SFabien Sanglard return false;
208*3f982cf4SFabien Sanglard }
209*3f982cf4SFabien Sanglard
Write(const OptRecordRdata & rdata)210*3f982cf4SFabien Sanglard bool MdnsWriter::Write(const OptRecordRdata& rdata) {
211*3f982cf4SFabien Sanglard // OPT records are currently not supported for outgoing messages.
212*3f982cf4SFabien Sanglard OSP_UNIMPLEMENTED();
213*3f982cf4SFabien Sanglard return false;
214*3f982cf4SFabien Sanglard }
215*3f982cf4SFabien Sanglard
Write(const MdnsRecord & record)216*3f982cf4SFabien Sanglard bool MdnsWriter::Write(const MdnsRecord& record) {
217*3f982cf4SFabien Sanglard Cursor cursor(this);
218*3f982cf4SFabien Sanglard if (Write(record.name()) && Write(static_cast<uint16_t>(record.dns_type())) &&
219*3f982cf4SFabien Sanglard Write(MakeRecordClass(record.dns_class(), record.record_type())) &&
220*3f982cf4SFabien Sanglard Write(static_cast<uint32_t>(record.ttl().count())) &&
221*3f982cf4SFabien Sanglard Write(record.rdata())) {
222*3f982cf4SFabien Sanglard cursor.Commit();
223*3f982cf4SFabien Sanglard return true;
224*3f982cf4SFabien Sanglard }
225*3f982cf4SFabien Sanglard return false;
226*3f982cf4SFabien Sanglard }
227*3f982cf4SFabien Sanglard
Write(const MdnsQuestion & question)228*3f982cf4SFabien Sanglard bool MdnsWriter::Write(const MdnsQuestion& question) {
229*3f982cf4SFabien Sanglard Cursor cursor(this);
230*3f982cf4SFabien Sanglard if (Write(question.name()) &&
231*3f982cf4SFabien Sanglard Write(static_cast<uint16_t>(question.dns_type())) &&
232*3f982cf4SFabien Sanglard Write(
233*3f982cf4SFabien Sanglard MakeQuestionClass(question.dns_class(), question.response_type()))) {
234*3f982cf4SFabien Sanglard cursor.Commit();
235*3f982cf4SFabien Sanglard return true;
236*3f982cf4SFabien Sanglard }
237*3f982cf4SFabien Sanglard return false;
238*3f982cf4SFabien Sanglard }
239*3f982cf4SFabien Sanglard
Write(const MdnsMessage & message)240*3f982cf4SFabien Sanglard bool MdnsWriter::Write(const MdnsMessage& message) {
241*3f982cf4SFabien Sanglard Cursor cursor(this);
242*3f982cf4SFabien Sanglard Header header;
243*3f982cf4SFabien Sanglard header.id = message.id();
244*3f982cf4SFabien Sanglard header.flags = MakeFlags(message.type(), message.is_truncated());
245*3f982cf4SFabien Sanglard header.question_count = message.questions().size();
246*3f982cf4SFabien Sanglard header.answer_count = message.answers().size();
247*3f982cf4SFabien Sanglard header.authority_record_count = message.authority_records().size();
248*3f982cf4SFabien Sanglard header.additional_record_count = message.additional_records().size();
249*3f982cf4SFabien Sanglard if (Write(header) && Write(message.questions()) && Write(message.answers()) &&
250*3f982cf4SFabien Sanglard Write(message.authority_records()) &&
251*3f982cf4SFabien Sanglard Write(message.additional_records())) {
252*3f982cf4SFabien Sanglard cursor.Commit();
253*3f982cf4SFabien Sanglard return true;
254*3f982cf4SFabien Sanglard }
255*3f982cf4SFabien Sanglard return false;
256*3f982cf4SFabien Sanglard }
257*3f982cf4SFabien Sanglard
Write(const IPAddress & address)258*3f982cf4SFabien Sanglard bool MdnsWriter::Write(const IPAddress& address) {
259*3f982cf4SFabien Sanglard uint8_t bytes[IPAddress::kV6Size];
260*3f982cf4SFabien Sanglard size_t size;
261*3f982cf4SFabien Sanglard if (address.IsV6()) {
262*3f982cf4SFabien Sanglard address.CopyToV6(bytes);
263*3f982cf4SFabien Sanglard size = IPAddress::kV6Size;
264*3f982cf4SFabien Sanglard } else {
265*3f982cf4SFabien Sanglard address.CopyToV4(bytes);
266*3f982cf4SFabien Sanglard size = IPAddress::kV4Size;
267*3f982cf4SFabien Sanglard }
268*3f982cf4SFabien Sanglard return Write(bytes, size);
269*3f982cf4SFabien Sanglard }
270*3f982cf4SFabien Sanglard
Write(const Rdata & rdata)271*3f982cf4SFabien Sanglard bool MdnsWriter::Write(const Rdata& rdata) {
272*3f982cf4SFabien Sanglard return absl::visit([this](const auto& rdata) { return this->Write(rdata); },
273*3f982cf4SFabien Sanglard rdata);
274*3f982cf4SFabien Sanglard }
275*3f982cf4SFabien Sanglard
Write(const Header & header)276*3f982cf4SFabien Sanglard bool MdnsWriter::Write(const Header& header) {
277*3f982cf4SFabien Sanglard Cursor cursor(this);
278*3f982cf4SFabien Sanglard if (Write(header.id) && Write(header.flags) && Write(header.question_count) &&
279*3f982cf4SFabien Sanglard Write(header.answer_count) && Write(header.authority_record_count) &&
280*3f982cf4SFabien Sanglard Write(header.additional_record_count)) {
281*3f982cf4SFabien Sanglard cursor.Commit();
282*3f982cf4SFabien Sanglard return true;
283*3f982cf4SFabien Sanglard }
284*3f982cf4SFabien Sanglard return false;
285*3f982cf4SFabien Sanglard }
286*3f982cf4SFabien Sanglard
287*3f982cf4SFabien Sanglard } // namespace discovery
288*3f982cf4SFabien Sanglard } // namespace openscreen
289