aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2019-09-23 12:50:59 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2019-09-23 15:29:18 +0200
commita5e662b6cf9ada7c4371c92405fe3de6866d7b0a (patch)
tree24e44df4ab5f8ff9e1f6ccab0769c4fdc6babb7f
parentembeddable-dll-service: add basic outline for embedding wireguard (diff)
downloadwireguard-windows-a5e662b6cf9ada7c4371c92405fe3de6866d7b0a.tar.xz
wireguard-windows-a5e662b6cf9ada7c4371c92405fe3de6866d7b0a.zip
updater: use winhttp to reduce filesize
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
-rw-r--r--updater/downloader.go32
-rw-r--r--updater/winhttp/mksyscall.go8
-rw-r--r--updater/winhttp/syscall_windows.go262
-rw-r--r--updater/winhttp/winhttp.go157
-rw-r--r--updater/winhttp/winhttp_test.go57
-rw-r--r--updater/winhttp/zsyscall_windows.go189
6 files changed, 684 insertions, 21 deletions
diff --git a/updater/downloader.go b/updater/downloader.go
index a12b5037..6e606532 100644
--- a/updater/downloader.go
+++ b/updater/downloader.go
@@ -11,11 +11,12 @@ import (
"fmt"
"hash"
"io"
- "net/http"
"os"
"sync/atomic"
"golang.org/x/crypto/blake2b"
+
+ "golang.zx2c4.com/wireguard/windows/updater/winhttp"
"golang.zx2c4.com/wireguard/windows/version"
)
@@ -47,18 +48,13 @@ type UpdateFound struct {
}
func CheckForUpdate() (*UpdateFound, error) {
- request, err := http.NewRequest(http.MethodGet, latestVersionURL, nil)
- if err != nil {
- return nil, err
- }
- request.Header.Add("User-Agent", version.UserAgent())
- response, err := http.DefaultClient.Do(request)
+ response, err := winhttp.Get(version.UserAgent(), latestVersionURL)
if err != nil {
return nil, err
}
- defer response.Body.Close()
+ defer response.Close()
var fileList [1024 * 512] /* 512 KiB */ byte
- bytesRead, err := response.Body.Read(fileList[:])
+ bytesRead, err := response.Read(fileList[:])
if err != nil && (err != io.EOF || bytesRead == 0) {
return nil, err
}
@@ -112,21 +108,15 @@ func DownloadVerifyAndExecute(userToken uintptr) (progress chan DownloadProgress
dp := DownloadProgress{Activity: "Downloading update"}
progress <- dp
- request, err := http.NewRequest(http.MethodGet, fmt.Sprintf(msiURL, update.name), nil)
- if err != nil {
- progress <- DownloadProgress{Error: err}
- return
- }
- request.Header.Add("User-Agent", version.UserAgent())
- request.Header.Set("Accept-Encoding", "identity")
- response, err := http.DefaultClient.Do(request)
+ response, err := winhttp.Get(version.UserAgent(), fmt.Sprintf(msiURL, update.name))
if err != nil {
progress <- DownloadProgress{Error: err}
return
}
- defer response.Body.Close()
- if response.ContentLength >= 0 {
- dp.BytesTotal = uint64(response.ContentLength)
+ defer response.Close()
+ length, err := response.Length()
+ if err == nil && length >= 0 {
+ dp.BytesTotal = length
progress <- dp
}
hasher, err := blake2b.New256(nil)
@@ -135,7 +125,7 @@ func DownloadVerifyAndExecute(userToken uintptr) (progress chan DownloadProgress
return
}
pm := &progressHashWatcher{&dp, progress, hasher}
- _, err = io.Copy(file, io.TeeReader(io.LimitReader(response.Body, 1024*1024*100 /* 100 MiB */), pm))
+ _, err = io.Copy(file, io.TeeReader(io.LimitReader(response, 1024*1024*100 /* 100 MiB */), pm))
if err != nil {
progress <- DownloadProgress{Error: err}
return
diff --git a/updater/winhttp/mksyscall.go b/updater/winhttp/mksyscall.go
new file mode 100644
index 00000000..e706766f
--- /dev/null
+++ b/updater/winhttp/mksyscall.go
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package winhttp
+
+//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go
diff --git a/updater/winhttp/syscall_windows.go b/updater/winhttp/syscall_windows.go
new file mode 100644
index 00000000..adabd16e
--- /dev/null
+++ b/updater/winhttp/syscall_windows.go
@@ -0,0 +1,262 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package winhttp
+
+import (
+ "golang.org/x/sys/windows"
+)
+
+type _HINTERNET windows.Handle
+
+type Error uint32
+
+const (
+ _WINHTTP_ACCESS_TYPE_DEFAULT_PROXY = 0
+ _WINHTTP_ACCESS_TYPE_NO_PROXY = 1
+ _WINHTTP_ACCESS_TYPE_NAMED_PROXY = 3
+ _WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY = 4
+
+ _WINHTTP_FLAG_ASYNC = 0x10000000
+
+ _WINHTTP_INVALID_STATUS_CALLBACK = ^uintptr(0)
+
+ _WINHTTP_CALLBACK_STATUS_RESOLVING_NAME = 0x00000001
+ _WINHTTP_CALLBACK_STATUS_NAME_RESOLVED = 0x00000002
+ _WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER = 0x00000004
+ _WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER = 0x00000008
+ _WINHTTP_CALLBACK_STATUS_SENDING_REQUEST = 0x00000010
+ _WINHTTP_CALLBACK_STATUS_REQUEST_SENT = 0x00000020
+ _WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE = 0x00000040
+ _WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED = 0x00000080
+ _WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION = 0x00000100
+ _WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED = 0x00000200
+ _WINHTTP_CALLBACK_STATUS_HANDLE_CREATED = 0x00000400
+ _WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING = 0x00000800
+ _WINHTTP_CALLBACK_STATUS_DETECTING_PROXY = 0x00001000
+ _WINHTTP_CALLBACK_STATUS_REDIRECT = 0x00004000
+ _WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE = 0x00008000
+ _WINHTTP_CALLBACK_STATUS_SECURE_FAILURE = 0x00010000
+ _WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE = 0x00020000
+ _WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE = 0x00040000
+ _WINHTTP_CALLBACK_STATUS_READ_COMPLETE = 0x00080000
+ _WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE = 0x00100000
+ _WINHTTP_CALLBACK_STATUS_REQUEST_ERROR = 0x00200000
+ _WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE = 0x00400000
+ _WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE = 0x01000000
+ _WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE = 0x02000000
+ _WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE = 0x04000000
+ _WINHTTP_CALLBACK_STATUS_SETTINGS_WRITE_COMPLETE = 0x10000000
+ _WINHTTP_CALLBACK_STATUS_SETTINGS_READ_COMPLETE = 0x20000000
+
+ _WINHTTP_CALLBACK_FLAG_RESOLVE_NAME = _WINHTTP_CALLBACK_STATUS_RESOLVING_NAME | _WINHTTP_CALLBACK_STATUS_NAME_RESOLVED
+ _WINHTTP_CALLBACK_FLAG_CONNECT_TO_SERVER = _WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER | _WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER
+ _WINHTTP_CALLBACK_FLAG_SEND_REQUEST = _WINHTTP_CALLBACK_STATUS_SENDING_REQUEST | _WINHTTP_CALLBACK_STATUS_REQUEST_SENT
+ _WINHTTP_CALLBACK_FLAG_RECEIVE_RESPONSE = _WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE | _WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED
+ _WINHTTP_CALLBACK_FLAG_CLOSE_CONNECTION = _WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION | _WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED
+ _WINHTTP_CALLBACK_FLAG_HANDLES = _WINHTTP_CALLBACK_STATUS_HANDLE_CREATED | _WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING
+ _WINHTTP_CALLBACK_FLAG_DETECTING_PROXY = _WINHTTP_CALLBACK_STATUS_DETECTING_PROXY
+ _WINHTTP_CALLBACK_FLAG_REDIRECT = _WINHTTP_CALLBACK_STATUS_REDIRECT
+ _WINHTTP_CALLBACK_FLAG_INTERMEDIATE_RESPONSE = _WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE
+ _WINHTTP_CALLBACK_FLAG_SECURE_FAILURE = _WINHTTP_CALLBACK_STATUS_SECURE_FAILURE
+ _WINHTTP_CALLBACK_FLAG_SENDREQUEST_COMPLETE = _WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE
+ _WINHTTP_CALLBACK_FLAG_HEADERS_AVAILABLE = _WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE
+ _WINHTTP_CALLBACK_FLAG_DATA_AVAILABLE = _WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE
+ _WINHTTP_CALLBACK_FLAG_READ_COMPLETE = _WINHTTP_CALLBACK_STATUS_READ_COMPLETE
+ _WINHTTP_CALLBACK_FLAG_WRITE_COMPLETE = _WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE
+ _WINHTTP_CALLBACK_FLAG_REQUEST_ERROR = _WINHTTP_CALLBACK_STATUS_REQUEST_ERROR
+ _WINHTTP_CALLBACK_FLAG_GETPROXYFORURL_COMPLETE = _WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE
+ _WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS = _WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE | _WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE | _WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE | _WINHTTP_CALLBACK_STATUS_READ_COMPLETE | _WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE | _WINHTTP_CALLBACK_STATUS_REQUEST_ERROR | _WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE
+ _WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS = 0xffffffff
+
+ _INTERNET_DEFAULT_PORT = 0
+ _INTERNET_DEFAULT_HTTP_PORT = 80
+ _INTERNET_DEFAULT_HTTPS_PORT = 443
+
+ _WINHTTP_FLAG_SECURE = 0x00800000
+ _WINHTTP_FLAG_ESCAPE_PERCENT = 0x00000004
+ _WINHTTP_FLAG_NULL_CODEPAGE = 0x00000008
+ _WINHTTP_FLAG_BYPASS_PROXY_CACHE = 0x00000100
+ _WINHTTP_FLAG_REFRESH = _WINHTTP_FLAG_BYPASS_PROXY_CACHE
+ _WINHTTP_FLAG_ESCAPE_DISABLE = 0x00000040
+ _WINHTTP_FLAG_ESCAPE_DISABLE_QUERY = 0x00000080
+
+ _WINHTTP_QUERY_MIME_VERSION = 0
+ _WINHTTP_QUERY_CONTENT_TYPE = 1
+ _WINHTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2
+ _WINHTTP_QUERY_CONTENT_ID = 3
+ _WINHTTP_QUERY_CONTENT_DESCRIPTION = 4
+ _WINHTTP_QUERY_CONTENT_LENGTH = 5
+ _WINHTTP_QUERY_CONTENT_LANGUAGE = 6
+ _WINHTTP_QUERY_ALLOW = 7
+ _WINHTTP_QUERY_PUBLIC = 8
+ _WINHTTP_QUERY_DATE = 9
+ _WINHTTP_QUERY_EXPIRES = 10
+ _WINHTTP_QUERY_LAST_MODIFIED = 11
+ _WINHTTP_QUERY_MESSAGE_ID = 12
+ _WINHTTP_QUERY_URI = 13
+ _WINHTTP_QUERY_DERIVED_FROM = 14
+ _WINHTTP_QUERY_COST = 15
+ _WINHTTP_QUERY_LINK = 16
+ _WINHTTP_QUERY_PRAGMA = 17
+ _WINHTTP_QUERY_VERSION = 18
+ _WINHTTP_QUERY_STATUS_CODE = 19
+ _WINHTTP_QUERY_STATUS_TEXT = 20
+ _WINHTTP_QUERY_RAW_HEADERS = 21
+ _WINHTTP_QUERY_RAW_HEADERS_CRLF = 22
+ _WINHTTP_QUERY_CONNECTION = 23
+ _WINHTTP_QUERY_ACCEPT = 24
+ _WINHTTP_QUERY_ACCEPT_CHARSET = 25
+ _WINHTTP_QUERY_ACCEPT_ENCODING = 26
+ _WINHTTP_QUERY_ACCEPT_LANGUAGE = 27
+ _WINHTTP_QUERY_AUTHORIZATION = 28
+ _WINHTTP_QUERY_CONTENT_ENCODING = 29
+ _WINHTTP_QUERY_FORWARDED = 30
+ _WINHTTP_QUERY_FROM = 31
+ _WINHTTP_QUERY_IF_MODIFIED_SINCE = 32
+ _WINHTTP_QUERY_LOCATION = 33
+ _WINHTTP_QUERY_ORIG_URI = 34
+ _WINHTTP_QUERY_REFERER = 35
+ _WINHTTP_QUERY_RETRY_AFTER = 36
+ _WINHTTP_QUERY_SERVER = 37
+ _WINHTTP_QUERY_TITLE = 38
+ _WINHTTP_QUERY_USER_AGENT = 39
+ _WINHTTP_QUERY_WWW_AUTHENTICATE = 40
+ _WINHTTP_QUERY_PROXY_AUTHENTICATE = 41
+ _WINHTTP_QUERY_ACCEPT_RANGES = 42
+ _WINHTTP_QUERY_SET_COOKIE = 43
+ _WINHTTP_QUERY_COOKIE = 44
+ _WINHTTP_QUERY_REQUEST_METHOD = 45
+ _WINHTTP_QUERY_REFRESH = 46
+ _WINHTTP_QUERY_CONTENT_DISPOSITION = 47
+ _WINHTTP_QUERY_AGE = 48
+ _WINHTTP_QUERY_CACHE_CONTROL = 49
+ _WINHTTP_QUERY_CONTENT_BASE = 50
+ _WINHTTP_QUERY_CONTENT_LOCATION = 51
+ _WINHTTP_QUERY_CONTENT_MD5 = 52
+ _WINHTTP_QUERY_CONTENT_RANGE = 53
+ _WINHTTP_QUERY_ETAG = 54
+ _WINHTTP_QUERY_HOST = 55
+ _WINHTTP_QUERY_IF_MATCH = 56
+ _WINHTTP_QUERY_IF_NONE_MATCH = 57
+ _WINHTTP_QUERY_IF_RANGE = 58
+ _WINHTTP_QUERY_IF_UNMODIFIED_SINCE = 59
+ _WINHTTP_QUERY_MAX_FORWARDS = 60
+ _WINHTTP_QUERY_PROXY_AUTHORIZATION = 61
+ _WINHTTP_QUERY_RANGE = 62
+ _WINHTTP_QUERY_TRANSFER_ENCODING = 63
+ _WINHTTP_QUERY_UPGRADE = 64
+ _WINHTTP_QUERY_VARY = 65
+ _WINHTTP_QUERY_VIA = 66
+ _WINHTTP_QUERY_WARNING = 67
+ _WINHTTP_QUERY_EXPECT = 68
+ _WINHTTP_QUERY_PROXY_CONNECTION = 69
+ _WINHTTP_QUERY_UNLESS_MODIFIED_SINCE = 70
+ _WINHTTP_QUERY_PROXY_SUPPORT = 75
+ _WINHTTP_QUERY_AUTHENTICATION_INFO = 76
+ _WINHTTP_QUERY_PASSPORT_URLS = 77
+ _WINHTTP_QUERY_PASSPORT_CONFIG = 78
+ _WINHTTP_QUERY_MAX = 78
+ _WINHTTP_QUERY_CUSTOM = 65535
+ _WINHTTP_QUERY_FLAG_REQUEST_HEADERS = 0x80000000
+ _WINHTTP_QUERY_FLAG_SYSTEMTIME = 0x40000000
+ _WINHTTP_QUERY_FLAG_NUMBER = 0x20000000
+ _WINHTTP_QUERY_FLAG_NUMBER64 = 0x08000000
+
+ _ICU_ESCAPE = 0x80000000
+ _ICU_ESCAPE_AUTHORITY = 0x00002000
+ _ICU_REJECT_USERPWD = 0x00004000
+
+ _INTERNET_SCHEME_HTTP = 1
+ _INTERNET_SCHEME_HTTPS = 2
+ _INTERNET_SCHEME_FTP = 3
+ _INTERNET_SCHEME_SOCKS = 4
+
+ _WINHTTP_ERROR_BASE = 12000
+ _ERROR_WINHTTP_OUT_OF_HANDLES = Error(12000 + 1)
+ _ERROR_WINHTTP_TIMEOUT = Error(12000 + 2)
+ _ERROR_WINHTTP_INTERNAL_ERROR = Error(12000 + 4)
+ _ERROR_WINHTTP_INVALID_URL = Error(12000 + 5)
+ _ERROR_WINHTTP_UNRECOGNIZED_SCHEME = Error(12000 + 6)
+ _ERROR_WINHTTP_NAME_NOT_RESOLVED = Error(12000 + 7)
+ _ERROR_WINHTTP_INVALID_OPTION = Error(12000 + 9)
+ _ERROR_WINHTTP_OPTION_NOT_SETTABLE = Error(12000 + 11)
+ _ERROR_WINHTTP_SHUTDOWN = Error(12000 + 12)
+ _ERROR_WINHTTP_LOGIN_FAILURE = Error(12000 + 15)
+ _ERROR_WINHTTP_OPERATION_CANCELLED = Error(12000 + 17)
+ _ERROR_WINHTTP_INCORRECT_HANDLE_TYPE = Error(12000 + 18)
+ _ERROR_WINHTTP_INCORRECT_HANDLE_STATE = Error(12000 + 19)
+ _ERROR_WINHTTP_CANNOT_CONNECT = Error(12000 + 29)
+ _ERROR_WINHTTP_CONNECTION_ERROR = Error(12000 + 30)
+ _ERROR_WINHTTP_RESEND_REQUEST = Error(12000 + 32)
+ _ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED = Error(12000 + 44)
+ _ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN = Error(12000 + 100)
+ _ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND = Error(12000 + 101)
+ _ERROR_WINHTTP_CANNOT_CALL_AFTER_SEND = Error(12000 + 102)
+ _ERROR_WINHTTP_CANNOT_CALL_AFTER_OPEN = Error(12000 + 103)
+ _ERROR_WINHTTP_HEADER_NOT_FOUND = Error(12000 + 150)
+ _ERROR_WINHTTP_INVALID_SERVER_RESPONSE = Error(12000 + 152)
+ _ERROR_WINHTTP_INVALID_HEADER = Error(12000 + 153)
+ _ERROR_WINHTTP_INVALID_QUERY_REQUEST = Error(12000 + 154)
+ _ERROR_WINHTTP_HEADER_ALREADY_EXISTS = Error(12000 + 155)
+ _ERROR_WINHTTP_REDIRECT_FAILED = Error(12000 + 156)
+ _ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR = Error(12000 + 178)
+ _ERROR_WINHTTP_BAD_AUTO_PROXY_SCRIPT = Error(12000 + 166)
+ _ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT = Error(12000 + 167)
+ _ERROR_WINHTTP_UNHANDLED_SCRIPT_TYPE = Error(12000 + 176)
+ _ERROR_WINHTTP_SCRIPT_EXECUTION_ERROR = Error(12000 + 177)
+ _ERROR_WINHTTP_NOT_INITIALIZED = Error(12000 + 172)
+ _ERROR_WINHTTP_SECURE_FAILURE = Error(12000 + 175)
+ _ERROR_WINHTTP_SECURE_CERT_DATE_INVALID = Error(12000 + 37)
+ _ERROR_WINHTTP_SECURE_CERT_CN_INVALID = Error(12000 + 38)
+ _ERROR_WINHTTP_SECURE_INVALID_CA = Error(12000 + 45)
+ _ERROR_WINHTTP_SECURE_CERT_REV_FAILED = Error(12000 + 57)
+ _ERROR_WINHTTP_SECURE_CHANNEL_ERROR = Error(12000 + 157)
+ _ERROR_WINHTTP_SECURE_INVALID_CERT = Error(12000 + 169)
+ _ERROR_WINHTTP_SECURE_CERT_REVOKED = Error(12000 + 170)
+ _ERROR_WINHTTP_SECURE_CERT_WRONG_USAGE = Error(12000 + 179)
+ _ERROR_WINHTTP_AUTODETECTION_FAILED = Error(12000 + 180)
+ _ERROR_WINHTTP_HEADER_COUNT_EXCEEDED = Error(12000 + 181)
+ _ERROR_WINHTTP_HEADER_SIZE_OVERFLOW = Error(12000 + 182)
+ _ERROR_WINHTTP_CHUNKED_ENCODING_HEADER_SIZE_OVERFLOW = Error(12000 + 183)
+ _ERROR_WINHTTP_RESPONSE_DRAIN_OVERFLOW = Error(12000 + 184)
+ _ERROR_WINHTTP_CLIENT_CERT_NO_PRIVATE_KEY = Error(12000 + 185)
+ _ERROR_WINHTTP_CLIENT_CERT_NO_ACCESS_PRIVATE_KEY = Error(12000 + 186)
+ _ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED_PROXY = Error(12000 + 187)
+ _ERROR_WINHTTP_SECURE_FAILURE_PROXY = Error(12000 + 188)
+ _ERROR_WINHTTP_RESERVED_189 = Error(12000 + 189)
+ _ERROR_WINHTTP_HTTP_PROTOCOL_MISMATCH = Error(12000 + 190)
+ _WINHTTP_ERROR_LAST = _WINHTTP_ERROR_BASE + 190
+)
+
+type _URL_COMPONENTS struct {
+ structSize uint32
+ scheme *uint16
+ schemeLength uint32
+ schemeType uint32
+ hostName *uint16
+ hostNameLength uint32
+ port uint16
+ username *uint16
+ usernameLength uint32
+ password *uint16
+ passwordLength uint32
+ urlPath *uint16
+ urlPathLength uint32
+ extraInfo *uint16
+ extraInfoLength uint32
+}
+
+//sys winHttpOpen(userAgent *uint16, accessType uint32, proxy *uint16, proxyBypass *uint16, flags uint32) (sessionHandle _HINTERNET, err error) = winhttp.WinHttpOpen
+//sys winHttpSetStatusCallback(handle _HINTERNET, callback uintptr, notificationFlags uint32, reserved uintptr) (previousCallback uintptr, err error) [failretval==_WINHTTP_INVALID_STATUS_CALLBACK] = winhttp.WinHttpSetStatusCallback
+//sys winHttpCloseHandle(handle _HINTERNET) (err error) = winhttp.WinHttpCloseHandle
+//sys winHttpConnect(sessionHandle _HINTERNET, serverName *uint16, serverPort uint16, reserved uint32) (handle _HINTERNET, err error) = winhttp.WinHttpConnect
+//sys winHttpOpenRequest(connectHandle _HINTERNET, verb *uint16, objectName *uint16, version *uint16, referrer *uint16, acceptTypes **uint16, flags uint32) (requestHandle _HINTERNET, err error) = winhttp.WinHttpOpenRequest
+//sys winHttpSendRequest(requestHandle _HINTERNET, headers *uint16, headersLength uint32, optional *byte, optionalLength uint32, totalLength uint32, context uintptr) (err error) = winhttp.WinHttpSendRequest
+//sys winHttpReceiveResponse(requestHandle _HINTERNET, reserved uintptr) (err error) = winhttp.WinHttpReceiveResponse
+//sys winHttpQueryHeaders(requestHandle _HINTERNET, infoLevel uint32, name *uint16, buffer unsafe.Pointer, bufferLen *uint32, index *uint32) (err error) = winhttp.WinHttpQueryHeaders
+//sys winHttpQueryDataAvailable(requestHandle _HINTERNET, bytesAvailable *uint32) (err error) = winhttp.WinHttpQueryDataAvailable
+//sys winHttpReadData(requestHandle _HINTERNET, buffer *byte, bufferSize uint32, bytesRead *uint32) (err error) = winhttp.WinHttpReadData
+//sys winHttpCrackUrl(url *uint16, urlSize uint32, flags uint32, components *_URL_COMPONENTS) (err error) = winhttp.WinHttpCrackUrl
diff --git a/updater/winhttp/winhttp.go b/updater/winhttp/winhttp.go
new file mode 100644
index 00000000..c75b5b57
--- /dev/null
+++ b/updater/winhttp/winhttp.go
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package winhttp
+
+import (
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+type Response struct {
+ session _HINTERNET
+ connection _HINTERNET
+ request _HINTERNET
+}
+
+func convertError(err *error) {
+ if *err == nil {
+ return
+ }
+ if se, ok := (*err).(syscall.Errno); ok {
+ if se > _WINHTTP_ERROR_BASE && se <= _WINHTTP_ERROR_LAST {
+ *err = Error(se)
+ }
+ }
+}
+
+func Get(userAgent string, url string) (response *Response, err error) {
+ response = new(Response)
+ defer convertError(&err)
+ defer func() {
+ if err != nil {
+ response.Close()
+ response = nil
+ }
+ }()
+ userAgent16, err := windows.UTF16PtrFromString(userAgent)
+ if err != nil {
+ return
+ }
+ url16, err := windows.UTF16PtrFromString(url)
+ if err != nil {
+ return
+ }
+ components := _URL_COMPONENTS{
+ structSize: uint32(unsafe.Sizeof(_URL_COMPONENTS{})),
+ hostName: &make([]uint16, 512)[0],
+ hostNameLength: 512,
+ urlPath: &make([]uint16, 512)[0],
+ urlPathLength: 512,
+ }
+ err = winHttpCrackUrl(url16, 0, _ICU_REJECT_USERPWD, &components)
+ if err != nil {
+ return
+ }
+ if components.schemeType != _INTERNET_SCHEME_HTTP && components.schemeType != _INTERNET_SCHEME_HTTPS {
+ err = _ERROR_WINHTTP_INVALID_URL
+ return
+ }
+ response.session, err = winHttpOpen(userAgent16, _WINHTTP_ACCESS_TYPE_NO_PROXY, nil, nil, 0)
+ if err != nil {
+ return
+ }
+ response.connection, err = winHttpConnect(response.session, components.hostName, components.port, 0)
+ if err != nil {
+ return
+ }
+ flags := uint32(_WINHTTP_FLAG_REFRESH)
+ if components.schemeType == _INTERNET_SCHEME_HTTPS {
+ flags |= _WINHTTP_FLAG_SECURE
+ }
+ response.request, err = winHttpOpenRequest(response.connection, windows.StringToUTF16Ptr("GET"), components.urlPath, nil, nil, nil, flags)
+ if err != nil {
+ return
+ }
+ err = winHttpSendRequest(response.request, nil, 0, nil, 0, 0, 0)
+ if err != nil {
+ return
+ }
+ err = winHttpReceiveResponse(response.request, 0)
+ if err != nil {
+ return
+ }
+ return
+}
+
+func (response *Response) Length() (length uint64, err error) {
+ defer convertError(&err)
+ numBuf := make([]uint16, 22)
+ numLen := uint32(len(numBuf) * 2)
+ err = winHttpQueryHeaders(response.request, _WINHTTP_QUERY_CONTENT_LENGTH, nil, unsafe.Pointer(&numBuf[0]), &numLen, nil)
+ if err != nil {
+ return
+ }
+ length, err = strconv.ParseUint(windows.UTF16ToString(numBuf[:numLen]), 10, 64)
+ if err != nil {
+ return
+ }
+ return
+}
+
+func (response *Response) Read(p []byte) (n int, err error) {
+ defer convertError(&err)
+ if len(p) == 0 {
+ return 0, nil
+ }
+ var bytesRead uint32
+ err = winHttpReadData(response.request, &p[0], uint32(len(p)), &bytesRead)
+ if err != nil {
+ return 0, nil
+ }
+ if bytesRead == 0 || int(bytesRead) < 0 {
+ return 0, io.EOF
+ }
+ return int(bytesRead), nil
+}
+
+func (response *Response) Close() (err error) {
+ defer convertError(&err)
+ var err1, err2, err3 error
+ if response.request != 0 {
+ err1 = winHttpCloseHandle(response.request)
+ }
+ if response.connection != 0 {
+ err2 = winHttpCloseHandle(response.connection)
+ }
+ if response.session != 0 {
+ err3 = winHttpCloseHandle(response.session)
+ }
+ switch {
+ case err1 != nil:
+ return err1
+ case err2 != nil:
+ return err2
+ case err3 != nil:
+ return err3
+ }
+ return nil
+}
+
+func (error Error) Error() string {
+ var message [2048]uint16
+ n, err := windows.FormatMessage(windows.FORMAT_MESSAGE_FROM_HMODULE|windows.FORMAT_MESSAGE_IGNORE_INSERTS|windows.FORMAT_MESSAGE_MAX_WIDTH_MASK,
+ modwinhttp.Handle(), uint32(error), 0, message[:], nil)
+ if err != nil {
+ return fmt.Sprintf("WinHTTP error #%d", error)
+ }
+ return strings.TrimSpace(windows.UTF16ToString(message[:n]))
+}
diff --git a/updater/winhttp/winhttp_test.go b/updater/winhttp/winhttp_test.go
new file mode 100644
index 00000000..42f38caa
--- /dev/null
+++ b/updater/winhttp/winhttp_test.go
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package winhttp
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "testing"
+)
+
+type progressPrinter struct {
+ downloaded uint64
+ total uint64
+}
+
+func (pp *progressPrinter) Write(p []byte) (int, error) {
+ bytes := len(p)
+ pp.downloaded += uint64(bytes)
+ fmt.Printf("%d/%d bytes, %f%%\n", pp.downloaded, pp.total, float64(pp.downloaded)/float64(pp.total)*100.0)
+ return bytes, nil
+}
+
+func TestResponse(t *testing.T) {
+ r, err := Get("WinHTTP Test Suite/1.0", "https://www.zx2c4.com/ip")
+ if err != nil {
+ t.Fatal(err)
+ }
+ length, err := r.Length()
+ if err != nil {
+ t.Fatal(err)
+ }
+ fmt.Printf("The length is %d\n", length)
+ bytes, err := ioutil.ReadAll(r)
+ if err != nil {
+ t.Fatal(err)
+ }
+ fmt.Println(string(bytes))
+ r.Close()
+
+ r, err = Get("WinHTTP Test Suite/1.0", "https://speed.hetzner.de/10GB.bin")
+ if err != nil {
+ t.Fatal(err)
+ }
+ length, err = r.Length()
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = io.Copy(&progressPrinter{total: length}, r)
+ if err != nil {
+ t.Fatal(err)
+ }
+ r.Close()
+}
diff --git a/updater/winhttp/zsyscall_windows.go b/updater/winhttp/zsyscall_windows.go
new file mode 100644
index 00000000..ba8e3e83
--- /dev/null
+++ b/updater/winhttp/zsyscall_windows.go
@@ -0,0 +1,189 @@
+// Code generated by 'go generate'; DO NOT EDIT.
+
+package winhttp
+
+import (
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+var _ unsafe.Pointer
+
+// Do the interface allocations only once for common
+// Errno values.
+const (
+ errnoERROR_IO_PENDING = 997
+)
+
+var (
+ errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
+)
+
+// errnoErr returns common boxed Errno values, to prevent
+// allocations at runtime.
+func errnoErr(e syscall.Errno) error {
+ switch e {
+ case 0:
+ return nil
+ case errnoERROR_IO_PENDING:
+ return errERROR_IO_PENDING
+ }
+ // TODO: add more here, after collecting data on the common
+ // error values see on Windows. (perhaps when running
+ // all.bat?)
+ return e
+}
+
+var (
+ modwinhttp = windows.NewLazySystemDLL("winhttp.dll")
+
+ procWinHttpOpen = modwinhttp.NewProc("WinHttpOpen")
+ procWinHttpSetStatusCallback = modwinhttp.NewProc("WinHttpSetStatusCallback")
+ procWinHttpCloseHandle = modwinhttp.NewProc("WinHttpCloseHandle")
+ procWinHttpConnect = modwinhttp.NewProc("WinHttpConnect")
+ procWinHttpOpenRequest = modwinhttp.NewProc("WinHttpOpenRequest")
+ procWinHttpSendRequest = modwinhttp.NewProc("WinHttpSendRequest")
+ procWinHttpReceiveResponse = modwinhttp.NewProc("WinHttpReceiveResponse")
+ procWinHttpQueryHeaders = modwinhttp.NewProc("WinHttpQueryHeaders")
+ procWinHttpQueryDataAvailable = modwinhttp.NewProc("WinHttpQueryDataAvailable")
+ procWinHttpReadData = modwinhttp.NewProc("WinHttpReadData")
+ procWinHttpCrackUrl = modwinhttp.NewProc("WinHttpCrackUrl")
+)
+
+func winHttpOpen(userAgent *uint16, accessType uint32, proxy *uint16, proxyBypass *uint16, flags uint32) (sessionHandle _HINTERNET, err error) {
+ r0, _, e1 := syscall.Syscall6(procWinHttpOpen.Addr(), 5, uintptr(unsafe.Pointer(userAgent)), uintptr(accessType), uintptr(unsafe.Pointer(proxy)), uintptr(unsafe.Pointer(proxyBypass)), uintptr(flags), 0)
+ sessionHandle = _HINTERNET(r0)
+ if sessionHandle == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func winHttpSetStatusCallback(handle _HINTERNET, callback uintptr, notificationFlags uint32, reserved uintptr) (previousCallback uintptr, err error) {
+ r0, _, e1 := syscall.Syscall6(procWinHttpSetStatusCallback.Addr(), 4, uintptr(handle), uintptr(callback), uintptr(notificationFlags), uintptr(reserved), 0, 0)
+ previousCallback = uintptr(r0)
+ if previousCallback == _WINHTTP_INVALID_STATUS_CALLBACK {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func winHttpCloseHandle(handle _HINTERNET) (err error) {
+ r1, _, e1 := syscall.Syscall(procWinHttpCloseHandle.Addr(), 1, uintptr(handle), 0, 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func winHttpConnect(sessionHandle _HINTERNET, serverName *uint16, serverPort uint16, reserved uint32) (handle _HINTERNET, err error) {
+ r0, _, e1 := syscall.Syscall6(procWinHttpConnect.Addr(), 4, uintptr(sessionHandle), uintptr(unsafe.Pointer(serverName)), uintptr(serverPort), uintptr(reserved), 0, 0)
+ handle = _HINTERNET(r0)
+ if handle == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func winHttpOpenRequest(connectHandle _HINTERNET, verb *uint16, objectName *uint16, version *uint16, referrer *uint16, acceptTypes **uint16, flags uint32) (requestHandle _HINTERNET, err error) {
+ r0, _, e1 := syscall.Syscall9(procWinHttpOpenRequest.Addr(), 7, uintptr(connectHandle), uintptr(unsafe.Pointer(verb)), uintptr(unsafe.Pointer(objectName)), uintptr(unsafe.Pointer(version)), uintptr(unsafe.Pointer(referrer)), uintptr(unsafe.Pointer(acceptTypes)), uintptr(flags), 0, 0)
+ requestHandle = _HINTERNET(r0)
+ if requestHandle == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func winHttpSendRequest(requestHandle _HINTERNET, headers *uint16, headersLength uint32, optional *byte, optionalLength uint32, totalLength uint32, context uintptr) (err error) {
+ r1, _, e1 := syscall.Syscall9(procWinHttpSendRequest.Addr(), 7, uintptr(requestHandle), uintptr(unsafe.Pointer(headers)), uintptr(headersLength), uintptr(unsafe.Pointer(optional)), uintptr(optionalLength), uintptr(totalLength), uintptr(context), 0, 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func winHttpReceiveResponse(requestHandle _HINTERNET, reserved uintptr) (err error) {
+ r1, _, e1 := syscall.Syscall(procWinHttpReceiveResponse.Addr(), 2, uintptr(requestHandle), uintptr(reserved), 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func winHttpQueryHeaders(requestHandle _HINTERNET, infoLevel uint32, name *uint16, buffer unsafe.Pointer, bufferLen *uint32, index *uint32) (err error) {
+ r1, _, e1 := syscall.Syscall6(procWinHttpQueryHeaders.Addr(), 6, uintptr(requestHandle), uintptr(infoLevel), uintptr(unsafe.Pointer(name)), uintptr(buffer), uintptr(unsafe.Pointer(bufferLen)), uintptr(unsafe.Pointer(index)))
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func winHttpQueryDataAvailable(requestHandle _HINTERNET, bytesAvailable *uint32) (err error) {
+ r1, _, e1 := syscall.Syscall(procWinHttpQueryDataAvailable.Addr(), 2, uintptr(requestHandle), uintptr(unsafe.Pointer(bytesAvailable)), 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func winHttpReadData(requestHandle _HINTERNET, buffer *byte, bufferSize uint32, bytesRead *uint32) (err error) {
+ r1, _, e1 := syscall.Syscall6(procWinHttpReadData.Addr(), 4, uintptr(requestHandle), uintptr(unsafe.Pointer(buffer)), uintptr(bufferSize), uintptr(unsafe.Pointer(bytesRead)), 0, 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func winHttpCrackUrl(url *uint16, urlSize uint32, flags uint32, components *_URL_COMPONENTS) (err error) {
+ r1, _, e1 := syscall.Syscall6(procWinHttpCrackUrl.Addr(), 4, uintptr(unsafe.Pointer(url)), uintptr(urlSize), uintptr(flags), uintptr(unsafe.Pointer(components)), 0, 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}