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/ranges/algorithm.h"
13 #include "base/strings/stringprintf.h"
14 #include "net/base/proxy_server.h"
15 #include "net/base/proxy_string_util.h"
16
17 namespace net {
18
ProxyChain()19 ProxyChain::ProxyChain() {
20 proxy_server_list_ = std::nullopt;
21 }
22
23 ProxyChain::ProxyChain(const ProxyChain& other) = default;
24 ProxyChain::ProxyChain(ProxyChain&& other) noexcept = default;
25
26 ProxyChain& ProxyChain::operator=(const ProxyChain& other) = default;
27 ProxyChain& ProxyChain::operator=(ProxyChain&& other) noexcept = default;
28 ProxyChain::~ProxyChain() = default;
29
ProxyChain(ProxyServer proxy_server)30 ProxyChain::ProxyChain(ProxyServer proxy_server)
31 : ProxyChain(std::vector<ProxyServer>{std::move(proxy_server)}) {}
32
ProxyChain(ProxyServer::Scheme scheme,const HostPortPair & host_port_pair)33 ProxyChain::ProxyChain(ProxyServer::Scheme scheme,
34 const HostPortPair& host_port_pair)
35 : ProxyChain(ProxyServer(scheme, host_port_pair)) {}
36
ProxyChain(std::vector<ProxyServer> proxy_server_list)37 ProxyChain::ProxyChain(std::vector<ProxyServer> proxy_server_list)
38 : proxy_server_list_(std::move(proxy_server_list)) {
39 if (!IsValidInternal()) {
40 proxy_server_list_ = std::nullopt;
41 }
42 }
43
GetProxyServer(size_t chain_index) const44 const ProxyServer& ProxyChain::GetProxyServer(size_t chain_index) const {
45 DCHECK(IsValid());
46 CHECK_LT(chain_index, proxy_server_list_.value().size());
47 return proxy_server_list_.value().at(chain_index);
48 }
49
proxy_servers() const50 const std::vector<ProxyServer>& ProxyChain::proxy_servers() const {
51 DCHECK(IsValid());
52 return proxy_server_list_.value();
53 }
54
SplitLast() const55 std::pair<ProxyChain, const ProxyServer&> ProxyChain::SplitLast() const {
56 DCHECK(IsValid());
57 DCHECK_NE(length(), 0u);
58 ProxyChain new_chain =
59 ProxyChain({proxy_server_list_->begin(), proxy_server_list_->end() - 1},
60 ip_protection_chain_id_);
61 return std::make_pair(new_chain, std::ref(proxy_server_list_->back()));
62 }
63
Prefix(size_t len) const64 ProxyChain ProxyChain::Prefix(size_t len) const {
65 DCHECK(IsValid());
66 DCHECK_LE(len, length());
67 return ProxyChain(
68 {proxy_server_list_->begin(), proxy_server_list_->begin() + len},
69 ip_protection_chain_id_);
70 }
71
First() const72 const ProxyServer& ProxyChain::First() const {
73 DCHECK(IsValid());
74 DCHECK_NE(length(), 0u);
75 return proxy_server_list_->front();
76 }
77
Last() const78 const ProxyServer& ProxyChain::Last() const {
79 DCHECK(IsValid());
80 DCHECK_NE(length(), 0u);
81 return proxy_server_list_->back();
82 }
83
ToDebugString() const84 std::string ProxyChain::ToDebugString() const {
85 if (!IsValid()) {
86 return "INVALID PROXY CHAIN";
87 }
88 std::string debug_string =
89 proxy_server_list_.value().empty() ? "direct://" : "";
90 for (const ProxyServer& proxy_server : proxy_server_list_.value()) {
91 if (!debug_string.empty()) {
92 debug_string += ", ";
93 }
94 debug_string += ProxyServerToProxyUri(proxy_server);
95 }
96 debug_string = "[" + debug_string + "]";
97 if (ip_protection_chain_id_ == 0) {
98 debug_string += " (IP Protection)";
99 } else if (ip_protection_chain_id_ >= 0) {
100 debug_string += base::StringPrintf(" (IP Protection chain %d)",
101 ip_protection_chain_id_);
102 }
103 return debug_string;
104 }
105
ProxyChain(std::vector<ProxyServer> proxy_server_list,int ip_protection_chain_id)106 ProxyChain::ProxyChain(std::vector<ProxyServer> proxy_server_list,
107 int ip_protection_chain_id)
108 : proxy_server_list_(std::move(proxy_server_list)),
109 ip_protection_chain_id_(ip_protection_chain_id) {
110 CHECK(IsValidInternal());
111 }
112
IsValidInternal() const113 bool ProxyChain::IsValidInternal() const {
114 if (!proxy_server_list_.has_value()) {
115 return false;
116 }
117 if (is_direct()) {
118 return true;
119 }
120 if (is_single_proxy()) {
121 bool is_valid = proxy_server_list_.value().at(0).is_valid();
122 if (proxy_server_list_.value().at(0).is_quic()) {
123 is_valid = is_valid && is_for_ip_protection();
124 }
125 return is_valid;
126 }
127 DCHECK(is_multi_proxy());
128
129 // Verify that the chain is zero or more SCHEME_QUIC servers followed by zero
130 // or more SCHEME_HTTPS servers.
131 bool seen_quic = false;
132 bool seen_https = false;
133 for (const auto& proxy_server : proxy_server_list_.value()) {
134 if (proxy_server.is_quic()) {
135 if (seen_https) {
136 // SCHEME_QUIC cannot follow SCHEME_HTTPS.
137 return false;
138 }
139 seen_quic = true;
140 } else if (proxy_server.is_https()) {
141 seen_https = true;
142 } else {
143 return false;
144 }
145 }
146
147 // QUIC is only allowed for IP protection.
148 return !seen_quic || is_for_ip_protection();
149 }
150
operator <<(std::ostream & os,const ProxyChain & proxy_chain)151 std::ostream& operator<<(std::ostream& os, const ProxyChain& proxy_chain) {
152 return os << proxy_chain.ToDebugString();
153 }
154
155 } // namespace net
156