mirror of
https://github.com/amnezia-vpn/amneziawg-windows-client.git
synced 2026-05-17 00:05:45 +03:00
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 commit9670f4298e. * Revert "remove application update feature #2" This reverts commitb0c96a9fd6. * 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:
2
go.mod
2
go.mod
@@ -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
|
||||
|
||||
@@ -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."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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
|
||||
},
|
||||
|
||||
@@ -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."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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
26
main.go
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
65
manager/updatestate.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
33
ui/tray.go
33
ui/tray.go
@@ -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)
|
||||
|
||||
24
ui/ui.go
24
ui/ui.go
@@ -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
142
ui/updatepage.go
Normal 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
34
updater/authenticode.go
Normal 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
17
updater/constants.go
Normal 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
200
updater/downloader.go
Normal 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
116
updater/msirunner.go
Normal 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
72
updater/signify.go
Normal 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
41
updater/updater_test.go
Normal 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
75
updater/versions.go
Normal 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
|
||||
}
|
||||
8
updater/winhttp/mksyscall.go
Normal file
8
updater/winhttp/mksyscall.go
Normal 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
|
||||
355
updater/winhttp/syscall_windows.go
Normal file
355
updater/winhttp/syscall_windows.go
Normal 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
230
updater/winhttp/winhttp.go
Normal 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]))
|
||||
}
|
||||
71
updater/winhttp/winhttp_test.go
Normal file
71
updater/winhttp/winhttp_test.go
Normal 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
|
||||
}
|
||||
155
updater/winhttp/zsyscall_windows.go
Normal file
155
updater/winhttp/zsyscall_windows.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user