restore update feature (#36)

* replace wg.exe with awg.exe

* update docs, update admin reg key

* rename awg modules

* build awg from source

* remove unused params

* improve work with tools

* safe rebranding: change upgrade code, windows class and name

* safe rebranding: wg -> awg

* update dependencies, fixed showing transfered KBs

* Revert "remove application update feature"

This reverts commit 9670f4298e.

* Revert "remove application update feature #2"

This reverts commit b0c96a9fd6.

* updatepage and tray change WireGuard to AmneziaWG

* move crypto from indirect to direct

---------

Signed-off-by: Roman Zakharchuk <romikb@mail.ru>
This commit is contained in:
Roman Zakharchuk
2025-03-10 04:58:37 +02:00
committed by GitHub
parent 8d0c33d304
commit a780e4a5c7
41 changed files with 1967 additions and 152 deletions

2
go.mod
View File

@@ -7,6 +7,7 @@ require (
github.com/amnezia-vpn/amneziawg-windows v0.1.4-0.20240526104134-db18f2297e5e
github.com/lxn/walk v0.0.0-20210112085537-c389da54e794
github.com/lxn/win v0.0.0-20210218163916-a377121e959e
golang.org/x/crypto v0.21.0
golang.org/x/sys v0.18.0
golang.org/x/text v0.14.0
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
@@ -14,7 +15,6 @@ require (
require (
github.com/tevino/abool/v2 v2.1.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/tools v0.6.0 // indirect

View File

@@ -714,9 +714,9 @@
"translatorComment": "Copied from source."
},
{
"id": "An update to WireGuard is available. It is highly advisable to update without delay.",
"message": "An update to WireGuard is available. It is highly advisable to update without delay.",
"translation": "Una actualització per WireGuard està disponible. Es recomana actualitzar immediatament.",
"id": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"message": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"translation": "Una actualització per AmneziaWG està disponible. Es recomana actualitzar immediatament.",
"translatorComment": "Copied from source."
},
{

View File

@@ -1308,15 +1308,15 @@
"translatorComment": "Copied from source."
},
{
"id": "WireGuard Update Available",
"message": "WireGuard Update Available",
"translation": "Aktualizace WireGuard je k dispozici",
"id": "AmneziaWG Update Available",
"message": "AmneziaWG Update Available",
"translation": "Aktualizace AmneziaWG je k dispozici",
"translatorComment": "Copied from source."
},
{
"id": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"message": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"translation": "Aktualizace aplikace WireGuard je nyní k dispozici. Doporučujeme ji aktualizovat co nejdříve.",
"id": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"message": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"translation": "Aktualizace aplikace AmneziaWG je nyní k dispozici. Doporučujeme ji aktualizovat co nejdříve.",
"translatorComment": "Copied from source."
},
{
@@ -1778,9 +1778,9 @@
]
},
{
"id": "An update to WireGuard is available. It is highly advisable to update without delay.",
"message": "An update to WireGuard is available. It is highly advisable to update without delay.",
"translation": "Aktualizace aplikace WireGuard je nyní k dispozici. Silně doporučujeme ji aktualizovat co nejdříve.",
"id": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"message": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"translation": "Aktualizace aplikace AmneziaWG je nyní k dispozici. Silně doporučujeme ji aktualizovat co nejdříve.",
"translatorComment": "Copied from source."
},
{

View File

@@ -1278,15 +1278,15 @@
"translatorComment": "Copied from source."
},
{
"id": "WireGuard Update Available",
"message": "WireGuard Update Available",
"translation": "WireGuard Aktualisierung verfügbar",
"id": "AmneziaWG Update Available",
"message": "AmneziaWG Update Available",
"translation": "AmneziaWG Aktualisierung verfügbar",
"translatorComment": "Copied from source."
},
{
"id": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"message": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"translation": "Eine Aktualisierung für WireGuard ist jetzt verfügbar. Es wird empfohlen diese schnellstmöglich durchzuführen.",
"id": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"message": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"translation": "Eine Aktualisierung für AmneziaWG ist jetzt verfügbar. Es wird empfohlen diese schnellstmöglich durchzuführen.",
"translatorComment": "Copied from source."
},
{
@@ -1718,9 +1718,9 @@
]
},
{
"id": "An update to WireGuard is available. It is highly advisable to update without delay.",
"message": "An update to WireGuard is available. It is highly advisable to update without delay.",
"translation": "Eine Aktualisierung für WireGuard ist verfügbar. Es ist höchst empfehlenswert diese sofort durchzuführen.",
"id": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"message": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"translation": "Eine Aktualisierung für AmneziaWG ist verfügbar. Es ist höchst empfehlenswert diese sofort durchzuführen.",
"translatorComment": "Copied from source."
},
{

View File

@@ -1351,16 +1351,16 @@
"fuzzy": true
},
{
"id": "WireGuard Update Available",
"message": "WireGuard Update Available",
"translation": "WireGuard Update Available",
"id": "AmneziaWG Update Available",
"message": "AmneziaWG Update Available",
"translation": "AmneziaWG Update Available",
"translatorComment": "Copied from source.",
"fuzzy": true
},
{
"id": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"message": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"translation": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"id": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"message": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"translation": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"translatorComment": "Copied from source.",
"fuzzy": true
},
@@ -1830,9 +1830,9 @@
"fuzzy": true
},
{
"id": "An update to WireGuard is available. It is highly advisable to update without delay.",
"message": "An update to WireGuard is available. It is highly advisable to update without delay.",
"translation": "An update to WireGuard is available. It is highly advisable to update without delay.",
"id": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"message": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"translation": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"translatorComment": "Copied from source.",
"fuzzy": true
},

View File

@@ -758,15 +758,15 @@
"translatorComment": "Copied from source."
},
{
"id": "WireGuard Update Available",
"message": "WireGuard Update Available",
"translation": "Actualización de WireGuard disponible",
"id": "AmneziaWG Update Available",
"message": "AmneziaWG Update Available",
"translation": "Actualización de AmneziaWG disponible",
"translatorComment": "Copied from source."
},
{
"id": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"message": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"translation": "Ya está disponible una actualización de WireGuard. Se recomienda actualizar lo antes posible.",
"id": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"message": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"translation": "Ya está disponible una actualización de AmneziaWG. Se recomienda actualizar lo antes posible.",
"translatorComment": "Copied from source."
},
{

View File

@@ -594,9 +594,9 @@
"translatorComment": "Copied from source."
},
{
"id": "WireGuard Update Available",
"message": "WireGuard Update Available",
"translation": "به‌روزرسانی WireGuard در دسترس است",
"id": "AmneziaWG Update Available",
"message": "AmneziaWG Update Available",
"translation": "به‌روزرسانی AmneziaWG در دسترس است",
"translatorComment": "Copied from source."
},
{

View File

@@ -342,15 +342,15 @@
"translatorComment": "Copied from source."
},
{
"id": "WireGuard Update Available",
"message": "WireGuard Update Available",
"translation": "WireGuard päivitys saatavilla",
"id": "AmneziaWG Update Available",
"message": "AmneziaWG Update Available",
"translation": "AmneziaWG päivitys saatavilla",
"translatorComment": "Copied from source."
},
{
"id": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"message": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"translation": "WireGuardin päivitys on nyt saatavilla. Sinua kehotetaan päivittämään mahdollisimman pian.",
"id": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"message": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"translation": "AmneziaWGin päivitys on nyt saatavilla. Sinua kehotetaan päivittämään mahdollisimman pian.",
"translatorComment": "Copied from source."
},
{

View File

@@ -1278,15 +1278,15 @@
"translatorComment": "Copied from source."
},
{
"id": "WireGuard Update Available",
"message": "WireGuard Update Available",
"translation": "WireGuard mise à jour est disponible",
"id": "AmneziaWG Update Available",
"message": "AmneziaWG Update Available",
"translation": "AmneziaWG mise à jour est disponible",
"translatorComment": "Copied from source."
},
{
"id": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"message": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"translation": "Une mise à jour du WireGuard est disponible. Il est conseillé de mettre votre WireGuard à jour dès que possible.",
"id": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"message": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"translation": "Une mise à jour du AmneziaWG est disponible. Il est conseillé de mettre votre AmneziaWG à jour dès que possible.",
"translatorComment": "Copied from source."
},
{
@@ -1718,9 +1718,9 @@
]
},
{
"id": "An update to WireGuard is available. It is highly advisable to update without delay.",
"message": "An update to WireGuard is available. It is highly advisable to update without delay.",
"translation": "Une mise à jour du WireGuard est disponible. Il est fortement conseillé de metter votre WireGuard à jour sans délai.",
"id": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"message": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"translation": "Une mise à jour du AmneziaWG est disponible. Il est fortement conseillé de metter votre AmneziaWG à jour sans délai.",
"translatorComment": "Copied from source."
},
{

View File

@@ -1278,15 +1278,15 @@
"translatorComment": "Copied from source."
},
{
"id": "WireGuard Update Available",
"message": "WireGuard Update Available",
"translation": "Aggiornamento di WireGuard disponibile",
"id": "AmneziaWG Update Available",
"message": "AmneziaWG Update Available",
"translation": "Aggiornamento di AmneziaWG disponibile",
"translatorComment": "Copied from source."
},
{
"id": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"message": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"translation": "Un aggiornamento di WireGuard è disponibile. Ti consigliamo di aggiornare il prima possibile.",
"id": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"message": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"translation": "Un aggiornamento di AmneziaWG è disponibile. Ti consigliamo di aggiornare il prima possibile.",
"translatorComment": "Copied from source."
},
{
@@ -1718,9 +1718,9 @@
]
},
{
"id": "An update to WireGuard is available. It is highly advisable to update without delay.",
"message": "An update to WireGuard is available. It is highly advisable to update without delay.",
"translation": "Un aggiornamento di WireGuard è disponibile. Ti consigliamo vivamente di aggiornare immediatamente.",
"id": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"message": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"translation": "Un aggiornamento di AmneziaWG è disponibile. Ti consigliamo vivamente di aggiornare immediatamente.",
"translatorComment": "Copied from source."
},
{

View File

@@ -1263,15 +1263,15 @@
"translatorComment": "Copied from source."
},
{
"id": "WireGuard Update Available",
"message": "WireGuard Update Available",
"translation": "WireGuard の更新が利用可能です",
"id": "AmneziaWG Update Available",
"message": "AmneziaWG Update Available",
"translation": "AmneziaWG の更新が利用可能です",
"translatorComment": "Copied from source."
},
{
"id": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"message": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"translation": "WireGuard の更新が利用可能になりました。できるだけ早く更新してください。",
"id": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"message": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"translation": "AmneziaWG の更新が利用可能になりました。できるだけ早く更新してください。",
"translatorComment": "Copied from source."
},
{
@@ -1688,9 +1688,9 @@
]
},
{
"id": "An update to WireGuard is available. It is highly advisable to update without delay.",
"message": "An update to WireGuard is available. It is highly advisable to update without delay.",
"translation": "WireGuard の更新が利用可能です。速やかに更新することを強く推奨します。",
"id": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"message": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"translation": "AmneziaWG の更新が利用可能です。速やかに更新することを強く推奨します。",
"translatorComment": "Copied from source."
},
{

View File

@@ -1096,14 +1096,14 @@
"translatorComment": "Copied from source."
},
{
"id": "WireGuard Update Available",
"message": "WireGuard Update Available",
"id": "AmneziaWG Update Available",
"message": "AmneziaWG Update Available",
"translation": "ਵਾਇਰਗਾਰਡ ਅੱਪਡੇਟ ਮੌਜੂਦ ਹੈ",
"translatorComment": "Copied from source."
},
{
"id": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"message": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"id": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"message": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"translation": "ਵਾਇਰਗਾਰਡ ਲਈ ਅੱਪਡੇਟ ਹੁਣ ਮੌਜੂਦ ਹੈ। ਜਿੰਨਾ ਛੇਤੀ ਹੋ ਸਕੇ ਤੁਹਾਨੂੰ ਅੱਪਡੇਟ ਕਰਨ ਦੀ ਸਲਾਹ ਦਿੱਤੀ ਜਾਂਦੀ ਹੈ।",
"translatorComment": "Copied from source."
},
@@ -1418,9 +1418,9 @@
]
},
{
"id": "An update to WireGuard is available. It is highly advisable to update without delay.",
"message": "An update to WireGuard is available. It is highly advisable to update without delay.",
"translation": "WireGuard ਲਈ ਅੱਪਡੇਟ ਮੌਜੂਦ ਹੈ। ਤੁਹਾਨੂੰ ਬਿਨਾਂ ਦੇਰ ਕੀਤਿਆਂ ਅੱਪਡੇਟ ਕਰਨ ਦੀ ਸਲਾਹ ਦਿੱਤੀ ਜਾਂਦੀ ਹੈ।",
"id": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"message": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"translation": "AmneziaWG ਲਈ ਅੱਪਡੇਟ ਮੌਜੂਦ ਹੈ। ਤੁਹਾਨੂੰ ਬਿਨਾਂ ਦੇਰ ਕੀਤਿਆਂ ਅੱਪਡੇਟ ਕਰਨ ਦੀ ਸਲਾਹ ਦਿੱਤੀ ਜਾਂਦੀ ਹੈ।",
"translatorComment": "Copied from source."
},
{

View File

@@ -1308,15 +1308,15 @@
"translatorComment": "Copied from source."
},
{
"id": "WireGuard Update Available",
"message": "WireGuard Update Available",
"translation": "Aktualizacja WireGuard jest dostępna",
"id": "AmneziaWG Update Available",
"message": "AmneziaWG Update Available",
"translation": "Aktualizacja AmneziaWG jest dostępna",
"translatorComment": "Copied from source."
},
{
"id": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"message": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"translation": "Aktualizacja WireGuard jest już dostępna. Zaleca się jak najszybszą aktualizację.",
"id": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"message": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"translation": "Aktualizacja AmneziaWG jest już dostępna. Zaleca się jak najszybszą aktualizację.",
"translatorComment": "Copied from source."
},
{
@@ -1778,9 +1778,9 @@
]
},
{
"id": "An update to WireGuard is available. It is highly advisable to update without delay.",
"message": "An update to WireGuard is available. It is highly advisable to update without delay.",
"translation": "Aktualizacja WireGuard jest dostępna. Zaleca się natychmiastową aktualizację.",
"id": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"message": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"translation": "Aktualizacja AmneziaWG jest dostępna. Zaleca się natychmiastową aktualizację.",
"translatorComment": "Copied from source."
},
{

View File

@@ -1293,15 +1293,15 @@
"translatorComment": "Copied from source."
},
{
"id": "WireGuard Update Available",
"message": "WireGuard Update Available",
"translation": "Actualizare disponibilă pentru WireGuard",
"id": "AmneziaWG Update Available",
"message": "AmneziaWG Update Available",
"translation": "Actualizare disponibilă pentru AmneziaWG",
"translatorComment": "Copied from source."
},
{
"id": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"message": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"translation": "O actualizare pentru WireGuard este acum disponibilă. Se recomandă efectuarea actualizării cât mai rapid posibil.",
"id": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"message": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"translation": "O actualizare pentru AmneziaWG este acum disponibilă. Se recomandă efectuarea actualizării cât mai rapid posibil.",
"translatorComment": "Copied from source."
},
{
@@ -1748,9 +1748,9 @@
]
},
{
"id": "An update to WireGuard is available. It is highly advisable to update without delay.",
"message": "An update to WireGuard is available. It is highly advisable to update without delay.",
"translation": "Este disponibilă o actualizare pentru WireGuard. Se recomandă ferm actualizarea imediată.",
"id": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"message": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"translation": "Este disponibilă o actualizare pentru AmneziaWG. Se recomandă ferm actualizarea imediată.",
"translatorComment": "Copied from source."
},
{

View File

@@ -1308,15 +1308,15 @@
"translatorComment": "Copied from source."
},
{
"id": "WireGuard Update Available",
"message": "WireGuard Update Available",
"translation": "Доступно обновление WireGuard",
"id": "AmneziaWG Update Available",
"message": "AmneziaWG Update Available",
"translation": "Доступно обновление AmneziaWG",
"translatorComment": "Copied from source."
},
{
"id": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"message": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"translation": "Доступно обновление для WireGuard. Рекомендуется обновить его как можно скорее.",
"id": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"message": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"translation": "Доступно обновление для AmneziaWG. Рекомендуется обновить его как можно скорее.",
"translatorComment": "Copied from source."
},
{
@@ -1788,9 +1788,9 @@
]
},
{
"id": "An update to WireGuard is available. It is highly advisable to update without delay.",
"message": "An update to WireGuard is available. It is highly advisable to update without delay.",
"translation": "Доступно обновление WireGuard. Настоятельно рекомендуем обновить приложение.",
"id": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"message": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"translation": "Доступно обновление AmneziaWG. Настоятельно рекомендуем обновить приложение.",
"translatorComment": "Copied from source."
},
{

View File

@@ -1308,15 +1308,15 @@
"translatorComment": "Copied from source."
},
{
"id": "WireGuard Update Available",
"message": "WireGuard Update Available",
"translation": "Dostupná aktualizácia pre WireGuard",
"id": "AmneziaWG Update Available",
"message": "AmneziaWG Update Available",
"translation": "Dostupná aktualizácia pre AmneziaWG",
"translatorComment": "Copied from source."
},
{
"id": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"message": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"translation": "Je k dispozícii aktualizácia programu WireGuard. Je odporúčané čo najskôr vykonať aktualizáciu.",
"id": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"message": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"translation": "Je k dispozícii aktualizácia programu AmneziaWG. Je odporúčané čo najskôr vykonať aktualizáciu.",
"translatorComment": "Copied from source."
},
{
@@ -1778,9 +1778,9 @@
]
},
{
"id": "An update to WireGuard is available. It is highly advisable to update without delay.",
"message": "An update to WireGuard is available. It is highly advisable to update without delay.",
"translation": "Je k dispozícii nová verzia programu WireGuard. Odporúčame bezodkladne vykonať aktualizáciu.",
"id": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"message": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"translation": "Je k dispozícii nová verzia programu AmneziaWG. Odporúčame bezodkladne vykonať aktualizáciu.",
"translatorComment": "Copied from source."
},
{

View File

@@ -1308,15 +1308,15 @@
"translatorComment": "Copied from source."
},
{
"id": "WireGuard Update Available",
"message": "WireGuard Update Available",
"translation": "Posodobitev WireGuarda je na voljo",
"id": "AmneziaWG Update Available",
"message": "AmneziaWG Update Available",
"translation": "Posodobitev AmneziaWGa je na voljo",
"translatorComment": "Copied from source."
},
{
"id": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"message": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"translation": "Posodobitev WireGuarda je na voljo. Svetujemo posodobitev čim prej.",
"id": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"message": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"translation": "Posodobitev AmneziaWGa je na voljo. Svetujemo posodobitev čim prej.",
"translatorComment": "Copied from source."
},
{
@@ -1778,9 +1778,9 @@
]
},
{
"id": "An update to WireGuard is available. It is highly advisable to update without delay.",
"message": "An update to WireGuard is available. It is highly advisable to update without delay.",
"translation": "Posodobitev WireGuarda je na voljo. Zelo priporočamo posodobitev brez odlašanja.",
"id": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"message": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"translation": "Posodobitev AmneziaWG je na voljo. Zelo priporočamo posodobitev brez odlašanja.",
"translatorComment": "Copied from source."
},
{

View File

@@ -1278,15 +1278,15 @@
"translatorComment": "Copied from source."
},
{
"id": "WireGuard Update Available",
"message": "WireGuard Update Available",
"translation": "WireGuard Güncellemesi Mevcut",
"id": "AmneziaWG Update Available",
"message": "AmneziaWG Update Available",
"translation": "AmneziaWG Güncellemesi Mevcut",
"translatorComment": "Copied from source."
},
{
"id": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"message": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"translation": "WireGuard için bir güncelleme mevcut. İlk fırsatta güncelleme yapmanız tavsiye edilir.",
"id": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"message": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"translation": "AmneziaWG için bir güncelleme mevcut. İlk fırsatta güncelleme yapmanız tavsiye edilir.",
"translatorComment": "Copied from source."
},
{
@@ -1718,9 +1718,9 @@
]
},
{
"id": "An update to WireGuard is available. It is highly advisable to update without delay.",
"message": "An update to WireGuard is available. It is highly advisable to update without delay.",
"translation": "WireGuard için bir güncelleme mevcut. Bekletmeden güncelleme yapmanız önemle tavsiye edilir.",
"id": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"message": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"translation": "AmneziaWG için bir güncelleme mevcut. Bekletmeden güncelleme yapmanız önemle tavsiye edilir.",
"translatorComment": "Copied from source."
},
{

View File

@@ -1263,15 +1263,15 @@
"translatorComment": "Copied from source."
},
{
"id": "WireGuard Update Available",
"message": "WireGuard Update Available",
"translation": "WireGuard 更新",
"id": "AmneziaWG Update Available",
"message": "AmneziaWG Update Available",
"translation": "AmneziaWG 更新",
"translatorComment": "Copied from source."
},
{
"id": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"message": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"translation": "新的 WireGuard 版本发布了。强烈建议您现在安装。",
"id": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"message": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"translation": "新的 AmneziaWG 版本发布了。强烈建议您现在安装。",
"translatorComment": "Copied from source."
},
{
@@ -1688,9 +1688,9 @@
]
},
{
"id": "An update to WireGuard is available. It is highly advisable to update without delay.",
"message": "An update to WireGuard is available. It is highly advisable to update without delay.",
"translation": "发现新版 WireGuard。强烈建议您现在安装。",
"id": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"message": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"translation": "发现新版 AmneziaWG。强烈建议您现在安装。",
"translatorComment": "Copied from source."
},
{

View File

@@ -1251,15 +1251,15 @@
"translatorComment": "Copied from source."
},
{
"id": "WireGuard Update Available",
"message": "WireGuard Update Available",
"translation": "WireGuard 更新",
"id": "AmneziaWG Update Available",
"message": "AmneziaWG Update Available",
"translation": "AmneziaWG 更新",
"translatorComment": "Copied from source."
},
{
"id": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"message": "An update to WireGuard is now available. You are advised to update as soon as possible.",
"translation": "更新的 WireGuard 已經為您準備好了。\n強烈建議您立即更新 WireGuard。",
"id": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"message": "An update to AmneziaWG is now available. You are advised to update as soon as possible.",
"translation": "更新的 AmneziaWG 已經為您準備好了。\n強烈建議您立即更新 AmneziaWG。",
"translatorComment": "Copied from source."
},
{
@@ -1676,9 +1676,9 @@
]
},
{
"id": "An update to WireGuard is available. It is highly advisable to update without delay.",
"message": "An update to WireGuard is available. It is highly advisable to update without delay.",
"translation": "更新的 WireGuard 已經為您準備好了。\n強烈建議您立即進行更新。",
"id": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"message": "An update to AmneziaWG is available. It is highly advisable to update without delay.",
"translation": "更新的 AmneziaWG 已經為您準備好了。\n強烈建議您立即進行更新。",
"translatorComment": "Copied from source."
},
{

26
main.go
View File

@@ -25,6 +25,7 @@ import (
"github.com/amnezia-vpn/amneziawg-windows-client/manager"
"github.com/amnezia-vpn/amneziawg-windows-client/ringlogger"
"github.com/amnezia-vpn/amneziawg-windows-client/ui"
"github.com/amnezia-vpn/amneziawg-windows-client/updater"
)
func setLogFile() {
@@ -71,6 +72,7 @@ func usage() {
"/tunnelservice CONFIG_PATH",
"/ui CMD_READ_HANDLE CMD_WRITE_HANDLE CMD_EVENT_HANDLE LOG_MAPPING_HANDLE",
"/dumplog [/tail]",
"/update",
}
builder := strings.Builder{}
for _, flag := range flags {
@@ -286,6 +288,30 @@ func main() {
fatal(err)
}
return
case "/update":
if len(os.Args) != 2 {
usage()
}
for progress := range updater.DownloadVerifyAndExecute(0) {
if len(progress.Activity) > 0 {
if progress.BytesTotal > 0 || progress.BytesDownloaded > 0 {
var percent float64
if progress.BytesTotal > 0 {
percent = float64(progress.BytesDownloaded) / float64(progress.BytesTotal) * 100.0
}
log.Printf("%s: %d/%d (%.2f%%)\n", progress.Activity, progress.BytesDownloaded, progress.BytesTotal, percent)
} else {
log.Println(progress.Activity)
}
}
if progress.Error != nil {
log.Printf("Error: %v\n", progress.Error)
}
if progress.Complete || progress.Error != nil {
return
}
}
return
}
usage()
}

View File

@@ -11,6 +11,7 @@ import (
"os"
"sync"
"github.com/amnezia-vpn/amneziawg-windows-client/updater"
"github.com/amnezia-vpn/amneziawg-windows/conf"
)
@@ -34,6 +35,8 @@ const (
TunnelChangeNotificationType NotificationType = iota
TunnelsChangeNotificationType
ManagerStoppingNotificationType
UpdateFoundNotificationType
UpdateProgressNotificationType
)
type MethodType int
@@ -50,6 +53,8 @@ const (
CreateMethodType
TunnelsMethodType
QuitMethodType
UpdateStateMethodType
UpdateMethodType
)
var (
@@ -76,6 +81,18 @@ type ManagerStoppingCallback struct {
var managerStoppingCallbacks = make(map[*ManagerStoppingCallback]bool)
type UpdateFoundCallback struct {
cb func(updateState UpdateState)
}
var updateFoundCallbacks = make(map[*UpdateFoundCallback]bool)
type UpdateProgressCallback struct {
cb func(dp updater.DownloadProgress)
}
var updateProgressCallbacks = make(map[*UpdateProgressCallback]bool)
func InitializeIPCClient(reader, writer, events *os.File) {
rpcDecoder = gob.NewDecoder(reader)
rpcEncoder = gob.NewEncoder(writer)
@@ -128,6 +145,44 @@ func InitializeIPCClient(reader, writer, events *os.File) {
for cb := range managerStoppingCallbacks {
cb.cb()
}
case UpdateFoundNotificationType:
var state UpdateState
err = decoder.Decode(&state)
if err != nil {
continue
}
for cb := range updateFoundCallbacks {
cb.cb(state)
}
case UpdateProgressNotificationType:
var dp updater.DownloadProgress
err = decoder.Decode(&dp.Activity)
if err != nil {
continue
}
err = decoder.Decode(&dp.BytesDownloaded)
if err != nil {
continue
}
err = decoder.Decode(&dp.BytesTotal)
if err != nil {
continue
}
var errStr string
err = decoder.Decode(&errStr)
if err != nil {
continue
}
if len(errStr) > 0 {
dp.Error = errors.New(errStr)
}
err = decoder.Decode(&dp.Complete)
if err != nil {
continue
}
for cb := range updateProgressCallbacks {
cb.cb(dp)
}
}
}
}()
@@ -354,6 +409,28 @@ func IPCClientQuit(stopTunnelsOnQuit bool) (alreadyQuit bool, err error) {
return
}
func IPCClientUpdateState() (updateState UpdateState, err error) {
rpcMutex.Lock()
defer rpcMutex.Unlock()
err = rpcEncoder.Encode(UpdateStateMethodType)
if err != nil {
return
}
err = rpcDecoder.Decode(&updateState)
if err != nil {
return
}
return
}
func IPCClientUpdate() error {
rpcMutex.Lock()
defer rpcMutex.Unlock()
return rpcEncoder.Encode(UpdateMethodType)
}
func IPCClientRegisterTunnelChange(cb func(tunnel *Tunnel, state, globalState TunnelState, err error)) *TunnelChangeCallback {
s := &TunnelChangeCallback{cb}
tunnelChangeCallbacks[s] = true
@@ -383,3 +460,23 @@ func IPCClientRegisterManagerStopping(cb func()) *ManagerStoppingCallback {
func (cb *ManagerStoppingCallback) Unregister() {
delete(managerStoppingCallbacks, cb)
}
func IPCClientRegisterUpdateFound(cb func(updateState UpdateState)) *UpdateFoundCallback {
s := &UpdateFoundCallback{cb}
updateFoundCallbacks[s] = true
return s
}
func (cb *UpdateFoundCallback) Unregister() {
delete(updateFoundCallbacks, cb)
}
func IPCClientRegisterUpdateProgress(cb func(dp updater.DownloadProgress)) *UpdateProgressCallback {
s := &UpdateProgressCallback{cb}
updateProgressCallbacks[s] = true
return s
}
func (cb *UpdateProgressCallback) Unregister() {
delete(updateProgressCallbacks, cb)
}

View File

@@ -19,6 +19,7 @@ import (
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/svc"
"github.com/amnezia-vpn/amneziawg-windows-client/updater"
"github.com/amnezia-vpn/amneziawg-windows/conf"
"github.com/amnezia-vpn/amneziawg-windows/services"
)
@@ -268,6 +269,26 @@ func (s *ManagerService) Quit(stopTunnelsOnQuit bool) (alreadyQuit bool, err err
return false, nil
}
func (s *ManagerService) UpdateState() UpdateState {
return updateState
}
func (s *ManagerService) Update() {
if s.elevatedToken == 0 {
return
}
progress := updater.DownloadVerifyAndExecute(uintptr(s.elevatedToken))
go func() {
for {
dp := <-progress
IPCServerNotifyUpdateProgress(dp)
if dp.Complete || dp.Error != nil {
return
}
}
}()
}
func (s *ManagerService) ServeConn(reader io.Reader, writer io.Writer) {
decoder := gob.NewDecoder(reader)
encoder := gob.NewEncoder(writer)
@@ -422,6 +443,14 @@ func (s *ManagerService) ServeConn(reader io.Reader, writer io.Writer) {
if err != nil {
return
}
case UpdateStateMethodType:
updateState := s.UpdateState()
err = encoder.Encode(updateState)
if err != nil {
return
}
case UpdateMethodType:
s.Update()
default:
return
}
@@ -498,6 +527,14 @@ func IPCServerNotifyTunnelsChange() {
notifyAll(TunnelsChangeNotificationType, false)
}
func IPCServerNotifyUpdateFound(state UpdateState) {
notifyAll(UpdateFoundNotificationType, false, state)
}
func IPCServerNotifyUpdateProgress(dp updater.DownloadProgress) {
notifyAll(UpdateProgressNotificationType, true, dp.Activity, dp.BytesDownloaded, dp.BytesTotal, errToString(dp.Error), dp.Complete)
}
func IPCServerNotifyManagerStopping() {
notifyAll(ManagerStoppingNotificationType, false)
time.Sleep(time.Millisecond * 200)

View File

@@ -259,6 +259,8 @@ func (service *managerService) Execute(args []string, r <-chan svc.ChangeRequest
}()
}
go checkForUpdates()
var sessionsPointer *windows.WTS_SESSION_INFO
var count uint32
err = windows.WTSEnumerateSessions(0, 0, 1, &sessionsPointer, &count)

65
manager/updatestate.go Normal file
View File

@@ -0,0 +1,65 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
*/
package manager
import (
"log"
"time"
_ "unsafe"
"github.com/amnezia-vpn/amneziawg-windows-client/services"
"github.com/amnezia-vpn/amneziawg-windows-client/updater"
"github.com/amnezia-vpn/amneziawg-windows-client/version"
)
//go:linkname fastrandn runtime.fastrandn
func fastrandn(n uint32) uint32
type UpdateState uint32
const (
UpdateStateUnknown UpdateState = iota
UpdateStateFoundUpdate
UpdateStateUpdatesDisabledUnofficialBuild
)
var updateState = UpdateStateUnknown
func jitterSleep(min, max time.Duration) {
time.Sleep(min + time.Millisecond*time.Duration(fastrandn(uint32((max-min+1)/time.Millisecond))))
}
func checkForUpdates() {
if !version.IsRunningOfficialVersion() {
log.Println("Build is not official, so updates are disabled")
updateState = UpdateStateUpdatesDisabledUnofficialBuild
IPCServerNotifyUpdateFound(updateState)
return
}
if services.StartedAtBoot() {
jitterSleep(time.Minute*2, time.Minute*5)
}
noError, didNotify := true, false
for {
update, err := updater.CheckForUpdate()
if err == nil && update != nil && !didNotify {
log.Println("An update is available")
updateState = UpdateStateFoundUpdate
IPCServerNotifyUpdateFound(updateState)
didNotify = true
} else if err != nil && !didNotify {
log.Printf("Update checker: %v", err)
if noError {
jitterSleep(time.Minute*4, time.Minute*6)
noError = false
} else {
jitterSleep(time.Minute*25, time.Minute*30)
}
} else {
jitterSleep(time.Hour-time.Minute*3, time.Hour+time.Minute*3)
}
}
}

View File

@@ -23,6 +23,7 @@ type ManageTunnelsWindow struct {
tabs *walk.TabWidget
tunnelsPage *TunnelsPage
logPage *LogPage
updatePage *UpdatePage
tunnelChangedCB *manager.TunnelChangeCallback
}
@@ -174,6 +175,20 @@ func (mtw *ManageTunnelsWindow) onTunnelChange(tunnel *manager.Tunnel, state, gl
})
}
func (mtw *ManageTunnelsWindow) UpdateFound() {
if mtw.updatePage != nil {
return
}
if IsAdmin {
mtw.SetTitle(l18n.Sprintf("%s (out of date)", mtw.Title()))
}
updatePage, err := NewUpdatePage()
if err == nil {
mtw.updatePage = updatePage
mtw.tabs.Pages().Add(updatePage.TabPage)
}
}
func (mtw *ManageTunnelsWindow) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
switch msg {
case win.WM_QUERYENDSESSION:

View File

@@ -8,6 +8,7 @@ package ui
import (
"sort"
"strings"
"time"
"github.com/amnezia-vpn/amneziawg-windows-client/l18n"
"github.com/amnezia-vpn/amneziawg-windows-client/manager"
@@ -339,6 +340,38 @@ func (tray *Tray) setTunnelState(tunnel *manager.Tunnel, state manager.TunnelSta
}
}
func (tray *Tray) UpdateFound() {
action := walk.NewAction()
action.SetText(l18n.Sprintf("An Update is Available!"))
menuIcon, _ := loadShieldIcon(16)
action.SetImage(menuIcon)
action.SetDefault(true)
showUpdateTab := func() {
if !tray.mtw.Visible() {
tray.mtw.tunnelsPage.listView.SelectFirstActiveTunnel()
}
tray.mtw.tabs.SetCurrentIndex(2)
raise(tray.mtw.Handle())
}
action.Triggered().Attach(showUpdateTab)
tray.clicked = showUpdateTab
tray.ContextMenu().Actions().Insert(tray.ContextMenu().Actions().Len()-2, action)
showUpdateBalloon := func() {
icon, _ := loadShieldIcon(128)
tray.ShowCustom(l18n.Sprintf("AmneziaWG Update Available"), l18n.Sprintf("An update to AmneziaWG is now available. You are advised to update as soon as possible."), icon)
}
timeSinceStart := time.Now().Sub(startTime)
if timeSinceStart < time.Second*3 {
time.AfterFunc(time.Second*3-timeSinceStart, func() {
tray.mtw.Synchronize(showUpdateBalloon)
})
} else {
showUpdateBalloon()
}
}
func (tray *Tray) onManageTunnels() {
tray.mtw.tunnelsPage.listView.SelectFirstActiveTunnel()
tray.mtw.tabs.SetCurrentIndex(0)

View File

@@ -67,6 +67,30 @@ func RunUI() {
})
})
onUpdateNotification := func(updateState manager.UpdateState) {
if updateState == manager.UpdateStateUnknown {
return
}
mtw.Synchronize(func() {
switch updateState {
case manager.UpdateStateFoundUpdate:
mtw.UpdateFound()
if tray != nil && IsAdmin {
tray.UpdateFound()
}
case manager.UpdateStateUpdatesDisabledUnofficialBuild:
mtw.SetTitle(l18n.Sprintf("%s (unsigned build, no updates)", mtw.Title()))
}
})
}
manager.IPCClientRegisterUpdateFound(onUpdateNotification)
go func() {
updateState, err := manager.IPCClientUpdateState()
if err == nil {
onUpdateNotification(updateState)
}
}()
if tray == nil {
win.ShowWindow(mtw.Handle(), win.SW_MINIMIZE)
}

142
ui/updatepage.go Normal file
View File

@@ -0,0 +1,142 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
*/
package ui
import (
"github.com/lxn/walk"
"github.com/amnezia-vpn/amneziawg-windows-client/l18n"
"github.com/amnezia-vpn/amneziawg-windows-client/manager"
"github.com/amnezia-vpn/amneziawg-windows-client/updater"
)
type UpdatePage struct {
*walk.TabPage
}
func NewUpdatePage() (*UpdatePage, error) {
var err error
var disposables walk.Disposables
defer disposables.Treat()
up := &UpdatePage{}
if up.TabPage, err = walk.NewTabPage(); err != nil {
return nil, err
}
disposables.Add(up)
up.SetTitle(l18n.Sprintf("An Update is Available!"))
tabIcon, _ := loadShieldIcon(16)
up.SetImage(tabIcon)
up.SetLayout(walk.NewVBoxLayout())
instructions, err := walk.NewTextLabel(up)
if err != nil {
return nil, err
}
instructions.SetText(l18n.Sprintf("An update to AmneziaWG is available. It is highly advisable to update without delay."))
instructions.SetMinMaxSize(walk.Size{1, 0}, walk.Size{0, 0})
status, err := walk.NewTextLabel(up)
if err != nil {
return nil, err
}
status.SetText(l18n.Sprintf("Status: Waiting for user"))
status.SetMinMaxSize(walk.Size{1, 0}, walk.Size{0, 0})
bar, err := walk.NewProgressBar(up)
if err != nil {
return nil, err
}
bar.SetVisible(false)
button, err := walk.NewPushButton(up)
if err != nil {
return nil, err
}
updateIcon, _ := loadSystemIcon("shell32", -47, 32)
button.SetImage(updateIcon)
button.SetText(l18n.Sprintf("Update Now"))
if !IsAdmin {
button.SetText(l18n.Sprintf("Please ask the system administrator to update."))
button.SetEnabled(false)
status.SetText(l18n.Sprintf("Status: Waiting for administrator"))
}
walk.NewVSpacer(up)
switchToUpdatingState := func() {
if !bar.Visible() {
up.SetSuspended(true)
button.SetEnabled(false)
button.SetVisible(false)
bar.SetVisible(true)
bar.SetMarqueeMode(true)
up.SetSuspended(false)
status.SetText(l18n.Sprintf("Status: Waiting for updater service"))
}
}
switchToReadyState := func() {
if bar.Visible() {
up.SetSuspended(true)
bar.SetVisible(false)
bar.SetValue(0)
bar.SetRange(0, 1)
bar.SetMarqueeMode(false)
button.SetVisible(true)
button.SetEnabled(true)
up.SetSuspended(false)
}
}
button.Clicked().Attach(func() {
switchToUpdatingState()
err := manager.IPCClientUpdate()
if err != nil {
switchToReadyState()
status.SetText(l18n.Sprintf("Error: %v. Please try again.", err))
}
})
manager.IPCClientRegisterUpdateProgress(func(dp updater.DownloadProgress) {
up.Synchronize(func() {
switchToUpdatingState()
if dp.Error != nil {
switchToReadyState()
err := dp.Error
status.SetText(l18n.Sprintf("Error: %v. Please try again.", err))
return
}
if len(dp.Activity) > 0 {
stateText := dp.Activity
status.SetText(l18n.Sprintf("Status: %s", stateText))
}
if dp.BytesTotal > 0 {
bar.SetMarqueeMode(false)
bar.SetRange(0, int(dp.BytesTotal))
bar.SetValue(int(dp.BytesDownloaded))
} else {
bar.SetMarqueeMode(true)
bar.SetValue(0)
bar.SetRange(0, 1)
}
if dp.Complete {
switchToReadyState()
status.SetText(l18n.Sprintf("Status: Complete!"))
return
}
})
})
disposables.Spare()
return up, nil
}

34
updater/authenticode.go Normal file
View File

@@ -0,0 +1,34 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
*/
package updater
import (
"unsafe"
"golang.org/x/sys/windows"
)
func verifyAuthenticode(path string) bool {
path16, err := windows.UTF16PtrFromString(path)
if err != nil {
return false
}
data := &windows.WinTrustData{
Size: uint32(unsafe.Sizeof(windows.WinTrustData{})),
UIChoice: windows.WTD_UI_NONE,
RevocationChecks: windows.WTD_REVOKE_WHOLECHAIN, // Full revocation checking, as this is called with network connectivity.
UnionChoice: windows.WTD_CHOICE_FILE,
StateAction: windows.WTD_STATEACTION_VERIFY,
FileOrCatalogOrBlobOrSgnrOrCert: unsafe.Pointer(&windows.WinTrustFileInfo{
Size: uint32(unsafe.Sizeof(windows.WinTrustFileInfo{})),
FilePath: path16,
}),
}
verified := windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data) == nil
data.StateAction = windows.WTD_STATEACTION_CLOSE
windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data)
return verified
}

17
updater/constants.go Normal file
View File

@@ -0,0 +1,17 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
*/
package updater
const (
releasePublicKeyBase64 = "RWRNqGKtBXftKTKPpBPGDMe8jHLnFQ0EdRy8Wg0apV6vTDFLAODD83G4"
updateServerHost = "download.wireguard.com"
updateServerPort = 443
updateServerUseHttps = true
latestVersionPath = "/windows-client/latest.sig"
msiPath = "/windows-client/%s"
msiArchPrefix = "wireguard-%s-"
msiSuffix = ".msi"
)

200
updater/downloader.go Normal file
View File

@@ -0,0 +1,200 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
*/
package updater
import (
"crypto/hmac"
"errors"
"fmt"
"hash"
"io"
"sync/atomic"
"golang.org/x/crypto/blake2b"
"github.com/amnezia-vpn/amneziawg-windows-client/elevate"
"github.com/amnezia-vpn/amneziawg-windows-client/updater/winhttp"
"github.com/amnezia-vpn/amneziawg-windows-client/version"
)
type DownloadProgress struct {
Activity string
BytesDownloaded uint64
BytesTotal uint64
Error error
Complete bool
}
type progressHashWatcher struct {
dp *DownloadProgress
c chan DownloadProgress
hashState hash.Hash
}
func (pm *progressHashWatcher) Write(p []byte) (int, error) {
bytes := len(p)
pm.dp.BytesDownloaded += uint64(bytes)
pm.c <- *pm.dp
pm.hashState.Write(p)
return bytes, nil
}
type UpdateFound struct {
name string
hash [blake2b.Size256]byte
}
func CheckForUpdate() (updateFound *UpdateFound, err error) {
updateFound, _, _, err = checkForUpdate(false)
return
}
func checkForUpdate(keepSession bool) (*UpdateFound, *winhttp.Session, *winhttp.Connection, error) {
if !version.IsRunningOfficialVersion() {
return nil, nil, nil, errors.New("Build is not official, so updates are disabled")
}
session, err := winhttp.NewSession(version.UserAgent())
if err != nil {
return nil, nil, nil, err
}
defer func() {
if err != nil || !keepSession {
session.Close()
}
}()
connection, err := session.Connect(updateServerHost, updateServerPort, updateServerUseHttps)
if err != nil {
return nil, nil, nil, err
}
defer func() {
if err != nil || !keepSession {
connection.Close()
}
}()
response, err := connection.Get(latestVersionPath, true)
if err != nil {
return nil, nil, nil, err
}
defer response.Close()
var fileList [1024 * 512] /* 512 KiB */ byte
bytesRead, err := response.Read(fileList[:])
if err != nil && (err != io.EOF || bytesRead == 0) {
return nil, nil, nil, err
}
files, err := readFileList(fileList[:bytesRead])
if err != nil {
return nil, nil, nil, err
}
updateFound, err := findCandidate(files)
if err != nil {
return nil, nil, nil, err
}
if keepSession {
return updateFound, session, connection, nil
}
return updateFound, nil, nil, nil
}
var updateInProgress = uint32(0)
func DownloadVerifyAndExecute(userToken uintptr) (progress chan DownloadProgress) {
progress = make(chan DownloadProgress, 128)
progress <- DownloadProgress{Activity: "Initializing"}
if !atomic.CompareAndSwapUint32(&updateInProgress, 0, 1) {
progress <- DownloadProgress{Error: errors.New("An update is already in progress")}
return
}
doIt := func() {
defer atomic.StoreUint32(&updateInProgress, 0)
progress <- DownloadProgress{Activity: "Checking for update"}
update, session, connection, err := checkForUpdate(true)
if err != nil {
progress <- DownloadProgress{Error: err}
return
}
defer connection.Close()
defer session.Close()
if update == nil {
progress <- DownloadProgress{Error: errors.New("No update was found")}
return
}
progress <- DownloadProgress{Activity: "Creating temporary file"}
file, err := msiTempFile()
if err != nil {
progress <- DownloadProgress{Error: err}
return
}
progress <- DownloadProgress{Activity: fmt.Sprintf("Msi destination is %#q", file.Name())}
defer func() {
if file != nil {
file.Delete()
}
}()
dp := DownloadProgress{Activity: "Downloading update"}
progress <- dp
response, err := connection.Get(fmt.Sprintf(msiPath, update.name), false)
if err != nil {
progress <- DownloadProgress{Error: err}
return
}
defer response.Close()
length, err := response.Length()
if err == nil && length >= 0 {
dp.BytesTotal = length
progress <- dp
}
hasher, err := blake2b.New256(nil)
if err != nil {
progress <- DownloadProgress{Error: err}
return
}
pm := &progressHashWatcher{&dp, progress, hasher}
_, err = io.Copy(file, io.TeeReader(io.LimitReader(response, 1024*1024*100 /* 100 MiB */), pm))
if err != nil {
progress <- DownloadProgress{Error: err}
return
}
if !hmac.Equal(hasher.Sum(nil), update.hash[:]) {
progress <- DownloadProgress{Error: errors.New("The downloaded update has the wrong hash")}
return
}
progress <- DownloadProgress{Activity: "Verifying authenticode signature"}
if !verifyAuthenticode(file.ExclusivePath()) {
progress <- DownloadProgress{Error: errors.New("The downloaded update does not have an authentic authenticode signature")}
return
}
progress <- DownloadProgress{Activity: "Installing update"}
err = runMsi(file, userToken)
if err != nil {
progress <- DownloadProgress{Error: err}
return
}
progress <- DownloadProgress{Complete: true}
}
if userToken == 0 {
go func() {
err := elevate.DoAsSystem(func() error {
doIt()
return nil
})
if err != nil {
progress <- DownloadProgress{Error: err}
}
}()
} else {
go doIt()
}
return progress
}

116
updater/msirunner.go Normal file
View File

@@ -0,0 +1,116 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
*/
package updater
import (
"crypto/rand"
"encoding/hex"
"errors"
"os"
"os/exec"
"path/filepath"
"runtime"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
type tempFile struct {
*os.File
originalHandle windows.Handle
}
func (t *tempFile) ExclusivePath() string {
if t.originalHandle != 0 {
t.Close() // TODO: sort of a toctou, but msi requires unshared file
t.originalHandle = 0
}
return t.Name()
}
func (t *tempFile) Delete() error {
if t.originalHandle == 0 {
name16, err := windows.UTF16PtrFromString(t.Name())
if err != nil {
return err
}
return windows.DeleteFile(name16) // TODO: how does this deal with reparse points?
}
disposition := byte(1)
err := windows.SetFileInformationByHandle(t.originalHandle, windows.FileDispositionInfo, &disposition, 1)
t.originalHandle = 0
t.Close()
return err
}
func runMsi(msi *tempFile, userToken uintptr) error {
system32, err := windows.GetSystemDirectory()
if err != nil {
return err
}
devNull, err := os.OpenFile(os.DevNull, os.O_RDWR, 0)
if err != nil {
return err
}
defer devNull.Close()
msiPath := msi.ExclusivePath()
attr := &os.ProcAttr{
Sys: &syscall.SysProcAttr{
Token: syscall.Token(userToken),
},
Files: []*os.File{devNull, devNull, devNull},
Dir: filepath.Dir(msiPath),
}
msiexec := filepath.Join(system32, "msiexec.exe")
proc, err := os.StartProcess(msiexec, []string{msiexec, "/qb!-", "/i", filepath.Base(msiPath)}, attr)
if err != nil {
return err
}
state, err := proc.Wait()
if err != nil {
return err
}
if !state.Success() {
return &exec.ExitError{ProcessState: state}
}
return nil
}
func msiTempFile() (*tempFile, error) {
var randBytes [32]byte
n, err := rand.Read(randBytes[:])
if err != nil {
return nil, err
}
if n != int(len(randBytes)) {
return nil, errors.New("Unable to generate random bytes")
}
sd, err := windows.SecurityDescriptorFromString("O:SYD:PAI(A;;FA;;;SY)(A;;FR;;;BA)")
if err != nil {
return nil, err
}
sa := &windows.SecurityAttributes{
Length: uint32(unsafe.Sizeof(windows.SecurityAttributes{})),
SecurityDescriptor: sd,
}
windir, err := windows.GetWindowsDirectory()
if err != nil {
return nil, err
}
name := filepath.Join(windir, "Temp", hex.EncodeToString(randBytes[:]))
name16 := windows.StringToUTF16Ptr(name)
fileHandle, err := windows.CreateFile(name16, windows.GENERIC_WRITE|windows.DELETE, 0, sa, windows.CREATE_NEW, windows.FILE_ATTRIBUTE_TEMPORARY, 0)
runtime.KeepAlive(sd)
if err != nil {
return nil, err
}
windows.MoveFileEx(name16, nil, windows.MOVEFILE_DELAY_UNTIL_REBOOT)
return &tempFile{
File: os.NewFile(uintptr(fileHandle), name),
originalHandle: fileHandle,
}, nil
}

72
updater/signify.go Normal file
View File

@@ -0,0 +1,72 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
*/
package updater
import (
"bytes"
"crypto/ed25519"
"encoding/base64"
"encoding/hex"
"errors"
"strings"
"golang.org/x/crypto/blake2b"
)
/*
* Generate with:
* $ b2sum -l 256 *.msi > list
* $ signify -S -e -s release.sec -m list
* $ upload ./list.sec
*/
type fileList map[string][blake2b.Size256]byte
func readFileList(input []byte) (fileList, error) {
publicKeyBytes, err := base64.StdEncoding.DecodeString(releasePublicKeyBase64)
if err != nil || len(publicKeyBytes) != ed25519.PublicKeySize+10 || publicKeyBytes[0] != 'E' || publicKeyBytes[1] != 'd' {
return nil, errors.New("Invalid public key")
}
lines := bytes.SplitN(input, []byte{'\n'}, 3)
if len(lines) != 3 {
return nil, errors.New("Signature input has too few lines")
}
if !bytes.HasPrefix(lines[0], []byte("untrusted comment: ")) {
return nil, errors.New("Signature input is missing untrusted comment")
}
signatureBytes, err := base64.StdEncoding.DecodeString(string(lines[1]))
if err != nil {
return nil, errors.New("Signature input is not valid base64")
}
if len(signatureBytes) != ed25519.SignatureSize+10 || !bytes.Equal(signatureBytes[:10], publicKeyBytes[:10]) {
return nil, errors.New("Signature input bytes are incorrect length, type, or keyid")
}
if !ed25519.Verify(publicKeyBytes[10:], lines[2], signatureBytes[10:]) {
return nil, errors.New("Signature is invalid")
}
fileLines := strings.Split(string(lines[2]), "\n")
fileHashes := make(map[string][blake2b.Size256]byte, len(fileLines))
for index, line := range fileLines {
if len(line) == 0 && index == len(fileLines)-1 {
break
}
first, second, ok := strings.Cut(line, " ")
if !ok {
return nil, errors.New("File hash line has too few components")
}
maybeHash, err := hex.DecodeString(first)
if err != nil || len(maybeHash) != blake2b.Size256 {
return nil, errors.New("File hash is invalid base64 or incorrect number of bytes")
}
var hash [blake2b.Size256]byte
copy(hash[:], maybeHash)
fileHashes[second] = hash
}
if len(fileHashes) == 0 {
return nil, errors.New("No file hashes found in signed input")
}
return fileHashes, nil
}

41
updater/updater_test.go Normal file
View File

@@ -0,0 +1,41 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
*/
package updater
import (
"testing"
)
func TestUpdate(t *testing.T) {
update, err := CheckForUpdate()
if err != nil {
t.Error(err)
return
}
if update == nil {
t.Error("No update available")
return
}
t.Log("Found update")
progress := DownloadVerifyAndExecute(0)
for {
dp := <-progress
if dp.Error != nil {
t.Error(dp.Error)
return
}
if len(dp.Activity) > 0 {
t.Log(dp.Activity)
}
if dp.BytesTotal > 0 {
t.Logf("Downloaded %d of %d", dp.BytesDownloaded, dp.BytesTotal)
}
if dp.Complete {
t.Log("Complete!")
break
}
}
}

75
updater/versions.go Normal file
View File

@@ -0,0 +1,75 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
*/
package updater
import (
"errors"
"fmt"
"strconv"
"strings"
"github.com/amnezia-vpn/amneziawg-windows-client/version"
)
func versionNewerThanUs(candidate string) (bool, error) {
candidateParts := strings.Split(candidate, ".")
ourParts := strings.Split(version.Number, ".")
if len(candidateParts) == 0 || len(ourParts) == 0 {
return false, errors.New("Empty version")
}
l := len(candidateParts)
if len(ourParts) > l {
l = len(ourParts)
}
for i := 0; i < l; i++ {
var err error
cP, oP := uint64(0), uint64(0)
if i < len(candidateParts) {
if len(candidateParts[i]) == 0 {
return false, errors.New("Empty version part")
}
cP, err = strconv.ParseUint(candidateParts[i], 10, 16)
if err != nil {
return false, errors.New("Invalid version integer part")
}
}
if i < len(ourParts) {
if len(ourParts[i]) == 0 {
return false, errors.New("Empty version part")
}
oP, err = strconv.ParseUint(ourParts[i], 10, 16)
if err != nil {
return false, errors.New("Invalid version integer part")
}
}
if cP == oP {
continue
}
return cP > oP, nil
}
return false, nil
}
func findCandidate(candidates fileList) (*UpdateFound, error) {
prefix := fmt.Sprintf(msiArchPrefix, version.Arch())
suffix := msiSuffix
for name, hash := range candidates {
if strings.HasPrefix(name, prefix) && strings.HasSuffix(name, suffix) {
version := strings.TrimSuffix(strings.TrimPrefix(name, prefix), suffix)
if len(version) > 128 {
return nil, errors.New("Version length is too long")
}
newer, err := versionNewerThanUs(version)
if err != nil {
return nil, err
}
if newer {
return &UpdateFound{name, hash}, nil
}
}
}
return nil, nil
}

View File

@@ -0,0 +1,8 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
*/
package winhttp
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go syscall_windows.go

View File

@@ -0,0 +1,355 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019-2022 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
_WINHTTP_FIRST_OPTION = _WINHTTP_OPTION_CALLBACK
_WINHTTP_OPTION_CALLBACK = 1
_WINHTTP_OPTION_RESOLVE_TIMEOUT = 2
_WINHTTP_OPTION_CONNECT_TIMEOUT = 3
_WINHTTP_OPTION_CONNECT_RETRIES = 4
_WINHTTP_OPTION_SEND_TIMEOUT = 5
_WINHTTP_OPTION_RECEIVE_TIMEOUT = 6
_WINHTTP_OPTION_RECEIVE_RESPONSE_TIMEOUT = 7
_WINHTTP_OPTION_HANDLE_TYPE = 9
_WINHTTP_OPTION_READ_BUFFER_SIZE = 12
_WINHTTP_OPTION_WRITE_BUFFER_SIZE = 13
_WINHTTP_OPTION_PARENT_HANDLE = 21
_WINHTTP_OPTION_EXTENDED_ERROR = 24
_WINHTTP_OPTION_SECURITY_FLAGS = 31
_WINHTTP_OPTION_SECURITY_CERTIFICATE_STRUCT = 32
_WINHTTP_OPTION_URL = 34
_WINHTTP_OPTION_SECURITY_KEY_BITNESS = 36
_WINHTTP_OPTION_PROXY = 38
_WINHTTP_OPTION_PROXY_RESULT_ENTRY = 39
_WINHTTP_OPTION_USER_AGENT = 41
_WINHTTP_OPTION_CONTEXT_VALUE = 45
_WINHTTP_OPTION_CLIENT_CERT_CONTEXT = 47
_WINHTTP_OPTION_REQUEST_PRIORITY = 58
_WINHTTP_OPTION_HTTP_VERSION = 59
_WINHTTP_OPTION_DISABLE_FEATURE = 63
_WINHTTP_OPTION_CODEPAGE = 68
_WINHTTP_OPTION_MAX_CONNS_PER_SERVER = 73
_WINHTTP_OPTION_MAX_CONNS_PER_1_0_SERVER = 74
_WINHTTP_OPTION_AUTOLOGON_POLICY = 77
_WINHTTP_OPTION_SERVER_CERT_CONTEXT = 78
_WINHTTP_OPTION_ENABLE_FEATURE = 79
_WINHTTP_OPTION_WORKER_THREAD_COUNT = 80
_WINHTTP_OPTION_PASSPORT_COBRANDING_TEXT = 81
_WINHTTP_OPTION_PASSPORT_COBRANDING_URL = 82
_WINHTTP_OPTION_CONFIGURE_PASSPORT_AUTH = 83
_WINHTTP_OPTION_SECURE_PROTOCOLS = 84
_WINHTTP_OPTION_ENABLETRACING = 85
_WINHTTP_OPTION_PASSPORT_SIGN_OUT = 86
_WINHTTP_OPTION_PASSPORT_RETURN_URL = 87
_WINHTTP_OPTION_REDIRECT_POLICY = 88
_WINHTTP_OPTION_MAX_HTTP_AUTOMATIC_REDIRECTS = 89
_WINHTTP_OPTION_MAX_HTTP_STATUS_CONTINUE = 90
_WINHTTP_OPTION_MAX_RESPONSE_HEADER_SIZE = 91
_WINHTTP_OPTION_MAX_RESPONSE_DRAIN_SIZE = 92
_WINHTTP_OPTION_CONNECTION_INFO = 93
_WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST = 94
_WINHTTP_OPTION_SPN = 96
_WINHTTP_OPTION_GLOBAL_PROXY_CREDS = 97
_WINHTTP_OPTION_GLOBAL_SERVER_CREDS = 98
_WINHTTP_OPTION_UNLOAD_NOTIFY_EVENT = 99
_WINHTTP_OPTION_REJECT_USERPWD_IN_URL = 100
_WINHTTP_OPTION_USE_GLOBAL_SERVER_CREDENTIALS = 101
_WINHTTP_OPTION_RECEIVE_PROXY_CONNECT_RESPONSE = 103
_WINHTTP_OPTION_IS_PROXY_CONNECT_RESPONSE = 104
_WINHTTP_OPTION_SERVER_SPN_USED = 106
_WINHTTP_OPTION_PROXY_SPN_USED = 107
_WINHTTP_OPTION_SERVER_CBT = 108
_WINHTTP_OPTION_UNSAFE_HEADER_PARSING = 110
_WINHTTP_OPTION_ASSURED_NON_BLOCKING_CALLBACKS = 111
_WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET = 114
_WINHTTP_OPTION_WEB_SOCKET_CLOSE_TIMEOUT = 115
_WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL = 116
_WINHTTP_OPTION_DECOMPRESSION = 118
_WINHTTP_OPTION_WEB_SOCKET_RECEIVE_BUFFER_SIZE = 122
_WINHTTP_OPTION_WEB_SOCKET_SEND_BUFFER_SIZE = 123
_WINHTTP_OPTION_TCP_PRIORITY_HINT = 128
_WINHTTP_OPTION_CONNECTION_FILTER = 131
_WINHTTP_OPTION_ENABLE_HTTP_PROTOCOL = 133
_WINHTTP_OPTION_HTTP_PROTOCOL_USED = 134
_WINHTTP_OPTION_KDC_PROXY_SETTINGS = 136
_WINHTTP_OPTION_ENCODE_EXTRA = 138
_WINHTTP_OPTION_DISABLE_STREAM_QUEUE = 139
_WINHTTP_OPTION_IPV6_FAST_FALLBACK = 140
_WINHTTP_OPTION_CONNECTION_STATS_V0 = 141
_WINHTTP_OPTION_REQUEST_TIMES = 142
_WINHTTP_OPTION_EXPIRE_CONNECTION = 143
_WINHTTP_OPTION_DISABLE_SECURE_PROTOCOL_FALLBACK = 144
_WINHTTP_OPTION_HTTP_PROTOCOL_REQUIRED = 145
_WINHTTP_OPTION_REQUEST_STATS = 146
_WINHTTP_OPTION_SERVER_CERT_CHAIN_CONTEXT = 147
_WINHTTP_LAST_OPTION = _WINHTTP_OPTION_SERVER_CERT_CHAIN_CONTEXT
_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_FLAG_SECURE_PROTOCOL_SSL2 = 0x00000008
_WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 = 0x00000020
_WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 = 0x00000080
_WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 = 0x00000200
_WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 = 0x00000800
_WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3 = 0x00002000
_WINHTTP_FLAG_SECURE_PROTOCOL_ALL = _WINHTTP_FLAG_SECURE_PROTOCOL_SSL2 | _WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 | _WINHTTP_FLAG_SECURE_PROTOCOL_TLS1
_WINHTTP_PROTOCOL_FLAG_HTTP2 = 0x1
_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
//sys winHttpSetOption(sessionOrRequestHandle _HINTERNET, option uint32, buffer unsafe.Pointer, bufferLen uint32) (err error) = winhttp.WinHttpSetOption

230
updater/winhttp/winhttp.go Normal file
View File

@@ -0,0 +1,230 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
*/
package winhttp
import (
"errors"
"fmt"
"io"
"runtime"
"strconv"
"strings"
"sync/atomic"
"unsafe"
"golang.org/x/sys/windows"
)
type Session struct {
handle _HINTERNET
}
type Connection struct {
handle _HINTERNET
session *Session
https bool
}
type Response struct {
handle _HINTERNET
connection *Connection
}
func convertError(err *error) {
if *err == nil {
return
}
var errno windows.Errno
if errors.As(*err, &errno) {
if errno > _WINHTTP_ERROR_BASE && errno <= _WINHTTP_ERROR_LAST {
*err = Error(errno)
}
}
}
func isWin7() bool {
maj, min, _ := windows.RtlGetNtVersionNumbers()
return maj < 6 || (maj == 6 && min <= 1)
}
func isWin8DotZeroOrBelow() bool {
maj, min, _ := windows.RtlGetNtVersionNumbers()
return maj < 6 || (maj == 6 && min <= 2)
}
func NewSession(userAgent string) (session *Session, err error) {
session = new(Session)
defer convertError(&err)
defer func() {
if err != nil {
session.Close()
session = nil
}
}()
userAgent16, err := windows.UTF16PtrFromString(userAgent)
if err != nil {
return
}
var proxyFlag uint32 = _WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY
if isWin7() {
proxyFlag = _WINHTTP_ACCESS_TYPE_DEFAULT_PROXY
}
session.handle, err = winHttpOpen(userAgent16, proxyFlag, nil, nil, 0)
if err != nil {
return
}
var enableHttp2 uint32 = _WINHTTP_PROTOCOL_FLAG_HTTP2
_ = winHttpSetOption(session.handle, _WINHTTP_OPTION_ENABLE_HTTP_PROTOCOL, unsafe.Pointer(&enableHttp2), uint32(unsafe.Sizeof(enableHttp2))) // Don't check return value, in case of old Windows
if isWin8DotZeroOrBelow() {
var enableTLS12 uint32 = _WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2
err = winHttpSetOption(session.handle, _WINHTTP_OPTION_SECURE_PROTOCOLS, unsafe.Pointer(&enableTLS12), uint32(unsafe.Sizeof(enableTLS12)))
if err != nil {
return
}
}
runtime.SetFinalizer(session, func(session *Session) {
session.Close()
})
return
}
func (session *Session) Close() (err error) {
defer convertError(&err)
handle := (_HINTERNET)(atomic.SwapUintptr((*uintptr)(&session.handle), 0))
if handle == 0 {
return
}
return winHttpCloseHandle(handle)
}
func (session *Session) Connect(server string, port uint16, https bool) (connection *Connection, err error) {
connection = &Connection{session: session}
defer convertError(&err)
defer func() {
if err != nil {
connection.Close()
connection = nil
}
}()
server16, err := windows.UTF16PtrFromString(server)
if err != nil {
return
}
connection.handle, err = winHttpConnect(session.handle, server16, port, 0)
if err != nil {
return
}
connection.https = https
runtime.SetFinalizer(connection, func(connection *Connection) {
connection.Close()
})
return
}
func (connection *Connection) Close() (err error) {
defer convertError(&err)
handle := (_HINTERNET)(atomic.SwapUintptr((*uintptr)(&connection.handle), 0))
if handle == 0 {
return
}
return winHttpCloseHandle(handle)
}
func (connection *Connection) Get(path string, refresh bool) (response *Response, err error) {
response = &Response{connection: connection}
defer convertError(&err)
defer func() {
if err != nil {
response.Close()
response = nil
}
}()
var flags uint32
if refresh {
flags |= _WINHTTP_FLAG_REFRESH
}
if connection.https {
flags |= _WINHTTP_FLAG_SECURE
}
path16, err := windows.UTF16PtrFromString(path)
if err != nil {
return
}
get16, err := windows.UTF16PtrFromString("GET")
if err != nil {
return
}
response.handle, err = winHttpOpenRequest(connection.handle, get16, path16, nil, nil, nil, flags)
if err != nil {
return
}
err = winHttpSendRequest(response.handle, nil, 0, nil, 0, 0, 0)
if err != nil {
return
}
err = winHttpReceiveResponse(response.handle, 0)
if err != nil {
return
}
runtime.SetFinalizer(response, func(response *Response) {
response.Close()
})
return
}
func (response *Response) Length() (length uint64, err error) {
defer convertError(&err)
numBuf := make([]uint16, 22)
numLen := uint32(len(numBuf) * 2)
err = winHttpQueryHeaders(response.handle, _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.handle, &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)
handle := (_HINTERNET)(atomic.SwapUintptr((*uintptr)(&response.handle), 0))
if handle == 0 {
return
}
return winHttpCloseHandle(handle)
}
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]))
}

View File

@@ -0,0 +1,71 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
*/
package winhttp
import (
"fmt"
"io"
"runtime"
"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) {
session, err := NewSession("WinHTTP Test Suite/1.0")
if err != nil {
t.Fatal(err)
}
connection, err := session.Connect("zx2c4.com", 443, true)
if err != nil {
t.Fatal(err)
}
r, err := connection.Get("/ip", true)
length, err := r.Length()
if err != nil {
t.Fatal(err)
}
fmt.Printf("The length is %d\n", length)
bytes, err := io.ReadAll(r)
if err != nil {
t.Fatal(err)
}
fmt.Println(string(bytes))
r.Close()
connection, err = session.Connect("speed.hetzner.de", 443, true)
if err != nil {
t.Fatal(err)
}
r, err = connection.Get("/10GB.bin", false)
if err != nil {
t.Fatal(err)
}
length, err = r.Length()
if err != nil {
t.Fatal(err)
}
amountRead, err := io.Copy(&progressPrinter{total: length}, r)
if err != nil {
t.Fatal(err)
}
r.Close()
if length != uint64(amountRead) {
t.Fatalf("Expected to read %d, but only read %d", length, amountRead)
}
runtime.GC() // Try to force the finalizers to be called
}

View File

@@ -0,0 +1,155 @@
// 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)
errERROR_EINVAL error = syscall.EINVAL
)
// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
return errERROR_EINVAL
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")
procWinHttpCloseHandle = modwinhttp.NewProc("WinHttpCloseHandle")
procWinHttpConnect = modwinhttp.NewProc("WinHttpConnect")
procWinHttpCrackUrl = modwinhttp.NewProc("WinHttpCrackUrl")
procWinHttpOpen = modwinhttp.NewProc("WinHttpOpen")
procWinHttpOpenRequest = modwinhttp.NewProc("WinHttpOpenRequest")
procWinHttpQueryDataAvailable = modwinhttp.NewProc("WinHttpQueryDataAvailable")
procWinHttpQueryHeaders = modwinhttp.NewProc("WinHttpQueryHeaders")
procWinHttpReadData = modwinhttp.NewProc("WinHttpReadData")
procWinHttpReceiveResponse = modwinhttp.NewProc("WinHttpReceiveResponse")
procWinHttpSendRequest = modwinhttp.NewProc("WinHttpSendRequest")
procWinHttpSetOption = modwinhttp.NewProc("WinHttpSetOption")
procWinHttpSetStatusCallback = modwinhttp.NewProc("WinHttpSetStatusCallback")
)
func winHttpCloseHandle(handle _HINTERNET) (err error) {
r1, _, e1 := syscall.Syscall(procWinHttpCloseHandle.Addr(), 1, uintptr(handle), 0, 0)
if r1 == 0 {
err = errnoErr(e1)
}
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 {
err = errnoErr(e1)
}
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 {
err = errnoErr(e1)
}
return
}
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 {
err = errnoErr(e1)
}
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 {
err = errnoErr(e1)
}
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 {
err = errnoErr(e1)
}
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 {
err = errnoErr(e1)
}
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 {
err = errnoErr(e1)
}
return
}
func winHttpReceiveResponse(requestHandle _HINTERNET, reserved uintptr) (err error) {
r1, _, e1 := syscall.Syscall(procWinHttpReceiveResponse.Addr(), 2, uintptr(requestHandle), uintptr(reserved), 0)
if r1 == 0 {
err = errnoErr(e1)
}
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 {
err = errnoErr(e1)
}
return
}
func winHttpSetOption(sessionOrRequestHandle _HINTERNET, option uint32, buffer unsafe.Pointer, bufferLen uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procWinHttpSetOption.Addr(), 4, uintptr(sessionOrRequestHandle), uintptr(option), uintptr(buffer), uintptr(bufferLen), 0, 0)
if r1 == 0 {
err = errnoErr(e1)
}
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 {
err = errnoErr(e1)
}
return
}