summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@github.com>2016-04-19 19:48:52 -0400
committerEdward Thomson <ethomson@github.com>2016-04-19 19:48:52 -0400
commit1dc449105b329ea4f8ea9982bc2da869d231c04a (patch)
tree3dede94adc9297468b3f5b2294b300b028a0b34f /src
parent95fbc81dafd64400d51637a27ecd49de5ea63145 (diff)
parent2638df771172a18dc5da89f039076fcc05ceb4ac (diff)
downloadlibgit2-1dc449105b329ea4f8ea9982bc2da869d231c04a.tar.gz
Merge pull request #3110 from libgit2/cmn/proxy-config
Proxy configuration
Diffstat (limited to 'src')
-rw-r--r--src/curl_stream.c95
-rw-r--r--src/netops.c22
-rw-r--r--src/openssl_stream.c4
-rw-r--r--src/proxy.c32
-rw-r--r--src/proxy.h14
-rw-r--r--src/push.c2
-rw-r--r--src/remote.c26
-rw-r--r--src/stream.h4
-rw-r--r--src/transports/http.c41
-rw-r--r--src/transports/local.c5
-rw-r--r--src/transports/smart.c6
-rw-r--r--src/transports/smart.h1
-rw-r--r--src/transports/winhttp.c141
13 files changed, 345 insertions, 48 deletions
diff --git a/src/curl_stream.c b/src/curl_stream.c
index 9963d94cc..98de187dd 100644
--- a/src/curl_stream.c
+++ b/src/curl_stream.c
@@ -13,6 +13,7 @@
#include "git2/transport.h"
#include "buffer.h"
#include "vector.h"
+#include "proxy.h"
typedef struct {
git_stream parent;
@@ -21,6 +22,8 @@ typedef struct {
char curl_error[CURL_ERROR_SIZE + 1];
git_cert_x509 cert_info;
git_strarray cert_info_strings;
+ git_proxy_options proxy;
+ git_cred *proxy_cred;
} curl_stream;
static int seterr_curl(curl_stream *s)
@@ -29,21 +32,94 @@ static int seterr_curl(curl_stream *s)
return -1;
}
+GIT_INLINE(int) error_no_credentials(void)
+{
+ giterr_set(GITERR_NET, "proxy authentication required, but no callback provided");
+ return GIT_EAUTH;
+}
+
+static int apply_proxy_creds(curl_stream *s)
+{
+ CURLcode res;
+ git_cred_userpass_plaintext *userpass;
+
+ if (!s->proxy_cred)
+ return GIT_ENOTFOUND;
+
+ userpass = (git_cred_userpass_plaintext *) s->proxy_cred;
+ if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYUSERNAME, userpass->username)) != CURLE_OK)
+ return seterr_curl(s);
+ if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYPASSWORD, userpass->password)) != CURLE_OK)
+ return seterr_curl(s);
+
+ return 0;
+}
+
+static int ask_and_apply_proxy_creds(curl_stream *s)
+{
+ int error;
+ git_proxy_options *opts = &s->proxy;
+
+ if (!opts->credentials)
+ return error_no_credentials();
+
+ /* TODO: see if PROXYAUTH_AVAIL helps us here */
+ git_cred_free(s->proxy_cred);
+ s->proxy_cred = NULL;
+ giterr_clear();
+ error = opts->credentials(&s->proxy_cred, opts->url, NULL, GIT_CREDTYPE_USERPASS_PLAINTEXT, opts->payload);
+ if (error == GIT_PASSTHROUGH)
+ return error_no_credentials();
+ if (error < 0) {
+ if (!giterr_last())
+ giterr_set(GITERR_NET, "proxy authentication was aborted by the user");
+ return error;
+ }
+
+ if (s->proxy_cred->credtype != GIT_CREDTYPE_USERPASS_PLAINTEXT) {
+ giterr_set(GITERR_NET, "credentials callback returned invalid credential type");
+ return -1;
+ }
+
+ return apply_proxy_creds(s);
+}
+
static int curls_connect(git_stream *stream)
{
curl_stream *s = (curl_stream *) stream;
- long sockextr;
- int failed_cert = 0;
+ long sockextr, connect_last = 0;
+ int failed_cert = 0, error;
+ bool retry_connect;
CURLcode res;
- res = curl_easy_perform(s->handle);
+
+ /* Apply any credentials we've already established */
+ error = apply_proxy_creds(s);
+ if (error < 0 && error != GIT_ENOTFOUND)
+ return seterr_curl(s);
+
+ do {
+ retry_connect = 0;
+ res = curl_easy_perform(s->handle);
+
+ curl_easy_getinfo(s->handle, CURLINFO_HTTP_CONNECTCODE, &connect_last);
+
+ /* HTTP 407 Proxy Authentication Required */
+ if (connect_last == 407) {
+ if ((error = ask_and_apply_proxy_creds(s)) < 0)
+ return error;
+
+ retry_connect = true;
+ }
+ } while (retry_connect);
if (res != CURLE_OK && res != CURLE_PEER_FAILED_VERIFICATION)
return seterr_curl(s);
if (res == CURLE_PEER_FAILED_VERIFICATION)
failed_cert = 1;
- if ((res = curl_easy_getinfo(s->handle, CURLINFO_LASTSOCKET, &sockextr)) != CURLE_OK)
+ if ((res = curl_easy_getinfo(s->handle, CURLINFO_LASTSOCKET, &sockextr)) != CURLE_OK) {
return seterr_curl(s);
+ }
s->socket = sockextr;
@@ -95,12 +171,19 @@ static int curls_certificate(git_cert **out, git_stream *stream)
return 0;
}
-static int curls_set_proxy(git_stream *stream, const char *proxy_url)
+static int curls_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts)
{
+ int error;
CURLcode res;
curl_stream *s = (curl_stream *) stream;
- if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXY, proxy_url)) != CURLE_OK)
+ if ((error = git_proxy_options_dup(&s->proxy, proxy_opts)) < 0)
+ return error;
+
+ if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXY, s->proxy.url)) != CURLE_OK)
+ return seterr_curl(s);
+
+ if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY)) != CURLE_OK)
return seterr_curl(s);
return 0;
diff --git a/src/netops.c b/src/netops.c
index c4241989f..90326ea59 100644
--- a/src/netops.c
+++ b/src/netops.c
@@ -257,16 +257,18 @@ int gitno_extract_url_parts(
*port = git__strdup(default_port);
GITERR_CHECK_ALLOC(*port);
- if (u.field_set & (1 << UF_PATH)) {
- *path = git__substrdup(_path, u.field_data[UF_PATH].len);
- GITERR_CHECK_ALLOC(*path);
- } else {
- git__free(*port);
- *port = NULL;
- git__free(*host);
- *host = NULL;
- giterr_set(GITERR_NET, "invalid url, missing path");
- return GIT_EINVALIDSPEC;
+ if (path) {
+ if (u.field_set & (1 << UF_PATH)) {
+ *path = git__substrdup(_path, u.field_data[UF_PATH].len);
+ GITERR_CHECK_ALLOC(*path);
+ } else {
+ git__free(*port);
+ *port = NULL;
+ git__free(*host);
+ *host = NULL;
+ giterr_set(GITERR_NET, "invalid url, missing path");
+ return GIT_EINVALIDSPEC;
+ }
}
if (u.field_set & (1 << UF_USERINFO)) {
diff --git a/src/openssl_stream.c b/src/openssl_stream.c
index a65f5586e..edea8fef7 100644
--- a/src/openssl_stream.c
+++ b/src/openssl_stream.c
@@ -496,11 +496,11 @@ int openssl_certificate(git_cert **out, git_stream *stream)
return 0;
}
-static int openssl_set_proxy(git_stream *stream, const char *proxy_url)
+static int openssl_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts)
{
openssl_stream *st = (openssl_stream *) stream;
- return git_stream_set_proxy(st->io, proxy_url);
+ return git_stream_set_proxy(st->io, proxy_opts);
}
ssize_t openssl_write(git_stream *stream, const char *data, size_t len, int flags)
diff --git a/src/proxy.c b/src/proxy.c
new file mode 100644
index 000000000..f53ac1151
--- /dev/null
+++ b/src/proxy.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "git2/proxy.h"
+
+int git_proxy_init_options(git_proxy_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_proxy_options, GIT_PROXY_OPTIONS_INIT);
+ return 0;
+}
+
+int git_proxy_options_dup(git_proxy_options *tgt, const git_proxy_options *src)
+{
+ if (!src) {
+ git_proxy_init_options(tgt, GIT_PROXY_OPTIONS_VERSION);
+ return 0;
+ }
+
+ memcpy(tgt, src, sizeof(git_proxy_options));
+ if (src->url) {
+ tgt->url = git__strdup(src->url);
+ GITERR_CHECK_ALLOC(tgt->url);
+ }
+
+ return 0;
+}
diff --git a/src/proxy.h b/src/proxy.h
new file mode 100644
index 000000000..bf9382737
--- /dev/null
+++ b/src/proxy.h
@@ -0,0 +1,14 @@
+/*
+* Copyright (C) the libgit2 contributors. All rights reserved.
+*
+* This file is part of libgit2, distributed under the GNU GPL v2 with
+* a Linking Exception. For full terms see the included COPYING file.
+*/
+#ifndef INCLUDE_proxy_h__
+#define INCLUDE_proxy_h__
+
+#include "git2/proxy.h"
+
+extern int git_proxy_options_dup(git_proxy_options *tgt, const git_proxy_options *src);
+
+#endif \ No newline at end of file
diff --git a/src/push.c b/src/push.c
index 0747259c8..b4901388b 100644
--- a/src/push.c
+++ b/src/push.c
@@ -639,7 +639,7 @@ int git_push_finish(git_push *push, const git_remote_callbacks *callbacks)
int error;
if (!git_remote_connected(push->remote) &&
- (error = git_remote_connect(push->remote, GIT_DIRECTION_PUSH, callbacks, push->custom_headers)) < 0)
+ (error = git_remote_connect(push->remote, GIT_DIRECTION_PUSH, callbacks, NULL, push->custom_headers)) < 0)
return error;
if ((error = filter_refs(push->remote)) < 0 ||
diff --git a/src/remote.c b/src/remote.c
index 8b7203ee2..5ff7f6826 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -695,7 +695,7 @@ static int set_transport_custom_headers(git_transport *t, const git_strarray *cu
return t->set_custom_headers(t, custom_headers);
}
-int git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_strarray *custom_headers)
+int git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_proxy_options *proxy, const git_strarray *custom_headers)
{
git_transport *t;
const char *url;
@@ -714,6 +714,9 @@ int git_remote_connect(git_remote *remote, git_direction direction, const git_re
payload = callbacks->payload;
}
+ if (proxy)
+ GITERR_CHECK_VERSION(proxy, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
+
t = remote->transport;
url = git_remote__urlfordirection(remote, direction);
@@ -738,7 +741,7 @@ int git_remote_connect(git_remote *remote, git_direction direction, const git_re
goto on_error;
if ((error = set_transport_callbacks(t, callbacks)) < 0 ||
- (error = t->connect(t, url, credentials, payload, direction, flags)) != 0)
+ (error = t->connect(t, url, credentials, payload, proxy, direction, flags)) != 0)
goto on_error;
remote->transport = t;
@@ -896,6 +899,7 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs, const
git_vector *to_active, specs = GIT_VECTOR_INIT, refs = GIT_VECTOR_INIT;
const git_remote_callbacks *cbs = NULL;
const git_strarray *custom_headers = NULL;
+ const git_proxy_options *proxy = NULL;
assert(remote);
@@ -903,10 +907,12 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs, const
GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
cbs = &opts->callbacks;
custom_headers = &opts->custom_headers;
+ GITERR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
+ proxy = &opts->proxy_opts;
}
if (!git_remote_connected(remote) &&
- (error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, custom_headers)) < 0)
+ (error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, proxy, custom_headers)) < 0)
goto on_error;
if (ls_to_vector(&refs, remote) < 0)
@@ -971,6 +977,7 @@ int git_remote_fetch(
git_buf reflog_msg_buf = GIT_BUF_INIT;
const git_remote_callbacks *cbs = NULL;
const git_strarray *custom_headers = NULL;
+ const git_proxy_options *proxy = NULL;
if (opts) {
GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
@@ -978,10 +985,12 @@ int git_remote_fetch(
custom_headers = &opts->custom_headers;
update_fetchhead = opts->update_fetchhead;
tagopt = opts->download_tags;
+ GITERR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
+ proxy = &opts->proxy_opts;
}
/* Connect and download everything */
- if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, custom_headers)) != 0)
+ if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, proxy, custom_headers)) != 0)
return error;
error = git_remote_download(remote, refspecs, opts);
@@ -2393,16 +2402,18 @@ int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const gi
git_refspec *spec;
const git_remote_callbacks *cbs = NULL;
const git_strarray *custom_headers = NULL;
+ const git_proxy_options *proxy = NULL;
assert(remote);
if (opts) {
cbs = &opts->callbacks;
custom_headers = &opts->custom_headers;
+ proxy = &opts->proxy_opts;
}
if (!git_remote_connected(remote) &&
- (error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, custom_headers)) < 0)
+ (error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, proxy, custom_headers)) < 0)
goto cleanup;
free_refspecs(&remote->active_refspecs);
@@ -2452,16 +2463,19 @@ int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_
int error;
const git_remote_callbacks *cbs = NULL;
const git_strarray *custom_headers = NULL;
+ const git_proxy_options *proxy = NULL;
if (opts) {
GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
cbs = &opts->callbacks;
custom_headers = &opts->custom_headers;
+ GITERR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
+ proxy = &opts->proxy_opts;
}
assert(remote && refspecs);
- if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, custom_headers)) < 0)
+ if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, proxy, custom_headers)) < 0)
return error;
if ((error = git_remote_upload(remote, refspecs, opts)) < 0)
diff --git a/src/stream.h b/src/stream.h
index 4692c7115..d35477591 100644
--- a/src/stream.h
+++ b/src/stream.h
@@ -35,14 +35,14 @@ GIT_INLINE(int) git_stream_supports_proxy(git_stream *st)
return st->proxy_support;
}
-GIT_INLINE(int) git_stream_set_proxy(git_stream *st, const char *proxy_url)
+GIT_INLINE(int) git_stream_set_proxy(git_stream *st, const git_proxy_options *proxy_opts)
{
if (!st->proxy_support) {
giterr_set(GITERR_INVALID, "proxy not supported on this stream");
return -1;
}
- return st->set_proxy(st, proxy_url);
+ return st->set_proxy(st, proxy_opts);
}
GIT_INLINE(ssize_t) git_stream_read(git_stream *st, void *data, size_t len)
diff --git a/src/transports/http.c b/src/transports/http.c
index 88b124bf7..7bb3374a0 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -555,10 +555,40 @@ static int write_chunk(git_stream *io, const char *buffer, size_t len)
return 0;
}
+static int apply_proxy_config(http_subtransport *t)
+{
+ int error;
+ git_proxy_t proxy_type;
+
+ if (!git_stream_supports_proxy(t->io))
+ return 0;
+
+ proxy_type = t->owner->proxy.type;
+
+ if (proxy_type == GIT_PROXY_NONE)
+ return 0;
+
+ if (proxy_type == GIT_PROXY_AUTO) {
+ char *url;
+ git_proxy_options opts = GIT_PROXY_OPTIONS_INIT;
+
+ if ((error = git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &url)) < 0)
+ return error;
+
+ opts.type = GIT_PROXY_SPECIFIED;
+ opts.url = url;
+ error = git_stream_set_proxy(t->io, &opts);
+ git__free(url);
+
+ return error;
+ }
+
+ return git_stream_set_proxy(t->io, &t->owner->proxy);
+}
+
static int http_connect(http_subtransport *t)
{
int error;
- char *proxy_url;
if (t->connected &&
http_should_keep_alive(&t->parser) &&
@@ -586,14 +616,7 @@ static int http_connect(http_subtransport *t)
GITERR_CHECK_VERSION(t->io, GIT_STREAM_VERSION, "git_stream");
- if (git_stream_supports_proxy(t->io) &&
- !git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url)) {
- error = git_stream_set_proxy(t->io, proxy_url);
- git__free(proxy_url);
-
- if (error < 0)
- return error;
- }
+ apply_proxy_config(t);
error = git_stream_connect(t->io);
diff --git a/src/transports/local.c b/src/transports/local.c
index 1c6e5f01e..4eae9dead 100644
--- a/src/transports/local.c
+++ b/src/transports/local.c
@@ -25,6 +25,7 @@
#include "odb.h"
#include "push.h"
#include "remote.h"
+#include "proxy.h"
typedef struct {
git_transport parent;
@@ -199,6 +200,7 @@ static int local_connect(
const char *url,
git_cred_acquire_cb cred_acquire_cb,
void *cred_acquire_payload,
+ const git_proxy_options *proxy,
int direction, int flags)
{
git_repository *repo;
@@ -209,6 +211,7 @@ static int local_connect(
GIT_UNUSED(cred_acquire_cb);
GIT_UNUSED(cred_acquire_payload);
+ GIT_UNUSED(proxy);
if (t->connected)
return 0;
@@ -439,7 +442,7 @@ static int local_push(
if (!url || t->parent.close(&t->parent) < 0 ||
t->parent.connect(&t->parent, url,
- NULL, NULL, GIT_DIRECTION_PUSH, flags))
+ NULL, NULL, NULL, GIT_DIRECTION_PUSH, flags))
goto on_error;
}
diff --git a/src/transports/smart.c b/src/transports/smart.c
index b0611c35e..11b4b09a4 100644
--- a/src/transports/smart.c
+++ b/src/transports/smart.c
@@ -8,6 +8,7 @@
#include "smart.h"
#include "refs.h"
#include "refspec.h"
+#include "proxy.h"
static int git_smart__recv_cb(gitno_buffer *buf)
{
@@ -199,6 +200,7 @@ static int git_smart__connect(
const char *url,
git_cred_acquire_cb cred_acquire_cb,
void *cred_acquire_payload,
+ const git_proxy_options *proxy,
int direction,
int flags)
{
@@ -216,6 +218,9 @@ static int git_smart__connect(
t->url = git__strdup(url);
GITERR_CHECK_ALLOC(t->url);
+ if (git_proxy_options_dup(&t->proxy, proxy) < 0)
+ return -1;
+
t->direction = direction;
t->flags = flags;
t->cred_acquire_cb = cred_acquire_cb;
@@ -439,6 +444,7 @@ static void git_smart__free(git_transport *transport)
git_pkt_free(p);
git_vector_free(refs);
+ git__free(t->proxy.url);
git_strarray_free(&t->custom_headers);
diff --git a/src/transports/smart.h b/src/transports/smart.h
index 800466adf..0a0c3fc1b 100644
--- a/src/transports/smart.h
+++ b/src/transports/smart.h
@@ -133,6 +133,7 @@ typedef struct {
char *url;
git_cred_acquire_cb cred_acquire_cb;
void *cred_acquire_payload;
+ git_proxy_options proxy;
int direction;
int flags;
git_transport_message_cb progress_cb;
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index 32b838084..580c3b91b 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -91,13 +91,39 @@ typedef struct {
git_smart_subtransport parent;
transport_smart *owner;
gitno_connection_data connection_data;
+ gitno_connection_data proxy_connection_data;
git_cred *cred;
git_cred *url_cred;
+ git_cred *proxy_cred;
int auth_mechanism;
HINTERNET session;
HINTERNET connection;
} winhttp_subtransport;
+static int apply_basic_credential_proxy(HINTERNET request, git_cred *cred)
+{
+ git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
+ wchar_t *user, *pass;
+ int error;
+
+ if ((error = git__utf8_to_16_alloc(&user, c->username)) < 0)
+ return error;
+
+ if ((error = git__utf8_to_16_alloc(&pass, c->password)) < 0)
+ return error;
+
+ if (!WinHttpSetCredentials(request, WINHTTP_AUTH_TARGET_PROXY, WINHTTP_AUTH_SCHEME_BASIC,
+ user, pass, NULL)) {
+ giterr_set(GITERR_OS, "failed to set proxy auth");
+ error = -1;
+ }
+
+ git__free(user);
+ git__free(pass);
+
+ return error;
+}
+
static int apply_basic_credential(HINTERNET request, git_cred *cred)
{
git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
@@ -271,6 +297,37 @@ static void winhttp_stream_close(winhttp_stream *s)
s->sent_request = 0;
}
+/**
+ * Extract the url and password from a URL. The outputs are pointers
+ * into the input.
+ */
+static int userpass_from_url(wchar_t **user, int *user_len,
+ wchar_t **pass, int *pass_len,
+ const wchar_t *url, int url_len)
+{
+ URL_COMPONENTS components = { 0 };
+
+ components.dwStructSize = sizeof(components);
+ /* These tell WinHttpCrackUrl that we're interested in the fields */
+ components.dwUserNameLength = 1;
+ components.dwPasswordLength = 1;
+
+ if (!WinHttpCrackUrl(url, url_len, 0, &components)) {
+ giterr_set(GITERR_OS, "failed to extract user/pass from url");
+ return -1;
+ }
+
+ *user = components.lpszUserName;
+ *user_len = components.dwUserNameLength;
+ *pass = components.lpszPassword;
+ *pass_len = components.dwPasswordLength;
+
+ return 0;
+}
+
+#define SCHEME_HTTP "http://"
+#define SCHEME_HTTPS "https://"
+
static int winhttp_stream_connect(winhttp_stream *s)
{
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
@@ -284,6 +341,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
int default_timeout = TIMEOUT_INFINITE;
int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
size_t i;
+ const git_proxy_options *proxy_opts;
/* Prepare URL */
git_buf_printf(&buf, "%s%s", t->connection_data.path, s->service_url);
@@ -317,26 +375,59 @@ static int winhttp_stream_connect(winhttp_stream *s)
goto on_error;
}
- /* Set proxy if necessary */
- if (git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url) < 0)
- goto on_error;
+ 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, !!t->connection_data.use_ssl, &proxy_url) < 0)
+ goto on_error;
+ }
+ else if (proxy_opts->type == GIT_PROXY_SPECIFIED) {
+ proxy_url = git__strdup(proxy_opts->url);
+ GITERR_CHECK_ALLOC(proxy_url);
+ }
if (proxy_url) {
+ git_buf processed_url = GIT_BUF_INIT;
WINHTTP_PROXY_INFO proxy_info;
wchar_t *proxy_wide;
- /* Convert URL to wide characters */
- int proxy_wide_len = git__utf8_to_16_alloc(&proxy_wide, proxy_url);
+ if (!git__prefixcmp(proxy_url, SCHEME_HTTP)) {
+ t->proxy_connection_data.use_ssl = false;
+ } else if (!git__prefixcmp(proxy_url, SCHEME_HTTPS)) {
+ t->proxy_connection_data.use_ssl = true;
+ } else {
+ giterr_set(GITERR_NET, "invalid URL: '%s'", proxy_url);
+ return -1;
+ }
- if (proxy_wide_len < 0) {
- giterr_set(GITERR_OS, "Failed to convert string to wide form");
+ if ((error = gitno_extract_url_parts(&t->proxy_connection_data.host, &t->proxy_connection_data.port, NULL,
+ &t->proxy_connection_data.user, &t->proxy_connection_data.pass, proxy_url, NULL)) < 0)
+ goto on_error;
+
+ if (t->proxy_connection_data.user && t->proxy_connection_data.pass) {
+ if ((error = git_cred_userpass_plaintext_new(&t->proxy_cred, t->proxy_connection_data.user, t->proxy_connection_data.pass)) < 0)
+ goto on_error;
+ }
+
+ if (t->proxy_connection_data.use_ssl)
+ git_buf_PUTS(&processed_url, SCHEME_HTTPS);
+ else
+ git_buf_PUTS(&processed_url, SCHEME_HTTP);
+
+ git_buf_puts(&processed_url, t->proxy_connection_data.host);
+ if (t->proxy_connection_data.port)
+ git_buf_printf(&processed_url, ":%s", t->proxy_connection_data.port);
+
+ if (git_buf_oom(&processed_url)) {
+ giterr_set_oom();
+ error = -1;
goto on_error;
}
- /* Strip any trailing forward slash on the proxy URL;
- * WinHTTP doesn't like it if one is present */
- if (proxy_wide_len > 1 && L'/' == proxy_wide[proxy_wide_len - 2])
- proxy_wide[proxy_wide_len - 2] = L'\0';
+ /* Convert URL to wide characters */
+ if ((error = git__utf8_to_16_alloc(&proxy_wide, processed_url.ptr)) < 0)
+ goto on_error;
+
proxy_info.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
proxy_info.lpszProxy = proxy_wide;
@@ -352,6 +443,14 @@ static int winhttp_stream_connect(winhttp_stream *s)
}
git__free(proxy_wide);
+
+ if (t->proxy_cred) {
+ if (t->proxy_cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT) {
+ if ((error = apply_basic_credential_proxy(s->request, t->proxy_cred)) < 0)
+ goto on_error;
+ }
+ }
+
}
/* Disable WinHTTP redirects so we can handle them manually. Why, you ask?
@@ -919,6 +1018,26 @@ replay:
goto replay;
}
+ /* Handle proxy authentication failures */
+ if (status_code == HTTP_STATUS_PROXY_AUTH_REQ) {
+ int allowed_types;
+
+ if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanism) < 0)
+ return -1;
+
+ /* TODO: extract the username from the url, no payload? */
+ if (t->owner->proxy.credentials) {
+ int cred_error = 1;
+ cred_error = t->owner->proxy.credentials(&t->proxy_cred, t->owner->proxy.url, NULL, allowed_types, NULL);
+
+ if (cred_error < 0)
+ return cred_error;
+ }
+
+ winhttp_stream_close(s);
+ goto replay;
+ }
+
/* Handle authentication failures */
if (HTTP_STATUS_DENIED == status_code && get_verb == s->verb) {
int allowed_types;