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 <optional>
8 #include <sstream>
9
10 #include "base/strings/string_number_conversions.h"
11 #include "net/base/proxy_string_util.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13
14 namespace net {
15
16 namespace {
17
TEST(ProxyChainTest,DefaultConstructor)18 TEST(ProxyChainTest, DefaultConstructor) {
19 ProxyChain proxy_chain;
20 EXPECT_FALSE(proxy_chain.IsValid());
21 }
22
TEST(ProxyChainTest,DirectProxy)23 TEST(ProxyChainTest, DirectProxy) {
24 ProxyChain proxy_chain1 = ProxyChain::Direct();
25 ProxyChain proxy_chain2 = ProxyChain(ProxyServer::Direct());
26 ProxyChain proxy_chain3 =
27 ProxyChain(std::vector<ProxyServer>{ProxyServer::Direct()});
28 ProxyChain proxy_chain4 = ProxyChain(
29 std::vector<ProxyServer>{ProxyServer::Direct(), ProxyServer::Direct()});
30 std::vector<ProxyServer> proxy_servers = {};
31
32 // Equal and valid proxy chains.
33 ASSERT_EQ(proxy_chain1, proxy_chain2);
34 EXPECT_TRUE(proxy_chain1.IsValid());
35 EXPECT_TRUE(proxy_chain2.IsValid());
36
37 EXPECT_TRUE(proxy_chain1.is_direct());
38 EXPECT_FALSE(proxy_chain1.is_single_proxy());
39 EXPECT_FALSE(proxy_chain1.is_multi_proxy());
40 ASSERT_EQ(proxy_chain1.length(), 0u);
41 ASSERT_EQ(proxy_chain1.proxy_servers(), proxy_servers);
42
43 // Not equal proxy chains.
44 ASSERT_NE(proxy_chain2, proxy_chain3);
45
46 EXPECT_FALSE(proxy_chain3.is_direct());
47 EXPECT_FALSE(proxy_chain3.is_single_proxy());
48 EXPECT_FALSE(proxy_chain3.is_multi_proxy());
49 ASSERT_EQ(proxy_chain3.length(), 0u);
50
51 // Equal and not valid proxy chains.
52 ASSERT_EQ(proxy_chain3, proxy_chain4);
53 EXPECT_FALSE(proxy_chain3.IsValid());
54 EXPECT_FALSE(proxy_chain4.IsValid());
55 }
56
TEST(ProxyChainTest,Ostream)57 TEST(ProxyChainTest, Ostream) {
58 ProxyChain proxy_chain =
59 ProxyChain::FromSchemeHostAndPort(ProxyServer::SCHEME_HTTP, "foo", 80);
60 std::ostringstream out;
61 out << proxy_chain;
62 EXPECT_EQ(out.str(), "[foo:80]");
63 }
64
TEST(ProxyChainTest,ToDebugString)65 TEST(ProxyChainTest, ToDebugString) {
66 ProxyChain proxy_chain1 =
67 ProxyChain(ProxyUriToProxyServer("foo:333", ProxyServer::SCHEME_SOCKS5));
68 EXPECT_EQ(proxy_chain1.ToDebugString(), "[socks5://foo:333]");
69
70 ProxyChain proxy_chain2 =
71 ProxyChain({ProxyUriToProxyServer("foo:444", ProxyServer::SCHEME_HTTPS),
72 ProxyUriToProxyServer("foo:555", ProxyServer::SCHEME_HTTPS)});
73 EXPECT_EQ(proxy_chain2.ToDebugString(), "[https://foo:444, https://foo:555]");
74
75 ProxyChain direct_proxy_chain = ProxyChain::Direct();
76 EXPECT_EQ(direct_proxy_chain.ToDebugString(), "[direct://]");
77
78 ProxyChain invalid_proxy_chain = ProxyChain();
79 EXPECT_EQ(invalid_proxy_chain.ToDebugString(), "INVALID PROXY CHAIN");
80 }
81
TEST(ProxyChainTest,FromSchemeHostAndPort)82 TEST(ProxyChainTest, FromSchemeHostAndPort) {
83 const struct {
84 const ProxyServer::Scheme input_scheme;
85 const char* const input_host;
86 const std::optional<uint16_t> input_port;
87 const char* const input_port_str;
88 const char* const expected_host;
89 const uint16_t expected_port;
90 } tests[] = {
91 {ProxyServer::SCHEME_HTTP, "foopy", 80, "80", "foopy", 80},
92
93 // Non-standard port
94 {ProxyServer::SCHEME_HTTP, "foopy", 10, "10", "foopy", 10},
95 {ProxyServer::SCHEME_HTTP, "foopy", 0, "0", "foopy", 0},
96
97 // Hostname canonicalization
98 {ProxyServer::SCHEME_HTTP, "FoOpY", 80, "80", "foopy", 80},
99 {ProxyServer::SCHEME_HTTP, "f\u00fcpy", 80, "80", "xn--fpy-hoa", 80},
100
101 // IPv4 literal
102 {ProxyServer::SCHEME_HTTP, "1.2.3.4", 80, "80", "1.2.3.4", 80},
103
104 // IPv4 literal canonicalization
105 {ProxyServer::SCHEME_HTTP, "127.1", 80, "80", "127.0.0.1", 80},
106 {ProxyServer::SCHEME_HTTP, "0x7F.0x1", 80, "80", "127.0.0.1", 80},
107 {ProxyServer::SCHEME_HTTP, "0177.01", 80, "80", "127.0.0.1", 80},
108
109 // IPv6 literal
110 {ProxyServer::SCHEME_HTTP, "[3ffe:2a00:100:7031::1]", 80, "80",
111 "[3ffe:2a00:100:7031::1]", 80},
112 {ProxyServer::SCHEME_HTTP, "3ffe:2a00:100:7031::1", 80, "80",
113 "[3ffe:2a00:100:7031::1]", 80},
114
115 // IPv6 literal canonicalization
116 {ProxyServer::SCHEME_HTTP, "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210", 80,
117 "80", "[fedc:ba98:7654:3210:fedc:ba98:7654:3210]", 80},
118 {ProxyServer::SCHEME_HTTP, "::192.9.5.5", 80, "80", "[::c009:505]", 80},
119
120 // Other schemes
121 {ProxyServer::SCHEME_HTTPS, "foopy", 111, "111", "foopy", 111},
122 {ProxyServer::SCHEME_QUIC, "foopy", 111, "111", "foopy", 111},
123 {ProxyServer::SCHEME_SOCKS4, "foopy", 111, "111", "foopy", 111},
124 {ProxyServer::SCHEME_SOCKS5, "foopy", 111, "111", "foopy", 111},
125
126 // Default ports
127 {ProxyServer::SCHEME_HTTP, "foopy", std::nullopt, "", "foopy", 80},
128 {ProxyServer::SCHEME_HTTPS, "foopy", std::nullopt, "", "foopy", 443},
129 {ProxyServer::SCHEME_QUIC, "foopy", std::nullopt, "", "foopy", 443},
130 {ProxyServer::SCHEME_SOCKS4, "foopy", std::nullopt, "", "foopy", 1080},
131 {ProxyServer::SCHEME_SOCKS5, "foopy", std::nullopt, "", "foopy", 1080},
132 };
133
134 for (size_t i = 0; i < std::size(tests); ++i) {
135 SCOPED_TRACE(base::NumberToString(i) + ": " + tests[i].input_host + ":" +
136 base::NumberToString(tests[i].input_port.value_or(-1)));
137 auto chain = ProxyChain::FromSchemeHostAndPort(
138 tests[i].input_scheme, tests[i].input_host, tests[i].input_port);
139 auto proxy = chain.proxy_server();
140
141 ASSERT_TRUE(proxy.is_valid());
142 EXPECT_EQ(proxy.scheme(), tests[i].input_scheme);
143 EXPECT_EQ(proxy.GetHost(), tests[i].expected_host);
144 EXPECT_EQ(proxy.GetPort(), tests[i].expected_port);
145
146 auto chain_from_string_port = ProxyChain::FromSchemeHostAndPort(
147 tests[i].input_scheme, tests[i].input_host, tests[i].input_port_str);
148 auto proxy_from_string_port = chain_from_string_port.proxy_server();
149 EXPECT_TRUE(proxy_from_string_port.is_valid());
150 EXPECT_EQ(proxy, proxy_from_string_port);
151 }
152 }
153
TEST(ProxyChainTest,InvalidHostname)154 TEST(ProxyChainTest, InvalidHostname) {
155 const char* const tests[]{
156 "",
157 "[]",
158 "[foo]",
159 "foo:",
160 "foo:80",
161 ":",
162 "http://foo",
163 "3ffe:2a00:100:7031::1]",
164 "[3ffe:2a00:100:7031::1",
165 "foo.80",
166 };
167
168 for (size_t i = 0; i < std::size(tests); ++i) {
169 SCOPED_TRACE(base::NumberToString(i) + ": " + tests[i]);
170 auto proxy = ProxyChain::FromSchemeHostAndPort(ProxyServer::SCHEME_HTTP,
171 tests[i], 80);
172 EXPECT_FALSE(proxy.proxy_server().is_valid());
173 }
174 }
175
TEST(ProxyChainTest,InvalidPort)176 TEST(ProxyChainTest, InvalidPort) {
177 const char* const tests[]{
178 "-1",
179 "65536",
180 "foo",
181 "0x35",
182 };
183
184 for (size_t i = 0; i < std::size(tests); ++i) {
185 SCOPED_TRACE(base::NumberToString(i) + ": " + tests[i]);
186 auto proxy = ProxyChain::FromSchemeHostAndPort(ProxyServer::SCHEME_HTTP,
187 "foopy", tests[i]);
188 EXPECT_FALSE(proxy.proxy_server().is_valid());
189 }
190 }
191
TEST(ProxyChainTest,SingleProxyChain)192 TEST(ProxyChainTest, SingleProxyChain) {
193 auto proxy_server =
194 ProxyUriToProxyServer("foo:333", ProxyServer::SCHEME_HTTPS);
195
196 std::vector<ProxyServer> proxy_servers = {proxy_server};
197 auto proxy = ProxyChain(proxy_servers);
198
199 EXPECT_FALSE(proxy.is_direct());
200 EXPECT_TRUE(proxy.is_single_proxy());
201 EXPECT_FALSE(proxy.is_multi_proxy());
202 ASSERT_EQ(proxy.proxy_servers(), proxy_servers);
203 ASSERT_EQ(proxy.length(), 1u);
204 ASSERT_EQ(proxy.GetProxyServer(0), proxy_server);
205 }
206
TEST(ProxyChainTest,MultiProxyChain)207 TEST(ProxyChainTest, MultiProxyChain) {
208 auto proxy_server1 =
209 ProxyUriToProxyServer("foo:333", ProxyServer::SCHEME_HTTPS);
210 auto proxy_server2 =
211 ProxyUriToProxyServer("foo:444", ProxyServer::SCHEME_HTTPS);
212 auto proxy_server3 =
213 ProxyUriToProxyServer("foo:555", ProxyServer::SCHEME_HTTPS);
214
215 std::vector<ProxyServer> proxy_servers = {proxy_server1, proxy_server2,
216 proxy_server3};
217 auto proxy = ProxyChain(proxy_servers);
218
219 EXPECT_FALSE(proxy.is_direct());
220 EXPECT_FALSE(proxy.is_single_proxy());
221 EXPECT_TRUE(proxy.is_multi_proxy());
222 ASSERT_EQ(proxy.proxy_servers(), proxy_servers);
223 ASSERT_EQ(proxy.length(), 3u);
224 ASSERT_EQ(proxy.GetProxyServer(0), proxy_server1);
225 ASSERT_EQ(proxy.GetProxyServer(1), proxy_server2);
226 ASSERT_EQ(proxy.GetProxyServer(2), proxy_server3);
227 }
228
TEST(ProxyChainTest,IsValid)229 TEST(ProxyChainTest, IsValid) {
230 ProxyServer direct_proxy =
231 ProxyUriToProxyServer("", ProxyServer::SCHEME_DIRECT);
232 ProxyServer http_proxy1 =
233 ProxyUriToProxyServer("foo:444", ProxyServer::SCHEME_HTTPS);
234 ProxyServer http_proxy2 =
235 ProxyUriToProxyServer("foo:555", ProxyServer::SCHEME_HTTPS);
236
237 // Single hop proxy of type Direct is valid.
238 EXPECT_TRUE(ProxyChain(direct_proxy).IsValid());
239
240 // Multi hop proxy with same type is valid.
241 EXPECT_TRUE(ProxyChain({http_proxy1, http_proxy2}).IsValid());
242 }
243
TEST(ProxyChainTest,Unequal)244 TEST(ProxyChainTest, Unequal) {
245 // Unordered proxy chains.
246 std::vector<ProxyChain> proxy_chain_list = {
247 ProxyUriToProxyChain("", ProxyServer::SCHEME_DIRECT),
248 ProxyUriToProxyChain("foo:333", ProxyServer::SCHEME_HTTP),
249 ProxyUriToProxyChain("foo:444", ProxyServer::SCHEME_HTTP),
250 ProxyChain({ProxyUriToProxyServer("foo:555", ProxyServer::SCHEME_HTTPS),
251 ProxyUriToProxyServer("foo:666", ProxyServer::SCHEME_HTTPS)}),
252 ProxyUriToProxyChain("socks4://foo:33", ProxyServer::SCHEME_SOCKS4),
253 ProxyUriToProxyChain("http://foo:33", ProxyServer::SCHEME_HTTP),
254 ProxyChain({ProxyUriToProxyChain("bar:33", ProxyServer::SCHEME_HTTP)})};
255
256 // Ordered proxy chains.
257 std::set<ProxyChain> proxy_chain_set(proxy_chain_list.begin(),
258 proxy_chain_list.end());
259
260 // Initial proxy chain list and set are equal.
261 ASSERT_EQ(proxy_chain_list.size(), proxy_chain_set.size());
262
263 for (const ProxyChain& proxy_chain1 : proxy_chain_list) {
264 auto proxy_chain2 = proxy_chain_set.begin();
265 // Chain set entries less than `proxy_chain1`.
266 while (*proxy_chain2 < proxy_chain1) {
267 EXPECT_TRUE(*proxy_chain2 < proxy_chain1);
268 EXPECT_FALSE(proxy_chain1 < *proxy_chain2);
269 EXPECT_FALSE(*proxy_chain2 == proxy_chain1);
270 EXPECT_FALSE(proxy_chain1 == *proxy_chain2);
271 ++proxy_chain2;
272 }
273
274 // Chain set entry for `proxy_chain1`.
275 EXPECT_FALSE(*proxy_chain2 < proxy_chain1);
276 EXPECT_FALSE(proxy_chain1 < *proxy_chain2);
277 EXPECT_TRUE(*proxy_chain2 == proxy_chain1);
278 EXPECT_TRUE(proxy_chain1 == *proxy_chain2);
279 ++proxy_chain2;
280
281 // Chain set entries greater than `proxy_chain1`.
282 while (proxy_chain2 != proxy_chain_set.end() &&
283 proxy_chain1 < *proxy_chain2) {
284 EXPECT_FALSE(*proxy_chain2 < proxy_chain1);
285 EXPECT_TRUE(proxy_chain1 < *proxy_chain2);
286 EXPECT_FALSE(*proxy_chain2 == proxy_chain1);
287 EXPECT_FALSE(proxy_chain1 == *proxy_chain2);
288 ++proxy_chain2;
289 }
290 ASSERT_EQ(proxy_chain2, proxy_chain_set.end());
291 }
292 }
293
TEST(ProxyChainTest,Equal)294 TEST(ProxyChainTest, Equal) {
295 ProxyServer proxy_server =
296 ProxyUriToProxyServer("foo:11", ProxyServer::SCHEME_HTTP);
297
298 ProxyChain proxy_chain1 = ProxyChain(proxy_server);
299 ProxyChain proxy_chain2 = ProxyChain(std::vector<ProxyServer>{proxy_server});
300 ProxyChain proxy_chain3 =
301 ProxyChain(ProxyServer::SCHEME_HTTP, HostPortPair("foo", 11));
302
303 EXPECT_FALSE(proxy_chain1 < proxy_chain2);
304 EXPECT_FALSE(proxy_chain2 < proxy_chain1);
305 EXPECT_TRUE(proxy_chain2 == proxy_chain1);
306 EXPECT_TRUE(proxy_chain2 == proxy_chain1);
307
308 EXPECT_FALSE(proxy_chain2 < proxy_chain3);
309 EXPECT_FALSE(proxy_chain3 < proxy_chain2);
310 EXPECT_TRUE(proxy_chain3 == proxy_chain2);
311 EXPECT_TRUE(proxy_chain3 == proxy_chain2);
312 }
313
314 } // namespace
315
316 } // namespace net
317