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/dnssd/impl/conversion_layer.h"
6*3f982cf4SFabien Sanglard
7*3f982cf4SFabien Sanglard #include <utility>
8*3f982cf4SFabien Sanglard
9*3f982cf4SFabien Sanglard #include "absl/strings/str_join.h"
10*3f982cf4SFabien Sanglard #include "absl/strings/str_split.h"
11*3f982cf4SFabien Sanglard #include "absl/types/optional.h"
12*3f982cf4SFabien Sanglard #include "absl/types/span.h"
13*3f982cf4SFabien Sanglard #include "discovery/dnssd/impl/constants.h"
14*3f982cf4SFabien Sanglard #include "discovery/dnssd/impl/instance_key.h"
15*3f982cf4SFabien Sanglard #include "discovery/dnssd/impl/service_key.h"
16*3f982cf4SFabien Sanglard #include "discovery/dnssd/public/dns_sd_instance.h"
17*3f982cf4SFabien Sanglard #include "discovery/mdns/mdns_records.h"
18*3f982cf4SFabien Sanglard #include "discovery/mdns/public/mdns_constants.h"
19*3f982cf4SFabien Sanglard
20*3f982cf4SFabien Sanglard namespace openscreen {
21*3f982cf4SFabien Sanglard namespace discovery {
22*3f982cf4SFabien Sanglard namespace {
23*3f982cf4SFabien Sanglard
AddServiceInfoToLabels(const std::string & service,const std::string & domain,std::vector<std::string> * labels)24*3f982cf4SFabien Sanglard void AddServiceInfoToLabels(const std::string& service,
25*3f982cf4SFabien Sanglard const std::string& domain,
26*3f982cf4SFabien Sanglard std::vector<std::string>* labels) {
27*3f982cf4SFabien Sanglard std::vector<std::string> service_labels = absl::StrSplit(service, '.');
28*3f982cf4SFabien Sanglard labels->insert(labels->end(), service_labels.begin(), service_labels.end());
29*3f982cf4SFabien Sanglard
30*3f982cf4SFabien Sanglard std::vector<std::string> domain_labels = absl::StrSplit(domain, '.');
31*3f982cf4SFabien Sanglard labels->insert(labels->end(), domain_labels.begin(), domain_labels.end());
32*3f982cf4SFabien Sanglard }
33*3f982cf4SFabien Sanglard
GetPtrDomainName(const std::string & service,const std::string & domain)34*3f982cf4SFabien Sanglard DomainName GetPtrDomainName(const std::string& service,
35*3f982cf4SFabien Sanglard const std::string& domain) {
36*3f982cf4SFabien Sanglard std::vector<std::string> labels;
37*3f982cf4SFabien Sanglard AddServiceInfoToLabels(service, domain, &labels);
38*3f982cf4SFabien Sanglard return DomainName{std::move(labels)};
39*3f982cf4SFabien Sanglard }
40*3f982cf4SFabien Sanglard
GetInstanceDomainName(const std::string & instance,const std::string & service,const std::string & domain)41*3f982cf4SFabien Sanglard DomainName GetInstanceDomainName(const std::string& instance,
42*3f982cf4SFabien Sanglard const std::string& service,
43*3f982cf4SFabien Sanglard const std::string& domain) {
44*3f982cf4SFabien Sanglard std::vector<std::string> labels;
45*3f982cf4SFabien Sanglard labels.emplace_back(instance);
46*3f982cf4SFabien Sanglard AddServiceInfoToLabels(service, domain, &labels);
47*3f982cf4SFabien Sanglard return DomainName{std::move(labels)};
48*3f982cf4SFabien Sanglard }
49*3f982cf4SFabien Sanglard
GetInstanceDomainName(const InstanceKey & key)50*3f982cf4SFabien Sanglard inline DomainName GetInstanceDomainName(const InstanceKey& key) {
51*3f982cf4SFabien Sanglard return GetInstanceDomainName(key.instance_id(), key.service_id(),
52*3f982cf4SFabien Sanglard key.domain_id());
53*3f982cf4SFabien Sanglard }
54*3f982cf4SFabien Sanglard
CreatePtrRecord(const DnsSdInstance & instance,const DomainName & domain)55*3f982cf4SFabien Sanglard MdnsRecord CreatePtrRecord(const DnsSdInstance& instance,
56*3f982cf4SFabien Sanglard const DomainName& domain) {
57*3f982cf4SFabien Sanglard PtrRecordRdata data(domain);
58*3f982cf4SFabien Sanglard auto outer_domain =
59*3f982cf4SFabien Sanglard GetPtrDomainName(instance.service_id(), instance.domain_id());
60*3f982cf4SFabien Sanglard return MdnsRecord(std::move(outer_domain), DnsType::kPTR, DnsClass::kIN,
61*3f982cf4SFabien Sanglard RecordType::kShared, kPtrRecordTtl, std::move(data));
62*3f982cf4SFabien Sanglard }
63*3f982cf4SFabien Sanglard
CreateSrvRecord(const DnsSdInstance & instance,const DomainName & domain)64*3f982cf4SFabien Sanglard MdnsRecord CreateSrvRecord(const DnsSdInstance& instance,
65*3f982cf4SFabien Sanglard const DomainName& domain) {
66*3f982cf4SFabien Sanglard uint16_t port = instance.port();
67*3f982cf4SFabien Sanglard SrvRecordRdata data(0, 0, port, domain);
68*3f982cf4SFabien Sanglard return MdnsRecord(domain, DnsType::kSRV, DnsClass::kIN, RecordType::kUnique,
69*3f982cf4SFabien Sanglard kSrvRecordTtl, std::move(data));
70*3f982cf4SFabien Sanglard }
71*3f982cf4SFabien Sanglard
CreateARecords(const DnsSdInstanceEndpoint & endpoint,const DomainName & domain)72*3f982cf4SFabien Sanglard std::vector<MdnsRecord> CreateARecords(const DnsSdInstanceEndpoint& endpoint,
73*3f982cf4SFabien Sanglard const DomainName& domain) {
74*3f982cf4SFabien Sanglard std::vector<MdnsRecord> records;
75*3f982cf4SFabien Sanglard for (const IPAddress& address : endpoint.addresses()) {
76*3f982cf4SFabien Sanglard if (address.IsV4()) {
77*3f982cf4SFabien Sanglard ARecordRdata data(address);
78*3f982cf4SFabien Sanglard records.emplace_back(domain, DnsType::kA, DnsClass::kIN,
79*3f982cf4SFabien Sanglard RecordType::kUnique, kARecordTtl, std::move(data));
80*3f982cf4SFabien Sanglard }
81*3f982cf4SFabien Sanglard }
82*3f982cf4SFabien Sanglard
83*3f982cf4SFabien Sanglard return records;
84*3f982cf4SFabien Sanglard }
85*3f982cf4SFabien Sanglard
CreateAAAARecords(const DnsSdInstanceEndpoint & endpoint,const DomainName & domain)86*3f982cf4SFabien Sanglard std::vector<MdnsRecord> CreateAAAARecords(const DnsSdInstanceEndpoint& endpoint,
87*3f982cf4SFabien Sanglard const DomainName& domain) {
88*3f982cf4SFabien Sanglard std::vector<MdnsRecord> records;
89*3f982cf4SFabien Sanglard for (const IPAddress& address : endpoint.addresses()) {
90*3f982cf4SFabien Sanglard if (address.IsV6()) {
91*3f982cf4SFabien Sanglard AAAARecordRdata data(address);
92*3f982cf4SFabien Sanglard records.emplace_back(domain, DnsType::kAAAA, DnsClass::kIN,
93*3f982cf4SFabien Sanglard RecordType::kUnique, kAAAARecordTtl,
94*3f982cf4SFabien Sanglard std::move(data));
95*3f982cf4SFabien Sanglard }
96*3f982cf4SFabien Sanglard }
97*3f982cf4SFabien Sanglard
98*3f982cf4SFabien Sanglard return records;
99*3f982cf4SFabien Sanglard }
100*3f982cf4SFabien Sanglard
CreateTxtRecord(const DnsSdInstance & endpoint,const DomainName & domain)101*3f982cf4SFabien Sanglard MdnsRecord CreateTxtRecord(const DnsSdInstance& endpoint,
102*3f982cf4SFabien Sanglard const DomainName& domain) {
103*3f982cf4SFabien Sanglard TxtRecordRdata data(endpoint.txt().GetData());
104*3f982cf4SFabien Sanglard return MdnsRecord(domain, DnsType::kTXT, DnsClass::kIN, RecordType::kUnique,
105*3f982cf4SFabien Sanglard kTXTRecordTtl, std::move(data));
106*3f982cf4SFabien Sanglard }
107*3f982cf4SFabien Sanglard
108*3f982cf4SFabien Sanglard } // namespace
109*3f982cf4SFabien Sanglard
CreateFromDnsTxt(const TxtRecordRdata & txt_data)110*3f982cf4SFabien Sanglard ErrorOr<DnsSdTxtRecord> CreateFromDnsTxt(const TxtRecordRdata& txt_data) {
111*3f982cf4SFabien Sanglard DnsSdTxtRecord txt;
112*3f982cf4SFabien Sanglard if (txt_data.texts().size() == 1 && txt_data.texts()[0] == "") {
113*3f982cf4SFabien Sanglard return txt;
114*3f982cf4SFabien Sanglard }
115*3f982cf4SFabien Sanglard
116*3f982cf4SFabien Sanglard // Iterate backwards so that the first key of each type is the one that is
117*3f982cf4SFabien Sanglard // present at the end, as pet spec.
118*3f982cf4SFabien Sanglard for (auto it = txt_data.texts().rbegin(); it != txt_data.texts().rend();
119*3f982cf4SFabien Sanglard it++) {
120*3f982cf4SFabien Sanglard const std::string& text = *it;
121*3f982cf4SFabien Sanglard size_t index_of_eq = text.find_first_of('=');
122*3f982cf4SFabien Sanglard if (index_of_eq != std::string::npos) {
123*3f982cf4SFabien Sanglard if (index_of_eq == 0) {
124*3f982cf4SFabien Sanglard return Error::Code::kParameterInvalid;
125*3f982cf4SFabien Sanglard }
126*3f982cf4SFabien Sanglard std::string key = text.substr(0, index_of_eq);
127*3f982cf4SFabien Sanglard std::string value = text.substr(index_of_eq + 1);
128*3f982cf4SFabien Sanglard absl::Span<const uint8_t> data(
129*3f982cf4SFabien Sanglard reinterpret_cast<const uint8_t*>(value.data()), value.size());
130*3f982cf4SFabien Sanglard const auto set_result =
131*3f982cf4SFabien Sanglard txt.SetValue(key, std::vector<uint8_t>(data.begin(), data.end()));
132*3f982cf4SFabien Sanglard if (!set_result.ok()) {
133*3f982cf4SFabien Sanglard return set_result;
134*3f982cf4SFabien Sanglard }
135*3f982cf4SFabien Sanglard } else {
136*3f982cf4SFabien Sanglard const auto set_result = txt.SetFlag(text, true);
137*3f982cf4SFabien Sanglard if (!set_result.ok()) {
138*3f982cf4SFabien Sanglard return set_result;
139*3f982cf4SFabien Sanglard }
140*3f982cf4SFabien Sanglard }
141*3f982cf4SFabien Sanglard }
142*3f982cf4SFabien Sanglard
143*3f982cf4SFabien Sanglard return txt;
144*3f982cf4SFabien Sanglard }
145*3f982cf4SFabien Sanglard
GetDomainName(const InstanceKey & key)146*3f982cf4SFabien Sanglard DomainName GetDomainName(const InstanceKey& key) {
147*3f982cf4SFabien Sanglard return GetInstanceDomainName(key.instance_id(), key.service_id(),
148*3f982cf4SFabien Sanglard key.domain_id());
149*3f982cf4SFabien Sanglard }
150*3f982cf4SFabien Sanglard
GetDomainName(const MdnsRecord & record)151*3f982cf4SFabien Sanglard DomainName GetDomainName(const MdnsRecord& record) {
152*3f982cf4SFabien Sanglard return IsPtrRecord(record)
153*3f982cf4SFabien Sanglard ? absl::get<PtrRecordRdata>(record.rdata()).ptr_domain()
154*3f982cf4SFabien Sanglard : record.name();
155*3f982cf4SFabien Sanglard }
156*3f982cf4SFabien Sanglard
GetInstanceQueryInfo(const InstanceKey & key)157*3f982cf4SFabien Sanglard DnsQueryInfo GetInstanceQueryInfo(const InstanceKey& key) {
158*3f982cf4SFabien Sanglard return {GetDomainName(key), DnsType::kANY, DnsClass::kANY};
159*3f982cf4SFabien Sanglard }
160*3f982cf4SFabien Sanglard
GetPtrQueryInfo(const ServiceKey & key)161*3f982cf4SFabien Sanglard DnsQueryInfo GetPtrQueryInfo(const ServiceKey& key) {
162*3f982cf4SFabien Sanglard auto domain = GetPtrDomainName(key.service_id(), key.domain_id());
163*3f982cf4SFabien Sanglard return {std::move(domain), DnsType::kPTR, DnsClass::kANY};
164*3f982cf4SFabien Sanglard }
165*3f982cf4SFabien Sanglard
HasValidDnsRecordAddress(const MdnsRecord & record)166*3f982cf4SFabien Sanglard bool HasValidDnsRecordAddress(const MdnsRecord& record) {
167*3f982cf4SFabien Sanglard return HasValidDnsRecordAddress(GetDomainName(record));
168*3f982cf4SFabien Sanglard }
169*3f982cf4SFabien Sanglard
HasValidDnsRecordAddress(const DomainName & domain)170*3f982cf4SFabien Sanglard bool HasValidDnsRecordAddress(const DomainName& domain) {
171*3f982cf4SFabien Sanglard return InstanceKey::TryCreate(domain).is_value() &&
172*3f982cf4SFabien Sanglard IsInstanceValid(domain.labels()[0]);
173*3f982cf4SFabien Sanglard }
174*3f982cf4SFabien Sanglard
IsPtrRecord(const MdnsRecord & record)175*3f982cf4SFabien Sanglard bool IsPtrRecord(const MdnsRecord& record) {
176*3f982cf4SFabien Sanglard return record.dns_type() == DnsType::kPTR;
177*3f982cf4SFabien Sanglard }
178*3f982cf4SFabien Sanglard
GetDnsRecords(const DnsSdInstance & instance)179*3f982cf4SFabien Sanglard std::vector<MdnsRecord> GetDnsRecords(const DnsSdInstance& instance) {
180*3f982cf4SFabien Sanglard auto domain = GetInstanceDomainName(InstanceKey(instance));
181*3f982cf4SFabien Sanglard
182*3f982cf4SFabien Sanglard return {CreatePtrRecord(instance, domain), CreateSrvRecord(instance, domain),
183*3f982cf4SFabien Sanglard CreateTxtRecord(instance, domain)};
184*3f982cf4SFabien Sanglard }
185*3f982cf4SFabien Sanglard
GetDnsRecords(const DnsSdInstanceEndpoint & endpoint)186*3f982cf4SFabien Sanglard std::vector<MdnsRecord> GetDnsRecords(const DnsSdInstanceEndpoint& endpoint) {
187*3f982cf4SFabien Sanglard auto domain = GetInstanceDomainName(InstanceKey(endpoint));
188*3f982cf4SFabien Sanglard
189*3f982cf4SFabien Sanglard std::vector<MdnsRecord> records =
190*3f982cf4SFabien Sanglard GetDnsRecords(static_cast<DnsSdInstance>(endpoint));
191*3f982cf4SFabien Sanglard
192*3f982cf4SFabien Sanglard std::vector<MdnsRecord> v4 = CreateARecords(endpoint, domain);
193*3f982cf4SFabien Sanglard std::vector<MdnsRecord> v6 = CreateAAAARecords(endpoint, domain);
194*3f982cf4SFabien Sanglard
195*3f982cf4SFabien Sanglard records.insert(records.end(), v4.begin(), v4.end());
196*3f982cf4SFabien Sanglard records.insert(records.end(), v6.begin(), v6.end());
197*3f982cf4SFabien Sanglard
198*3f982cf4SFabien Sanglard return records;
199*3f982cf4SFabien Sanglard }
200*3f982cf4SFabien Sanglard
201*3f982cf4SFabien Sanglard } // namespace discovery
202*3f982cf4SFabien Sanglard } // namespace openscreen
203