Skip to content

Commit 002b2ff

Browse files
authored
Merge pull request libgit2#6026 from libgit2/ethomson/proxy
Update proxy configuration
2 parents d56b407 + 3c0f14c commit 002b2ff

File tree

14 files changed

+565
-107
lines changed

14 files changed

+565
-107
lines changed

src/net.c

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,46 @@ static const char *default_port_for_scheme(const char *scheme)
3535
return NULL;
3636
}
3737

38+
int git_net_url_dup(git_net_url *out, git_net_url *in)
39+
{
40+
if (in->scheme) {
41+
out->scheme = git__strdup(in->scheme);
42+
GIT_ERROR_CHECK_ALLOC(out->scheme);
43+
}
44+
45+
if (in->host) {
46+
out->host = git__strdup(in->host);
47+
GIT_ERROR_CHECK_ALLOC(out->host);
48+
}
49+
50+
if (in->port) {
51+
out->port = git__strdup(in->port);
52+
GIT_ERROR_CHECK_ALLOC(out->port);
53+
}
54+
55+
if (in->path) {
56+
out->path = git__strdup(in->path);
57+
GIT_ERROR_CHECK_ALLOC(out->path);
58+
}
59+
60+
if (in->query) {
61+
out->query = git__strdup(in->query);
62+
GIT_ERROR_CHECK_ALLOC(out->query);
63+
}
64+
65+
if (in->username) {
66+
out->username = git__strdup(in->username);
67+
GIT_ERROR_CHECK_ALLOC(out->username);
68+
}
69+
70+
if (in->password) {
71+
out->password = git__strdup(in->password);
72+
GIT_ERROR_CHECK_ALLOC(out->password);
73+
}
74+
75+
return 0;
76+
}
77+
3878
int git_net_url_parse(git_net_url *url, const char *given)
3979
{
4080
struct http_parser_url u = {0};
@@ -404,6 +444,80 @@ int git_net_url_fmt_path(git_buf *buf, git_net_url *url)
404444
return git_buf_oom(buf) ? -1 : 0;
405445
}
406446

447+
static bool matches_pattern(
448+
git_net_url *url,
449+
const char *pattern,
450+
size_t pattern_len)
451+
{
452+
const char *domain, *port = NULL, *colon;
453+
size_t host_len, domain_len, port_len = 0, wildcard = 0;
454+
455+
GIT_UNUSED(url);
456+
GIT_UNUSED(pattern);
457+
458+
if (!pattern_len)
459+
return false;
460+
else if (pattern_len == 1 && pattern[0] == '*')
461+
return true;
462+
else if (pattern_len > 1 && pattern[0] == '*' && pattern[1] == '.')
463+
wildcard = 2;
464+
else if (pattern[0] == '.')
465+
wildcard = 1;
466+
467+
domain = pattern + wildcard;
468+
domain_len = pattern_len - wildcard;
469+
470+
if ((colon = memchr(domain, ':', domain_len)) != NULL) {
471+
domain_len = colon - domain;
472+
port = colon + 1;
473+
port_len = pattern_len - wildcard - domain_len - 1;
474+
}
475+
476+
/* A pattern's port *must* match if it's specified */
477+
if (port_len && git__strlcmp(url->port, port, port_len) != 0)
478+
return false;
479+
480+
/* No wildcard? Host must match exactly. */
481+
if (!wildcard)
482+
return !git__strlcmp(url->host, domain, domain_len);
483+
484+
/* Wildcard: ensure there's (at least) a suffix match */
485+
if ((host_len = strlen(url->host)) < domain_len ||
486+
memcmp(url->host + (host_len - domain_len), domain, domain_len))
487+
return false;
488+
489+
/* The pattern is *.domain and the host is simply domain */
490+
if (host_len == domain_len)
491+
return true;
492+
493+
/* The pattern is *.domain and the host is foo.domain */
494+
return (url->host[host_len - domain_len - 1] == '.');
495+
}
496+
497+
bool git_net_url_matches_pattern(git_net_url *url, const char *pattern)
498+
{
499+
return matches_pattern(url, pattern, strlen(pattern));
500+
}
501+
502+
bool git_net_url_matches_pattern_list(
503+
git_net_url *url,
504+
const char *pattern_list)
505+
{
506+
const char *pattern, *pattern_end, *sep;
507+
508+
for (pattern = pattern_list;
509+
pattern && *pattern;
510+
pattern = sep ? sep + 1 : NULL) {
511+
sep = strchr(pattern, ',');
512+
pattern_end = sep ? sep : strchr(pattern, '\0');
513+
514+
if (matches_pattern(url, pattern, (pattern_end - pattern)))
515+
return true;
516+
}
517+
518+
return false;
519+
}
520+
407521
void git_net_url_dispose(git_net_url *url)
408522
{
409523
if (url->username)

src/net.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ typedef struct git_net_url {
2121

2222
#define GIT_NET_URL_INIT { NULL }
2323

24+
/** Duplicate a URL */
25+
extern int git_net_url_dup(git_net_url *out, git_net_url *in);
26+
2427
/** Parses a string containing a URL into a structure. */
2528
extern int git_net_url_parse(git_net_url *url, const char *str);
2629

@@ -54,6 +57,14 @@ extern int git_net_url_fmt(git_buf *out, git_net_url *url);
5457
/** Place the path and query string into the given buffer. */
5558
extern int git_net_url_fmt_path(git_buf *buf, git_net_url *url);
5659

60+
/** Determines if the url matches given pattern or pattern list */
61+
extern bool git_net_url_matches_pattern(
62+
git_net_url *url,
63+
const char *pattern);
64+
extern bool git_net_url_matches_pattern_list(
65+
git_net_url *url,
66+
const char *pattern_list);
67+
5768
/** Disposes the contents of the structure. */
5869
extern void git_net_url_dispose(git_net_url *url);
5970

src/remote.c

Lines changed: 107 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -849,75 +849,140 @@ int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote
849849
return remote->transport->ls(out, size, remote->transport);
850850
}
851851

852-
int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url)
852+
static int lookup_config(char **out, git_config *cfg, const char *name)
853853
{
854-
git_config *cfg;
855854
git_config_entry *ce = NULL;
856-
git_buf val = GIT_BUF_INIT;
857855
int error;
858856

859-
GIT_ASSERT_ARG(remote);
857+
if ((error = git_config__lookup_entry(&ce, cfg, name, false)) < 0)
858+
return error;
860859

861-
if (!proxy_url || !remote->repo)
862-
return -1;
860+
if (ce && ce->value) {
861+
*out = git__strdup(ce->value);
862+
GIT_ERROR_CHECK_ALLOC(*out);
863+
} else {
864+
error = GIT_ENOTFOUND;
865+
}
866+
867+
git_config_entry_free(ce);
868+
return error;
869+
}
863870

864-
*proxy_url = NULL;
871+
static void url_config_trim(git_net_url *url)
872+
{
873+
size_t len = strlen(url->path);
865874

866-
if ((error = git_repository_config__weakptr(&cfg, remote->repo)) < 0)
867-
return error;
875+
if (url->path[len - 1] == '/') {
876+
len--;
877+
} else {
878+
while (len && url->path[len - 1] != '/')
879+
len--;
880+
}
868881

869-
/* Go through the possible sources for proxy configuration, from most specific
870-
* to least specific. */
882+
url->path[len] = '\0';
883+
}
884+
885+
static int http_proxy_config(char **out, git_remote *remote, git_net_url *url)
886+
{
887+
git_config *cfg;
888+
git_buf buf = GIT_BUF_INIT;
889+
git_net_url lookup_url = GIT_NET_URL_INIT;
890+
int error;
891+
892+
if ((error = git_net_url_dup(&lookup_url, url)) < 0 ||
893+
(error = git_repository_config__weakptr(&cfg, remote->repo)) < 0)
894+
goto done;
871895

872896
/* remote.<name>.proxy config setting */
873897
if (remote->name && remote->name[0]) {
874-
git_buf buf = GIT_BUF_INIT;
898+
git_buf_clear(&buf);
875899

876-
if ((error = git_buf_printf(&buf, "remote.%s.proxy", remote->name)) < 0)
877-
return error;
900+
if ((error = git_buf_printf(&buf, "remote.%s.proxy", remote->name)) < 0 ||
901+
(error = lookup_config(out, cfg, buf.ptr)) != GIT_ENOTFOUND)
902+
goto done;
903+
}
878904

879-
error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false);
880-
git_buf_dispose(&buf);
905+
while (true) {
906+
git_buf_clear(&buf);
881907

882-
if (error < 0)
883-
return error;
908+
if ((error = git_buf_puts(&buf, "http.")) < 0 ||
909+
(error = git_net_url_fmt(&buf, &lookup_url)) < 0 ||
910+
(error = git_buf_puts(&buf, ".proxy")) < 0 ||
911+
(error = lookup_config(out, cfg, buf.ptr)) != GIT_ENOTFOUND)
912+
goto done;
884913

885-
if (ce && ce->value) {
886-
*proxy_url = git__strdup(ce->value);
887-
goto found;
888-
}
914+
if (! lookup_url.path[0])
915+
break;
916+
917+
url_config_trim(&lookup_url);
889918
}
890919

891-
/* http.proxy config setting */
892-
if ((error = git_config__lookup_entry(&ce, cfg, "http.proxy", false)) < 0)
893-
return error;
920+
git_buf_clear(&buf);
894921

895-
if (ce && ce->value) {
896-
*proxy_url = git__strdup(ce->value);
897-
goto found;
898-
}
922+
error = lookup_config(out, cfg, "http.proxy");
923+
924+
done:
925+
git_buf_dispose(&buf);
926+
git_net_url_dispose(&lookup_url);
927+
return error;
928+
}
929+
930+
static int http_proxy_env(char **out, git_remote *remote, git_net_url *url)
931+
{
932+
git_buf proxy_env = GIT_BUF_INIT, no_proxy_env = GIT_BUF_INIT;
933+
bool use_ssl = (strcmp(url->scheme, "https") == 0);
934+
int error;
935+
936+
GIT_UNUSED(remote);
899937

900938
/* http_proxy / https_proxy environment variables */
901-
error = git__getenv(&val, use_ssl ? "https_proxy" : "http_proxy");
939+
error = git__getenv(&proxy_env, use_ssl ? "https_proxy" : "http_proxy");
902940

903941
/* try uppercase environment variables */
904942
if (error == GIT_ENOTFOUND)
905-
error = git__getenv(&val, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY");
943+
error = git__getenv(&proxy_env, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY");
906944

907-
if (error < 0) {
908-
if (error == GIT_ENOTFOUND) {
909-
git_error_clear();
910-
error = 0;
911-
}
945+
if (error)
946+
goto done;
912947

913-
return error;
914-
}
948+
/* no_proxy/NO_PROXY environment variables */
949+
error = git__getenv(&no_proxy_env, "no_proxy");
915950

916-
*proxy_url = git_buf_detach(&val);
951+
if (error == GIT_ENOTFOUND)
952+
error = git__getenv(&no_proxy_env, "NO_PROXY");
917953

918-
found:
919-
GIT_ERROR_CHECK_ALLOC(*proxy_url);
920-
git_config_entry_free(ce);
954+
if (error && error != GIT_ENOTFOUND)
955+
goto done;
956+
957+
if (!git_net_url_matches_pattern_list(url, no_proxy_env.ptr))
958+
*out = git_buf_detach(&proxy_env);
959+
else
960+
error = GIT_ENOTFOUND;
961+
962+
done:
963+
git_buf_dispose(&proxy_env);
964+
git_buf_dispose(&no_proxy_env);
965+
return error;
966+
}
967+
968+
int git_remote__http_proxy(char **out, git_remote *remote, git_net_url *url)
969+
{
970+
int error;
971+
972+
GIT_ASSERT_ARG(out);
973+
GIT_ASSERT_ARG(remote);
974+
GIT_ASSERT_ARG(remote->repo);
975+
976+
*out = NULL;
977+
978+
/*
979+
* Go through the possible sources for proxy configuration,
980+
* Examine the various git config options first, then
981+
* consult environment variables.
982+
*/
983+
if ((error = http_proxy_config(out, remote, url)) != GIT_ENOTFOUND ||
984+
(error = http_proxy_env(out, remote, url)) != GIT_ENOTFOUND)
985+
return error;
921986

922987
return 0;
923988
}

src/remote.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include "refspec.h"
1717
#include "vector.h"
18+
#include "net.h"
1819

1920
#define GIT_REMOTE_ORIGIN "origin"
2021

@@ -46,7 +47,7 @@ typedef struct git_remote_connection_opts {
4647
int git_remote__connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_remote_connection_opts *conn);
4748

4849
int git_remote__urlfordirection(git_buf *url_out, struct git_remote *remote, int direction, const git_remote_callbacks *callbacks);
49-
int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url);
50+
int git_remote__http_proxy(char **out, git_remote *remote, git_net_url *url);
5051

5152
git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname);
5253
git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname);

src/transports/http.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,6 @@ static int lookup_proxy(
290290
{
291291
const char *proxy;
292292
git_remote *remote;
293-
bool use_ssl;
294293
char *config = NULL;
295294
int error = 0;
296295

@@ -304,9 +303,8 @@ static int lookup_proxy(
304303

305304
case GIT_PROXY_AUTO:
306305
remote = transport->owner->owner;
307-
use_ssl = !strcmp(transport->server.url.scheme, "https");
308306

309-
error = git_remote__get_http_proxy(remote, use_ssl, &config);
307+
error = git_remote__http_proxy(&config, remote, &transport->server.url);
310308

311309
if (error || !config)
312310
goto done;

src/transports/winhttp.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
429429
proxy_opts = &t->owner->proxy;
430430
if (proxy_opts->type == GIT_PROXY_AUTO) {
431431
/* Set proxy if necessary */
432-
if (git_remote__get_http_proxy(t->owner->owner, (strcmp(t->server.url.scheme, "https") == 0), &proxy_url) < 0)
432+
if (git_remote__http_proxy(&proxy_url, t->owner->owner, &t->server.url) < 0)
433433
goto on_error;
434434
}
435435
else if (proxy_opts->type == GIT_PROXY_SPECIFIED) {
@@ -742,7 +742,7 @@ static void CALLBACK winhttp_status(
742742
git_error_set(GIT_ERROR_HTTP, "unknown security error %lu", status);
743743

744744
break;
745-
745+
746746
case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST:
747747
((winhttp_stream *) ctx)->status_sending_request_reached = 1;
748748

0 commit comments

Comments
 (0)