• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 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/base/proxy_chain.h"
6 
7 #include <ostream>
8 #include <vector>
9 
10 #include "base/check.h"
11 #include "base/no_destructor.h"
12 #include "base/pickle.h"
13 #include "base/ranges/algorithm.h"
14 #include "base/strings/stringprintf.h"
15 #include "build/buildflag.h"
16 #include "net/base/proxy_server.h"
17 #include "net/base/proxy_string_util.h"
18 #include "net/net_buildflags.h"
19 
20 namespace net {
21 
22 namespace {
ShouldAllowQuicForAllChains()23 bool ShouldAllowQuicForAllChains() {
24   bool should_allow = false;
25 
26 #if BUILDFLAG(ENABLE_QUIC_PROXY_SUPPORT)
27   should_allow = true;
28 #endif  // BUILDFLAG(ENABLE_QUIC_PROXY_SUPPORT)
29 
30   return should_allow;
31 }
32 }  // namespace
33 
ProxyChain()34 ProxyChain::ProxyChain() {
35   proxy_server_list_ = std::nullopt;
36 }
37 
38 ProxyChain::ProxyChain(const ProxyChain& other) = default;
39 ProxyChain::ProxyChain(ProxyChain&& other) noexcept = default;
40 
41 ProxyChain& ProxyChain::operator=(const ProxyChain& other) = default;
42 ProxyChain& ProxyChain::operator=(ProxyChain&& other) noexcept = default;
43 ProxyChain::~ProxyChain() = default;
44 
ProxyChain(ProxyServer proxy_server)45 ProxyChain::ProxyChain(ProxyServer proxy_server)
46     : ProxyChain(std::vector<ProxyServer>{std::move(proxy_server)}) {}
47 
ProxyChain(ProxyServer::Scheme scheme,const HostPortPair & host_port_pair)48 ProxyChain::ProxyChain(ProxyServer::Scheme scheme,
49                        const HostPortPair& host_port_pair)
50     : ProxyChain(ProxyServer(scheme, host_port_pair)) {}
51 
ProxyChain(std::vector<ProxyServer> proxy_server_list)52 ProxyChain::ProxyChain(std::vector<ProxyServer> proxy_server_list)
53     : proxy_server_list_(std::move(proxy_server_list)) {
54   if (!IsValidInternal()) {
55     proxy_server_list_ = std::nullopt;
56   }
57 }
58 
InitFromPickle(base::PickleIterator * pickle_iter)59 bool ProxyChain::InitFromPickle(base::PickleIterator* pickle_iter) {
60   if (!pickle_iter->ReadInt(&ip_protection_chain_id_)) {
61     return false;
62   }
63   size_t chain_length = 0;
64   if (!pickle_iter->ReadLength(&chain_length)) {
65     return false;
66   }
67 
68   std::vector<ProxyServer> proxy_server_list;
69   for (size_t i = 0; i < chain_length; ++i) {
70     proxy_server_list.push_back(ProxyServer::CreateFromPickle(pickle_iter));
71   }
72   proxy_server_list_ = std::move(proxy_server_list);
73   if (!IsValidInternal()) {
74     proxy_server_list_ = std::nullopt;
75     return false;
76   }
77   return true;
78 }
79 
Persist(base::Pickle * pickle) const80 void ProxyChain::Persist(base::Pickle* pickle) const {
81   DCHECK(IsValid());
82   pickle->WriteInt(ip_protection_chain_id_);
83   if (length() > static_cast<size_t>(INT_MAX) - 1) {
84     pickle->WriteInt(0);
85     return;
86   }
87   pickle->WriteInt(static_cast<int>(length()));
88   for (const auto& proxy_server : proxy_server_list_.value()) {
89     proxy_server.Persist(pickle);
90   }
91 }
92 
GetProxyServer(size_t chain_index) const93 const ProxyServer& ProxyChain::GetProxyServer(size_t chain_index) const {
94   DCHECK(IsValid());
95   CHECK_LT(chain_index, proxy_server_list_.value().size());
96   return proxy_server_list_.value().at(chain_index);
97 }
98 
proxy_servers() const99 const std::vector<ProxyServer>& ProxyChain::proxy_servers() const {
100   DCHECK(IsValid());
101   return proxy_server_list_.value();
102 }
103 
SplitLast() const104 std::pair<ProxyChain, const ProxyServer&> ProxyChain::SplitLast() const {
105   DCHECK(IsValid());
106   DCHECK_NE(length(), 0u);
107   ProxyChain new_chain =
108       ProxyChain({proxy_server_list_->begin(), proxy_server_list_->end() - 1},
109                  ip_protection_chain_id_);
110   return std::make_pair(new_chain, std::ref(proxy_server_list_->back()));
111 }
112 
Prefix(size_t len) const113 ProxyChain ProxyChain::Prefix(size_t len) const {
114   DCHECK(IsValid());
115   DCHECK_LE(len, length());
116   return ProxyChain(
117       {proxy_server_list_->begin(), proxy_server_list_->begin() + len},
118       ip_protection_chain_id_);
119 }
120 
First() const121 const ProxyServer& ProxyChain::First() const {
122   DCHECK(IsValid());
123   DCHECK_NE(length(), 0u);
124   return proxy_server_list_->front();
125 }
126 
Last() const127 const ProxyServer& ProxyChain::Last() const {
128   DCHECK(IsValid());
129   DCHECK_NE(length(), 0u);
130   return proxy_server_list_->back();
131 }
132 
ToDebugString() const133 std::string ProxyChain::ToDebugString() const {
134   if (!IsValid()) {
135     return "INVALID PROXY CHAIN";
136   }
137   std::string debug_string =
138       proxy_server_list_.value().empty() ? "direct://" : "";
139   for (const ProxyServer& proxy_server : proxy_server_list_.value()) {
140     if (!debug_string.empty()) {
141       debug_string += ", ";
142     }
143     debug_string += ProxyServerToProxyUri(proxy_server);
144   }
145   debug_string = "[" + debug_string + "]";
146   if (ip_protection_chain_id_ == 0) {
147     debug_string += " (IP Protection)";
148   } else if (ip_protection_chain_id_ >= 0) {
149     debug_string += base::StringPrintf(" (IP Protection chain %d)",
150                                        ip_protection_chain_id_);
151   }
152   return debug_string;
153 }
154 
ProxyChain(std::vector<ProxyServer> proxy_server_list,int ip_protection_chain_id)155 ProxyChain::ProxyChain(std::vector<ProxyServer> proxy_server_list,
156                        int ip_protection_chain_id)
157     : proxy_server_list_(std::move(proxy_server_list)),
158       ip_protection_chain_id_(ip_protection_chain_id) {
159   CHECK(IsValidInternal());
160 }
161 
IsValidInternal() const162 bool ProxyChain::IsValidInternal() const {
163   if (!proxy_server_list_.has_value()) {
164     return false;
165   }
166   if (is_direct()) {
167     return true;
168   }
169   bool should_allow_quic =
170       is_for_ip_protection() || ShouldAllowQuicForAllChains();
171   if (is_single_proxy()) {
172     bool is_valid = proxy_server_list_.value().at(0).is_valid();
173     if (proxy_server_list_.value().at(0).is_quic()) {
174       is_valid = is_valid && should_allow_quic;
175     }
176     return is_valid;
177   }
178   DCHECK(is_multi_proxy());
179 
180 #if !BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
181   // A chain can only be multi-proxy in release builds if it is for ip
182   // protection.
183   if (!is_for_ip_protection() && is_multi_proxy()) {
184     return false;
185   }
186 #endif  // !BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
187 
188   // Verify that the chain is zero or more SCHEME_QUIC servers followed by zero
189   // or more SCHEME_HTTPS servers.
190   bool seen_quic = false;
191   bool seen_https = false;
192   for (const auto& proxy_server : proxy_server_list_.value()) {
193     if (proxy_server.is_quic()) {
194       if (seen_https) {
195         // SCHEME_QUIC cannot follow SCHEME_HTTPS.
196         return false;
197       }
198       seen_quic = true;
199     } else if (proxy_server.is_https()) {
200       seen_https = true;
201     } else {
202       return false;
203     }
204   }
205 
206   // QUIC is only allowed for IP protection unless in debug builds where it is
207   // generally available.
208   return !seen_quic || should_allow_quic;
209 }
210 
operator <<(std::ostream & os,const ProxyChain & proxy_chain)211 std::ostream& operator<<(std::ostream& os, const ProxyChain& proxy_chain) {
212   return os << proxy_chain.ToDebugString();
213 }
214 
215 }  // namespace net
216