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