Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / net / base / ip_pattern.cc
blob61c8c2e4df886888ac8c6a5e5adf6e5b2b3d829c
1 // Copyright 2014 The Chromium 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.
5 #include "net/base/ip_pattern.h"
7 #include <string>
9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/string_tokenizer.h"
16 namespace net {
18 class IPPattern::ComponentPattern {
19 public:
20 ComponentPattern();
21 void AppendRange(uint32_t min, uint32_t max);
22 bool Match(uint32_t value) const;
24 private:
25 struct Range {
26 public:
27 Range(uint32_t min, uint32_t max) : minimum(min), maximum(max) {}
28 uint32_t minimum;
29 uint32_t maximum;
31 typedef std::vector<Range> RangeVector;
33 RangeVector ranges_;
35 DISALLOW_COPY_AND_ASSIGN(ComponentPattern);
38 IPPattern::ComponentPattern::ComponentPattern() {}
40 void IPPattern::ComponentPattern::AppendRange(uint32_t min, uint32_t max) {
41 ranges_.push_back(Range(min, max));
44 bool IPPattern::ComponentPattern::Match(uint32_t value) const {
45 // Simple linear search should be fine, as we usually only have very few
46 // distinct ranges to test.
47 for (RangeVector::const_iterator range_it = ranges_.begin();
48 range_it != ranges_.end(); ++range_it) {
49 if (range_it->maximum >= value && range_it->minimum <= value)
50 return true;
52 return false;
55 IPPattern::IPPattern() : is_ipv4_(true) {}
57 IPPattern::~IPPattern() {
58 STLDeleteElements(&component_patterns_);
61 bool IPPattern::Match(const IPAddressNumber& address) const {
62 if (ip_mask_.empty())
63 return false;
64 bool address_is_ipv4 = address.size() == kIPv4AddressSize;
65 if (address_is_ipv4 != is_ipv4_)
66 return false;
68 ComponentPatternList::const_iterator pattern_it(component_patterns_.begin());
69 int fixed_value_index = 0;
70 // IPv6 |address| vectors have 16 pieces, while our |ip_mask_| has only
71 // 8, so it is easier to count separately.
72 int address_index = 0;
73 for (size_t i = 0; i < ip_mask_.size(); ++i) {
74 uint32_t value_to_test = address[address_index++];
75 if (!is_ipv4_) {
76 value_to_test = (value_to_test << 8) + address[address_index++];
78 if (ip_mask_[i]) {
79 if (component_values_[fixed_value_index++] != value_to_test)
80 return false;
81 continue;
83 if (!(*pattern_it)->Match(value_to_test))
84 return false;
85 ++pattern_it;
87 return true;
90 bool IPPattern::ParsePattern(const std::string& ip_pattern) {
91 DCHECK(ip_mask_.empty());
92 if (ip_pattern.find(':') != std::string::npos) {
93 is_ipv4_ = false;
96 std::vector<base::StringPiece> components =
97 base::SplitStringPiece(ip_pattern, is_ipv4_ ? "." : ":",
98 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
99 if (components.size() != (is_ipv4_ ? 4u : 8u)) {
100 DVLOG(1) << "Invalid component count: " << ip_pattern;
101 return false;
103 for (base::StringPiece component : components) {
104 if (component.empty()) {
105 DVLOG(1) << "Empty component: " << ip_pattern;
106 return false;
108 if (component == "*") {
109 // Let standard code handle this below.
110 component = is_ipv4_ ? "[0-255]" : "[0-FFFF]";
111 } else if (component[0] != '[') {
112 // This value will just have a specific integer to match.
113 uint32_t value;
114 if (!ValueTextToInt(component, &value))
115 return false;
116 ip_mask_.push_back(true);
117 component_values_.push_back(value);
118 continue;
120 if (component[component.size() - 1] != ']') {
121 DVLOG(1) << "Missing close bracket: " << ip_pattern;
122 return false;
124 // Now we know the size() is at least 2.
125 if (component.size() == 2) {
126 DVLOG(1) << "Empty bracket: " << ip_pattern;
127 return false;
129 // We'll need a pattern to match this bracketed component.
130 scoped_ptr<ComponentPattern> component_pattern(new ComponentPattern);
131 // Trim leading and trailing bracket before calling for parsing.
132 if (!ParseComponentPattern(component.substr(1, component.size() - 2),
133 component_pattern.get())) {
134 return false;
136 ip_mask_.push_back(false);
137 component_patterns_.push_back(component_pattern.release());
139 return true;
142 bool IPPattern::ParseComponentPattern(const base::StringPiece& text,
143 ComponentPattern* pattern) const {
144 // We're given a comma separated set of ranges, some of which may be simple
145 // constants.
146 for (const std::string& range : base::SplitString(
147 text, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
148 base::StringTokenizer range_pair(range, "-");
149 uint32_t min = 0;
150 range_pair.GetNext();
151 if (!ValueTextToInt(range_pair.token_piece(), &min))
152 return false;
153 uint32_t max = min; // Sometimes we have no distinct max.
154 if (range_pair.GetNext()) {
155 if (!ValueTextToInt(range_pair.token_piece(), &max))
156 return false;
158 if (range_pair.GetNext()) {
159 // Too many "-" in this range specifier.
160 DVLOG(1) << "Too many hyphens in range: ";
161 return false;
163 pattern->AppendRange(min, max);
165 return true;
168 bool IPPattern::ValueTextToInt(const base::StringPiece& input,
169 uint32_t* output) const {
170 bool ok = is_ipv4_ ? base::StringToUint(input, output) :
171 base::HexStringToUInt(input, output);
172 if (!ok) {
173 DVLOG(1) << "Could not convert value to number: " << input;
174 return false;
176 if (is_ipv4_ && *output > 255u) {
177 DVLOG(1) << "IPv4 component greater than 255";
178 return false;
180 if (!is_ipv4_ && *output > 0xFFFFu) {
181 DVLOG(1) << "IPv6 component greater than 0xFFFF";
182 return false;
184 return ok;
187 } // namespace net