summaryrefslogtreecommitdiff
path: root/chromium/net/base/host_mapping_rules.cc
blob: e9de7436a9abdb9d4a9e774941d664e0bd612caa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Copyright 2010 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/base/host_mapping_rules.h"

#include <string>

#include "base/logging.h"
#include "base/strings/pattern.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
#include "net/base/host_port_pair.h"
#include "net/base/url_util.h"
#include "url/gurl.h"
#include "url/third_party/mozilla/url_parse.h"
#include "url/url_canon.h"

namespace net {

struct HostMappingRules::MapRule {
  MapRule() = default;

  std::string hostname_pattern;
  std::string replacement_hostname;
  int replacement_port = -1;
};

struct HostMappingRules::ExclusionRule {
  std::string hostname_pattern;
};

HostMappingRules::HostMappingRules() = default;

HostMappingRules::HostMappingRules(const HostMappingRules& host_mapping_rules) =
    default;

HostMappingRules::~HostMappingRules() = default;

HostMappingRules& HostMappingRules::operator=(
    const HostMappingRules& host_mapping_rules) = default;

bool HostMappingRules::RewriteHost(HostPortPair* host_port) const {
  // Check if the hostname was remapped.
  for (const auto& map_rule : map_rules_) {
    // The rule's hostname_pattern will be something like:
    //     www.foo.com
    //     *.foo.com
    //     www.foo.com:1234
    //     *.foo.com:1234
    // First, we'll check for a match just on hostname.
    // If that fails, we'll check for a match with both hostname and port.
    if (!base::MatchPattern(host_port->host(), map_rule.hostname_pattern)) {
      std::string host_port_string = host_port->ToString();
      if (!base::MatchPattern(host_port_string, map_rule.hostname_pattern))
        continue;  // This rule doesn't apply.
    }

    // Check if the hostname was excluded.
    for (const auto& exclusion_rule : exclusion_rules_) {
      if (base::MatchPattern(host_port->host(),
                             exclusion_rule.hostname_pattern))
        return false;
    }

    host_port->set_host(map_rule.replacement_hostname);
    if (map_rule.replacement_port != -1)
      host_port->set_port(static_cast<uint16_t>(map_rule.replacement_port));
    return true;
  }

  return false;
}

HostMappingRules::RewriteResult HostMappingRules::RewriteUrl(GURL& url) const {
  // Must be a valid and standard URL. Otherwise, Chrome might not know how to
  // find/replace the contained host or port.
  DCHECK(url.is_valid());
  DCHECK(url.IsStandard());
  DCHECK(url.has_host());

  HostPortPair host_port_pair = HostPortPair::FromURL(url);
  if (!RewriteHost(&host_port_pair))
    return RewriteResult::kNoMatchingRule;

  GURL::Replacements replacements;
  std::string port_str = base::NumberToString(host_port_pair.port());
  replacements.SetPortStr(port_str);
  std::string host_str = host_port_pair.HostForURL();
  replacements.SetHostStr(host_str);
  GURL new_url = url.ReplaceComponents(replacements);

  if (!new_url.is_valid())
    return RewriteResult::kInvalidRewrite;

  DCHECK(new_url.IsStandard());
  DCHECK(new_url.has_host());
  DCHECK_EQ(url.EffectiveIntPort() == url::PORT_UNSPECIFIED,
            new_url.EffectiveIntPort() == url::PORT_UNSPECIFIED);

  url = std::move(new_url);
  return RewriteResult::kRewritten;
}

bool HostMappingRules::AddRuleFromString(base::StringPiece rule_string) {
  std::vector<base::StringPiece> parts = base::SplitStringPiece(
      base::TrimWhitespaceASCII(rule_string, base::TRIM_ALL), " ",
      base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);

  // Test for EXCLUSION rule.
  if (parts.size() == 2 &&
      base::EqualsCaseInsensitiveASCII(parts[0], "exclude")) {
    ExclusionRule rule;
    rule.hostname_pattern = base::ToLowerASCII(parts[1]);
    exclusion_rules_.push_back(rule);
    return true;
  }

  // Test for MAP rule.
  if (parts.size() == 3 && base::EqualsCaseInsensitiveASCII(parts[0], "map")) {
    MapRule rule;
    rule.hostname_pattern = base::ToLowerASCII(parts[1]);

    if (!ParseHostAndPort(parts[2], &rule.replacement_hostname,
                          &rule.replacement_port)) {
      return false;  // Failed parsing the hostname/port.
    }

    map_rules_.push_back(rule);
    return true;
  }

  return false;
}

void HostMappingRules::SetRulesFromString(base::StringPiece rules_string) {
  exclusion_rules_.clear();
  map_rules_.clear();

  std::vector<base::StringPiece> rules = base::SplitStringPiece(
      rules_string, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
  for (base::StringPiece rule : rules) {
    bool ok = AddRuleFromString(rule);
    LOG_IF(ERROR, !ok) << "Failed parsing rule: " << rule;
  }
}

}  // namespace net