mirror of
				https://git.proxmox.com/git/libgit2
				synced 2025-11-04 12:12:46 +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/mingw-compat.h"
 | 
			
		||||
# include "win32/error.h"
 | 
			
		||||
# include "win32/version.h"
 | 
			
		||||
# ifdef GIT_THREADS
 | 
			
		||||
#	include "win32/pthread.h"
 | 
			
		||||
#endif
 | 
			
		||||
# endif
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -58,6 +58,7 @@ typedef struct {
 | 
			
		||||
	const char *service_url;
 | 
			
		||||
	const wchar_t *verb;
 | 
			
		||||
	HINTERNET request;
 | 
			
		||||
	wchar_t *request_uri;
 | 
			
		||||
	char *chunk_buffer;
 | 
			
		||||
	unsigned chunk_buffer_len;
 | 
			
		||||
	HANDLE post_body;
 | 
			
		||||
@ -145,10 +146,10 @@ static int winhttp_stream_connect(winhttp_stream *s)
 | 
			
		||||
	winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
 | 
			
		||||
	git_buf buf = GIT_BUF_INIT;
 | 
			
		||||
	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 };
 | 
			
		||||
	BOOL peerdist = FALSE;
 | 
			
		||||
	int error = -1;
 | 
			
		||||
	int error = -1, wide_len;
 | 
			
		||||
 | 
			
		||||
	/* Prepare 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))
 | 
			
		||||
		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 */
 | 
			
		||||
	s->request = WinHttpOpenRequest(
 | 
			
		||||
			t->connection,
 | 
			
		||||
			s->verb,
 | 
			
		||||
			url,
 | 
			
		||||
			s->request_uri,
 | 
			
		||||
			NULL,
 | 
			
		||||
			WINHTTP_NO_REFERER,
 | 
			
		||||
			types,
 | 
			
		||||
@ -179,19 +198,36 @@ static int winhttp_stream_connect(winhttp_stream *s)
 | 
			
		||||
 | 
			
		||||
	if (proxy_url) {
 | 
			
		||||
		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;
 | 
			
		||||
		 * WinHTTP doesn't like it if one is present */
 | 
			
		||||
		if (L'/' == url[wide_len - 1])
 | 
			
		||||
			url[wide_len - 1] = L'\0';
 | 
			
		||||
		if (wide_len > 1 && L'/' == proxy_wide[wide_len - 2])
 | 
			
		||||
			proxy_wide[wide_len - 2] = L'\0';
 | 
			
		||||
 | 
			
		||||
		proxy_info.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
 | 
			
		||||
		proxy_info.lpszProxy = url;
 | 
			
		||||
		proxy_info.lpszProxy = proxy_wide;
 | 
			
		||||
		proxy_info.lpszProxyBypass = NULL;
 | 
			
		||||
 | 
			
		||||
		if (!WinHttpSetOption(s->request,
 | 
			
		||||
@ -199,8 +235,11 @@ static int winhttp_stream_connect(winhttp_stream *s)
 | 
			
		||||
			&proxy_info,
 | 
			
		||||
			sizeof(WINHTTP_PROXY_INFO))) {
 | 
			
		||||
			giterr_set(GITERR_OS, "Failed to set proxy");
 | 
			
		||||
			git__free(proxy_wide);
 | 
			
		||||
			goto on_error;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		git__free(proxy_wide);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* 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_subtransport *t = OWNING_SUBTRANSPORT(s);
 | 
			
		||||
	DWORD dw_bytes_read;
 | 
			
		||||
	char replay_count = 0;
 | 
			
		||||
 | 
			
		||||
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 */
 | 
			
		||||
	if (!s->request && winhttp_stream_connect(s) < 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
@ -445,10 +491,70 @@ replay:
 | 
			
		||||
			WINHTTP_HEADER_NAME_BY_INDEX,
 | 
			
		||||
			&status_code, &status_code_length,
 | 
			
		||||
			WINHTTP_NO_HEADER_INDEX)) {
 | 
			
		||||
				giterr_set(GITERR_OS, "Failed to retreive status code");
 | 
			
		||||
				giterr_set(GITERR_OS, "Failed to retrieve status code");
 | 
			
		||||
				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 */
 | 
			
		||||
		if (HTTP_STATUS_DENIED == status_code &&
 | 
			
		||||
			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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (s->request_uri) {
 | 
			
		||||
		git__free(s->request_uri);
 | 
			
		||||
		s->request_uri = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (s->request) {
 | 
			
		||||
		WinHttpCloseHandle(s->request);
 | 
			
		||||
		s->request = NULL;
 | 
			
		||||
@ -876,7 +987,7 @@ static int winhttp_receivepack(
 | 
			
		||||
{
 | 
			
		||||
	/* WinHTTP only supports Transfer-Encoding: chunked
 | 
			
		||||
	 * 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)
 | 
			
		||||
		s->parent.write = winhttp_stream_write_chunked;
 | 
			
		||||
 | 
			
		||||
@ -8,34 +8,70 @@
 | 
			
		||||
#include "common.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)
 | 
			
		||||
{
 | 
			
		||||
	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)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	if (FormatMessageW(
 | 
			
		||||
				FORMAT_MESSAGE_ALLOCATE_BUFFER |
 | 
			
		||||
				FORMAT_MESSAGE_FROM_SYSTEM |
 | 
			
		||||
				FORMAT_MESSAGE_IGNORE_INSERTS,
 | 
			
		||||
				NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
 | 
			
		||||
				(LPWSTR)&lpMsgBuf, 0, NULL)) {
 | 
			
		||||
		int utf8_size = WideCharToMultiByte(CP_UTF8, 0, lpMsgBuf, -1, NULL, 0, NULL, NULL);
 | 
			
		||||
#ifdef GIT_WINHTTP
 | 
			
		||||
	/* Errors raised by WinHTTP are not in the system resource table */
 | 
			
		||||
	if (error_code >= WINHTTP_ERROR_BASE &&
 | 
			
		||||
		error_code <= WINHTTP_ERROR_LAST)
 | 
			
		||||
		hModule = GetModuleHandleW(L"winhttp");
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
		char *lpMsgBuf_utf8 = git__malloc(utf8_size * sizeof(char));
 | 
			
		||||
		if (lpMsgBuf_utf8 == NULL) {
 | 
			
		||||
			LocalFree(lpMsgBuf);
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
		if (!WideCharToMultiByte(CP_UTF8, 0, lpMsgBuf, -1, lpMsgBuf_utf8, utf8_size, NULL, NULL)) {
 | 
			
		||||
			LocalFree(lpMsgBuf);
 | 
			
		||||
			git__free(lpMsgBuf_utf8);
 | 
			
		||||
			return NULL;
 | 
			
		||||
	GIT_UNUSED(hModule);
 | 
			
		||||
 | 
			
		||||
	if (hModule)
 | 
			
		||||
		dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
 | 
			
		||||
	else
 | 
			
		||||
		dwFlags |= FORMAT_MESSAGE_FROM_SYSTEM;
 | 
			
		||||
 | 
			
		||||
	if (FormatMessageW(dwFlags, hModule, error_code,
 | 
			
		||||
		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);
 | 
			
		||||
		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