diff options
author | Mathieu Parent <math.parent@gmail.com> | 2021-02-11 22:53:16 +0100 |
---|---|---|
committer | Edward Thomson <ethomson@edwardthomson.com> | 2021-09-01 20:39:25 -0400 |
commit | e5a3277452095a908787c3ea0279fbec21c0a76a (patch) | |
tree | 2f3b310f05331474d5dff2bb3c49bfaeac84d704 | |
parent | d56b4079e0474b2f96abe0487a44359970e43c2a (diff) | |
download | libgit2-e5a3277452095a908787c3ea0279fbec21c0a76a.tar.gz |
Add NO_PROXY env support
Item 2 of 3 from #4164
Signed-off-by: Mathieu Parent <math.parent@gmail.com>
-rw-r--r-- | src/remote.c | 93 | ||||
-rw-r--r-- | src/remote.h | 4 | ||||
-rw-r--r-- | src/transports/http.c | 2 | ||||
-rw-r--r-- | src/transports/winhttp.c | 4 | ||||
-rw-r--r-- | tests/online/clone.c | 66 | ||||
-rw-r--r-- | tests/remote/no_proxy.c | 40 |
6 files changed, 200 insertions, 9 deletions
diff --git a/src/remote.c b/src/remote.c index 73375b352..e63f54a9b 100644 --- a/src/remote.c +++ b/src/remote.c @@ -849,11 +849,70 @@ int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote return remote->transport->ls(out, size, remote->transport); } -int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url) +int git_remote__get_http_proxy_bypass(git_net_url *url, git_buf *no_proxy_env, bool *bypass) +{ + int error = 0; + char *p_start = no_proxy_env->ptr; + size_t p_length = 0; + char c; + git_buf hostport = GIT_BUF_INIT; + + error = git_buf_printf(&hostport, "%s:%s", url->host, url->port); + if (error < 0) + return error; + + *bypass = false; + + do { + c = *(p_start + p_length); + if ((c == ',') || (c == 0)) { + if ((p_length == 1) && (*p_start == '*')) { + // wildcard match (*) + goto found; + } else if ((p_length == strlen(url->host)) && !memcmp(p_start, url->host, p_length)) { + // exact host match + goto found; + } else if ((p_length == strlen(hostport.ptr)) && !memcmp(p_start, hostport.ptr, p_length)) { + // exact host:port match + goto found; + } else { + if ((p_length >= 2) && (*p_start == '*') && (*(p_start + 1) == '.')) { + // *.foo == .foo + p_start++; + p_length--; + } + if ((*p_start == '.') && (strlen(url->host) > p_length) && !memcmp(p_start, url->host + strlen(url->host) - p_length, p_length)) { + // host suffix match (.example.org) + goto found; + } else if ((*p_start == '.') && (strlen(hostport.ptr) > p_length) && !memcmp(p_start, hostport.ptr + strlen(hostport.ptr) - p_length, p_length)) { + // host:port suffix match (.example.org:443) + goto found; + } + } + p_start += p_length + 1; + p_length = 0; + } else { + p_length++; + } + } while(c != 0); + + goto end; + +found: + *bypass = true; + +end: + git_buf_dispose(&hostport); + return 0; +} + +int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, git_net_url *url, char **proxy_url) { git_config *cfg; git_config_entry *ce = NULL; - git_buf val = GIT_BUF_INIT; + git_buf proxy_env = GIT_BUF_INIT; + git_buf no_proxy_env = GIT_BUF_INIT; + bool bypass = false; int error; GIT_ASSERT_ARG(remote); @@ -898,11 +957,11 @@ int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_ur } /* http_proxy / https_proxy environment variables */ - error = git__getenv(&val, use_ssl ? "https_proxy" : "http_proxy"); + error = git__getenv(&proxy_env, use_ssl ? "https_proxy" : "http_proxy"); /* try uppercase environment variables */ if (error == GIT_ENOTFOUND) - error = git__getenv(&val, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY"); + error = git__getenv(&proxy_env, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY"); if (error < 0) { if (error == GIT_ENOTFOUND) { @@ -913,13 +972,35 @@ int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_ur return error; } - *proxy_url = git_buf_detach(&val); + /* no_proxy/NO_PROXY environment variables */ + error = git__getenv(&no_proxy_env, "no_proxy"); + if (error == GIT_ENOTFOUND) + error = git__getenv(&no_proxy_env, "NO_PROXY"); + + if (error == GIT_ENOTFOUND) { + git_error_clear(); + error = 0; + } else if (error < 0) { + goto cleanup; + } else { + error = git_remote__get_http_proxy_bypass(url, &no_proxy_env, &bypass); + } + + if (bypass) { + git_buf_dispose(&proxy_env); + goto cleanup; + } else { + *proxy_url = git_buf_detach(&proxy_env); + } found: GIT_ERROR_CHECK_ALLOC(*proxy_url); + +cleanup: + git_buf_dispose(&no_proxy_env); git_config_entry_free(ce); - return 0; + return error; } /* DWIM `refspecs` based on `refs` and append the output to `out` */ diff --git a/src/remote.h b/src/remote.h index df75ed359..ffcefdf7f 100644 --- a/src/remote.h +++ b/src/remote.h @@ -9,6 +9,7 @@ #include "common.h" +#include "net.h" #include "git2/remote.h" #include "git2/transport.h" #include "git2/sys/transport.h" @@ -46,7 +47,8 @@ typedef struct git_remote_connection_opts { int git_remote__connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_remote_connection_opts *conn); int git_remote__urlfordirection(git_buf *url_out, struct git_remote *remote, int direction, const git_remote_callbacks *callbacks); -int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url); +int git_remote__get_http_proxy_bypass(git_net_url *url, git_buf *no_proxy_env, bool *bypass); +int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, git_net_url *url, char **proxy_url); git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname); git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname); diff --git a/src/transports/http.c b/src/transports/http.c index 691bceb75..5468674e0 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -306,7 +306,7 @@ static int lookup_proxy( remote = transport->owner->owner; use_ssl = !strcmp(transport->server.url.scheme, "https"); - error = git_remote__get_http_proxy(remote, use_ssl, &config); + error = git_remote__get_http_proxy(remote, use_ssl, &transport->server.url, &config); if (error || !config) goto done; diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index ea2195a99..8dc39d8bb 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -373,6 +373,7 @@ static int winhttp_stream_connect(winhttp_stream *s) { winhttp_subtransport *t = OWNING_SUBTRANSPORT(s); git_buf buf = GIT_BUF_INIT; + bool use_ssl; char *proxy_url = NULL; wchar_t ct[MAX_CONTENT_TYPE_LEN]; LPCWSTR types[] = { L"*/*", NULL }; @@ -429,7 +430,8 @@ static int winhttp_stream_connect(winhttp_stream *s) proxy_opts = &t->owner->proxy; if (proxy_opts->type == GIT_PROXY_AUTO) { /* Set proxy if necessary */ - if (git_remote__get_http_proxy(t->owner->owner, (strcmp(t->server.url.scheme, "https") == 0), &proxy_url) < 0) + use_ssl = strcmp(t->server.url.scheme, "https") == 0; + if (git_remote__get_http_proxy(t->owner->owner, use_ssl, &t->server.url, &proxy_url) < 0) goto on_error; } else if (proxy_opts->type == GIT_PROXY_SPECIFIED) { diff --git a/tests/online/clone.c b/tests/online/clone.c index dbf45dcb3..d9b1837df 100644 --- a/tests/online/clone.c +++ b/tests/online/clone.c @@ -36,6 +36,7 @@ static char *_remote_expectcontinue = NULL; static int _orig_proxies_need_reset = 0; static char *_orig_http_proxy = NULL; static char *_orig_https_proxy = NULL; +static char *_orig_no_proxy = NULL; static int ssl_cert(git_cert *cert, int valid, const char *host, void *payload) { @@ -110,9 +111,11 @@ void test_online_clone__cleanup(void) if (_orig_proxies_need_reset) { cl_setenv("HTTP_PROXY", _orig_http_proxy); cl_setenv("HTTPS_PROXY", _orig_https_proxy); + cl_setenv("NO_PROXY", _orig_no_proxy); git__free(_orig_http_proxy); git__free(_orig_https_proxy); + git__free(_orig_no_proxy); } git_libgit2_opts(GIT_OPT_SET_SSL_CERT_LOCATIONS, NULL, NULL); @@ -854,6 +857,7 @@ void test_online_clone__proxy_credentials_in_environment(void) _orig_http_proxy = cl_getenv("HTTP_PROXY"); _orig_https_proxy = cl_getenv("HTTPS_PROXY"); + _orig_no_proxy = cl_getenv("NO_PROXY"); _orig_proxies_need_reset = 1; g_options.fetch_opts.proxy_opts.type = GIT_PROXY_AUTO; @@ -865,6 +869,7 @@ void test_online_clone__proxy_credentials_in_environment(void) cl_setenv("HTTP_PROXY", url.ptr); cl_setenv("HTTPS_PROXY", url.ptr); + cl_setenv("NO_PROXY", NULL); cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options)); @@ -893,6 +898,67 @@ void test_online_clone__proxy_credentials_in_url_https(void) git_buf_dispose(&url); } +struct no_proxy_test_entry { + char no_proxy[128]; + bool bypass; +}; + +static struct no_proxy_test_entry no_proxy_test_entries[] = { + {"*", true}, + {"github.com", true}, + {"github.com:443", true}, + {"github.com:80", false}, + {".github.com", false}, + {"*.github.com", false}, + {".com", true}, + {"*.com", true}, + {".com:443", true}, + {"*.com:443", true}, + {".com:80", false}, + {"*.com:80", false}, + {"", false} +}; + +void test_online_clone__no_proxy_in_environment(void) +{ + int error = 0; + unsigned int i; + git_buf proxy_url = GIT_BUF_INIT; + + _orig_http_proxy = cl_getenv("HTTP_PROXY"); + _orig_https_proxy = cl_getenv("HTTPS_PROXY"); + _orig_no_proxy = cl_getenv("NO_PROXY"); + _orig_proxies_need_reset = 1; + + g_options.fetch_opts.proxy_opts.type = GIT_PROXY_AUTO; + g_options.fetch_opts.proxy_opts.certificate_check = proxy_cert_cb; + + cl_git_pass(git_buf_printf(&proxy_url, "http://does-not-exists.example.org:1234/")); + + cl_setenv("HTTP_PROXY", proxy_url.ptr); + cl_setenv("HTTPS_PROXY", proxy_url.ptr); + + + for (i = 0; i < ARRAY_SIZE(no_proxy_test_entries); ++i) { + cl_setenv("NO_PROXY", no_proxy_test_entries[i].no_proxy); + error = git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options); + + if (no_proxy_test_entries[i].bypass) { + cl_assert_(error == 0, no_proxy_test_entries[i].no_proxy); + } else { + cl_assert_(error == -1, no_proxy_test_entries[i].no_proxy); + } + + if (g_repo) { + git_repository_free(g_repo); + g_repo = NULL; + } + cl_fixture_cleanup("./foo"); + } + + git_buf_dispose(&proxy_url); +} + void test_online_clone__proxy_auto_not_detected(void) { g_options.fetch_opts.proxy_opts.type = GIT_PROXY_AUTO; diff --git a/tests/remote/no_proxy.c b/tests/remote/no_proxy.c new file mode 100644 index 000000000..4f758415c --- /dev/null +++ b/tests/remote/no_proxy.c @@ -0,0 +1,40 @@ +#include "clar_libgit2.h" +#include "remote.h" + +/* Suite data */ +struct no_proxy_test_entry { + char url[128]; + char no_proxy[128]; + bool bypass; +}; + +static struct no_proxy_test_entry no_proxy_test_entries[] = { + {"https://example.com/", "", false}, + {"https://example.com/", "example.org", false}, + {"https://example.com/", "*", true}, + {"https://example.com/", "example.com,example.org", true}, + {"https://example.com/", ".example.com,example.org", false}, + {"https://foo.example.com/", ".example.com,example.org", true}, + {"https://example.com/", "foo.example.com,example.org", false}, + +}; + +void test_remote_no_proxy__entries(void) +{ + unsigned int i; + git_net_url url = GIT_NET_URL_INIT; + git_buf no_proxy = GIT_BUF_INIT; + bool bypass = false; + + for (i = 0; i < ARRAY_SIZE(no_proxy_test_entries); ++i) { + cl_git_pass(git_net_url_parse(&url, no_proxy_test_entries[i].url)); + cl_git_pass(git_buf_sets(&no_proxy, no_proxy_test_entries[i].no_proxy)); + cl_git_pass(git_remote__get_http_proxy_bypass(&url, &no_proxy, &bypass)); + + cl_assert_(bypass == no_proxy_test_entries[i].bypass, no_proxy_test_entries[i].no_proxy); + + git_net_url_dispose(&url); + git_buf_dispose(&no_proxy); + } + +} |