diff --git a/http.c b/http.c index 412f7af250..ea9b16861b 100644 --- a/http.c +++ b/http.c @@ -761,6 +761,69 @@ static int has_proxy_cert_password(void) return 1; } +static const struct socks_proxy_type { + const char *name; + long curlsym; +} socks_proxy_types[] = { + { "socks", CURLPROXY_SOCKS4 }, + { "socks4", CURLPROXY_SOCKS4 }, + { "socks4a", CURLPROXY_SOCKS4A }, + { "socks5", CURLPROXY_SOCKS5 }, + { "socks5h", CURLPROXY_SOCKS5_HOSTNAME }, +}; + +static const struct socks_proxy_type *find_socks_proxy_type(const char *protocol) +{ + int i; + + if (!protocol) + return NULL; + + for (i = 0; i < ARRAY_SIZE(socks_proxy_types); i++) { + if (!strcmp(socks_proxy_types[i].name, protocol)) + return &socks_proxy_types[i]; + } + + return NULL; +} + +static int is_socks_proxy_protocol(const char *protocol) +{ + return !!find_socks_proxy_type(protocol); +} + +static int set_curl_proxy_type(CURL *result, const char *protocol) +{ + const struct socks_proxy_type *socks_proxy_type; + + if (!protocol || !strcmp(protocol, "http")) + return 0; + + socks_proxy_type = find_socks_proxy_type(protocol); + if (socks_proxy_type) { + curl_easy_setopt(result, CURLOPT_PROXYTYPE, socks_proxy_type->curlsym); + return 0; + } + + if (!strcmp(protocol, "https")) { + curl_easy_setopt(result, CURLOPT_PROXYTYPE, (long)CURLPROXY_HTTPS); + + if (http_proxy_ssl_cert) + curl_easy_setopt(result, CURLOPT_PROXY_SSLCERT, + http_proxy_ssl_cert); + + if (http_proxy_ssl_key) + curl_easy_setopt(result, CURLOPT_PROXY_SSLKEY, + http_proxy_ssl_key); + + if (has_proxy_cert_password()) + curl_easy_setopt(result, CURLOPT_PROXY_KEYPASSWD, + proxy_cert_auth.password); + } + + return -1; +} + /* Return 1 if redactions have been made, 0 otherwise. */ static int redact_sensitive_header(struct strbuf *header, size_t offset) { @@ -1231,30 +1294,6 @@ static CURL *get_curl_handle(void) } else if (curl_http_proxy) { struct strbuf proxy = STRBUF_INIT; - if (starts_with(curl_http_proxy, "socks5h")) - curl_easy_setopt(result, - CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS5_HOSTNAME); - else if (starts_with(curl_http_proxy, "socks5")) - curl_easy_setopt(result, - CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS5); - else if (starts_with(curl_http_proxy, "socks4a")) - curl_easy_setopt(result, - CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS4A); - else if (starts_with(curl_http_proxy, "socks")) - curl_easy_setopt(result, - CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS4); - else if (starts_with(curl_http_proxy, "https")) { - curl_easy_setopt(result, CURLOPT_PROXYTYPE, (long)CURLPROXY_HTTPS); - - if (http_proxy_ssl_cert) - curl_easy_setopt(result, CURLOPT_PROXY_SSLCERT, http_proxy_ssl_cert); - - if (http_proxy_ssl_key) - curl_easy_setopt(result, CURLOPT_PROXY_SSLKEY, http_proxy_ssl_key); - - if (has_proxy_cert_password()) - curl_easy_setopt(result, CURLOPT_PROXY_KEYPASSWD, proxy_cert_auth.password); - } if (strstr(curl_http_proxy, "://")) credential_from_url(&proxy_auth, curl_http_proxy); else { @@ -1264,6 +1303,10 @@ static CURL *get_curl_handle(void) strbuf_release(&url); } + if (set_curl_proxy_type(result, proxy_auth.protocol) < 0) + die("Invalid proxy URL '%s': unsupported proxy scheme '%s'", + curl_http_proxy, proxy_auth.protocol); + if (!proxy_auth.host) die("Invalid proxy URL '%s'", curl_http_proxy); @@ -1274,7 +1317,7 @@ static CURL *get_curl_handle(void) 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")) + if (!is_socks_proxy_protocol(proxy_auth.protocol)) die("Invalid proxy URL '%s': only SOCKS proxies support paths", curl_http_proxy); diff --git a/t/t5564-http-proxy.sh b/t/t5564-http-proxy.sh index b4d95b12ca..817cd1f984 100755 --- a/t/t5564-http-proxy.sh +++ b/t/t5564-http-proxy.sh @@ -102,4 +102,10 @@ test_expect_success 'Unix socket requires localhost' - <<\EOT } EOT +test_expect_success 'unknown proxy scheme is rejected' ' + test_must_fail git clone -c http.proxy=htpp://127.0.0.1 \ + https://example.com/repo.git 2>err && + test_grep "unsupported proxy scheme '\''htpp'\''" err +' + test_done