mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-07 23:19:28 +00:00
Merge pull request #1448 from phkelley/development
Avoid pre-Win7 WinHTTP self-redirect quirk
This commit is contained in:
commit
08283cbdb8
@ -29,9 +29,10 @@
|
|||||||
# include "win32/msvc-compat.h"
|
# include "win32/msvc-compat.h"
|
||||||
# include "win32/mingw-compat.h"
|
# include "win32/mingw-compat.h"
|
||||||
# include "win32/error.h"
|
# include "win32/error.h"
|
||||||
|
# include "win32/version.h"
|
||||||
# ifdef GIT_THREADS
|
# ifdef GIT_THREADS
|
||||||
# include "win32/pthread.h"
|
# include "win32/pthread.h"
|
||||||
#endif
|
# endif
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ typedef struct {
|
|||||||
const char *service_url;
|
const char *service_url;
|
||||||
const wchar_t *verb;
|
const wchar_t *verb;
|
||||||
HINTERNET request;
|
HINTERNET request;
|
||||||
|
wchar_t *request_uri;
|
||||||
char *chunk_buffer;
|
char *chunk_buffer;
|
||||||
unsigned chunk_buffer_len;
|
unsigned chunk_buffer_len;
|
||||||
HANDLE post_body;
|
HANDLE post_body;
|
||||||
@ -145,10 +146,10 @@ static int winhttp_stream_connect(winhttp_stream *s)
|
|||||||
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
|
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
|
||||||
git_buf buf = GIT_BUF_INIT;
|
git_buf buf = GIT_BUF_INIT;
|
||||||
char *proxy_url = NULL;
|
char *proxy_url = NULL;
|
||||||
wchar_t url[GIT_WIN_PATH], ct[MAX_CONTENT_TYPE_LEN];
|
wchar_t ct[MAX_CONTENT_TYPE_LEN];
|
||||||
wchar_t *types[] = { L"*/*", NULL };
|
wchar_t *types[] = { L"*/*", NULL };
|
||||||
BOOL peerdist = FALSE;
|
BOOL peerdist = FALSE;
|
||||||
int error = -1;
|
int error = -1, wide_len;
|
||||||
|
|
||||||
/* 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);
|
||||||
@ -156,13 +157,31 @@ static int winhttp_stream_connect(winhttp_stream *s)
|
|||||||
if (git_buf_oom(&buf))
|
if (git_buf_oom(&buf))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
git__utf8_to_16(url, GIT_WIN_PATH, git_buf_cstr(&buf));
|
/* Convert URL to wide characters */
|
||||||
|
wide_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
|
||||||
|
git_buf_cstr(&buf), -1, NULL, 0);
|
||||||
|
|
||||||
|
if (!wide_len) {
|
||||||
|
giterr_set(GITERR_OS, "Failed to measure string for wide conversion");
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->request_uri = git__malloc(wide_len * sizeof(wchar_t));
|
||||||
|
|
||||||
|
if (!s->request_uri)
|
||||||
|
goto on_error;
|
||||||
|
|
||||||
|
if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
|
||||||
|
git_buf_cstr(&buf), -1, s->request_uri, wide_len)) {
|
||||||
|
giterr_set(GITERR_OS, "Failed to convert string to wide form");
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
/* Establish request */
|
/* Establish request */
|
||||||
s->request = WinHttpOpenRequest(
|
s->request = WinHttpOpenRequest(
|
||||||
t->connection,
|
t->connection,
|
||||||
s->verb,
|
s->verb,
|
||||||
url,
|
s->request_uri,
|
||||||
NULL,
|
NULL,
|
||||||
WINHTTP_NO_REFERER,
|
WINHTTP_NO_REFERER,
|
||||||
types,
|
types,
|
||||||
@ -179,19 +198,36 @@ static int winhttp_stream_connect(winhttp_stream *s)
|
|||||||
|
|
||||||
if (proxy_url) {
|
if (proxy_url) {
|
||||||
WINHTTP_PROXY_INFO proxy_info;
|
WINHTTP_PROXY_INFO proxy_info;
|
||||||
size_t wide_len;
|
wchar_t *proxy_wide;
|
||||||
|
|
||||||
git__utf8_to_16(url, GIT_WIN_PATH, proxy_url);
|
/* Convert URL to wide characters */
|
||||||
|
wide_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
|
||||||
|
proxy_url, -1, NULL, 0);
|
||||||
|
|
||||||
wide_len = wcslen(url);
|
if (!wide_len) {
|
||||||
|
giterr_set(GITERR_OS, "Failed to measure string for wide conversion");
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy_wide = git__malloc(wide_len * sizeof(wchar_t));
|
||||||
|
|
||||||
|
if (!proxy_wide)
|
||||||
|
goto on_error;
|
||||||
|
|
||||||
|
if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
|
||||||
|
proxy_url, -1, proxy_wide, wide_len)) {
|
||||||
|
giterr_set(GITERR_OS, "Failed to convert string to wide form");
|
||||||
|
git__free(proxy_wide);
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
/* Strip any trailing forward slash on the proxy URL;
|
/* Strip any trailing forward slash on the proxy URL;
|
||||||
* WinHTTP doesn't like it if one is present */
|
* WinHTTP doesn't like it if one is present */
|
||||||
if (L'/' == url[wide_len - 1])
|
if (wide_len > 1 && L'/' == proxy_wide[wide_len - 2])
|
||||||
url[wide_len - 1] = L'\0';
|
proxy_wide[wide_len - 2] = L'\0';
|
||||||
|
|
||||||
proxy_info.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
|
proxy_info.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
|
||||||
proxy_info.lpszProxy = url;
|
proxy_info.lpszProxy = proxy_wide;
|
||||||
proxy_info.lpszProxyBypass = NULL;
|
proxy_info.lpszProxyBypass = NULL;
|
||||||
|
|
||||||
if (!WinHttpSetOption(s->request,
|
if (!WinHttpSetOption(s->request,
|
||||||
@ -199,8 +235,11 @@ static int winhttp_stream_connect(winhttp_stream *s)
|
|||||||
&proxy_info,
|
&proxy_info,
|
||||||
sizeof(WINHTTP_PROXY_INFO))) {
|
sizeof(WINHTTP_PROXY_INFO))) {
|
||||||
giterr_set(GITERR_OS, "Failed to set proxy");
|
giterr_set(GITERR_OS, "Failed to set proxy");
|
||||||
|
git__free(proxy_wide);
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
git__free(proxy_wide);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Strip unwanted headers (X-P2P-PeerDist, X-P2P-PeerDistEx) that WinHTTP
|
/* Strip unwanted headers (X-P2P-PeerDist, X-P2P-PeerDistEx) that WinHTTP
|
||||||
@ -348,8 +387,15 @@ static int winhttp_stream_read(
|
|||||||
winhttp_stream *s = (winhttp_stream *)stream;
|
winhttp_stream *s = (winhttp_stream *)stream;
|
||||||
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
|
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
|
||||||
DWORD dw_bytes_read;
|
DWORD dw_bytes_read;
|
||||||
|
char replay_count = 0;
|
||||||
|
|
||||||
replay:
|
replay:
|
||||||
|
/* Enforce a reasonable cap on the number of replays */
|
||||||
|
if (++replay_count >= 7) {
|
||||||
|
giterr_set(GITERR_NET, "Too many redirects or authentication replays");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Connect if necessary */
|
/* Connect if necessary */
|
||||||
if (!s->request && winhttp_stream_connect(s) < 0)
|
if (!s->request && winhttp_stream_connect(s) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
@ -445,10 +491,70 @@ replay:
|
|||||||
WINHTTP_HEADER_NAME_BY_INDEX,
|
WINHTTP_HEADER_NAME_BY_INDEX,
|
||||||
&status_code, &status_code_length,
|
&status_code, &status_code_length,
|
||||||
WINHTTP_NO_HEADER_INDEX)) {
|
WINHTTP_NO_HEADER_INDEX)) {
|
||||||
giterr_set(GITERR_OS, "Failed to retreive status code");
|
giterr_set(GITERR_OS, "Failed to retrieve status code");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The implementation of WinHTTP prior to Windows 7 will not
|
||||||
|
* redirect to an identical URI. Some Git hosters use self-redirects
|
||||||
|
* as part of their DoS mitigation strategy. Check first to see if we
|
||||||
|
* have a redirect status code, and that we haven't already streamed
|
||||||
|
* a post body. (We can't replay a streamed POST.) */
|
||||||
|
if (!s->chunked &&
|
||||||
|
(HTTP_STATUS_MOVED == status_code ||
|
||||||
|
HTTP_STATUS_REDIRECT == status_code ||
|
||||||
|
(HTTP_STATUS_REDIRECT_METHOD == status_code &&
|
||||||
|
get_verb == s->verb) ||
|
||||||
|
HTTP_STATUS_REDIRECT_KEEP_VERB == status_code)) {
|
||||||
|
|
||||||
|
/* Check for Windows 7. This workaround is only necessary on
|
||||||
|
* Windows Vista and earlier. Windows 7 is version 6.1. */
|
||||||
|
if (!git_has_win32_version(6, 1)) {
|
||||||
|
wchar_t *location;
|
||||||
|
DWORD location_length;
|
||||||
|
int redirect_cmp;
|
||||||
|
|
||||||
|
/* OK, fetch the Location header from the redirect. */
|
||||||
|
if (WinHttpQueryHeaders(s->request,
|
||||||
|
WINHTTP_QUERY_LOCATION,
|
||||||
|
WINHTTP_HEADER_NAME_BY_INDEX,
|
||||||
|
WINHTTP_NO_OUTPUT_BUFFER,
|
||||||
|
&location_length,
|
||||||
|
WINHTTP_NO_HEADER_INDEX) ||
|
||||||
|
GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
||||||
|
giterr_set(GITERR_OS, "Failed to read Location header");
|
||||||
|
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 */
|
||||||
|
redirect_cmp = wcscmp(location, s->request_uri);
|
||||||
|
git__free(location);
|
||||||
|
|
||||||
|
if (!redirect_cmp) {
|
||||||
|
/* Replay the request */
|
||||||
|
WinHttpCloseHandle(s->request);
|
||||||
|
s->request = NULL;
|
||||||
|
s->sent_request = 0;
|
||||||
|
|
||||||
|
goto replay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Handle authentication failures */
|
/* Handle authentication failures */
|
||||||
if (HTTP_STATUS_DENIED == status_code &&
|
if (HTTP_STATUS_DENIED == status_code &&
|
||||||
get_verb == s->verb && t->owner->cred_acquire_cb) {
|
get_verb == s->verb && t->owner->cred_acquire_cb) {
|
||||||
@ -747,6 +853,11 @@ static void winhttp_stream_free(git_smart_subtransport_stream *stream)
|
|||||||
s->post_body = NULL;
|
s->post_body = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (s->request_uri) {
|
||||||
|
git__free(s->request_uri);
|
||||||
|
s->request_uri = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (s->request) {
|
if (s->request) {
|
||||||
WinHttpCloseHandle(s->request);
|
WinHttpCloseHandle(s->request);
|
||||||
s->request = NULL;
|
s->request = NULL;
|
||||||
@ -876,7 +987,7 @@ static int winhttp_receivepack(
|
|||||||
{
|
{
|
||||||
/* WinHTTP only supports Transfer-Encoding: chunked
|
/* WinHTTP only supports Transfer-Encoding: chunked
|
||||||
* on Windows Vista (NT 6.0) and higher. */
|
* on Windows Vista (NT 6.0) and higher. */
|
||||||
s->chunked = LOBYTE(LOWORD(GetVersion())) >= 6;
|
s->chunked = git_has_win32_version(6, 0);
|
||||||
|
|
||||||
if (s->chunked)
|
if (s->chunked)
|
||||||
s->parent.write = winhttp_stream_write_chunked;
|
s->parent.write = winhttp_stream_write_chunked;
|
||||||
|
@ -8,34 +8,70 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
|
||||||
|
#ifdef GIT_WINHTTP
|
||||||
|
# include <winhttp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define WC_ERR_INVALID_CHARS 0x80
|
||||||
|
|
||||||
char *git_win32_get_error_message(DWORD error_code)
|
char *git_win32_get_error_message(DWORD error_code)
|
||||||
{
|
{
|
||||||
LPWSTR lpMsgBuf = NULL;
|
LPWSTR lpMsgBuf = NULL;
|
||||||
|
HMODULE hModule = NULL;
|
||||||
|
char *utf8_msg = NULL;
|
||||||
|
int utf8_size;
|
||||||
|
DWORD dwFlags =
|
||||||
|
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS;
|
||||||
|
|
||||||
if (!error_code)
|
if (!error_code)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (FormatMessageW(
|
#ifdef GIT_WINHTTP
|
||||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
/* Errors raised by WinHTTP are not in the system resource table */
|
||||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
if (error_code >= WINHTTP_ERROR_BASE &&
|
||||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
error_code <= WINHTTP_ERROR_LAST)
|
||||||
NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
hModule = GetModuleHandleW(L"winhttp");
|
||||||
(LPWSTR)&lpMsgBuf, 0, NULL)) {
|
#endif
|
||||||
int utf8_size = WideCharToMultiByte(CP_UTF8, 0, lpMsgBuf, -1, NULL, 0, NULL, NULL);
|
|
||||||
|
|
||||||
char *lpMsgBuf_utf8 = git__malloc(utf8_size * sizeof(char));
|
GIT_UNUSED(hModule);
|
||||||
if (lpMsgBuf_utf8 == NULL) {
|
|
||||||
LocalFree(lpMsgBuf);
|
if (hModule)
|
||||||
return NULL;
|
dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
|
||||||
}
|
else
|
||||||
if (!WideCharToMultiByte(CP_UTF8, 0, lpMsgBuf, -1, lpMsgBuf_utf8, utf8_size, NULL, NULL)) {
|
dwFlags |= FORMAT_MESSAGE_FROM_SYSTEM;
|
||||||
LocalFree(lpMsgBuf);
|
|
||||||
git__free(lpMsgBuf_utf8);
|
if (FormatMessageW(dwFlags, hModule, error_code,
|
||||||
return NULL;
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
(LPWSTR)&lpMsgBuf, 0, NULL)) {
|
||||||
|
|
||||||
|
/* Invalid code point check supported on Vista+ only */
|
||||||
|
if (git_has_win32_version(6, 0))
|
||||||
|
dwFlags = WC_ERR_INVALID_CHARS;
|
||||||
|
else
|
||||||
|
dwFlags = 0;
|
||||||
|
|
||||||
|
utf8_size = WideCharToMultiByte(CP_UTF8, dwFlags,
|
||||||
|
lpMsgBuf, -1, NULL, 0, NULL, NULL);
|
||||||
|
|
||||||
|
if (!utf8_size) {
|
||||||
|
assert(0);
|
||||||
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utf8_msg = git__malloc(utf8_size);
|
||||||
|
|
||||||
|
if (!utf8_msg)
|
||||||
|
goto on_error;
|
||||||
|
|
||||||
|
if (!WideCharToMultiByte(CP_UTF8, dwFlags,
|
||||||
|
lpMsgBuf, -1, utf8_msg, utf8_size, NULL, NULL)) {
|
||||||
|
git__free(utf8_msg);
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
on_error:
|
||||||
LocalFree(lpMsgBuf);
|
LocalFree(lpMsgBuf);
|
||||||
return lpMsgBuf_utf8;
|
|
||||||
}
|
}
|
||||||
return NULL;
|
|
||||||
|
return utf8_msg;
|
||||||
}
|
}
|
||||||
|
20
src/win32/version.h
Normal file
20
src/win32/version.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* 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_win32_version_h__
|
||||||
|
#define INCLUDE_win32_version_h__
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
GIT_INLINE(int) git_has_win32_version(int major, int minor)
|
||||||
|
{
|
||||||
|
WORD wVersion = LOWORD(GetVersion());
|
||||||
|
|
||||||
|
return LOBYTE(wVersion) > major ||
|
||||||
|
(LOBYTE(wVersion) == major && HIBYTE(wVersion) >= minor);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user