Commit 4a88eb20 by Ben Straub

Win32: handle http->https redirects

parent 4dbdbf64
...@@ -73,7 +73,7 @@ typedef struct { ...@@ -73,7 +73,7 @@ typedef struct {
typedef struct { typedef struct {
git_smart_subtransport parent; git_smart_subtransport parent;
transport_smart *owner; transport_smart *owner;
const char *path; char *path;
char *host; char *host;
char *port; char *port;
char *user_from_url; char *user_from_url;
...@@ -152,6 +152,7 @@ static int winhttp_stream_connect(winhttp_stream *s) ...@@ -152,6 +152,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
wchar_t *types[] = { L"*/*", NULL }; wchar_t *types[] = { L"*/*", NULL };
BOOL peerdist = FALSE; BOOL peerdist = FALSE;
int error = -1, wide_len; int error = -1, wide_len;
unsigned long disable_redirects = WINHTTP_DISABLE_REDIRECTS;
/* Prepare URL */ /* Prepare URL */
git_buf_printf(&buf, "%s%s", t->path, s->service_url); git_buf_printf(&buf, "%s%s", t->path, s->service_url);
...@@ -244,6 +245,14 @@ static int winhttp_stream_connect(winhttp_stream *s) ...@@ -244,6 +245,14 @@ static int winhttp_stream_connect(winhttp_stream *s)
git__free(proxy_wide); git__free(proxy_wide);
} }
if (!WinHttpSetOption(s->request,
WINHTTP_OPTION_DISABLE_FEATURE,
&disable_redirects,
sizeof(disable_redirects))) {
giterr_set(GITERR_OS, "Failed to disable redirects");
goto on_error;
}
/* Strip unwanted headers (X-P2P-PeerDist, X-P2P-PeerDistEx) that WinHTTP /* Strip unwanted headers (X-P2P-PeerDist, X-P2P-PeerDistEx) that WinHTTP
* adds itself. This option may not be supported by the underlying * adds itself. This option may not be supported by the underlying
* platform, so we do not error-check it */ * platform, so we do not error-check it */
...@@ -380,6 +389,142 @@ static int write_chunk(HINTERNET request, const char *buffer, size_t len) ...@@ -380,6 +389,142 @@ static int write_chunk(HINTERNET request, const char *buffer, size_t len)
return 0; return 0;
} }
static void free_connection_data(winhttp_subtransport *t)
{
if (t->host) {
git__free(t->host);
t->host = NULL;
}
if (t->port) {
git__free(t->port);
t->port = NULL;
}
if (t->user_from_url) {
git__free(t->user_from_url);
t->user_from_url = NULL;
}
if (t->pass_from_url) {
git__free(t->pass_from_url);
t->pass_from_url = NULL;
}
if (t->path) {
git__free(t->path);
t->path = NULL;
}
}
static int set_connection_data_from_url(
winhttp_subtransport *t, const char *url, const char *service_suffix)
{
int error = 0;
const char *default_port = NULL;
char *original_host = NULL;
const char *original_url = url;
if (!git__prefixcmp(url, prefix_http)) {
url += strlen(prefix_http);
default_port = "80";
if (t->use_ssl) {
giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP not allowed");
return -1;
}
}
if (!git__prefixcmp(url, prefix_https)) {
url += strlen(prefix_https);
default_port = "443";
t->use_ssl = 1;
}
if (!default_port) {
giterr_set(GITERR_NET, "Unrecognized URL prefix");
return -1;
}
/* preserve original host name for checking */
original_host = t->host;
t->host = NULL;
free_connection_data(t);
error = gitno_extract_url_parts(
&t->host, &t->port, &t->user_from_url, &t->pass_from_url,
url, default_port);
if (!error) {
const char *path = strchr(url, '/');
size_t pathlen = strlen(path);
size_t suffixlen = service_suffix ? strlen(service_suffix) : 0;
if (suffixlen &&
!memcmp(path + pathlen - suffixlen, service_suffix, suffixlen))
t->path = git__strndup(path, pathlen - suffixlen);
else
t->path = git__strdup(path);
/* Allow '/'-led urls, or a change of protocol */
if (original_host != NULL) {
if (strcmp(original_host, t->host) && original_url[0] != '/') {
giterr_set(GITERR_NET, "Cross host redirect not allowed");
error = -1;
}
git__free(original_host);
}
}
return error;
}
static int winhttp_connect(
winhttp_subtransport *t,
const char *url)
{
wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")";
git_win32_path host;
int32_t port;
const char *default_port = "80";
/* Prepare port */
if (git__strtol32(&port, t->port, NULL, 10) < 0)
return -1;
/* Prepare host */
git_win32_path_from_c(host, t->host);
/* Establish session */
t->session = WinHttpOpen(
ua,
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
0);
if (!t->session) {
giterr_set(GITERR_OS, "Failed to init WinHTTP");
return -1;
}
/* Establish connection */
t->connection = WinHttpConnect(
t->session,
host,
(INTERNET_PORT) port,
0);
if (!t->connection) {
giterr_set(GITERR_OS, "Failed to connect to host");
return -1;
}
return 0;
}
static int winhttp_stream_read( static int winhttp_stream_read(
git_smart_subtransport_stream *stream, git_smart_subtransport_stream *stream,
char *buffer, char *buffer,
...@@ -511,50 +656,52 @@ replay: ...@@ -511,50 +656,52 @@ replay:
/* Check for Windows 7. This workaround is only necessary on /* Check for Windows 7. This workaround is only necessary on
* Windows Vista and earlier. Windows 7 is version 6.1. */ * Windows Vista and earlier. Windows 7 is version 6.1. */
if (!git_has_win32_version(6, 1, 0)) { wchar_t *location;
wchar_t *location; DWORD location_length;
DWORD location_length; char *location8;
int redirect_cmp;
/* OK, fetch the Location header from the redirect. */
/* OK, fetch the Location header from the redirect. */ if (WinHttpQueryHeaders(s->request,
if (WinHttpQueryHeaders(s->request, WINHTTP_QUERY_LOCATION,
WINHTTP_QUERY_LOCATION, WINHTTP_HEADER_NAME_BY_INDEX,
WINHTTP_HEADER_NAME_BY_INDEX, WINHTTP_NO_OUTPUT_BUFFER,
WINHTTP_NO_OUTPUT_BUFFER, &location_length,
&location_length, WINHTTP_NO_HEADER_INDEX) ||
WINHTTP_NO_HEADER_INDEX) || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
GetLastError() != ERROR_INSUFFICIENT_BUFFER) { giterr_set(GITERR_OS, "Failed to read Location header");
giterr_set(GITERR_OS, "Failed to read Location header"); return -1;
return -1; }
}
location = git__malloc(location_length);
GITERR_CHECK_ALLOC(location);
if (!WinHttpQueryHeaders(s->request,
WINHTTP_QUERY_LOCATION,
WINHTTP_HEADER_NAME_BY_INDEX,
location,
&location_length,
WINHTTP_NO_HEADER_INDEX)) {
giterr_set(GITERR_OS, "Failed to read Location header");
git__free(location);
return -1;
}
/* Compare the Location header with the request URI */ location = git__malloc(location_length);
redirect_cmp = wcscmp(location, s->request_uri); location8 = git__malloc(location_length);
GITERR_CHECK_ALLOC(location);
if (!WinHttpQueryHeaders(s->request,
WINHTTP_QUERY_LOCATION,
WINHTTP_HEADER_NAME_BY_INDEX,
location,
&location_length,
WINHTTP_NO_HEADER_INDEX)) {
giterr_set(GITERR_OS, "Failed to read Location header");
git__free(location); git__free(location);
return -1;
if (!redirect_cmp) { }
/* Replay the request */ git__utf16_to_8(location8, location_length, location);
WinHttpCloseHandle(s->request); git__free(location);
s->request = NULL;
s->sent_request = 0; /* Replay the request */
WinHttpCloseHandle(s->request);
goto replay; s->request = NULL;
} s->sent_request = 0;
if (!git__prefixcmp_icase(location8, prefix_https)) {
/* Upgrade to secure connection; disconnect and start over */
set_connection_data_from_url(t, location8, s->service_url);
winhttp_connect(t, location8);
} }
git__free(location8);
goto replay;
} }
/* Handle authentication failures */ /* Handle authentication failures */
...@@ -888,68 +1035,6 @@ static int winhttp_stream_alloc(winhttp_subtransport *t, winhttp_stream **stream ...@@ -888,68 +1035,6 @@ static int winhttp_stream_alloc(winhttp_subtransport *t, winhttp_stream **stream
return 0; return 0;
} }
static int winhttp_connect(
winhttp_subtransport *t,
const char *url)
{
wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")";
git_win32_path host;
int32_t port;
const char *default_port = "80";
int ret;
if (!git__prefixcmp(url, prefix_http)) {
url = url + strlen(prefix_http);
default_port = "80";
}
if (!git__prefixcmp(url, prefix_https)) {
url += strlen(prefix_https);
default_port = "443";
t->use_ssl = 1;
}
if ((ret = gitno_extract_url_parts(&t->host, &t->port, &t->user_from_url,
&t->pass_from_url, url, default_port)) < 0)
return ret;
t->path = strchr(url, '/');
/* Prepare port */
if (git__strtol32(&port, t->port, NULL, 10) < 0)
return -1;
/* Prepare host */
git_win32_path_from_c(host, t->host);
/* Establish session */
t->session = WinHttpOpen(
ua,
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
0);
if (!t->session) {
giterr_set(GITERR_OS, "Failed to init WinHTTP");
return -1;
}
/* Establish connection */
t->connection = WinHttpConnect(
t->session,
host,
(INTERNET_PORT)port,
0);
if (!t->connection) {
giterr_set(GITERR_OS, "Failed to connect to host");
return -1;
}
return 0;
}
static int winhttp_uploadpack_ls( static int winhttp_uploadpack_ls(
winhttp_subtransport *t, winhttp_subtransport *t,
winhttp_stream *s) winhttp_stream *s)
...@@ -1014,7 +1099,8 @@ static int winhttp_action( ...@@ -1014,7 +1099,8 @@ static int winhttp_action(
int ret = -1; int ret = -1;
if (!t->connection && if (!t->connection &&
winhttp_connect(t, url) < 0) (set_connection_data_from_url(t, url, NULL) < 0 ||
winhttp_connect(t, url) < 0))
return -1; return -1;
if (winhttp_stream_alloc(t, &s) < 0) if (winhttp_stream_alloc(t, &s) < 0)
...@@ -1056,25 +1142,7 @@ static int winhttp_close(git_smart_subtransport *subtransport) ...@@ -1056,25 +1142,7 @@ static int winhttp_close(git_smart_subtransport *subtransport)
winhttp_subtransport *t = (winhttp_subtransport *)subtransport; winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
int ret = 0; int ret = 0;
if (t->host) { free_connection_data(t);
git__free(t->host);
t->host = NULL;
}
if (t->port) {
git__free(t->port);
t->port = NULL;
}
if (t->user_from_url) {
git__free(t->user_from_url);
t->user_from_url = NULL;
}
if (t->pass_from_url) {
git__free(t->pass_from_url);
t->pass_from_url = NULL;
}
if (t->cred) { if (t->cred) {
t->cred->free(t->cred); t->cred->free(t->cred);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment