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