• 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 <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