• 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 <string>
11 #include <vector>
12 
13 #include "base/big_endian.h"
14 #include "base/check.h"
15 #include "base/containers/span.h"
16 #include "base/strings/string_piece.h"
17 #include "net/base/ip_address.h"
18 #include "net/base/url_util.h"
19 #include "net/dns/public/dns_protocol.h"
20 #include "third_party/abseil-cpp/absl/types/optional.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(base::StringPiece dotted_form_name)27 bool IsValidDnsName(base::StringPiece dotted_form_name) {
28   return DottedNameToNetwork(dotted_form_name,
29                              /*require_valid_internet_hostname=*/false)
30       .has_value();
31 }
32 
IsValidDnsRecordName(base::StringPiece dotted_form_name)33 bool IsValidDnsRecordName(base::StringPiece 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(base::StringPiece dotted_form_name,bool require_valid_internet_hostname)42 absl::optional<std::vector<uint8_t>> DottedNameToNetwork(
43     base::StringPiece 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 absl::nullopt;
52 
53   const char* buf = dotted_form_name.data();
54   size_t n = dotted_form_name.size();
55   uint8_t label[dns_protocol::kMaxLabelLength];
56   size_t labellen = 0; /* <= sizeof label */
57   std::vector<uint8_t> name(dns_protocol::kMaxNameLength, 0);
58   size_t namelen = 0; /* <= sizeof name */
59   char ch;
60 
61   for (;;) {
62     if (!n)
63       break;
64     ch = *buf++;
65     --n;
66     if (ch == '.') {
67       // Don't allow empty labels per http://crbug.com/456391.
68       if (!labellen) {
69         DCHECK(!require_valid_internet_hostname);
70         return absl::nullopt;
71       }
72       if (namelen + labellen + 1 > name.size()) {
73         DCHECK(!require_valid_internet_hostname);
74         return absl::nullopt;
75       }
76       name[namelen++] = static_cast<uint8_t>(labellen);
77       memcpy(name.data() + namelen, label, labellen);
78       namelen += labellen;
79       labellen = 0;
80       continue;
81     }
82     if (labellen >= sizeof(label)) {
83       DCHECK(!require_valid_internet_hostname);
84       return absl::nullopt;
85     }
86     label[labellen++] = ch;
87   }
88 
89   // Allow empty label at end of name to disable suffix search.
90   if (labellen) {
91     if (namelen + labellen + 1 > name.size()) {
92       DCHECK(!require_valid_internet_hostname);
93       return absl::nullopt;
94     }
95     name[namelen++] = static_cast<uint8_t>(labellen);
96     memcpy(name.data() + namelen, label, labellen);
97     namelen += labellen;
98     labellen = 0;
99   }
100 
101   if (namelen + 1 > name.size()) {
102     DCHECK(!require_valid_internet_hostname);
103     return absl::nullopt;
104   }
105   if (namelen == 0) {  // Empty names e.g. "", "." are not valid.
106     DCHECK(!require_valid_internet_hostname);
107     return absl::nullopt;
108   }
109   name[namelen++] = 0;  // This is the root label (of length 0).
110 
111   name.resize(namelen);
112   return name;
113 }
114 
NetworkToDottedName(base::span<const uint8_t> dns_network_wire_name,bool require_complete)115 absl::optional<std::string> NetworkToDottedName(
116     base::span<const uint8_t> dns_network_wire_name,
117     bool require_complete) {
118   base::BigEndianReader reader(dns_network_wire_name.data(),
119                                dns_network_wire_name.size());
120   return NetworkToDottedName(reader, require_complete);
121 }
122 
NetworkToDottedName(base::StringPiece dns_network_wire_name,bool require_complete)123 absl::optional<std::string> NetworkToDottedName(
124     base::StringPiece dns_network_wire_name,
125     bool require_complete) {
126   auto reader = base::BigEndianReader::FromStringPiece(dns_network_wire_name);
127   return NetworkToDottedName(reader, require_complete);
128 }
129 
NetworkToDottedName(base::BigEndianReader & reader,bool require_complete)130 absl::optional<std::string> NetworkToDottedName(base::BigEndianReader& reader,
131                                                 bool require_complete) {
132   std::string ret;
133   size_t octets_read = 0;
134   while (reader.remaining() > 0) {
135     // DNS name compression not allowed because it does not make sense without
136     // the context of a full DNS message.
137     if ((*reader.ptr() & dns_protocol::kLabelMask) ==
138         dns_protocol::kLabelPointer)
139       return absl::nullopt;
140 
141     base::StringPiece label;
142     if (!reader.ReadU8LengthPrefixed(&label))
143       return absl::nullopt;
144 
145     // Final zero-length label not included in size enforcement.
146     if (label.size() != 0)
147       octets_read += label.size() + 1;
148 
149     if (label.size() > dns_protocol::kMaxLabelLength)
150       return absl::nullopt;
151     if (octets_read > dns_protocol::kMaxNameLength)
152       return absl::nullopt;
153 
154     if (label.size() == 0)
155       return ret;
156 
157     if (!ret.empty())
158       ret.append(".");
159 
160     ret.append(label.data(), label.size());
161   }
162 
163   if (require_complete)
164     return absl::nullopt;
165 
166   // If terminating zero-length label was not included in the input, no need to
167   // recheck against max name length because terminating zero-length label does
168   // not count against the limit.
169 
170   return ret;
171 }
172 
UrlCanonicalizeNameIfAble(base::StringPiece name)173 std::string UrlCanonicalizeNameIfAble(base::StringPiece name) {
174   std::string canonicalized;
175   url::StdStringCanonOutput output(&canonicalized);
176   url::CanonHostInfo host_info;
177   url::CanonicalizeHostVerbose(name.data(), url::Component(0, name.size()),
178                                &output, &host_info);
179 
180   if (host_info.family == url::CanonHostInfo::Family::BROKEN) {
181     return std::string(name);
182   }
183 
184   output.Complete();
185   return canonicalized;
186 }
187 
188 }  // namespace net::dns_names_util
189