1*6777b538SAndroid Build Coastguard Worker // Copyright 2022 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker #include "net/dns/dns_names_util.h"
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Worker #include <cstddef>
8*6777b538SAndroid Build Coastguard Worker #include <cstdint>
9*6777b538SAndroid Build Coastguard Worker #include <cstring>
10*6777b538SAndroid Build Coastguard Worker #include <optional>
11*6777b538SAndroid Build Coastguard Worker #include <string>
12*6777b538SAndroid Build Coastguard Worker #include <string_view>
13*6777b538SAndroid Build Coastguard Worker #include <vector>
14*6777b538SAndroid Build Coastguard Worker
15*6777b538SAndroid Build Coastguard Worker #include "base/check.h"
16*6777b538SAndroid Build Coastguard Worker #include "base/containers/span.h"
17*6777b538SAndroid Build Coastguard Worker #include "base/containers/span_reader.h"
18*6777b538SAndroid Build Coastguard Worker #include "net/base/ip_address.h"
19*6777b538SAndroid Build Coastguard Worker #include "net/base/url_util.h"
20*6777b538SAndroid Build Coastguard Worker #include "net/dns/public/dns_protocol.h"
21*6777b538SAndroid Build Coastguard Worker #include "url/third_party/mozilla/url_parse.h"
22*6777b538SAndroid Build Coastguard Worker #include "url/url_canon.h"
23*6777b538SAndroid Build Coastguard Worker #include "url/url_canon_stdstring.h"
24*6777b538SAndroid Build Coastguard Worker
25*6777b538SAndroid Build Coastguard Worker namespace net::dns_names_util {
26*6777b538SAndroid Build Coastguard Worker
IsValidDnsName(std::string_view dotted_form_name)27*6777b538SAndroid Build Coastguard Worker bool IsValidDnsName(std::string_view dotted_form_name) {
28*6777b538SAndroid Build Coastguard Worker return DottedNameToNetwork(dotted_form_name,
29*6777b538SAndroid Build Coastguard Worker /*require_valid_internet_hostname=*/false)
30*6777b538SAndroid Build Coastguard Worker .has_value();
31*6777b538SAndroid Build Coastguard Worker }
32*6777b538SAndroid Build Coastguard Worker
IsValidDnsRecordName(std::string_view dotted_form_name)33*6777b538SAndroid Build Coastguard Worker bool IsValidDnsRecordName(std::string_view dotted_form_name) {
34*6777b538SAndroid Build Coastguard Worker IPAddress ip_address;
35*6777b538SAndroid Build Coastguard Worker return IsValidDnsName(dotted_form_name) &&
36*6777b538SAndroid Build Coastguard Worker !HostStringIsLocalhost(dotted_form_name) &&
37*6777b538SAndroid Build Coastguard Worker !ip_address.AssignFromIPLiteral(dotted_form_name) &&
38*6777b538SAndroid Build Coastguard Worker !ParseURLHostnameToAddress(dotted_form_name, &ip_address);
39*6777b538SAndroid Build Coastguard Worker }
40*6777b538SAndroid Build Coastguard Worker
41*6777b538SAndroid Build Coastguard Worker // Based on DJB's public domain code.
DottedNameToNetwork(std::string_view dotted_form_name,bool require_valid_internet_hostname)42*6777b538SAndroid Build Coastguard Worker std::optional<std::vector<uint8_t>> DottedNameToNetwork(
43*6777b538SAndroid Build Coastguard Worker std::string_view dotted_form_name,
44*6777b538SAndroid Build Coastguard Worker bool require_valid_internet_hostname) {
45*6777b538SAndroid Build Coastguard Worker // Use full IsCanonicalizedHostCompliant() validation if not
46*6777b538SAndroid Build Coastguard Worker // `is_unrestricted`. All subsequent validity checks should not apply unless
47*6777b538SAndroid Build Coastguard Worker // `is_unrestricted` because IsCanonicalizedHostCompliant() is expected to be
48*6777b538SAndroid Build Coastguard Worker // more strict than any validation here.
49*6777b538SAndroid Build Coastguard Worker if (require_valid_internet_hostname &&
50*6777b538SAndroid Build Coastguard Worker !IsCanonicalizedHostCompliant(dotted_form_name))
51*6777b538SAndroid Build Coastguard Worker return std::nullopt;
52*6777b538SAndroid Build Coastguard Worker
53*6777b538SAndroid Build Coastguard Worker const char* buf = dotted_form_name.data();
54*6777b538SAndroid Build Coastguard Worker size_t n = dotted_form_name.size();
55*6777b538SAndroid Build Coastguard Worker uint8_t label[dns_protocol::kMaxLabelLength];
56*6777b538SAndroid Build Coastguard Worker size_t labellen = 0; /* <= sizeof label */
57*6777b538SAndroid Build Coastguard Worker std::vector<uint8_t> name(dns_protocol::kMaxNameLength, 0);
58*6777b538SAndroid Build Coastguard Worker size_t namelen = 0; /* <= sizeof name */
59*6777b538SAndroid Build Coastguard Worker char ch;
60*6777b538SAndroid Build Coastguard Worker
61*6777b538SAndroid Build Coastguard Worker for (;;) {
62*6777b538SAndroid Build Coastguard Worker if (!n)
63*6777b538SAndroid Build Coastguard Worker break;
64*6777b538SAndroid Build Coastguard Worker ch = *buf++;
65*6777b538SAndroid Build Coastguard Worker --n;
66*6777b538SAndroid Build Coastguard Worker if (ch == '.') {
67*6777b538SAndroid Build Coastguard Worker // Don't allow empty labels per http://crbug.com/456391.
68*6777b538SAndroid Build Coastguard Worker if (!labellen) {
69*6777b538SAndroid Build Coastguard Worker DCHECK(!require_valid_internet_hostname);
70*6777b538SAndroid Build Coastguard Worker return std::nullopt;
71*6777b538SAndroid Build Coastguard Worker }
72*6777b538SAndroid Build Coastguard Worker if (namelen + labellen + 1 > name.size()) {
73*6777b538SAndroid Build Coastguard Worker DCHECK(!require_valid_internet_hostname);
74*6777b538SAndroid Build Coastguard Worker return std::nullopt;
75*6777b538SAndroid Build Coastguard Worker }
76*6777b538SAndroid Build Coastguard Worker name[namelen++] = static_cast<uint8_t>(labellen);
77*6777b538SAndroid Build Coastguard Worker memcpy(name.data() + namelen, label, labellen);
78*6777b538SAndroid Build Coastguard Worker namelen += labellen;
79*6777b538SAndroid Build Coastguard Worker labellen = 0;
80*6777b538SAndroid Build Coastguard Worker continue;
81*6777b538SAndroid Build Coastguard Worker }
82*6777b538SAndroid Build Coastguard Worker if (labellen >= sizeof(label)) {
83*6777b538SAndroid Build Coastguard Worker DCHECK(!require_valid_internet_hostname);
84*6777b538SAndroid Build Coastguard Worker return std::nullopt;
85*6777b538SAndroid Build Coastguard Worker }
86*6777b538SAndroid Build Coastguard Worker label[labellen++] = ch;
87*6777b538SAndroid Build Coastguard Worker }
88*6777b538SAndroid Build Coastguard Worker
89*6777b538SAndroid Build Coastguard Worker // Allow empty label at end of name to disable suffix search.
90*6777b538SAndroid Build Coastguard Worker if (labellen) {
91*6777b538SAndroid Build Coastguard Worker if (namelen + labellen + 1 > name.size()) {
92*6777b538SAndroid Build Coastguard Worker DCHECK(!require_valid_internet_hostname);
93*6777b538SAndroid Build Coastguard Worker return std::nullopt;
94*6777b538SAndroid Build Coastguard Worker }
95*6777b538SAndroid Build Coastguard Worker name[namelen++] = static_cast<uint8_t>(labellen);
96*6777b538SAndroid Build Coastguard Worker memcpy(name.data() + namelen, label, labellen);
97*6777b538SAndroid Build Coastguard Worker namelen += labellen;
98*6777b538SAndroid Build Coastguard Worker labellen = 0;
99*6777b538SAndroid Build Coastguard Worker }
100*6777b538SAndroid Build Coastguard Worker
101*6777b538SAndroid Build Coastguard Worker if (namelen + 1 > name.size()) {
102*6777b538SAndroid Build Coastguard Worker DCHECK(!require_valid_internet_hostname);
103*6777b538SAndroid Build Coastguard Worker return std::nullopt;
104*6777b538SAndroid Build Coastguard Worker }
105*6777b538SAndroid Build Coastguard Worker if (namelen == 0) { // Empty names e.g. "", "." are not valid.
106*6777b538SAndroid Build Coastguard Worker DCHECK(!require_valid_internet_hostname);
107*6777b538SAndroid Build Coastguard Worker return std::nullopt;
108*6777b538SAndroid Build Coastguard Worker }
109*6777b538SAndroid Build Coastguard Worker name[namelen++] = 0; // This is the root label (of length 0).
110*6777b538SAndroid Build Coastguard Worker
111*6777b538SAndroid Build Coastguard Worker name.resize(namelen);
112*6777b538SAndroid Build Coastguard Worker return name;
113*6777b538SAndroid Build Coastguard Worker }
114*6777b538SAndroid Build Coastguard Worker
NetworkToDottedName(base::span<const uint8_t> span,bool require_complete)115*6777b538SAndroid Build Coastguard Worker std::optional<std::string> NetworkToDottedName(base::span<const uint8_t> span,
116*6777b538SAndroid Build Coastguard Worker bool require_complete) {
117*6777b538SAndroid Build Coastguard Worker auto reader = base::SpanReader(span);
118*6777b538SAndroid Build Coastguard Worker return NetworkToDottedName(reader, require_complete);
119*6777b538SAndroid Build Coastguard Worker }
120*6777b538SAndroid Build Coastguard Worker
NetworkToDottedName(base::SpanReader<const uint8_t> & reader,bool require_complete)121*6777b538SAndroid Build Coastguard Worker std::optional<std::string> NetworkToDottedName(
122*6777b538SAndroid Build Coastguard Worker base::SpanReader<const uint8_t>& reader,
123*6777b538SAndroid Build Coastguard Worker bool require_complete) {
124*6777b538SAndroid Build Coastguard Worker std::string ret;
125*6777b538SAndroid Build Coastguard Worker size_t octets_read = 0u;
126*6777b538SAndroid Build Coastguard Worker while (reader.remaining() > 0u) {
127*6777b538SAndroid Build Coastguard Worker // DNS name compression not allowed because it does not make sense without
128*6777b538SAndroid Build Coastguard Worker // the context of a full DNS message.
129*6777b538SAndroid Build Coastguard Worker if ((reader.remaining_span()[0u] & dns_protocol::kLabelMask) ==
130*6777b538SAndroid Build Coastguard Worker dns_protocol::kLabelPointer) {
131*6777b538SAndroid Build Coastguard Worker return std::nullopt;
132*6777b538SAndroid Build Coastguard Worker }
133*6777b538SAndroid Build Coastguard Worker
134*6777b538SAndroid Build Coastguard Worker base::span<const uint8_t> label;
135*6777b538SAndroid Build Coastguard Worker if (!ReadU8LengthPrefixed(reader, &label)) {
136*6777b538SAndroid Build Coastguard Worker return std::nullopt;
137*6777b538SAndroid Build Coastguard Worker }
138*6777b538SAndroid Build Coastguard Worker
139*6777b538SAndroid Build Coastguard Worker // Final zero-length label not included in size enforcement.
140*6777b538SAndroid Build Coastguard Worker if (!label.empty()) {
141*6777b538SAndroid Build Coastguard Worker octets_read += label.size() + 1u;
142*6777b538SAndroid Build Coastguard Worker }
143*6777b538SAndroid Build Coastguard Worker
144*6777b538SAndroid Build Coastguard Worker if (label.size() > dns_protocol::kMaxLabelLength) {
145*6777b538SAndroid Build Coastguard Worker return std::nullopt;
146*6777b538SAndroid Build Coastguard Worker }
147*6777b538SAndroid Build Coastguard Worker if (octets_read > dns_protocol::kMaxNameLength) {
148*6777b538SAndroid Build Coastguard Worker return std::nullopt;
149*6777b538SAndroid Build Coastguard Worker }
150*6777b538SAndroid Build Coastguard Worker
151*6777b538SAndroid Build Coastguard Worker if (label.empty()) {
152*6777b538SAndroid Build Coastguard Worker return ret;
153*6777b538SAndroid Build Coastguard Worker }
154*6777b538SAndroid Build Coastguard Worker
155*6777b538SAndroid Build Coastguard Worker if (!ret.empty()) {
156*6777b538SAndroid Build Coastguard Worker ret.append(".");
157*6777b538SAndroid Build Coastguard Worker }
158*6777b538SAndroid Build Coastguard Worker
159*6777b538SAndroid Build Coastguard Worker ret.append(base::as_string_view(label));
160*6777b538SAndroid Build Coastguard Worker }
161*6777b538SAndroid Build Coastguard Worker
162*6777b538SAndroid Build Coastguard Worker if (require_complete) {
163*6777b538SAndroid Build Coastguard Worker return std::nullopt;
164*6777b538SAndroid Build Coastguard Worker }
165*6777b538SAndroid Build Coastguard Worker
166*6777b538SAndroid Build Coastguard Worker // If terminating zero-length label was not included in the input, no need to
167*6777b538SAndroid Build Coastguard Worker // recheck against max name length because terminating zero-length label does
168*6777b538SAndroid Build Coastguard Worker // not count against the limit.
169*6777b538SAndroid Build Coastguard Worker
170*6777b538SAndroid Build Coastguard Worker return ret;
171*6777b538SAndroid Build Coastguard Worker }
172*6777b538SAndroid Build Coastguard Worker
ReadU8LengthPrefixed(base::SpanReader<const uint8_t> & reader,base::span<const uint8_t> * out)173*6777b538SAndroid Build Coastguard Worker bool ReadU8LengthPrefixed(base::SpanReader<const uint8_t>& reader,
174*6777b538SAndroid Build Coastguard Worker base::span<const uint8_t>* out) {
175*6777b538SAndroid Build Coastguard Worker base::SpanReader<const uint8_t> inner_reader = reader;
176*6777b538SAndroid Build Coastguard Worker uint8_t len;
177*6777b538SAndroid Build Coastguard Worker if (!inner_reader.ReadU8BigEndian(len)) {
178*6777b538SAndroid Build Coastguard Worker return false;
179*6777b538SAndroid Build Coastguard Worker }
180*6777b538SAndroid Build Coastguard Worker std::optional<base::span<const uint8_t>> bytes = inner_reader.Read(len);
181*6777b538SAndroid Build Coastguard Worker if (!bytes) {
182*6777b538SAndroid Build Coastguard Worker return false;
183*6777b538SAndroid Build Coastguard Worker }
184*6777b538SAndroid Build Coastguard Worker *out = *bytes;
185*6777b538SAndroid Build Coastguard Worker reader = inner_reader;
186*6777b538SAndroid Build Coastguard Worker return true;
187*6777b538SAndroid Build Coastguard Worker }
188*6777b538SAndroid Build Coastguard Worker
ReadU16LengthPrefixed(base::SpanReader<const uint8_t> & reader,base::span<const uint8_t> * out)189*6777b538SAndroid Build Coastguard Worker bool ReadU16LengthPrefixed(base::SpanReader<const uint8_t>& reader,
190*6777b538SAndroid Build Coastguard Worker base::span<const uint8_t>* out) {
191*6777b538SAndroid Build Coastguard Worker base::SpanReader<const uint8_t> inner_reader = reader;
192*6777b538SAndroid Build Coastguard Worker uint16_t len;
193*6777b538SAndroid Build Coastguard Worker if (!inner_reader.ReadU16BigEndian(len)) {
194*6777b538SAndroid Build Coastguard Worker return false;
195*6777b538SAndroid Build Coastguard Worker }
196*6777b538SAndroid Build Coastguard Worker std::optional<base::span<const uint8_t>> bytes = inner_reader.Read(len);
197*6777b538SAndroid Build Coastguard Worker if (!bytes) {
198*6777b538SAndroid Build Coastguard Worker return false;
199*6777b538SAndroid Build Coastguard Worker }
200*6777b538SAndroid Build Coastguard Worker *out = *bytes;
201*6777b538SAndroid Build Coastguard Worker reader = inner_reader;
202*6777b538SAndroid Build Coastguard Worker return true;
203*6777b538SAndroid Build Coastguard Worker }
204*6777b538SAndroid Build Coastguard Worker
UrlCanonicalizeNameIfAble(std::string_view name)205*6777b538SAndroid Build Coastguard Worker std::string UrlCanonicalizeNameIfAble(std::string_view name) {
206*6777b538SAndroid Build Coastguard Worker std::string canonicalized;
207*6777b538SAndroid Build Coastguard Worker url::StdStringCanonOutput output(&canonicalized);
208*6777b538SAndroid Build Coastguard Worker url::CanonHostInfo host_info;
209*6777b538SAndroid Build Coastguard Worker url::CanonicalizeHostVerbose(name.data(), url::Component(0, name.size()),
210*6777b538SAndroid Build Coastguard Worker &output, &host_info);
211*6777b538SAndroid Build Coastguard Worker
212*6777b538SAndroid Build Coastguard Worker if (host_info.family == url::CanonHostInfo::Family::BROKEN) {
213*6777b538SAndroid Build Coastguard Worker return std::string(name);
214*6777b538SAndroid Build Coastguard Worker }
215*6777b538SAndroid Build Coastguard Worker
216*6777b538SAndroid Build Coastguard Worker output.Complete();
217*6777b538SAndroid Build Coastguard Worker return canonicalized;
218*6777b538SAndroid Build Coastguard Worker }
219*6777b538SAndroid Build Coastguard Worker
220*6777b538SAndroid Build Coastguard Worker } // namespace net::dns_names_util
221