1 // Copyright 2018 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.
4 
5 #include "services/network/network_service_proxy_delegate.h"
6 #include "net/base/url_util.h"
7 #include "net/http/http_request_headers.h"
8 #include "net/http/http_util.h"
9 #include "net/proxy_resolution/proxy_info.h"
10 #include "net/proxy_resolution/proxy_resolution_service.h"
11 #include "services/network/url_loader.h"
12 #include "url/url_constants.h"
13 
14 namespace network {
15 namespace {
16 
ApplyProxyConfigToProxyInfo(const net::ProxyConfig::ProxyRules & rules,const net::ProxyRetryInfoMap & proxy_retry_info,const GURL & url,net::ProxyInfo * proxy_info)17 bool ApplyProxyConfigToProxyInfo(const net::ProxyConfig::ProxyRules& rules,
18                                  const net::ProxyRetryInfoMap& proxy_retry_info,
19                                  const GURL& url,
20                                  net::ProxyInfo* proxy_info) {
21   DCHECK(proxy_info);
22   if (rules.empty())
23     return false;
24 
25   rules.Apply(url, proxy_info);
26   proxy_info->DeprioritizeBadProxies(proxy_retry_info);
27   return !proxy_info->is_empty() && !proxy_info->proxy_server().is_direct();
28 }
29 
30 // Checks if |target_proxy| is in |proxy_list|.
CheckProxyList(const net::ProxyList & proxy_list,const net::ProxyServer & target_proxy)31 bool CheckProxyList(const net::ProxyList& proxy_list,
32                     const net::ProxyServer& target_proxy) {
33   for (const auto& proxy : proxy_list.GetAll()) {
34     if (!proxy.is_direct() &&
35         proxy.host_port_pair().Equals(target_proxy.host_port_pair())) {
36       return true;
37     }
38   }
39   return false;
40 }
41 
42 // Returns true if there is a possibility that |proxy_rules->Apply()| can
43 // choose |target_proxy|. This does not consider the bypass rules; it only
44 // scans the possible set of proxy server.
RulesContainsProxy(const net::ProxyConfig::ProxyRules & proxy_rules,const net::ProxyServer & target_proxy)45 bool RulesContainsProxy(const net::ProxyConfig::ProxyRules& proxy_rules,
46                         const net::ProxyServer& target_proxy) {
47   switch (proxy_rules.type) {
48     case net::ProxyConfig::ProxyRules::Type::EMPTY:
49       return false;
50 
51     case net::ProxyConfig::ProxyRules::Type::PROXY_LIST:
52       return CheckProxyList(proxy_rules.single_proxies, target_proxy);
53 
54     case net::ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME:
55       return CheckProxyList(proxy_rules.proxies_for_http, target_proxy) ||
56              CheckProxyList(proxy_rules.proxies_for_https, target_proxy);
57   }
58 
59   NOTREACHED();
60   return false;
61 }
62 
IsValidCustomProxyConfig(const mojom::CustomProxyConfig & config)63 bool IsValidCustomProxyConfig(const mojom::CustomProxyConfig& config) {
64   switch (config.rules.type) {
65     case net::ProxyConfig::ProxyRules::Type::EMPTY:
66       return true;
67 
68     case net::ProxyConfig::ProxyRules::Type::PROXY_LIST:
69       return !config.rules.single_proxies.IsEmpty();
70 
71     case net::ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME:
72       return !config.rules.proxies_for_http.IsEmpty() ||
73              !config.rules.proxies_for_https.IsEmpty();
74   }
75 
76   NOTREACHED();
77   return false;
78 }
79 
80 // Merges headers from |in| to |out|. If the header already exists in |out| they
81 // are combined.
MergeRequestHeaders(net::HttpRequestHeaders * out,const net::HttpRequestHeaders & in)82 void MergeRequestHeaders(net::HttpRequestHeaders* out,
83                          const net::HttpRequestHeaders& in) {
84   for (net::HttpRequestHeaders::Iterator it(in); it.GetNext();) {
85     std::string old_value;
86     if (out->GetHeader(it.name(), &old_value)) {
87       out->SetHeader(it.name(), old_value + ", " + it.value());
88     } else {
89       out->SetHeader(it.name(), it.value());
90     }
91   }
92 }
93 
94 }  // namespace
95 
NetworkServiceProxyDelegate(mojom::CustomProxyConfigPtr initial_config,mojo::PendingReceiver<mojom::CustomProxyConfigClient> config_client_receiver)96 NetworkServiceProxyDelegate::NetworkServiceProxyDelegate(
97     mojom::CustomProxyConfigPtr initial_config,
98     mojo::PendingReceiver<mojom::CustomProxyConfigClient>
99         config_client_receiver)
100     : proxy_config_(std::move(initial_config)),
101       receiver_(this, std::move(config_client_receiver)) {
102   // Make sure there is always a valid proxy config so we don't need to null
103   // check it.
104   if (!proxy_config_)
105     proxy_config_ = mojom::CustomProxyConfig::New();
106 }
107 
~NetworkServiceProxyDelegate()108 NetworkServiceProxyDelegate::~NetworkServiceProxyDelegate() {}
109 
OnResolveProxy(const GURL & url,const std::string & method,const net::ProxyRetryInfoMap & proxy_retry_info,net::ProxyInfo * result)110 void NetworkServiceProxyDelegate::OnResolveProxy(
111     const GURL& url,
112     const std::string& method,
113     const net::ProxyRetryInfoMap& proxy_retry_info,
114     net::ProxyInfo* result) {
115   if (!EligibleForProxy(*result, method))
116     return;
117 
118   net::ProxyInfo proxy_info;
119   if (ApplyProxyConfigToProxyInfo(proxy_config_->rules, proxy_retry_info, url,
120                                   &proxy_info)) {
121     DCHECK(!proxy_info.is_empty() && !proxy_info.is_direct());
122     result->OverrideProxyList(proxy_info.proxy_list());
123   }
124 }
125 
OnFallback(const net::ProxyServer & bad_proxy,int net_error)126 void NetworkServiceProxyDelegate::OnFallback(const net::ProxyServer& bad_proxy,
127                                              int net_error) {}
128 
OnBeforeTunnelRequest(const net::ProxyServer & proxy_server,net::HttpRequestHeaders * extra_headers)129 void NetworkServiceProxyDelegate::OnBeforeTunnelRequest(
130     const net::ProxyServer& proxy_server,
131     net::HttpRequestHeaders* extra_headers) {
132   if (IsInProxyConfig(proxy_server))
133     MergeRequestHeaders(extra_headers, proxy_config_->connect_tunnel_headers);
134 }
135 
OnTunnelHeadersReceived(const net::ProxyServer & proxy_server,const net::HttpResponseHeaders & response_headers)136 net::Error NetworkServiceProxyDelegate::OnTunnelHeadersReceived(
137     const net::ProxyServer& proxy_server,
138     const net::HttpResponseHeaders& response_headers) {
139   return net::OK;
140 }
141 
OnCustomProxyConfigUpdated(mojom::CustomProxyConfigPtr proxy_config)142 void NetworkServiceProxyDelegate::OnCustomProxyConfigUpdated(
143     mojom::CustomProxyConfigPtr proxy_config) {
144   DCHECK(IsValidCustomProxyConfig(*proxy_config));
145   proxy_config_ = std::move(proxy_config);
146 }
147 
MarkProxiesAsBad(base::TimeDelta bypass_duration,const net::ProxyList & bad_proxies_list,MarkProxiesAsBadCallback callback)148 void NetworkServiceProxyDelegate::MarkProxiesAsBad(
149     base::TimeDelta bypass_duration,
150     const net::ProxyList& bad_proxies_list,
151     MarkProxiesAsBadCallback callback) {
152   std::vector<net::ProxyServer> bad_proxies = bad_proxies_list.GetAll();
153 
154   // Synthesize a suitable |ProxyInfo| to add the proxies to the
155   // |ProxyRetryInfoMap| of the proxy service.
156   //
157   // TODO(eroman): Support this more directly on ProxyResolutionService.
158   net::ProxyList proxy_list;
159   for (const auto& bad_proxy : bad_proxies)
160     proxy_list.AddProxyServer(bad_proxy);
161   proxy_list.AddProxyServer(net::ProxyServer::Direct());
162 
163   net::ProxyInfo proxy_info;
164   proxy_info.UseProxyList(proxy_list);
165 
166   proxy_resolution_service_->MarkProxiesAsBadUntil(
167       proxy_info, bypass_duration, bad_proxies, net::NetLogWithSource());
168 
169   std::move(callback).Run();
170 }
171 
ClearBadProxiesCache()172 void NetworkServiceProxyDelegate::ClearBadProxiesCache() {
173   proxy_resolution_service_->ClearBadProxiesCache();
174 }
175 
IsInProxyConfig(const net::ProxyServer & proxy_server) const176 bool NetworkServiceProxyDelegate::IsInProxyConfig(
177     const net::ProxyServer& proxy_server) const {
178   if (!proxy_server.is_valid() || proxy_server.is_direct())
179     return false;
180 
181   if (RulesContainsProxy(proxy_config_->rules, proxy_server))
182     return true;
183 
184   return false;
185 }
186 
MayProxyURL(const GURL & url) const187 bool NetworkServiceProxyDelegate::MayProxyURL(const GURL& url) const {
188   return !proxy_config_->rules.empty();
189 }
190 
EligibleForProxy(const net::ProxyInfo & proxy_info,const std::string & method) const191 bool NetworkServiceProxyDelegate::EligibleForProxy(
192     const net::ProxyInfo& proxy_info,
193     const std::string& method) const {
194   bool has_existing_config =
195       !proxy_info.is_direct() || proxy_info.proxy_list().size() > 1u;
196   if (!proxy_config_->should_override_existing_config && has_existing_config)
197     return false;
198 
199   if (!proxy_config_->allow_non_idempotent_methods &&
200       !net::HttpUtil::IsMethodIdempotent(method)) {
201     return false;
202   }
203 
204   return true;
205 }
206 
207 }  // namespace network
208