Skip to content

Commit 4bad011

Browse files
committed
Merge branch 'rh/http-proxy-path'
The value of http.proxy can have "path" at the end for a socks proxy that listens to a unix-domain socket, but we started to discard it when we taught proxy auth code path to use the credential helpers, which has been corrected. * rh/http-proxy-path: http: do not ignore proxy path
2 parents d65332f + 0ca365c commit 4bad011

File tree

4 files changed

+128
-3
lines changed

4 files changed

+128
-3
lines changed

Documentation/config/http.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ http.proxy::
55
proxy string with a user name but no password, in which case git will
66
attempt to acquire one in the same way it does for other credentials. See
77
linkgit:gitcredentials[7] for more information. The syntax thus is
8-
'[protocol://][user[:password]@]proxyhost[:port]'. This can be overridden
9-
on a per-remote basis; see remote.<name>.proxy
8+
'[protocol://][user[:password]@]proxyhost[:port][/path]'. This can be
9+
overridden on a per-remote basis; see remote.<name>.proxy
1010
+
1111
Any proxy, however configured, must be completely transparent and must not
1212
modify, transform, or buffer the request or response in any way. Proxies which

http.c

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1227,6 +1227,8 @@ static CURL *get_curl_handle(void)
12271227
*/
12281228
curl_easy_setopt(result, CURLOPT_PROXY, "");
12291229
} else if (curl_http_proxy) {
1230+
struct strbuf proxy = STRBUF_INIT;
1231+
12301232
if (starts_with(curl_http_proxy, "socks5h"))
12311233
curl_easy_setopt(result,
12321234
CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME);
@@ -1265,7 +1267,27 @@ static CURL *get_curl_handle(void)
12651267
if (!proxy_auth.host)
12661268
die("Invalid proxy URL '%s'", curl_http_proxy);
12671269

1268-
curl_easy_setopt(result, CURLOPT_PROXY, proxy_auth.host);
1270+
strbuf_addstr(&proxy, proxy_auth.host);
1271+
if (proxy_auth.path) {
1272+
curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW);
1273+
1274+
if (ver->version_num < 0x075400)
1275+
die("libcurl 7.84 or later is required to support paths in proxy URLs");
1276+
1277+
if (!starts_with(proxy_auth.protocol, "socks"))
1278+
die("Invalid proxy URL '%s': only SOCKS proxies support paths",
1279+
curl_http_proxy);
1280+
1281+
if (strcasecmp(proxy_auth.host, "localhost"))
1282+
die("Invalid proxy URL '%s': host must be localhost if a path is present",
1283+
curl_http_proxy);
1284+
1285+
strbuf_addch(&proxy, '/');
1286+
strbuf_add_percentencode(&proxy, proxy_auth.path, 0);
1287+
}
1288+
curl_easy_setopt(result, CURLOPT_PROXY, proxy.buf);
1289+
strbuf_release(&proxy);
1290+
12691291
var_override(&curl_no_proxy, getenv("NO_PROXY"));
12701292
var_override(&curl_no_proxy, getenv("no_proxy"));
12711293
curl_easy_setopt(result, CURLOPT_NOPROXY, curl_no_proxy);

t/socks4-proxy.pl

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
use strict;
2+
use IO::Select;
3+
use IO::Socket::UNIX;
4+
use IO::Socket::INET;
5+
6+
my $path = shift;
7+
8+
unlink($path);
9+
my $server = IO::Socket::UNIX->new(Listen => 1, Local => $path)
10+
or die "unable to listen on $path: $!";
11+
12+
$| = 1;
13+
print "ready\n";
14+
15+
while (my $client = $server->accept()) {
16+
sysread $client, my $buf, 8;
17+
my ($version, $cmd, $port, $ip) = unpack 'CCnN', $buf;
18+
next unless $version == 4; # socks4
19+
next unless $cmd == 1; # TCP stream connection
20+
21+
# skip NUL-terminated id
22+
while (sysread $client, my $char, 1) {
23+
last unless ord($char);
24+
}
25+
26+
# version(0), reply(5a == granted), port (ignored), ip (ignored)
27+
syswrite $client, "\x00\x5a\x00\x00\x00\x00\x00\x00";
28+
29+
my $remote = IO::Socket::INET->new(PeerHost => $ip, PeerPort => $port)
30+
or die "unable to connect to $ip/$port: $!";
31+
32+
my $io = IO::Select->new($client, $remote);
33+
while ($io->count) {
34+
for my $fh ($io->can_read(0)) {
35+
for my $pair ([$client, $remote], [$remote, $client]) {
36+
my ($from, $to) = @$pair;
37+
next unless $fh == $from;
38+
39+
my $r = sysread $from, my $buf, 1024;
40+
if (!defined $r || $r <= 0) {
41+
$io->remove($from);
42+
next;
43+
}
44+
syswrite $to, $buf;
45+
}
46+
}
47+
}
48+
}

t/t5564-http-proxy.sh

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,59 @@ test_expect_success 'clone can prompt for proxy password' '
3939
expect_askpass pass proxuser
4040
'
4141

42+
start_socks() {
43+
mkfifo socks_output &&
44+
{
45+
"$PERL_PATH" "$TEST_DIRECTORY/socks4-proxy.pl" "$1" >socks_output &
46+
echo $! > "$TRASH_DIRECTORY/socks.pid"
47+
} &&
48+
read line <socks_output &&
49+
test "$line" = ready
50+
}
51+
52+
# The %30 tests that the correct amount of percent-encoding is applied to the
53+
# proxy string passed to curl.
54+
test_lazy_prereq SOCKS_PROXY '
55+
test_have_prereq PERL &&
56+
start_socks "$TRASH_DIRECTORY/%30.sock"
57+
'
58+
59+
test_atexit '
60+
test ! -e "$TRASH_DIRECTORY/socks.pid" ||
61+
kill "$(cat "$TRASH_DIRECTORY/socks.pid")"
62+
'
63+
64+
# The below tests morally ought to be gated on a prerequisite that Git is
65+
# linked with a libcurl that supports Unix socket paths for proxies (7.84 or
66+
# later), but this is not easy to test right now. Instead, we || the tests with
67+
# this function.
68+
old_libcurl_error() {
69+
grep -Fx "fatal: libcurl 7.84 or later is required to support paths in proxy URLs" "$1"
70+
}
71+
72+
test_expect_success SOCKS_PROXY 'clone via Unix socket' '
73+
test_when_finished "rm -rf clone" &&
74+
test_config_global http.proxy "socks4://localhost$PWD/%2530.sock" && {
75+
{
76+
GIT_TRACE_CURL=$PWD/trace git clone "$HTTPD_URL/smart/repo.git" clone 2>err &&
77+
grep -i "SOCKS4 request granted" trace
78+
} ||
79+
old_libcurl_error err
80+
}
81+
'
82+
83+
test_expect_success 'Unix socket requires socks*:' - <<\EOT
84+
! git clone -c http.proxy=localhost/path https://example.com/repo.git 2>err && {
85+
grep -Fx "fatal: Invalid proxy URL 'localhost/path': only SOCKS proxies support paths" err ||
86+
old_libcurl_error err
87+
}
88+
EOT
89+
90+
test_expect_success 'Unix socket requires localhost' - <<\EOT
91+
! git clone -c http.proxy=socks4://127.0.0.1/path https://example.com/repo.git 2>err && {
92+
grep -Fx "fatal: Invalid proxy URL 'socks4://127.0.0.1/path': host must be localhost if a path is present" err ||
93+
old_libcurl_error err
94+
}
95+
EOT
96+
4297
test_done

0 commit comments

Comments
 (0)