diff options
author | Edward Thomson <ethomson@edwardthomson.com> | 2021-09-01 21:57:16 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-01 21:57:16 -0400 |
commit | 002b2ffe69d9d17e5b5570c5cd3cc13bac4b004c (patch) | |
tree | 1ae5a9dd0aab3ffec9d7433efff5b97696db9b9d | |
parent | d56b4079e0474b2f96abe0487a44359970e43c2a (diff) | |
parent | 3c0f14cc95debb426bd53150aac0eef1a7f625d8 (diff) | |
download | libgit2-002b2ffe69d9d17e5b5570c5cd3cc13bac4b004c.tar.gz |
Merge pull request #6026 from libgit2/ethomson/proxy
Update proxy configuration
-rw-r--r-- | src/net.c | 114 | ||||
-rw-r--r-- | src/net.h | 11 | ||||
-rw-r--r-- | src/remote.c | 149 | ||||
-rw-r--r-- | src/remote.h | 3 | ||||
-rw-r--r-- | src/transports/http.c | 4 | ||||
-rw-r--r-- | src/transports/winhttp.c | 4 | ||||
-rw-r--r-- | src/util.h | 11 | ||||
-rw-r--r-- | tests/core/string.c | 11 | ||||
-rw-r--r-- | tests/network/url/joinpath.c (renamed from tests/network/joinpath.c) | 16 | ||||
-rw-r--r-- | tests/network/url/parse.c (renamed from tests/network/urlparse.c) | 78 | ||||
-rw-r--r-- | tests/network/url/pattern.c | 103 | ||||
-rw-r--r-- | tests/network/url/redirect.c (renamed from tests/network/redirect.c) | 24 | ||||
-rw-r--r-- | tests/online/clone.c | 5 | ||||
-rw-r--r-- | tests/remote/httpproxy.c | 139 |
14 files changed, 565 insertions, 107 deletions
@@ -35,6 +35,46 @@ static const char *default_port_for_scheme(const char *scheme) return NULL; } +int git_net_url_dup(git_net_url *out, git_net_url *in) +{ + if (in->scheme) { + out->scheme = git__strdup(in->scheme); + GIT_ERROR_CHECK_ALLOC(out->scheme); + } + + if (in->host) { + out->host = git__strdup(in->host); + GIT_ERROR_CHECK_ALLOC(out->host); + } + + if (in->port) { + out->port = git__strdup(in->port); + GIT_ERROR_CHECK_ALLOC(out->port); + } + + if (in->path) { + out->path = git__strdup(in->path); + GIT_ERROR_CHECK_ALLOC(out->path); + } + + if (in->query) { + out->query = git__strdup(in->query); + GIT_ERROR_CHECK_ALLOC(out->query); + } + + if (in->username) { + out->username = git__strdup(in->username); + GIT_ERROR_CHECK_ALLOC(out->username); + } + + if (in->password) { + out->password = git__strdup(in->password); + GIT_ERROR_CHECK_ALLOC(out->password); + } + + return 0; +} + int git_net_url_parse(git_net_url *url, const char *given) { struct http_parser_url u = {0}; @@ -404,6 +444,80 @@ int git_net_url_fmt_path(git_buf *buf, git_net_url *url) return git_buf_oom(buf) ? -1 : 0; } +static bool matches_pattern( + git_net_url *url, + const char *pattern, + size_t pattern_len) +{ + const char *domain, *port = NULL, *colon; + size_t host_len, domain_len, port_len = 0, wildcard = 0; + + GIT_UNUSED(url); + GIT_UNUSED(pattern); + + if (!pattern_len) + return false; + else if (pattern_len == 1 && pattern[0] == '*') + return true; + else if (pattern_len > 1 && pattern[0] == '*' && pattern[1] == '.') + wildcard = 2; + else if (pattern[0] == '.') + wildcard = 1; + + domain = pattern + wildcard; + domain_len = pattern_len - wildcard; + + if ((colon = memchr(domain, ':', domain_len)) != NULL) { + domain_len = colon - domain; + port = colon + 1; + port_len = pattern_len - wildcard - domain_len - 1; + } + + /* A pattern's port *must* match if it's specified */ + if (port_len && git__strlcmp(url->port, port, port_len) != 0) + return false; + + /* No wildcard? Host must match exactly. */ + if (!wildcard) + return !git__strlcmp(url->host, domain, domain_len); + + /* Wildcard: ensure there's (at least) a suffix match */ + if ((host_len = strlen(url->host)) < domain_len || + memcmp(url->host + (host_len - domain_len), domain, domain_len)) + return false; + + /* The pattern is *.domain and the host is simply domain */ + if (host_len == domain_len) + return true; + + /* The pattern is *.domain and the host is foo.domain */ + return (url->host[host_len - domain_len - 1] == '.'); +} + +bool git_net_url_matches_pattern(git_net_url *url, const char *pattern) +{ + return matches_pattern(url, pattern, strlen(pattern)); +} + +bool git_net_url_matches_pattern_list( + git_net_url *url, + const char *pattern_list) +{ + const char *pattern, *pattern_end, *sep; + + for (pattern = pattern_list; + pattern && *pattern; + pattern = sep ? sep + 1 : NULL) { + sep = strchr(pattern, ','); + pattern_end = sep ? sep : strchr(pattern, '\0'); + + if (matches_pattern(url, pattern, (pattern_end - pattern))) + return true; + } + + return false; +} + void git_net_url_dispose(git_net_url *url) { if (url->username) @@ -21,6 +21,9 @@ typedef struct git_net_url { #define GIT_NET_URL_INIT { NULL } +/** Duplicate a URL */ +extern int git_net_url_dup(git_net_url *out, git_net_url *in); + /** Parses a string containing a URL into a structure. */ extern int git_net_url_parse(git_net_url *url, const char *str); @@ -54,6 +57,14 @@ extern int git_net_url_fmt(git_buf *out, git_net_url *url); /** Place the path and query string into the given buffer. */ extern int git_net_url_fmt_path(git_buf *buf, git_net_url *url); +/** Determines if the url matches given pattern or pattern list */ +extern bool git_net_url_matches_pattern( + git_net_url *url, + const char *pattern); +extern bool git_net_url_matches_pattern_list( + git_net_url *url, + const char *pattern_list); + /** Disposes the contents of the structure. */ extern void git_net_url_dispose(git_net_url *url); diff --git a/src/remote.c b/src/remote.c index 73375b352..7dddea93a 100644 --- a/src/remote.c +++ b/src/remote.c @@ -849,75 +849,140 @@ 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) +static int lookup_config(char **out, git_config *cfg, const char *name) { - git_config *cfg; git_config_entry *ce = NULL; - git_buf val = GIT_BUF_INIT; int error; - GIT_ASSERT_ARG(remote); + if ((error = git_config__lookup_entry(&ce, cfg, name, false)) < 0) + return error; - if (!proxy_url || !remote->repo) - return -1; + if (ce && ce->value) { + *out = git__strdup(ce->value); + GIT_ERROR_CHECK_ALLOC(*out); + } else { + error = GIT_ENOTFOUND; + } + + git_config_entry_free(ce); + return error; +} - *proxy_url = NULL; +static void url_config_trim(git_net_url *url) +{ + size_t len = strlen(url->path); - if ((error = git_repository_config__weakptr(&cfg, remote->repo)) < 0) - return error; + if (url->path[len - 1] == '/') { + len--; + } else { + while (len && url->path[len - 1] != '/') + len--; + } - /* Go through the possible sources for proxy configuration, from most specific - * to least specific. */ + url->path[len] = '\0'; +} + +static int http_proxy_config(char **out, git_remote *remote, git_net_url *url) +{ + git_config *cfg; + git_buf buf = GIT_BUF_INIT; + git_net_url lookup_url = GIT_NET_URL_INIT; + int error; + + if ((error = git_net_url_dup(&lookup_url, url)) < 0 || + (error = git_repository_config__weakptr(&cfg, remote->repo)) < 0) + goto done; /* remote.<name>.proxy config setting */ if (remote->name && remote->name[0]) { - git_buf buf = GIT_BUF_INIT; + git_buf_clear(&buf); - if ((error = git_buf_printf(&buf, "remote.%s.proxy", remote->name)) < 0) - return error; + if ((error = git_buf_printf(&buf, "remote.%s.proxy", remote->name)) < 0 || + (error = lookup_config(out, cfg, buf.ptr)) != GIT_ENOTFOUND) + goto done; + } - error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false); - git_buf_dispose(&buf); + while (true) { + git_buf_clear(&buf); - if (error < 0) - return error; + if ((error = git_buf_puts(&buf, "http.")) < 0 || + (error = git_net_url_fmt(&buf, &lookup_url)) < 0 || + (error = git_buf_puts(&buf, ".proxy")) < 0 || + (error = lookup_config(out, cfg, buf.ptr)) != GIT_ENOTFOUND) + goto done; - if (ce && ce->value) { - *proxy_url = git__strdup(ce->value); - goto found; - } + if (! lookup_url.path[0]) + break; + + url_config_trim(&lookup_url); } - /* http.proxy config setting */ - if ((error = git_config__lookup_entry(&ce, cfg, "http.proxy", false)) < 0) - return error; + git_buf_clear(&buf); - if (ce && ce->value) { - *proxy_url = git__strdup(ce->value); - goto found; - } + error = lookup_config(out, cfg, "http.proxy"); + +done: + git_buf_dispose(&buf); + git_net_url_dispose(&lookup_url); + return error; +} + +static int http_proxy_env(char **out, git_remote *remote, git_net_url *url) +{ + git_buf proxy_env = GIT_BUF_INIT, no_proxy_env = GIT_BUF_INIT; + bool use_ssl = (strcmp(url->scheme, "https") == 0); + int error; + + GIT_UNUSED(remote); /* 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) { - git_error_clear(); - error = 0; - } + if (error) + goto done; - return error; - } + /* no_proxy/NO_PROXY environment variables */ + error = git__getenv(&no_proxy_env, "no_proxy"); - *proxy_url = git_buf_detach(&val); + if (error == GIT_ENOTFOUND) + error = git__getenv(&no_proxy_env, "NO_PROXY"); -found: - GIT_ERROR_CHECK_ALLOC(*proxy_url); - git_config_entry_free(ce); + if (error && error != GIT_ENOTFOUND) + goto done; + + if (!git_net_url_matches_pattern_list(url, no_proxy_env.ptr)) + *out = git_buf_detach(&proxy_env); + else + error = GIT_ENOTFOUND; + +done: + git_buf_dispose(&proxy_env); + git_buf_dispose(&no_proxy_env); + return error; +} + +int git_remote__http_proxy(char **out, git_remote *remote, git_net_url *url) +{ + int error; + + GIT_ASSERT_ARG(out); + GIT_ASSERT_ARG(remote); + GIT_ASSERT_ARG(remote->repo); + + *out = NULL; + + /* + * Go through the possible sources for proxy configuration, + * Examine the various git config options first, then + * consult environment variables. + */ + if ((error = http_proxy_config(out, remote, url)) != GIT_ENOTFOUND || + (error = http_proxy_env(out, remote, url)) != GIT_ENOTFOUND) + return error; return 0; } diff --git a/src/remote.h b/src/remote.h index df75ed359..ce92db76a 100644 --- a/src/remote.h +++ b/src/remote.h @@ -15,6 +15,7 @@ #include "refspec.h" #include "vector.h" +#include "net.h" #define GIT_REMOTE_ORIGIN "origin" @@ -46,7 +47,7 @@ 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__http_proxy(char **out, git_remote *remote, git_net_url *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..914335aba 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -290,7 +290,6 @@ static int lookup_proxy( { const char *proxy; git_remote *remote; - bool use_ssl; char *config = NULL; int error = 0; @@ -304,9 +303,8 @@ static int lookup_proxy( case GIT_PROXY_AUTO: 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__http_proxy(&config, remote, &transport->server.url); if (error || !config) goto done; diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index ea2195a99..178773a41 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -429,7 +429,7 @@ 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) + if (git_remote__http_proxy(&proxy_url, t->owner->owner, &t->server.url) < 0) goto on_error; } else if (proxy_opts->type == GIT_PROXY_SPECIFIED) { @@ -742,7 +742,7 @@ static void CALLBACK winhttp_status( git_error_set(GIT_ERROR_HTTP, "unknown security error %lu", status); break; - + case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST: ((winhttp_stream *) ctx)->status_sending_request_reached = 1; diff --git a/src/util.h b/src/util.h index a7735366b..68c2b1804 100644 --- a/src/util.h +++ b/src/util.h @@ -168,6 +168,17 @@ extern int git__strncasecmp(const char *a, const char *b, size_t sz); extern int git__strcasesort_cmp(const char *a, const char *b); +/* + * Compare some NUL-terminated `a` to a possibly non-NUL terminated + * `b` of length `b_len`; like `strncmp` but ensuring that + * `strlen(a) == b_len` as well. + */ +GIT_INLINE(int) git__strlcmp(const char *a, const char *b, size_t b_len) +{ + int cmp = strncmp(a, b, b_len); + return cmp ? cmp : (int)a[b_len]; +} + typedef struct { git_atomic32 refcount; void *owner; diff --git a/tests/core/string.c b/tests/core/string.c index 85db0c662..928dfbcc1 100644 --- a/tests/core/string.c +++ b/tests/core/string.c @@ -123,3 +123,14 @@ void test_core_string__strcasecmp(void) cl_assert(git__strcasecmp("et", "e\342\202\254ghi=") < 0); cl_assert(git__strcasecmp("\303\215", "\303\255") < 0); } + +void test_core_string__strlcmp(void) +{ + const char foo[3] = { 'f', 'o', 'o' }; + + cl_assert(git__strlcmp("foo", "foo", 3) == 0); + cl_assert(git__strlcmp("foo", foo, 3) == 0); + cl_assert(git__strlcmp("foo", "foobar", 3) == 0); + cl_assert(git__strlcmp("foobar", "foo", 3) > 0); + cl_assert(git__strlcmp("foo", "foobar", 6) < 0); +} diff --git a/tests/network/joinpath.c b/tests/network/url/joinpath.c index da8393b91..bf4557138 100644 --- a/tests/network/joinpath.c +++ b/tests/network/url/joinpath.c @@ -4,19 +4,19 @@ static git_net_url source, target; -void test_network_joinpath__initialize(void) +void test_network_url_joinpath__initialize(void) { memset(&source, 0, sizeof(source)); memset(&target, 0, sizeof(target)); } -void test_network_joinpath__cleanup(void) +void test_network_url_joinpath__cleanup(void) { git_net_url_dispose(&source); git_net_url_dispose(&target); } -void test_network_joinpath__target_paths_and_queries(void) +void test_network_url_joinpath__target_paths_and_queries(void) { cl_git_pass(git_net_url_parse(&source, "http://example.com/a/b")); @@ -31,7 +31,7 @@ void test_network_joinpath__target_paths_and_queries(void) git_net_url_dispose(&target); } -void test_network_joinpath__source_query_removed(void) +void test_network_url_joinpath__source_query_removed(void) { cl_git_pass(git_net_url_parse(&source, "http://example.com/a/b?query&one&two")); @@ -46,7 +46,7 @@ void test_network_joinpath__source_query_removed(void) git_net_url_dispose(&target); } -void test_network_joinpath__source_lacks_path(void) +void test_network_url_joinpath__source_lacks_path(void) { cl_git_pass(git_net_url_parse(&source, "http://example.com")); @@ -91,7 +91,7 @@ void test_network_joinpath__source_lacks_path(void) git_net_url_dispose(&target); } -void test_network_joinpath__source_is_slash(void) +void test_network_url_joinpath__source_is_slash(void) { cl_git_pass(git_net_url_parse(&source, "http://example.com/")); @@ -137,7 +137,7 @@ void test_network_joinpath__source_is_slash(void) } -void test_network_joinpath__source_has_query(void) +void test_network_url_joinpath__source_has_query(void) { cl_git_pass(git_net_url_parse(&source, "http://example.com?query")); @@ -183,7 +183,7 @@ void test_network_joinpath__source_has_query(void) } -void test_network_joinpath__empty_query_ignored(void) +void test_network_url_joinpath__empty_query_ignored(void) { cl_git_pass(git_net_url_parse(&source, "http://example.com/foo")); diff --git a/tests/network/urlparse.c b/tests/network/url/parse.c index 91c1a527c..edfd6abec 100644 --- a/tests/network/urlparse.c +++ b/tests/network/url/parse.c @@ -3,19 +3,19 @@ static git_net_url conndata; -void test_network_urlparse__initialize(void) +void test_network_url_parse__initialize(void) { memset(&conndata, 0, sizeof(conndata)); } -void test_network_urlparse__cleanup(void) +void test_network_url_parse__cleanup(void) { git_net_url_dispose(&conndata); } /* Hostname */ -void test_network_urlparse__hostname_trivial(void) +void test_network_url_parse__hostname_trivial(void) { cl_git_pass(git_net_url_parse(&conndata, "http://example.com/resource")); cl_assert_equal_s(conndata.scheme, "http"); @@ -27,7 +27,7 @@ void test_network_urlparse__hostname_trivial(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); } -void test_network_urlparse__hostname_root(void) +void test_network_url_parse__hostname_root(void) { cl_git_pass(git_net_url_parse(&conndata, "http://example.com/")); cl_assert_equal_s(conndata.scheme, "http"); @@ -39,7 +39,7 @@ void test_network_urlparse__hostname_root(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); } -void test_network_urlparse__hostname_implied_root(void) +void test_network_url_parse__hostname_implied_root(void) { cl_git_pass(git_net_url_parse(&conndata, "http://example.com")); cl_assert_equal_s(conndata.scheme, "http"); @@ -51,7 +51,7 @@ void test_network_urlparse__hostname_implied_root(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); } -void test_network_urlparse__hostname_implied_root_custom_port(void) +void test_network_url_parse__hostname_implied_root_custom_port(void) { cl_git_pass(git_net_url_parse(&conndata, "http://example.com:42")); cl_assert_equal_s(conndata.scheme, "http"); @@ -63,7 +63,7 @@ void test_network_urlparse__hostname_implied_root_custom_port(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); } -void test_network_urlparse__hostname_implied_root_empty_port(void) +void test_network_url_parse__hostname_implied_root_empty_port(void) { cl_git_pass(git_net_url_parse(&conndata, "http://example.com:")); cl_assert_equal_s(conndata.scheme, "http"); @@ -75,7 +75,7 @@ void test_network_urlparse__hostname_implied_root_empty_port(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); } -void test_network_urlparse__hostname_encoded_password(void) +void test_network_url_parse__hostname_encoded_password(void) { cl_git_pass(git_net_url_parse(&conndata, "https://user:pass%2fis%40bad@hostname.com:1234/")); @@ -88,7 +88,7 @@ void test_network_urlparse__hostname_encoded_password(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); } -void test_network_urlparse__hostname_user(void) +void test_network_url_parse__hostname_user(void) { cl_git_pass(git_net_url_parse(&conndata, "https://user@example.com/resource")); @@ -101,7 +101,7 @@ void test_network_urlparse__hostname_user(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); } -void test_network_urlparse__hostname_user_pass(void) +void test_network_url_parse__hostname_user_pass(void) { /* user:pass@hostname.tld/resource */ cl_git_pass(git_net_url_parse(&conndata, @@ -115,7 +115,7 @@ void test_network_urlparse__hostname_user_pass(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); } -void test_network_urlparse__hostname_port(void) +void test_network_url_parse__hostname_port(void) { /* hostname.tld:port/resource */ cl_git_pass(git_net_url_parse(&conndata, @@ -129,7 +129,7 @@ void test_network_urlparse__hostname_port(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); } -void test_network_urlparse__hostname_empty_port(void) +void test_network_url_parse__hostname_empty_port(void) { cl_git_pass(git_net_url_parse(&conndata, "http://example.com:/resource")); cl_assert_equal_s(conndata.scheme, "http"); @@ -141,7 +141,7 @@ void test_network_urlparse__hostname_empty_port(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); } -void test_network_urlparse__hostname_user_port(void) +void test_network_url_parse__hostname_user_port(void) { /* user@hostname.tld:port/resource */ cl_git_pass(git_net_url_parse(&conndata, @@ -155,7 +155,7 @@ void test_network_urlparse__hostname_user_port(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); } -void test_network_urlparse__hostname_user_pass_port(void) +void test_network_url_parse__hostname_user_pass_port(void) { /* user:pass@hostname.tld:port/resource */ cl_git_pass(git_net_url_parse(&conndata, @@ -171,7 +171,7 @@ void test_network_urlparse__hostname_user_pass_port(void) /* IPv4 addresses */ -void test_network_urlparse__ipv4_trivial(void) +void test_network_url_parse__ipv4_trivial(void) { cl_git_pass(git_net_url_parse(&conndata, "http://192.168.1.1/resource")); cl_assert_equal_s(conndata.scheme, "http"); @@ -183,7 +183,7 @@ void test_network_urlparse__ipv4_trivial(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); } -void test_network_urlparse__ipv4_root(void) +void test_network_url_parse__ipv4_root(void) { cl_git_pass(git_net_url_parse(&conndata, "http://192.168.1.1/")); cl_assert_equal_s(conndata.scheme, "http"); @@ -195,7 +195,7 @@ void test_network_urlparse__ipv4_root(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); } -void test_network_urlparse__ipv4_implied_root(void) +void test_network_url_parse__ipv4_implied_root(void) { cl_git_pass(git_net_url_parse(&conndata, "http://192.168.1.1")); cl_assert_equal_s(conndata.scheme, "http"); @@ -207,7 +207,7 @@ void test_network_urlparse__ipv4_implied_root(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); } -void test_network_urlparse__ipv4_implied_root_custom_port(void) +void test_network_url_parse__ipv4_implied_root_custom_port(void) { cl_git_pass(git_net_url_parse(&conndata, "http://192.168.1.1:42")); cl_assert_equal_s(conndata.scheme, "http"); @@ -219,7 +219,7 @@ void test_network_urlparse__ipv4_implied_root_custom_port(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); } -void test_network_urlparse__ipv4_implied_root_empty_port(void) +void test_network_url_parse__ipv4_implied_root_empty_port(void) { cl_git_pass(git_net_url_parse(&conndata, "http://192.168.1.1:")); cl_assert_equal_s(conndata.scheme, "http"); @@ -231,7 +231,7 @@ void test_network_urlparse__ipv4_implied_root_empty_port(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); } -void test_network_urlparse__ipv4_encoded_password(void) +void test_network_url_parse__ipv4_encoded_password(void) { cl_git_pass(git_net_url_parse(&conndata, "https://user:pass%2fis%40bad@192.168.1.1:1234/")); @@ -244,7 +244,7 @@ void test_network_urlparse__ipv4_encoded_password(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); } -void test_network_urlparse__ipv4_user(void) +void test_network_url_parse__ipv4_user(void) { cl_git_pass(git_net_url_parse(&conndata, "https://user@192.168.1.1/resource")); @@ -257,7 +257,7 @@ void test_network_urlparse__ipv4_user(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); } -void test_network_urlparse__ipv4_user_pass(void) +void test_network_url_parse__ipv4_user_pass(void) { cl_git_pass(git_net_url_parse(&conndata, "https://user:pass@192.168.1.1/resource")); @@ -270,7 +270,7 @@ void test_network_urlparse__ipv4_user_pass(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); } -void test_network_urlparse__ipv4_port(void) +void test_network_url_parse__ipv4_port(void) { cl_git_pass(git_net_url_parse(&conndata, "https://192.168.1.1:9191/resource")); @@ -283,7 +283,7 @@ void test_network_urlparse__ipv4_port(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); } -void test_network_urlparse__ipv4_empty_port(void) +void test_network_url_parse__ipv4_empty_port(void) { cl_git_pass(git_net_url_parse(&conndata, "http://192.168.1.1:/resource")); cl_assert_equal_s(conndata.scheme, "http"); @@ -295,7 +295,7 @@ void test_network_urlparse__ipv4_empty_port(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); } -void test_network_urlparse__ipv4_user_port(void) +void test_network_url_parse__ipv4_user_port(void) { cl_git_pass(git_net_url_parse(&conndata, "https://user@192.168.1.1:9191/resource")); @@ -308,7 +308,7 @@ void test_network_urlparse__ipv4_user_port(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); } -void test_network_urlparse__ipv4_user_pass_port(void) +void test_network_url_parse__ipv4_user_pass_port(void) { cl_git_pass(git_net_url_parse(&conndata, "https://user:pass@192.168.1.1:9191/resource")); @@ -323,7 +323,7 @@ void test_network_urlparse__ipv4_user_pass_port(void) /* IPv6 addresses */ -void test_network_urlparse__ipv6_trivial(void) +void test_network_url_parse__ipv6_trivial(void) { cl_git_pass(git_net_url_parse(&conndata, "http://[fe80::dcad:beff:fe00:0001]/resource")); cl_assert_equal_s(conndata.scheme, "http"); @@ -335,7 +335,7 @@ void test_network_urlparse__ipv6_trivial(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); } -void test_network_urlparse__ipv6_root(void) +void test_network_url_parse__ipv6_root(void) { cl_git_pass(git_net_url_parse(&conndata, "http://[fe80::dcad:beff:fe00:0001]/")); cl_assert_equal_s(conndata.scheme, "http"); @@ -347,7 +347,7 @@ void test_network_urlparse__ipv6_root(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); } -void test_network_urlparse__ipv6_implied_root(void) +void test_network_url_parse__ipv6_implied_root(void) { cl_git_pass(git_net_url_parse(&conndata, "http://[fe80::dcad:beff:fe00:0001]")); cl_assert_equal_s(conndata.scheme, "http"); @@ -359,7 +359,7 @@ void test_network_urlparse__ipv6_implied_root(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); } -void test_network_urlparse__ipv6_implied_root_custom_port(void) +void test_network_url_parse__ipv6_implied_root_custom_port(void) { cl_git_pass(git_net_url_parse(&conndata, "http://[fe80::dcad:beff:fe00:0001]:42")); cl_assert_equal_s(conndata.scheme, "http"); @@ -371,7 +371,7 @@ void test_network_urlparse__ipv6_implied_root_custom_port(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); } -void test_network_urlparse__ipv6_implied_root_empty_port(void) +void test_network_url_parse__ipv6_implied_root_empty_port(void) { cl_git_pass(git_net_url_parse(&conndata, "http://[fe80::dcad:beff:fe00:0001]:")); cl_assert_equal_s(conndata.scheme, "http"); @@ -383,7 +383,7 @@ void test_network_urlparse__ipv6_implied_root_empty_port(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); } -void test_network_urlparse__ipv6_encoded_password(void) +void test_network_url_parse__ipv6_encoded_password(void) { cl_git_pass(git_net_url_parse(&conndata, "https://user:pass%2fis%40bad@[fe80::dcad:beff:fe00:0001]:1234/")); @@ -396,7 +396,7 @@ void test_network_urlparse__ipv6_encoded_password(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); } -void test_network_urlparse__ipv6_user(void) +void test_network_url_parse__ipv6_user(void) { cl_git_pass(git_net_url_parse(&conndata, "https://user@[fe80::dcad:beff:fe00:0001]/resource")); @@ -409,7 +409,7 @@ void test_network_urlparse__ipv6_user(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); } -void test_network_urlparse__ipv6_user_pass(void) +void test_network_url_parse__ipv6_user_pass(void) { cl_git_pass(git_net_url_parse(&conndata, "https://user:pass@[fe80::dcad:beff:fe00:0001]/resource")); @@ -422,7 +422,7 @@ void test_network_urlparse__ipv6_user_pass(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); } -void test_network_urlparse__ipv6_port(void) +void test_network_url_parse__ipv6_port(void) { cl_git_pass(git_net_url_parse(&conndata, "https://[fe80::dcad:beff:fe00:0001]:9191/resource")); @@ -435,7 +435,7 @@ void test_network_urlparse__ipv6_port(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); } -void test_network_urlparse__ipv6_empty_port(void) +void test_network_url_parse__ipv6_empty_port(void) { cl_git_pass(git_net_url_parse(&conndata, "http://[fe80::dcad:beff:fe00:0001]:/resource")); cl_assert_equal_s(conndata.scheme, "http"); @@ -447,7 +447,7 @@ void test_network_urlparse__ipv6_empty_port(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); } -void test_network_urlparse__ipv6_user_port(void) +void test_network_url_parse__ipv6_user_port(void) { cl_git_pass(git_net_url_parse(&conndata, "https://user@[fe80::dcad:beff:fe00:0001]:9191/resource")); @@ -460,7 +460,7 @@ void test_network_urlparse__ipv6_user_port(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); } -void test_network_urlparse__ipv6_user_pass_port(void) +void test_network_url_parse__ipv6_user_pass_port(void) { cl_git_pass(git_net_url_parse(&conndata, "https://user:pass@[fe80::dcad:beff:fe00:0001]:9191/resource")); @@ -473,7 +473,7 @@ void test_network_urlparse__ipv6_user_pass_port(void) cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); } -void test_network_urlparse__ipv6_invalid_addresses(void) +void test_network_url_parse__ipv6_invalid_addresses(void) { /* Opening bracket missing */ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata, diff --git a/tests/network/url/pattern.c b/tests/network/url/pattern.c new file mode 100644 index 000000000..5e4495f70 --- /dev/null +++ b/tests/network/url/pattern.c @@ -0,0 +1,103 @@ +#include "clar_libgit2.h" +#include "net.h" + +struct url_pattern { + const char *url; + const char *pattern; + bool matches; +}; + +void test_network_url_pattern__single(void) +{ + git_net_url url; + size_t i; + + struct url_pattern url_patterns[] = { + /* Wildcard matches */ + { "https://example.com/", "", false }, + { "https://example.com/", "*", true }, + + /* Literal and wildcard matches */ + { "https://example.com/", "example.com", true }, + { "https://example.com/", ".example.com", true }, + { "https://example.com/", "*.example.com", true }, + { "https://www.example.com/", "www.example.com", true }, + { "https://www.example.com/", ".example.com", true }, + { "https://www.example.com/", "*.example.com", true }, + + /* Literal and wildcard failures */ + { "https://example.com/", "example.org", false }, + { "https://example.com/", ".example.org", false }, + { "https://example.com/", "*.example.org", false }, + { "https://foo.example.com/", "www.example.com", false }, + + /* + * A port in the pattern is optional; if no port is + * present, it matches *all* ports. + */ + { "https://example.com/", "example.com:443", true }, + { "https://example.com/", "example.com:80", false }, + { "https://example.com:1443/", "example.com", true }, + + /* Failures with similar prefix/suffix */ + { "https://texample.com/", "example.com", false }, + { "https://example.com/", "mexample.com", false }, + { "https://example.com:44/", "example.com:443", false }, + { "https://example.com:443/", "example.com:44", false }, + }; + + for (i = 0; i < ARRAY_SIZE(url_patterns); i++) { + cl_git_pass(git_net_url_parse(&url, url_patterns[i].url)); + cl_assert_(git_net_url_matches_pattern(&url, url_patterns[i].pattern) == url_patterns[i].matches, url_patterns[i].pattern); + git_net_url_dispose(&url); + } +} + +void test_network_url_pattern__list(void) +{ + git_net_url url; + size_t i; + + struct url_pattern url_patterns[] = { + /* Wildcard matches */ + { "https://example.com/", "", false }, + { "https://example.com/", "*", true }, + { "https://example.com/", ",example.com,", true }, + { "https://example.com/", "foo,,example.com,,bar", true }, + { "https://example.com/", "foo,,zzz,,*,,bar", true }, + + /* Literals */ + { "https://example.com/", "example.com", true }, + { "https://example.com/", "foo.bar,example.com", true }, + { "https://example.com/", "foo.bar", false }, + { "https://example.com/", "foo.bar,example.org", false }, + { "https://www.example.com/", "foo.example.com,www.example.com,bar.example.com", true }, + { "https://www.example.com/", "foo.example.com,baz.example.com,bar.example.com", false }, + { "https://foo.example.com/", "www.example.com", false }, + { "https://foo.example.com/", "bar.example.com,www.example.com,", false }, + + /* Wildcards */ + { "https://example.com/", ".example.com", true }, + { "https://example.com/", "*.example.com", true }, + { "https://example.com/", "foo.com,bar.com,.example.com", true }, + { "https://example.com/", ".foo.com,.bar.com,.example.com", true }, + { "https://example.com/", ".foo.com,.bar.com,asdf.com", false }, + { "https://example.com/", "*.foo,*.bar,*.example.com,*.asdf", true }, + { "https://example.com/", "*.foo,*.bar,*.asdf", false }, + + + /* Ports! */ + { "https://example.com/", "example.com:443", true }, + { "https://example.com/", "example.com:42,example.com:443,example.com:99", true }, + { "https://example.com/", "example.com:42,example.com:80,example.org:443", false }, + { "https://example.com:1443/", "example.com", true }, + { "https://example.com:44/", "example.com:443", false }, + { "https://example.com:443/", "example.com:44", false }, + }; + + for (i = 0; i < ARRAY_SIZE(url_patterns); i++) { + cl_git_pass(git_net_url_parse(&url, url_patterns[i].url)); + cl_assert_(git_net_url_matches_pattern_list(&url, url_patterns[i].pattern) == url_patterns[i].matches, url_patterns[i].pattern); + git_net_url_dispose(&url); + } +} diff --git a/tests/network/redirect.c b/tests/network/url/redirect.c index 7ce1310db..2c0b614d9 100644 --- a/tests/network/redirect.c +++ b/tests/network/url/redirect.c @@ -4,17 +4,17 @@ static git_net_url conndata; -void test_network_redirect__initialize(void) +void test_network_url_redirect__initialize(void) { memset(&conndata, 0, sizeof(conndata)); } -void test_network_redirect__cleanup(void) +void test_network_url_redirect__cleanup(void) { git_net_url_dispose(&conndata); } -void test_network_redirect__redirect_http(void) +void test_network_url_redirect__redirect_http(void) { cl_git_pass(git_net_url_parse(&conndata, "http://example.com/foo/bar/baz")); @@ -28,7 +28,7 @@ void test_network_redirect__redirect_http(void) cl_assert_equal_p(conndata.password, NULL); } -void test_network_redirect__redirect_ssl(void) +void test_network_url_redirect__redirect_ssl(void) { cl_git_pass(git_net_url_parse(&conndata, "https://example.com/foo/bar/baz")); @@ -42,7 +42,7 @@ void test_network_redirect__redirect_ssl(void) cl_assert_equal_p(conndata.password, NULL); } -void test_network_redirect__redirect_leaves_root_path(void) +void test_network_url_redirect__redirect_leaves_root_path(void) { cl_git_pass(git_net_url_parse(&conndata, "https://example.com/foo/bar/baz")); @@ -56,7 +56,7 @@ void test_network_redirect__redirect_leaves_root_path(void) cl_assert_equal_p(conndata.password, NULL); } -void test_network_redirect__redirect_encoded_username_password(void) +void test_network_url_redirect__redirect_encoded_username_password(void) { cl_git_pass(git_net_url_parse(&conndata, "https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz")); @@ -70,7 +70,7 @@ void test_network_redirect__redirect_encoded_username_password(void) cl_assert_equal_s(conndata.password, "pass@word%zyx%v"); } -void test_network_redirect__redirect_cross_host_denied(void) +void test_network_url_redirect__redirect_cross_host_denied(void) { cl_git_pass(git_net_url_parse(&conndata, "https://bar.com/bar/baz")); cl_git_fail_with(git_net_url_apply_redirect(&conndata, @@ -78,7 +78,7 @@ void test_network_redirect__redirect_cross_host_denied(void) -1); } -void test_network_redirect__redirect_http_downgrade_denied(void) +void test_network_url_redirect__redirect_http_downgrade_denied(void) { cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/baz")); cl_git_fail_with(git_net_url_apply_redirect(&conndata, @@ -86,7 +86,7 @@ void test_network_redirect__redirect_http_downgrade_denied(void) -1); } -void test_network_redirect__redirect_relative(void) +void test_network_url_redirect__redirect_relative(void) { cl_git_pass(git_net_url_parse(&conndata, "http://foo.com/bar/baz/biff")); cl_git_pass(git_net_url_apply_redirect(&conndata, @@ -99,7 +99,7 @@ void test_network_redirect__redirect_relative(void) cl_assert_equal_p(conndata.password, NULL); } -void test_network_redirect__redirect_relative_ssl(void) +void test_network_url_redirect__redirect_relative_ssl(void) { cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/baz/biff")); cl_git_pass(git_net_url_apply_redirect(&conndata, @@ -112,7 +112,7 @@ void test_network_redirect__redirect_relative_ssl(void) cl_assert_equal_p(conndata.password, NULL); } -void test_network_redirect__service_query_no_query_params_in_location(void) +void test_network_url_redirect__service_query_no_query_params_in_location(void) { cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/info/refs?service=git-upload-pack")); cl_git_pass(git_net_url_apply_redirect(&conndata, @@ -120,7 +120,7 @@ void test_network_redirect__service_query_no_query_params_in_location(void) cl_assert_equal_s(conndata.path, "/baz"); } -void test_network_redirect__service_query_with_query_params_in_location(void) +void test_network_url_redirect__service_query_with_query_params_in_location(void) { cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/info/refs?service=git-upload-pack")); cl_git_pass(git_net_url_apply_redirect(&conndata, diff --git a/tests/online/clone.c b/tests/online/clone.c index dbf45dcb3..7d43c6a09 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)); diff --git a/tests/remote/httpproxy.c b/tests/remote/httpproxy.c new file mode 100644 index 000000000..097db4cd5 --- /dev/null +++ b/tests/remote/httpproxy.c @@ -0,0 +1,139 @@ +#include "clar_libgit2.h" +#include "remote.h" +#include "net.h" + +static git_repository *repo; +static git_net_url url = GIT_NET_URL_INIT; + +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; + +void test_remote_httpproxy__initialize(void) +{ + git_remote *remote; + + repo = cl_git_sandbox_init("testrepo"); + cl_git_pass(git_remote_create(&remote, repo, "lg2", "https://github.com/libgit2/libgit2")); + cl_git_pass(git_net_url_parse(&url, "https://github.com/libgit2/libgit2")); + + git_remote_free(remote); + + orig_proxies_need_reset = 0; +} + +void test_remote_httpproxy__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_net_url_dispose(&url); + cl_git_sandbox_cleanup(); +} + +void assert_proxy_is(const char *expected) +{ + git_remote *remote; + char *proxy; + + cl_git_pass(git_remote_lookup(&remote, repo, "lg2")); + cl_git_pass(git_remote__http_proxy(&proxy, remote, &url)); + + if (expected) + cl_assert_equal_s(proxy, expected); + else + cl_assert_equal_p(proxy, expected); + + git_remote_free(remote); + git__free(proxy); +} + +void assert_config_match(const char *config, const char *expected) +{ + git_remote *remote; + char *proxy; + + if (config) + cl_repo_set_string(repo, config, expected); + + cl_git_pass(git_remote_lookup(&remote, repo, "lg2")); + cl_git_pass(git_remote__http_proxy(&proxy, remote, &url)); + + if (expected) + cl_assert_equal_s(proxy, expected); + else + cl_assert_equal_p(proxy, expected); + + git_remote_free(remote); + git__free(proxy); +} + +void test_remote_httpproxy__config_overrides(void) +{ + /* + * http.proxy should be honored, then http.<url>.proxy should + * be honored in increasing specificity of the url. finally, + * remote.<name>.proxy is the most specific. + */ + assert_config_match(NULL, NULL); + assert_config_match("http.proxy", "http://localhost:1/"); + assert_config_match("http.https://github.com.proxy", "http://localhost:2/"); + assert_config_match("http.https://github.com/.proxy", "http://localhost:3/"); + assert_config_match("http.https://github.com/libgit2.proxy", "http://localhost:4/"); + assert_config_match("http.https://github.com/libgit2/.proxy", "http://localhost:5/"); + assert_config_match("http.https://github.com/libgit2/libgit2.proxy", "http://localhost:6/"); + assert_config_match("remote.lg2.proxy", "http://localhost:7/"); +} + +void test_remote_httpproxy__config_empty_overrides(void) +{ + /* + * with greater specificity, an empty config entry overrides + * a set one + */ + assert_config_match("http.proxy", "http://localhost:1/"); + assert_config_match("http.https://github.com.proxy", ""); + assert_config_match("http.https://github.com/libgit2/libgit2.proxy", "http://localhost:2/"); + assert_config_match("remote.lg2.proxy", ""); +} + +void test_remote_httpproxy__env(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; + + /* HTTP proxy is ignored for HTTPS */ + cl_setenv("HTTP_PROXY", "http://localhost:9/"); + assert_proxy_is(NULL); + + /* HTTPS proxy is honored for HTTPS */ + cl_setenv("HTTPS_PROXY", "http://localhost:10/"); + assert_proxy_is("http://localhost:10/"); + + /* NO_PROXY is honored */ + cl_setenv("NO_PROXY", "github.com:443"); + assert_proxy_is(NULL); + + cl_setenv("NO_PROXY", "github.com:80"); + assert_proxy_is("http://localhost:10/"); + + cl_setenv("NO_PROXY", "github.com"); + assert_proxy_is(NULL); + + cl_setenv("NO_PROXY", "github.dev,github.com,github.foo"); + assert_proxy_is(NULL); + + /* configuration overrides environment variables */ + cl_setenv("NO_PROXY", "github.none"); + assert_config_match("http.https://github.com.proxy", "http://localhost:11/"); +} |