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 /src | |
parent | d56b4079e0474b2f96abe0487a44359970e43c2a (diff) | |
parent | 3c0f14cc95debb426bd53150aac0eef1a7f625d8 (diff) | |
download | libgit2-002b2ffe69d9d17e5b5570c5cd3cc13bac4b004c.tar.gz |
Merge pull request #6026 from libgit2/ethomson/proxy
Update proxy configuration
Diffstat (limited to 'src')
-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 |
7 files changed, 248 insertions, 48 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; |