xref: /aosp_15_r20/external/cronet/net/dns/dns_names_util.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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