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
This commit is contained in:
@ -5,8 +5,8 @@ http.proxy::
|
|||||||
proxy string with a user name but no password, in which case git will
|
proxy string with a user name but no password, in which case git will
|
||||||
attempt to acquire one in the same way it does for other credentials. See
|
attempt to acquire one in the same way it does for other credentials. See
|
||||||
linkgit:gitcredentials[7] for more information. The syntax thus is
|
linkgit:gitcredentials[7] for more information. The syntax thus is
|
||||||
'[protocol://][user[:password]@]proxyhost[:port]'. This can be overridden
|
'[protocol://][user[:password]@]proxyhost[:port][/path]'. This can be
|
||||||
on a per-remote basis; see remote.<name>.proxy
|
overridden on a per-remote basis; see remote.<name>.proxy
|
||||||
+
|
+
|
||||||
Any proxy, however configured, must be completely transparent and must not
|
Any proxy, however configured, must be completely transparent and must not
|
||||||
modify, transform, or buffer the request or response in any way. Proxies which
|
modify, transform, or buffer the request or response in any way. Proxies which
|
||||||
|
24
http.c
24
http.c
@ -1227,6 +1227,8 @@ static CURL *get_curl_handle(void)
|
|||||||
*/
|
*/
|
||||||
curl_easy_setopt(result, CURLOPT_PROXY, "");
|
curl_easy_setopt(result, CURLOPT_PROXY, "");
|
||||||
} else if (curl_http_proxy) {
|
} else if (curl_http_proxy) {
|
||||||
|
struct strbuf proxy = STRBUF_INIT;
|
||||||
|
|
||||||
if (starts_with(curl_http_proxy, "socks5h"))
|
if (starts_with(curl_http_proxy, "socks5h"))
|
||||||
curl_easy_setopt(result,
|
curl_easy_setopt(result,
|
||||||
CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME);
|
CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME);
|
||||||
@ -1265,7 +1267,27 @@ static CURL *get_curl_handle(void)
|
|||||||
if (!proxy_auth.host)
|
if (!proxy_auth.host)
|
||||||
die("Invalid proxy URL '%s'", curl_http_proxy);
|
die("Invalid proxy URL '%s'", curl_http_proxy);
|
||||||
|
|
||||||
curl_easy_setopt(result, CURLOPT_PROXY, proxy_auth.host);
|
strbuf_addstr(&proxy, proxy_auth.host);
|
||||||
|
if (proxy_auth.path) {
|
||||||
|
curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW);
|
||||||
|
|
||||||
|
if (ver->version_num < 0x075400)
|
||||||
|
die("libcurl 7.84 or later is required to support paths in proxy URLs");
|
||||||
|
|
||||||
|
if (!starts_with(proxy_auth.protocol, "socks"))
|
||||||
|
die("Invalid proxy URL '%s': only SOCKS proxies support paths",
|
||||||
|
curl_http_proxy);
|
||||||
|
|
||||||
|
if (strcasecmp(proxy_auth.host, "localhost"))
|
||||||
|
die("Invalid proxy URL '%s': host must be localhost if a path is present",
|
||||||
|
curl_http_proxy);
|
||||||
|
|
||||||
|
strbuf_addch(&proxy, '/');
|
||||||
|
strbuf_add_percentencode(&proxy, proxy_auth.path, 0);
|
||||||
|
}
|
||||||
|
curl_easy_setopt(result, CURLOPT_PROXY, proxy.buf);
|
||||||
|
strbuf_release(&proxy);
|
||||||
|
|
||||||
var_override(&curl_no_proxy, getenv("NO_PROXY"));
|
var_override(&curl_no_proxy, getenv("NO_PROXY"));
|
||||||
var_override(&curl_no_proxy, getenv("no_proxy"));
|
var_override(&curl_no_proxy, getenv("no_proxy"));
|
||||||
curl_easy_setopt(result, CURLOPT_NOPROXY, curl_no_proxy);
|
curl_easy_setopt(result, CURLOPT_NOPROXY, curl_no_proxy);
|
||||||
|
48
t/socks4-proxy.pl
Normal file
48
t/socks4-proxy.pl
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use strict;
|
||||||
|
use IO::Select;
|
||||||
|
use IO::Socket::UNIX;
|
||||||
|
use IO::Socket::INET;
|
||||||
|
|
||||||
|
my $path = shift;
|
||||||
|
|
||||||
|
unlink($path);
|
||||||
|
my $server = IO::Socket::UNIX->new(Listen => 1, Local => $path)
|
||||||
|
or die "unable to listen on $path: $!";
|
||||||
|
|
||||||
|
$| = 1;
|
||||||
|
print "ready\n";
|
||||||
|
|
||||||
|
while (my $client = $server->accept()) {
|
||||||
|
sysread $client, my $buf, 8;
|
||||||
|
my ($version, $cmd, $port, $ip) = unpack 'CCnN', $buf;
|
||||||
|
next unless $version == 4; # socks4
|
||||||
|
next unless $cmd == 1; # TCP stream connection
|
||||||
|
|
||||||
|
# skip NUL-terminated id
|
||||||
|
while (sysread $client, my $char, 1) {
|
||||||
|
last unless ord($char);
|
||||||
|
}
|
||||||
|
|
||||||
|
# version(0), reply(5a == granted), port (ignored), ip (ignored)
|
||||||
|
syswrite $client, "\x00\x5a\x00\x00\x00\x00\x00\x00";
|
||||||
|
|
||||||
|
my $remote = IO::Socket::INET->new(PeerHost => $ip, PeerPort => $port)
|
||||||
|
or die "unable to connect to $ip/$port: $!";
|
||||||
|
|
||||||
|
my $io = IO::Select->new($client, $remote);
|
||||||
|
while ($io->count) {
|
||||||
|
for my $fh ($io->can_read(0)) {
|
||||||
|
for my $pair ([$client, $remote], [$remote, $client]) {
|
||||||
|
my ($from, $to) = @$pair;
|
||||||
|
next unless $fh == $from;
|
||||||
|
|
||||||
|
my $r = sysread $from, my $buf, 1024;
|
||||||
|
if (!defined $r || $r <= 0) {
|
||||||
|
$io->remove($from);
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
syswrite $to, $buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -39,4 +39,59 @@ test_expect_success 'clone can prompt for proxy password' '
|
|||||||
expect_askpass pass proxuser
|
expect_askpass pass proxuser
|
||||||
'
|
'
|
||||||
|
|
||||||
|
start_socks() {
|
||||||
|
mkfifo socks_output &&
|
||||||
|
{
|
||||||
|
"$PERL_PATH" "$TEST_DIRECTORY/socks4-proxy.pl" "$1" >socks_output &
|
||||||
|
echo $! > "$TRASH_DIRECTORY/socks.pid"
|
||||||
|
} &&
|
||||||
|
read line <socks_output &&
|
||||||
|
test "$line" = ready
|
||||||
|
}
|
||||||
|
|
||||||
|
# The %30 tests that the correct amount of percent-encoding is applied to the
|
||||||
|
# proxy string passed to curl.
|
||||||
|
test_lazy_prereq SOCKS_PROXY '
|
||||||
|
test_have_prereq PERL &&
|
||||||
|
start_socks "$TRASH_DIRECTORY/%30.sock"
|
||||||
|
'
|
||||||
|
|
||||||
|
test_atexit '
|
||||||
|
test ! -e "$TRASH_DIRECTORY/socks.pid" ||
|
||||||
|
kill "$(cat "$TRASH_DIRECTORY/socks.pid")"
|
||||||
|
'
|
||||||
|
|
||||||
|
# The below tests morally ought to be gated on a prerequisite that Git is
|
||||||
|
# linked with a libcurl that supports Unix socket paths for proxies (7.84 or
|
||||||
|
# later), but this is not easy to test right now. Instead, we || the tests with
|
||||||
|
# this function.
|
||||||
|
old_libcurl_error() {
|
||||||
|
grep -Fx "fatal: libcurl 7.84 or later is required to support paths in proxy URLs" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_expect_success SOCKS_PROXY 'clone via Unix socket' '
|
||||||
|
test_when_finished "rm -rf clone" &&
|
||||||
|
test_config_global http.proxy "socks4://localhost$PWD/%2530.sock" && {
|
||||||
|
{
|
||||||
|
GIT_TRACE_CURL=$PWD/trace git clone "$HTTPD_URL/smart/repo.git" clone 2>err &&
|
||||||
|
grep -i "SOCKS4 request granted" trace
|
||||||
|
} ||
|
||||||
|
old_libcurl_error err
|
||||||
|
}
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'Unix socket requires socks*:' - <<\EOT
|
||||||
|
! git clone -c http.proxy=localhost/path https://example.com/repo.git 2>err && {
|
||||||
|
grep -Fx "fatal: Invalid proxy URL 'localhost/path': only SOCKS proxies support paths" err ||
|
||||||
|
old_libcurl_error err
|
||||||
|
}
|
||||||
|
EOT
|
||||||
|
|
||||||
|
test_expect_success 'Unix socket requires localhost' - <<\EOT
|
||||||
|
! git clone -c http.proxy=socks4://127.0.0.1/path https://example.com/repo.git 2>err && {
|
||||||
|
grep -Fx "fatal: Invalid proxy URL 'socks4://127.0.0.1/path': host must be localhost if a path is present" err ||
|
||||||
|
old_libcurl_error err
|
||||||
|
}
|
||||||
|
EOT
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
Reference in New Issue
Block a user