blob: 86ef010d5b9749712e1f079ed7735ac4db3cf979 [file] [log] [blame]
Jason Jeremy Imana21be272020-10-21 17:53:45 +09001// Copyright 2020 The Chromium OS Authors. All rights reserved.
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 "patchpanel/dns/dns_util.h"
6
7#include <errno.h>
8#include <limits.h>
9
10#include <string>
11
12#include "base/optional.h"
13
14#include "patchpanel/dns/dns_protocol.h"
15
16namespace {
17
18// RFC 1035, section 2.3.4: labels 63 octets or less.
19// Section 3.1: Each label is represented as a one octet length field followed
20// by that number of octets.
21const int kMaxLabelLength = 63;
22
23} // namespace
24
25#if defined(OS_POSIX)
26#include <netinet/in.h>
27#if !defined(OS_NACL)
28#include <net/if.h>
29#if !defined(OS_ANDROID)
30#include <ifaddrs.h>
31#endif // !defined(OS_ANDROID)
32#endif // !defined(OS_NACL)
33#endif // defined(OS_POSIX)
34
35#if defined(OS_ANDROID)
36#include "net/android/network_library.h"
37#endif
38
39namespace patchpanel {
40namespace {
41
42// Based on DJB's public domain code.
43bool DNSDomainFromDot(const base::StringPiece& dotted,
44 bool is_unrestricted,
45 std::string* out) {
46 const char* buf = dotted.data();
47 size_t n = dotted.size();
48 char label[kMaxLabelLength];
49 size_t labellen = 0; /* <= sizeof label */
50 char name[dns_protocol::kMaxNameLength];
51 size_t namelen = 0; /* <= sizeof name */
52 char ch;
53
54 for (;;) {
55 if (!n)
56 break;
57 ch = *buf++;
58 --n;
59 if (ch == '.') {
60 // Don't allow empty labels per http://crbug.com/456391.
61 if (!labellen)
62 return false;
63 if (namelen + labellen + 1 > sizeof name)
64 return false;
65 name[namelen++] = static_cast<char>(labellen);
66 memcpy(name + namelen, label, labellen);
67 namelen += labellen;
68 labellen = 0;
69 continue;
70 }
71 if (labellen >= sizeof label)
72 return false;
73 if (!is_unrestricted && !IsValidHostLabelCharacter(ch, labellen == 0)) {
74 return false;
75 }
76 label[labellen++] = ch;
77 }
78
79 // Allow empty label at end of name to disable suffix search.
80 if (labellen) {
81 if (namelen + labellen + 1 > sizeof name)
82 return false;
83 name[namelen++] = static_cast<char>(labellen);
84 memcpy(name + namelen, label, labellen);
85 namelen += labellen;
86 labellen = 0;
87 }
88
89 if (namelen + 1 > sizeof name)
90 return false;
91 if (namelen == 0) // Empty names e.g. "", "." are not valid.
92 return false;
93 name[namelen++] = 0; // This is the root label (of length 0).
94
95 *out = std::string(name, namelen);
96 return true;
97}
98
99} // namespace
100
101bool DNSDomainFromDot(const base::StringPiece& dotted, std::string* out) {
102 return DNSDomainFromDot(dotted, false /* is_unrestricted */, out);
103}
104
105bool IsValidHostLabelCharacter(char c, bool is_first_char) {
106 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
107 (c >= '0' && c <= '9') || (!is_first_char && c == '-') || c == '_';
108}
109
110base::Optional<std::string> DnsDomainToString(base::StringPiece domain) {
111 std::string ret;
112
113 for (unsigned i = 0; i < domain.size() && domain[i]; i += domain[i] + 1) {
114#if CHAR_MIN < 0
115 if (domain[i] < 0)
116 return base::nullopt;
117#endif
118 if (domain[i] > kMaxLabelLength)
119 return base::nullopt;
120
121 if (i)
122 ret += ".";
123
124 if (static_cast<unsigned>(domain[i]) + i + 1 > domain.size())
125 return base::nullopt;
126
127 ret.append(domain.data() + i + 1, domain[i]);
128 }
129 return ret;
130}
131
132} // namespace patchpanel