aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--COPYING2
-rw-r--r--Makefile92
-rw-r--r--README.md118
-rw-r--r--build.bat44
-rw-r--r--conf/admin_windows.go36
-rw-r--r--conf/config.go22
-rw-r--r--conf/dnsresolver_windows.go2
-rw-r--r--conf/dpapi/dpapi_windows.go61
-rw-r--r--conf/dpapi/dpapi_windows_test.go2
-rw-r--r--conf/dpapi/mksyscall.go8
-rwxr-xr-xconf/dpapi/test.exebin0 -> 3622400 bytes
-rw-r--r--conf/dpapi/zdpapi_windows.go68
-rw-r--r--conf/filewriter_windows.go89
-rw-r--r--conf/migration_windows.go90
-rw-r--r--conf/mksyscall.go2
-rw-r--r--conf/name.go2
-rw-r--r--conf/parser.go10
-rw-r--r--conf/parser_test.go2
-rw-r--r--conf/path_windows.go126
-rw-r--r--conf/store.go92
-rw-r--r--conf/store_test.go2
-rw-r--r--conf/storewatcher.go2
-rw-r--r--conf/storewatcher_windows.go26
-rw-r--r--conf/writer.go15
-rw-r--r--conf/zsyscall_windows.go41
-rw-r--r--docs/adminregistry.md41
-rw-r--r--docs/attacksurface.md (renamed from attacksurface.md)10
-rw-r--r--docs/buildrun.md98
-rw-r--r--docs/enterprise.md86
-rw-r--r--docs/netquirk.md33
-rw-r--r--elevate/doas.go2
-rw-r--r--elevate/loader.go2
-rw-r--r--elevate/membership.go6
-rw-r--r--elevate/mksyscall.go2
-rw-r--r--elevate/privileges.go2
-rw-r--r--elevate/shellexecute.go2
-rw-r--r--elevate/syscall_windows.go5
-rw-r--r--elevate/zsyscall_windows.go57
-rw-r--r--embeddable-dll-service/README.md46
-rw-r--r--embeddable-dll-service/build.bat24
-rw-r--r--embeddable-dll-service/csharp/DemoUI/MainWindow.Designer.cs87
-rw-r--r--embeddable-dll-service/csharp/DemoUI/MainWindow.cs213
-rw-r--r--embeddable-dll-service/csharp/DemoUI/MainWindow.resx60
-rw-r--r--embeddable-dll-service/csharp/DemoUI/Program.cs44
-rw-r--r--embeddable-dll-service/csharp/DemoUI/app.manifest48
-rw-r--r--embeddable-dll-service/csharp/Program.cs92
-rw-r--r--embeddable-dll-service/csharp/README.md15
-rw-r--r--embeddable-dll-service/csharp/TunnelDll/Keypair.cs (renamed from embeddable-dll-service/csharp/Keypair.cs)4
-rw-r--r--embeddable-dll-service/csharp/TunnelDll/Ringlogger.cs (renamed from embeddable-dll-service/csharp/Ringlogger.cs)2
-rw-r--r--embeddable-dll-service/csharp/TunnelDll/Service.cs (renamed from embeddable-dll-service/csharp/Service.cs)28
-rw-r--r--embeddable-dll-service/csharp/TunnelDll/Win32.cs (renamed from embeddable-dll-service/csharp/Win32.cs)2
-rw-r--r--embeddable-dll-service/csharp/demo-client.csproj14
-rw-r--r--embeddable-dll-service/csharp/demo-client.csproj.user8
-rw-r--r--embeddable-dll-service/csharp/demo-client.sln25
-rw-r--r--embeddable-dll-service/main.go4
-rw-r--r--go-patches/0001-runtime-allow-builtin-write-function-to-be-redirecte.patch59
-rw-r--r--go-patches/0002-cmd-link-handle-grouped-resource-sections.patch339
-rw-r--r--go-patches/highres-timer.patch395
-rw-r--r--go-patches/no-ctrlc-handler.patch184
-rw-r--r--go.mod20
-rw-r--r--go.mod.master2
-rw-r--r--go.sum43
-rw-r--r--gotext.go4
-rw-r--r--installer/.gitignore2
-rw-r--r--installer/build.bat20
-rw-r--r--installer/customactions.c445
-rw-r--r--installer/fetcher/.gitignore6
-rw-r--r--installer/fetcher/Makefile42
-rw-r--r--installer/fetcher/constants.h17
-rw-r--r--installer/fetcher/crypto.c2252
-rw-r--r--installer/fetcher/crypto.h29
-rw-r--r--installer/fetcher/fetcher.c315
-rw-r--r--installer/fetcher/filelist.c168
-rw-r--r--installer/fetcher/filelist.h17
-rw-r--r--installer/fetcher/icon.svg92
-rw-r--r--installer/fetcher/manifest.xml34
-rw-r--r--installer/fetcher/resources.rc41
-rw-r--r--installer/fetcher/systeminfo.c67
-rw-r--r--installer/fetcher/systeminfo.h15
-rw-r--r--installer/fetcher/version.h12
-rw-r--r--installer/wireguard.wxs91
-rw-r--r--l18n/l18n.go2
-rw-r--r--locales/de/messages.gotext.json134
-rw-r--r--locales/es-ES/messages.gotext.json41
-rw-r--r--locales/fa/messages.gotext.json62
-rw-r--r--locales/fi/messages.gotext.json303
-rw-r--r--locales/fr/messages.gotext.json176
-rw-r--r--locales/id/messages.gotext.json54
-rw-r--r--locales/it/messages.gotext.json136
-rw-r--r--locales/ja/messages.gotext.json136
-rw-r--r--locales/pa-IN/messages.gotext.json1223
-rw-r--r--locales/pl/messages.gotext.json1890
-rw-r--r--locales/ro/messages.gotext.json166
-rw-r--r--locales/ru/messages.gotext.json194
-rw-r--r--locales/sk/messages.gotext.json376
-rw-r--r--locales/sl/messages.gotext.json168
-rw-r--r--locales/uk/messages.gotext.json373
-rw-r--r--locales/vi/messages.gotext.json48
-rw-r--r--locales/zh-CN/messages.gotext.json162
-rw-r--r--locales/zh-TW/messages.gotext.json136
-rw-r--r--main.go80
-rw-r--r--manager/install.go10
-rw-r--r--manager/interfacecleanup.go10
-rw-r--r--manager/ipc_client.go2
-rw-r--r--manager/ipc_pipe.go18
-rw-r--r--manager/ipc_server.go125
-rw-r--r--manager/legacystore.go129
-rw-r--r--manager/service.go79
-rw-r--r--manager/tunneltracker.go240
-rw-r--r--manager/updatestate.go4
-rw-r--r--quickinstall.bat4
-rw-r--r--resources.rc21
-rw-r--r--ringlogger/cli_test.go2
-rw-r--r--ringlogger/dump.go32
-rw-r--r--ringlogger/global.go39
-rw-r--r--ringlogger/ringlogger.go31
-rw-r--r--services/errors.go7
-rw-r--r--services/names.go2
-rw-r--r--tunnel/addressconfig.go15
-rw-r--r--tunnel/defaultroutemonitor.go2
-rw-r--r--tunnel/deterministicguid.go2
-rw-r--r--tunnel/firewall/blocker.go28
-rw-r--r--tunnel/firewall/helpers.go6
-rw-r--r--tunnel/firewall/mksyscall.go2
-rw-r--r--tunnel/firewall/rules.go2
-rw-r--r--tunnel/firewall/syscall_windows.go2
-rw-r--r--tunnel/firewall/types_windows.go2
-rw-r--r--tunnel/firewall/types_windows_32.go (renamed from tunnel/firewall/types_windows_386.go)4
-rw-r--r--tunnel/firewall/types_windows_64.go (renamed from tunnel/firewall/types_windows_amd64.go)4
-rw-r--r--tunnel/firewall/types_windows_test.go2
-rw-r--r--tunnel/firewall/zsyscall_windows.go107
-rw-r--r--tunnel/interfacewatcher.go2
-rw-r--r--tunnel/ipcpermissions.go2
-rw-r--r--tunnel/scriptrunner.go77
-rw-r--r--tunnel/service.go48
-rw-r--r--tunnel/winipcfg/interface_change_handler.go2
-rw-r--r--tunnel/winipcfg/luid.go10
-rw-r--r--tunnel/winipcfg/mksyscall.go2
-rw-r--r--tunnel/winipcfg/netsh.go17
-rw-r--r--tunnel/winipcfg/route_change_handler.go2
-rw-r--r--tunnel/winipcfg/types.go73
-rw-r--r--tunnel/winipcfg/types_32.go (renamed from tunnel/winipcfg/types_386.go)4
-rw-r--r--tunnel/winipcfg/types_64.go (renamed from tunnel/winipcfg/types_amd64.go)4
-rw-r--r--tunnel/winipcfg/types_test.go2
-rw-r--r--tunnel/winipcfg/types_test_32.go (renamed from tunnel/winipcfg/types_test_386.go)4
-rw-r--r--tunnel/winipcfg/types_test_64.go (renamed from tunnel/winipcfg/types_test_amd64.go)4
-rw-r--r--tunnel/winipcfg/unicast_address_change_handler.go2
-rw-r--r--tunnel/winipcfg/winipcfg.go2
-rw-r--r--tunnel/winipcfg/winipcfg_test.go114
-rw-r--r--tunnel/winipcfg/zwinipcfg_windows.go193
-rw-r--r--tunnel/wintun_test.go2
-rw-r--r--ui/aboutdialog.go4
-rw-r--r--ui/confview.go41
-rw-r--r--ui/editdialog.go8
-rw-r--r--ui/filesave.go2
-rw-r--r--ui/icon/dot.svg (renamed from ui/icon/dot-gray.svg)0
-rw-r--r--ui/iconprovider.go10
-rw-r--r--ui/listview.go2
-rw-r--r--ui/logpage.go4
-rw-r--r--ui/managewindow.go2
-rw-r--r--ui/raise.go2
-rw-r--r--ui/syntax/highlighter.c641
-rw-r--r--ui/syntax/highlighter.go621
-rw-r--r--ui/syntax/highlighter.h39
-rw-r--r--ui/syntax/syntaxedit.c415
-rw-r--r--ui/syntax/syntaxedit.go422
-rw-r--r--ui/syntax/syntaxedit.h31
-rw-r--r--ui/tray.go179
-rw-r--r--ui/tunnelspage.go30
-rw-r--r--ui/ui.go3
-rw-r--r--ui/updatepage.go4
-rw-r--r--updater/constants.go9
-rw-r--r--updater/downloader.go94
-rw-r--r--updater/msirunner_linux.go2
-rw-r--r--updater/msirunner_windows.go51
-rw-r--r--updater/signify.go2
-rw-r--r--updater/updater_test.go2
-rw-r--r--updater/versions.go15
-rwxr-xr-xupdater/winhttp/httptest.exebin0 -> 3318784 bytes
-rw-r--r--updater/winhttp/mksyscall.go8
-rw-r--r--updater/winhttp/syscall_windows.go345
-rw-r--r--updater/winhttp/winhttp.go214
-rw-r--r--updater/winhttp/winhttp_test.go72
-rw-r--r--updater/winhttp/zsyscall_windows.go155
-rw-r--r--version/certificate_test.go42
-rw-r--r--version/certificate_windows.go115
-rw-r--r--version/debugging_linux.go2
-rw-r--r--version/official_windows.go72
-rw-r--r--version/os_windows.go2
-rw-r--r--version/useragent.go59
-rw-r--r--version/version.go10
-rw-r--r--version/version.h2
-rw-r--r--version/wintrust/certificate_windows.go59
-rw-r--r--version/wintrust/mksyscall.go8
-rw-r--r--version/wintrust/wintrust_windows.go116
-rw-r--r--version/wintrust/zsyscall_windows.go69
-rw-r--r--zgotext.go3637
198 files changed, 15654 insertions, 6057 deletions
diff --git a/.gitignore b/.gitignore
index 3883ded0..152ec5d1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,12 @@
# Dependencies
/.deps
+/.distfiles
# Build Output
/x86
/amd64
+/arm
+/arm64
# Misc
/locales/*/out.gotext.json
diff --git a/COPYING b/COPYING
index dd8dd389..33db31ae 100644
--- a/COPYING
+++ b/COPYING
@@ -1,4 +1,4 @@
-Copyright (C) 2018-2019 WireGuard LLC
+Copyright (C) 2018-2020 WireGuard LLC. All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
diff --git a/Makefile b/Makefile
index c84e0a01..e714d9d9 100644
--- a/Makefile
+++ b/Makefile
@@ -1,62 +1,91 @@
-GOFLAGS := -ldflags="-H windowsgui -s -w" -v -tags walk_use_cgo -trimpath
-export CGO_ENABLED := 1
-export CGO_CFLAGS := -O3 -Wall -Wno-unused-function -Wno-switch -std=gnu11 -DWINVER=0x0601
-export CGO_LDFLAGS := -Wl,--dynamicbase -Wl,--nxcompat -Wl,--export-all-symbols
+GOFLAGS := -tags load_wintun_from_rsrc -ldflags="-H windowsgui -s -w" -v -trimpath
export GOOS := windows
+export PATH := $(CURDIR)/.deps/go/bin:$(PATH)
-rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))
-SOURCE_FILES := $(call rwildcard,,*.go *.c *.h) .deps/prepared go.mod go.sum
-RESOURCE_FILES := resources.rc version/version.h manifest.xml $(patsubst %.svg,%.ico,$(wildcard ui/icon/*.svg))
+VERSION := $(shell sed -n 's/^\s*Number\s*=\s*"\([0-9.]\+\)"$$/\1/p' version/version.go)
+empty :=
+space := $(empty) $(empty)
+comma := ,
+RCFLAGS := -DWIREGUARD_VERSION_ARRAY=$(subst $(space),$(comma),$(wordlist 1,4,$(subst .,$(space),$(VERSION)) 0 0 0 0)) -DWIREGUARD_VERSION_STR=$(VERSION) -O coff -c 65001
+
+rwildcard=$(foreach d,$(filter-out .deps,$(wildcard $1*)),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))
+SOURCE_FILES := $(call rwildcard,,*.go) .deps/go/prepared go.mod go.sum
+RESOURCE_FILES := resources.rc version/version.go manifest.xml $(patsubst %.svg,%.ico,$(wildcard ui/icon/*.svg)) .deps/wintun/prepared
DEPLOYMENT_HOST ?= winvm
DEPLOYMENT_PATH ?= Desktop
-all: amd64/wireguard.exe x86/wireguard.exe
+all: amd64/wireguard.exe x86/wireguard.exe arm64/wireguard.exe arm/wireguard.exe
+
+define download =
+.distfiles/$(1):
+ mkdir -p .distfiles
+ if ! curl -L#o $$@.unverified $(2); then rm -f $$@.unverified; exit 1; fi
+ if ! echo "$(3) $$@.unverified" | sha256sum -c; then rm -f $$@.unverified; exit 1; fi
+ if ! mv $$@.unverified $$@; then rm -f $$@.unverified; exit 1; fi
+endef
+
+$(eval $(call download,go.tar.gz,https://golang.org/dl/go1.16beta1.linux-amd64.tar.gz,3931a0d493d411d6c697df6f15d5292fdd8031fde7014fded399effdad4c12d8))
+$(eval $(call download,wintun.zip,https://www.wintun.net/builds/wintun-0.10.zip,45bbe63a7cc60e5b6123b8d06747ba703ab3fd636298a50953db10da1d70f5b6))
+
+.deps/go/prepared: .distfiles/go.tar.gz $(wildcard go-patches/*.patch)
+ mkdir -p .deps
+ rm -rf .deps/go
+ tar -C .deps -xzf .distfiles/go.tar.gz
+ chmod -R +w .deps/go
+ cat $(filter %.patch,$^) | patch -f -N -r- -p1 -d .deps/go
+ cd .deps/go/src && GOARCH=amd64 GOOS=linux go build -v -o ../pkg/tool/linux_amd64/link cmd/link
+ touch $@
-.deps/prepared: $(wildcard go-patches/*.patch)
- rm -rf .deps && mkdir -p .deps
- if ! rsync --exclude=pkg/obj/go-build/trim.txt -aqL $$(go env GOROOT)/ .deps/goroot; then chmod -R +w .deps/goroot; exit 1; fi
- chmod -R +w .deps/goroot
- cat $^ | patch -f -N -r- -p1 -d .deps/goroot
+.deps/wintun/prepared: .distfiles/wintun.zip
+ mkdir -p .deps
+ rm -rf .deps/wintun
+ bsdtar -C .deps -xf .distfiles/wintun.zip
touch $@
%.ico: %.svg
- convert -background none $< -define icon:auto-resize="256,192,128,96,64,48,32,24,16" $@
+ convert -background none $< -define icon:auto-resize="256,192,128,96,64,48,40,32,24,20,16" -compress zip $@
resources_amd64.syso: $(RESOURCE_FILES)
- x86_64-w64-mingw32-windres -i $< -o $@ -O coff
+ x86_64-w64-mingw32-windres $(RCFLAGS) -I .deps/wintun/bin/amd64 -i $< -o $@
resources_386.syso: $(RESOURCE_FILES)
- i686-w64-mingw32-windres -i $< -o $@ -O coff
+ i686-w64-mingw32-windres $(RCFLAGS) -I .deps/wintun/bin/x86 -i $< -o $@
+
+resources_arm.syso: $(RESOURCE_FILES)
+ armv7-w64-mingw32-windres $(RCFLAGS) -I .deps/wintun/bin/arm -i $< -o $@
-amd64/wireguard.exe: export CC := x86_64-w64-mingw32-gcc
amd64/wireguard.exe: export GOARCH := amd64
-amd64/wireguard.exe: CGO_LDFLAGS += -Wl,--high-entropy-va
amd64/wireguard.exe: resources_amd64.syso $(SOURCE_FILES)
- GOROOT="$(CURDIR)/.deps/goroot" go build $(GOFLAGS) -o $@
+ go build $(GOFLAGS) -o $@
-x86/wireguard.exe: export CC := i686-w64-mingw32-gcc
x86/wireguard.exe: export GOARCH := 386
x86/wireguard.exe: resources_386.syso $(SOURCE_FILES)
- GOROOT="$(CURDIR)/.deps/goroot" go build $(GOFLAGS) -o $@
+ go build $(GOFLAGS) -o $@
+
+arm/wireguard.exe: export GOARCH := arm
+arm/wireguard.exe: export GOARM := 7
+arm/wireguard.exe: resources_arm.syso $(SOURCE_FILES)
+ go build $(GOFLAGS) -o $@
+
+arm64/wireguard.exe: arm/wireguard.exe
+ mkdir -p $(@D)
+ cp $< $@
-remaster: export CC := x86_64-w64-mingw32-gcc
remaster: export GOARCH := amd64
remaster: export GOPROXY := direct
-remaster:
+remaster: .deps/go/prepared
rm -f go.sum go.mod
cp go.mod.master go.mod
go get -d
-fmt: export CC := x86_64-w64-mingw32-gcc
fmt: export GOARCH := amd64
-fmt:
+fmt: .deps/go/prepared
go fmt ./...
generate: export GOOS :=
-generate: export CGO_ENABLED := 0
-generate:
- go generate ./...
+generate: .deps/go/prepared
+ go generate -mod=mod ./...
crowdin:
find locales -maxdepth 1 -mindepth 1 -type d \! -name en -exec rm -rf {} +
@@ -69,6 +98,9 @@ deploy: amd64/wireguard.exe
scp $< $(DEPLOYMENT_HOST):$(DEPLOYMENT_PATH)
clean:
- rm -rf *.syso ui/icon/*.ico x86/ amd64/ .deps
+ rm -rf *.syso ui/icon/*.ico x86/ amd64/ arm/ arm64/ .deps
+
+distclean: clean
+ rm -rf .distfiles
-.PHONY: deploy clean fmt remaster generate all
+.PHONY: deploy clean distclean fmt remaster generate all
diff --git a/README.md b/README.md
index 33cc6fa5..296fd81b 100644
--- a/README.md
+++ b/README.md
@@ -1,102 +1,46 @@
# [WireGuard](https://www.wireguard.com/) for Windows
-***If you've come here looking to simply run WireGuard for Windows, [you may download it here](https://www.wireguard.com/install/).***
+This is a fully-featured WireGuard client for Windows that uses [Wintun](https://www.wintun.net/). It is the only official and recommended way of using WireGuard on Windows.
-This is a fully-featured WireGuard client for Windows that uses [Wintun](https://www.wintun.net/).
+## Download &amp; Install
-### Building
+If you've come here looking to simply run WireGuard for Windows, [the main download page has links](https://www.wireguard.com/install/). There you will find two things:
-Windows 10 64-bit or Windows Server 2019, and Git for Windows is required. The build script will take care of downloading, verifying, and extracting the right versions of the various dependencies:
+- [The WireGuard Installer](https://download.wireguard.com/windows-client/wireguard-installer.exe) &ndash; This selects the most recent version for your architecture, downloads it, checks signatures and hashes, and installs it.
+- [Standalone MSIs](https://download.wireguard.com/windows-client/) &ndash; These are for system admins who wish to deploy the MSIs directly. For most end users, the ordinary installer takes care of downloading these automatically.
-```
-C:\Projects> git clone https://git.zx2c4.com/wireguard-windows
-C:\Projects> cd wireguard-windows
-C:\Projects\wireguard-windows> build
-```
-
-### Running
-
-After you've built the application, run `amd64\wireguard.exe` or `x86\wireguard.exe` to install the manager service and show the UI.
-
-```
-C:\Projects\wireguard-windows> amd64\wireguard.exe
-```
-
-Since WireGuard requires the Wintun driver to be installed, and this generally requires a valid Microsoft signature, you may benefit from first installing a release of WireGuard for Windows from the official [wireguard.com](https://www.wireguard.com/install/) builds, which bundles a Microsoft-signed Wintun, and then subsequently run your own wireguard.exe. Alternatively, you can craft your own installer using the `quickinstall.bat` script.
-
-### Optional: Localizing
-
-To translate WireGuard UI to your language:
-
-1. Upgrade `resources.rc` accordingly. Follow the pattern.
-
-2. Make a new directory in `locales\` containing the language ID:
-
- ```
- C:\Projects\wireguard-windows> mkdir locales\<langID>
- ```
-
-3. Configure and run `build` to prepare initial `locales\<langID>\messages.gotext.json` file:
-
- ```
- C:\Projects\wireguard-windows> set GoGenerate=yes
- C:\Projects\wireguard-windows> build
- C:\Projects\wireguard-windows> copy locales\<langID>\out.gotext.json locales\<langID>\messages.gotext.json
- ```
-
-4. Translate `locales\<langID>\messages.gotext.json`. See other language message files how to translate messages and how to tackle plural. For this step, the project is currently using [CrowdIn](https://crowdin.com/translate/WireGuard); please make sure your translations make it there in order to be added here.
-
-5. Run `build` from the step 3 again, and test.
-
-6. Repeat from step 4.
+## Documentation
-### Optional: Creating the Installer
+In addition to this [`README.md`](README.md), the following documents are also available:
-The installer build script will take care of downloading, verifying, and extracting the right versions of the various dependencies:
+- [`adminregistry.md`](docs/adminregistry.md) &ndash; A list of registry keys settable by the system administrator for changing the behavior of the application.
+- [`attacksurface.md`](docs/attacksurface.md) &ndash; A discussion of the various components from a security perspective, so that future auditors of this code have a head start in assessing its security design.
+- [`buildrun.md`](docs/buildrun.md) &ndash; Instructions on building, localizing, running, and developing for this repository.
+- [`enterprise.md`](docs/enterprise.md) &ndash; A summary of various features and tips for making the application usable in enterprise settings.
+- [`netquirk.md`](docs/netquirk.md) &ndash; A description of various networking quirks and "kill-switch" semantics.
-```
-C:\Projects\wireguard-windows> cd installer
-C:\Projects\wireguard-windows\installer> build
-```
-
-### Optional: Signing Binaries
-
-Add a file called `sign.bat` in the root of this repository with these contents, or similar:
-
-```
-set SigningCertificate=DF98E075A012ED8C86FBCF14854B8F9555CB3D45
-set TimestampServer=http://timestamp.digicert.com
-```
-
-After, run the above `build` commands as usual, from a shell that has [`signtool.exe`](https://docs.microsoft.com/en-us/windows/desktop/SecCrypto/signtool) in its `PATH`, such as the Visual Studio 2017 command prompt.
-
-### Alternative: Building from Linux
+## License
-You must first have Go ≥1.12, Mingw, and ImageMagick installed.
+This repository is MIT-licensed.
-```
-$ sudo apt install mingw-w64 golang-go imagemagick
-$ git clone https://git.zx2c4.com/wireguard-windows
-$ cd wireguard-windows
-$ make
-```
-
-You can deploy the 64-bit build to an SSH host specified by the `DEPLOYMENT_HOST` environment variable (default "winvm") to the remote directory specified by the `DEPLOYMENT_PATH` environment variable (default "Desktop") by using the `deploy` target:
+```text
+Copyright (C) 2018-2020 WireGuard LLC. All Rights Reserved.
-```
-$ make deploy
-```
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
-### [`wg(8)`](https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8) Support for Windows
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
-The command line utility [`wg(8)`](https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8) works well on Windows. Being a Unix-centric project, it compiles with a Makefile and MingW:
-
-```
-$ git clone https://git.zx2c4.com/wireguard-tools
-$ PLATFORM=windows make -C wireguard-tools/src
-$ stat wireguard-tools/src/wg.exe
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
```
-
-It interacts with WireGuard instances run by the main WireGuard for Windows program.
-
-When building on Windows, the aforementioned `build.bat` script takes care of building this.
diff --git a/build.bat b/build.bat
index 53bbc07c..da6ad338 100644
--- a/build.bat
+++ b/build.bat
@@ -1,6 +1,6 @@
@echo off
rem SPDX-License-Identifier: MIT
-rem Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+rem Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
setlocal enabledelayedexpansion
set BUILDDIR=%~dp0
@@ -13,49 +13,53 @@ if exist .deps\prepared goto :render
rmdir /s /q .deps 2> NUL
mkdir .deps || goto :error
cd .deps || goto :error
- call :download go.zip https://dl.google.com/go/go1.15.2.windows-amd64.zip e72782cc6de233188c75b06849368826eaa1b8bd9e1cd766db9466a12b7138ca || goto :error
- rem Mirror of https://musl.cc/i686-w64-mingw32-native.zip
- call :download mingw-x86.zip https://download.wireguard.com/windows-toolchain/distfiles/i686-w64-mingw32-native-20200907.zip c972c00993727ac9bff83c799f4df65662adb95bc871fa30cfa8857e744a7fbd || goto :error
- rem Mirror of https://musl.cc/x86_64-w64-mingw32-native.zip
- call :download mingw-amd64.zip https://download.wireguard.com/windows-toolchain/distfiles/x86_64-w64-mingw32-native-20200907.zip e34fbacbd25b007a8074fc96f7e08f886241e0473a055987ee57483c37567aa5 || goto :error
+ call :download go.zip https://golang.org/dl/go1.16beta1.windows-amd64.zip f06e2d7f300843473527e8fdd2d496aef5ffa6507ade0ac1141934e5c6ca7d63 || goto :error
+ rem Mirror of https://github.com/mstorsjo/llvm-mingw/releases/download/20201020/llvm-mingw-20201020-msvcrt-x86_64.zip
+ call :download llvm-mingw-msvcrt.zip https://download.wireguard.com/windows-toolchain/distfiles/llvm-mingw-20201020-msvcrt-x86_64.zip 2e46593245090df96d15e360e092f0b62b97e93866e0162dca7f93b16722b844 || goto :error
rem Mirror of https://imagemagick.org/download/binaries/ImageMagick-7.0.8-42-portable-Q16-x64.zip
call :download imagemagick.zip https://download.wireguard.com/windows-toolchain/distfiles/ImageMagick-7.0.8-42-portable-Q16-x64.zip 584e069f56456ce7dde40220948ff9568ac810688c892c5dfb7f6db902aa05aa "convert.exe colors.xml delegates.xml" || goto :error
rem Mirror of https://sourceforge.net/projects/ezwinports/files/make-4.2.1-without-guile-w32-bin.zip
call :download make.zip https://download.wireguard.com/windows-toolchain/distfiles/make-4.2.1-without-guile-w32-bin.zip 30641be9602712be76212b99df7209f4f8f518ba764cf564262bc9d6e4047cc7 "--strip-components 1 bin" || goto :error
- call :download wireguard-tools.zip https://git.zx2c4.com/wireguard-tools/snapshot/wireguard-tools-1.0.20200319.zip f0f186924b67696e5dac6020270b0ac27fd7d96b4976605d1cded405d27b2f54 "--exclude wg-quick --strip-components 1" || goto :error
+ call :download wireguard-tools.zip https://git.zx2c4.com/wireguard-tools/snapshot/wireguard-tools-5e24780d4cb259b7392db0fe7f0c2f129bd598f3.zip c900b42401b8d661c948b508ef906636d2eb0f6af85760b2f66ccc175687e98f "--exclude wg-quick --strip-components 1" || goto :error
rem Mirror of https://sourceforge.net/projects/gnuwin32/files/patch/2.5.9-7/patch-2.5.9-7-bin.zip with fixed manifest
call :download patch.zip https://download.wireguard.com/windows-toolchain/distfiles/patch-2.5.9-7-bin-fixed-manifest.zip 25977006ca9713f2662a5d0a2ed3a5a138225b8be3757035bd7da9dcf985d0a1 "--strip-components 1 bin" || goto :error
+ call :download wintun.zip https://www.wintun.net/builds/wintun-0.10.zip 45bbe63a7cc60e5b6123b8d06747ba703ab3fd636298a50953db10da1d70f5b6 || goto :error
echo [+] Patching go
for %%a in ("..\go-patches\*.patch") do .\patch.exe -f -N -r- -d go -p1 --binary < "%%a" || goto :error
+ cd go\src || goto :error
+ ..\bin\go build -v -o ..\pkg\tool\windows_amd64\link.exe cmd/link || goto :error
+ cd ..\.. || goto :error
copy /y NUL prepared > NUL || goto :error
cd .. || goto :error
:render
echo [+] Rendering icons
- for %%a in ("ui\icon\*.svg") do convert -background none "%%~fa" -define icon:auto-resize="256,192,128,96,64,48,32,24,16" "%%~dpna.ico" || goto :error
+ for %%a in ("ui\icon\*.svg") do convert -background none "%%~fa" -define icon:auto-resize="256,192,128,96,64,48,40,32,24,20,16" -compress zip "%%~dpna.ico" || goto :error
:build
+ for /f "tokens=3" %%a in ('findstr /r "Number.*=.*[0-9.]*" .\version\version.go') do set WIREGUARD_VERSION=%%a
+ set WIREGUARD_VERSION=%WIREGUARD_VERSION:"=%
+ for /f "tokens=1-4" %%a in ("%WIREGUARD_VERSION:.= % 0 0 0") do set WIREGUARD_VERSION_ARRAY=%%a,%%b,%%c,%%d
set GOOS=windows
+ set GOARM=7
set GOPATH=%BUILDDIR%.deps\gopath
set GOROOT=%BUILDDIR%.deps\go
+ set PATH=%BUILDDIR%.deps\llvm-mingw\bin;%PATH%
if "%GoGenerate%"=="yes" (
echo [+] Regenerating files
- set PATH=!BUILDDIR!.deps\x86_64-w64-mingw32-native\bin;!PATH!
go generate ./... || exit /b 1
)
- set CGO_ENABLED=1
- set CGO_CFLAGS=-O3 -Wall -Wno-unused-function -Wno-switch -std=gnu11 -DWINVER=0x0601
- set CGO_LDFLAGS=-Wl,--dynamicbase -Wl,--nxcompat -Wl,--export-all-symbols
call :build_plat x86 i686 386 || goto :error
- set CGO_LDFLAGS=%CGO_LDFLAGS% -Wl,--high-entropy-va
call :build_plat amd64 x86_64 amd64 || goto :error
+ call :build_plat arm armv7 arm || goto :error
+ call :build_plat arm64 aarch64 arm64 || goto :error
:sign
if exist .\sign.bat call .\sign.bat
if "%SigningCertificate%"=="" goto :success
if "%TimestampServer%"=="" goto :success
echo [+] Signing
- signtool sign /sha1 "%SigningCertificate%" /fd sha256 /tr "%TimestampServer%" /td sha256 /d WireGuard x86\wireguard.exe x86\wg.exe amd64\wireguard.exe amd64\wg.exe || goto :error
+ signtool sign /sha1 "%SigningCertificate%" /fd sha256 /tr "%TimestampServer%" /td sha256 /d WireGuard x86\wireguard.exe x86\wg.exe amd64\wireguard.exe amd64\wg.exe arm\wireguard.exe arm\wg.exe arm64\wireguard.exe arm64\wg.exe || goto :error
:success
echo [+] Success. Launch wireguard.exe.
@@ -73,18 +77,20 @@ if exist .deps\prepared goto :render
goto :eof
:build_plat
- set PATH=%BUILDDIR%.deps\%~2-w64-mingw32-native\bin;%PATH%
- set CC=%~2-w64-mingw32-gcc
set GOARCH=%~3
mkdir %1 >NUL 2>&1
echo [+] Assembling resources %1
- windres -i resources.rc -o resources.syso -O coff || exit /b %errorlevel%
+ %~2-w64-mingw32-windres -I ".deps\wintun\bin\%~1" -DWIREGUARD_VERSION_ARRAY=%WIREGUARD_VERSION_ARRAY% -DWIREGUARD_VERSION_STR=%WIREGUARD_VERSION% -i resources.rc -o "resources_%~3.syso" -O coff -c 65001 || exit /b %errorlevel%
echo [+] Building program %1
- go build -ldflags="-H windowsgui -s -w" -tags walk_use_cgo -trimpath -v -o "%~1\wireguard.exe" || exit /b 1
+ if %1==arm64 (
+ copy "arm\wireguard.exe" "%~1\wireguard.exe" || exit /b 1
+ ) else (
+ go build -tags load_wintun_from_rsrc -ldflags="-H windowsgui -s -w" -trimpath -v -o "%~1\wireguard.exe" || exit /b 1
+ )
if not exist "%~1\wg.exe" (
echo [+] Building command line tools %1
del .deps\src\*.exe .deps\src\*.o .deps\src\wincompat\*.o 2> NUL
- make --no-print-directory -C .deps\src PLATFORM=windows CC=%CC% V=1 LDFLAGS=-s RUNSTATEDIR= SYSTEMDUNITDIR= -j%NUMBER_OF_PROCESSORS% || exit /b 1
+ make --no-print-directory -C .deps\src PLATFORM=windows CC=%~2-w64-mingw32-gcc WINDRES=%~2-w64-mingw32-windres V=1 LDFLAGS=-s RUNSTATEDIR= SYSTEMDUNITDIR= -j%NUMBER_OF_PROCESSORS% || exit /b 1
move /Y .deps\src\wg.exe "%~1\wg.exe" > NUL || exit /b 1
)
goto :eof
diff --git a/conf/admin_windows.go b/conf/admin_windows.go
new file mode 100644
index 00000000..5636831c
--- /dev/null
+++ b/conf/admin_windows.go
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
+ */
+
+package conf
+
+import "golang.org/x/sys/windows/registry"
+
+const adminRegKey = `Software\WireGuard`
+
+var adminKey registry.Key
+
+func openAdminKey() (registry.Key, error) {
+ if adminKey != 0 {
+ return adminKey, nil
+ }
+ var err error
+ adminKey, err = registry.OpenKey(registry.LOCAL_MACHINE, adminRegKey, registry.QUERY_VALUE|registry.WOW64_64KEY)
+ if err != nil {
+ return 0, err
+ }
+ return adminKey, nil
+}
+
+func AdminBool(name string) bool {
+ key, err := openAdminKey()
+ if err != nil {
+ return false
+ }
+ val, _, err := key.GetIntegerValue(name)
+ if err != nil {
+ return false
+ }
+ return val != 0
+}
diff --git a/conf/config.go b/conf/config.go
index e1951d47..16438002 100644
--- a/conf/config.go
+++ b/conf/config.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package conf
@@ -49,6 +49,10 @@ type Interface struct {
MTU uint16
DNS []net.IP
DNSSearch []string
+ PreUp string
+ PostUp string
+ PreDown string
+ PostDown string
}
type Peer struct {
@@ -81,6 +85,14 @@ func (r *IPCidr) IPNet() net.IPNet {
}
}
+func (r *IPCidr) MaskSelf() {
+ bits := int(r.Bits())
+ mask := net.CIDRMask(int(r.Cidr), bits)
+ for i := 0; i < bits/8; i++ {
+ r.IP[i] &= mask[i]
+ }
+}
+
func (e *Endpoint) String() string {
if strings.IndexByte(e.Host, ':') > 0 {
return fmt.Sprintf("[%s]:%d", e.Host, e.Port)
@@ -230,3 +242,11 @@ func (conf *Config) DeduplicateNetworkEntries() {
peer.AllowedIPs = peer.AllowedIPs[:i]
}
}
+
+func (conf *Config) Redact() {
+ conf.Interface.PrivateKey = Key{}
+ for i := range conf.Peers {
+ conf.Peers[i].PublicKey = Key{}
+ conf.Peers[i].PresharedKey = Key{}
+ }
+}
diff --git a/conf/dnsresolver_windows.go b/conf/dnsresolver_windows.go
index d6c2f1c7..c1faf78c 100644
--- a/conf/dnsresolver_windows.go
+++ b/conf/dnsresolver_windows.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package conf
diff --git a/conf/dpapi/dpapi_windows.go b/conf/dpapi/dpapi_windows.go
index 851ec1ee..45ad950e 100644
--- a/conf/dpapi/dpapi_windows.go
+++ b/conf/dpapi/dpapi_windows.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package dpapi
@@ -9,78 +9,59 @@ import (
"errors"
"runtime"
"unsafe"
+ "fmt"
"golang.org/x/sys/windows"
)
-const (
- dpCRYPTPROTECT_UI_FORBIDDEN uint32 = 0x1
- dpCRYPTPROTECT_LOCAL_MACHINE uint32 = 0x4
- dpCRYPTPROTECT_CRED_SYNC uint32 = 0x8
- dpCRYPTPROTECT_AUDIT uint32 = 0x10
- dpCRYPTPROTECT_NO_RECOVERY uint32 = 0x20
- dpCRYPTPROTECT_VERIFY_PROTECTION uint32 = 0x40
- dpCRYPTPROTECT_CRED_REGENERATE uint32 = 0x80
-)
-
-type dpBlob struct {
- len uint32
- data uintptr
+func bytesToBlob(bytes []byte) *windows.DataBlob {
+ blob := &windows.DataBlob{Size: uint32(len(bytes))}
+ if len(bytes) > 0 {
+ blob.Data = &bytes[0]
+ }
+ return blob
}
-func bytesToBlob(bytes []byte) *dpBlob {
- blob := &dpBlob{}
- blob.len = uint32(len(bytes))
- if len(bytes) > 0 {
- blob.data = uintptr(unsafe.Pointer(&bytes[0]))
- }
- return blob
-}
-
-//sys cryptProtectData(dataIn *dpBlob, name *uint16, optionalEntropy *dpBlob, reserved uintptr, promptStruct uintptr, flags uint32, dataOut *dpBlob) (err error) = crypt32.CryptProtectData
-
func Encrypt(data []byte, name string) ([]byte, error) {
- out := dpBlob{}
- err := cryptProtectData(bytesToBlob(data), windows.StringToUTF16Ptr(name), nil, 0, 0, dpCRYPTPROTECT_UI_FORBIDDEN, &out)
+ out := windows.DataBlob{}
+ err := windows.CryptProtectData(bytesToBlob(data), windows.StringToUTF16Ptr(name), nil, 0, nil, windows.CRYPTPROTECT_UI_FORBIDDEN, &out)
if err != nil {
- return nil, errors.New("Unable to encrypt DPAPI protected data: " + err.Error())
+ return nil, fmt.Errorf("unable to encrypt DPAPI protected data: %w", err)
}
outSlice := *(*[]byte)(unsafe.Pointer(&(struct {
- addr uintptr
+ addr *byte
len int
cap int
- }{out.data, int(out.len), int(out.len)})))
+ }{out.Data, int(out.Size), int(out.Size)})))
ret := make([]byte, len(outSlice))
copy(ret, outSlice)
- windows.LocalFree(windows.Handle(out.data))
+ windows.LocalFree(windows.Handle(unsafe.Pointer(out.Data)))
return ret, nil
}
-//sys cryptUnprotectData(dataIn *dpBlob, name **uint16, optionalEntropy *dpBlob, reserved uintptr, promptStruct uintptr, flags uint32, dataOut *dpBlob) (err error) = crypt32.CryptUnprotectData
-
func Decrypt(data []byte, name string) ([]byte, error) {
- out := dpBlob{}
+ out := windows.DataBlob{}
var outName *uint16
utf16Name, err := windows.UTF16PtrFromString(name)
if err != nil {
return nil, err
}
- err = cryptUnprotectData(bytesToBlob(data), &outName, nil, 0, 0, dpCRYPTPROTECT_UI_FORBIDDEN, &out)
+ err = windows.CryptUnprotectData(bytesToBlob(data), &outName, nil, 0, nil, windows.CRYPTPROTECT_UI_FORBIDDEN, &out)
if err != nil {
- return nil, errors.New("Unable to decrypt DPAPI protected data: " + err.Error())
+ return nil, fmt.Errorf("unable to decrypt DPAPI protected data: %w", err)
}
outSlice := *(*[]byte)(unsafe.Pointer(&(struct {
- addr uintptr
+ addr *byte
len int
cap int
- }{out.data, int(out.len), int(out.len)})))
+ }{out.Data, int(out.Size), int(out.Size)})))
ret := make([]byte, len(outSlice))
copy(ret, outSlice)
- windows.LocalFree(windows.Handle(out.data))
+ windows.LocalFree(windows.Handle(unsafe.Pointer(out.Data)))
// Note: this ridiculous open-coded strcmp is not constant time.
different := false
@@ -101,7 +82,7 @@ func Decrypt(data []byte, name string) ([]byte, error) {
windows.LocalFree(windows.Handle(unsafe.Pointer(outName)))
if different {
- return nil, errors.New("The input name does not match the stored name")
+ return nil, errors.New("input name does not match the stored name")
}
return ret, nil
diff --git a/conf/dpapi/dpapi_windows_test.go b/conf/dpapi/dpapi_windows_test.go
index 8356f2d4..2c0d28e6 100644
--- a/conf/dpapi/dpapi_windows_test.go
+++ b/conf/dpapi/dpapi_windows_test.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package dpapi
diff --git a/conf/dpapi/mksyscall.go b/conf/dpapi/mksyscall.go
deleted file mode 100644
index 3d467f76..00000000
--- a/conf/dpapi/mksyscall.go
+++ /dev/null
@@ -1,8 +0,0 @@
-/* SPDX-License-Identifier: MIT
- *
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
- */
-
-package dpapi
-
-//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zdpapi_windows.go dpapi_windows.go
diff --git a/conf/dpapi/test.exe b/conf/dpapi/test.exe
new file mode 100755
index 00000000..9e5f23a7
--- /dev/null
+++ b/conf/dpapi/test.exe
Binary files differ
diff --git a/conf/dpapi/zdpapi_windows.go b/conf/dpapi/zdpapi_windows.go
deleted file mode 100644
index e48d36b2..00000000
--- a/conf/dpapi/zdpapi_windows.go
+++ /dev/null
@@ -1,68 +0,0 @@
-// Code generated by 'go generate'; DO NOT EDIT.
-
-package dpapi
-
-import (
- "syscall"
- "unsafe"
-
- "golang.org/x/sys/windows"
-)
-
-var _ unsafe.Pointer
-
-// Do the interface allocations only once for common
-// Errno values.
-const (
- errnoERROR_IO_PENDING = 997
-)
-
-var (
- errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
-)
-
-// errnoErr returns common boxed Errno values, to prevent
-// allocations at runtime.
-func errnoErr(e syscall.Errno) error {
- switch e {
- case 0:
- return nil
- case errnoERROR_IO_PENDING:
- return errERROR_IO_PENDING
- }
- // TODO: add more here, after collecting data on the common
- // error values see on Windows. (perhaps when running
- // all.bat?)
- return e
-}
-
-var (
- modcrypt32 = windows.NewLazySystemDLL("crypt32.dll")
-
- procCryptProtectData = modcrypt32.NewProc("CryptProtectData")
- procCryptUnprotectData = modcrypt32.NewProc("CryptUnprotectData")
-)
-
-func cryptProtectData(dataIn *dpBlob, name *uint16, optionalEntropy *dpBlob, reserved uintptr, promptStruct uintptr, flags uint32, dataOut *dpBlob) (err error) {
- r1, _, e1 := syscall.Syscall9(procCryptProtectData.Addr(), 7, uintptr(unsafe.Pointer(dataIn)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(optionalEntropy)), uintptr(reserved), uintptr(promptStruct), uintptr(flags), uintptr(unsafe.Pointer(dataOut)), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
- }
- return
-}
-
-func cryptUnprotectData(dataIn *dpBlob, name **uint16, optionalEntropy *dpBlob, reserved uintptr, promptStruct uintptr, flags uint32, dataOut *dpBlob) (err error) {
- r1, _, e1 := syscall.Syscall9(procCryptUnprotectData.Addr(), 7, uintptr(unsafe.Pointer(dataIn)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(optionalEntropy)), uintptr(reserved), uintptr(promptStruct), uintptr(flags), uintptr(unsafe.Pointer(dataOut)), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
- }
- return
-}
diff --git a/conf/filewriter_windows.go b/conf/filewriter_windows.go
new file mode 100644
index 00000000..bb538265
--- /dev/null
+++ b/conf/filewriter_windows.go
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
+ */
+
+package conf
+
+import (
+ "crypto/rand"
+ "encoding/hex"
+ "sync/atomic"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+var encryptedFileSd unsafe.Pointer
+
+func randomFileName() string {
+ var randBytes [32]byte
+ _, err := rand.Read(randBytes[:])
+ if err != nil {
+ panic(err)
+ }
+ return hex.EncodeToString(randBytes[:]) + ".tmp"
+}
+
+func writeLockedDownFile(destination string, overwrite bool, contents []byte) error {
+ var err error
+ sa := &windows.SecurityAttributes{Length: uint32(unsafe.Sizeof(windows.SecurityAttributes{}))}
+ sa.SecurityDescriptor = (*windows.SECURITY_DESCRIPTOR)(atomic.LoadPointer(&encryptedFileSd))
+ if sa.SecurityDescriptor == nil {
+ sa.SecurityDescriptor, err = windows.SecurityDescriptorFromString("O:SYG:SYD:PAI(A;;FA;;;SY)(A;;SD;;;BA)")
+ if err != nil {
+ return err
+ }
+ atomic.StorePointer(&encryptedFileSd, unsafe.Pointer(sa.SecurityDescriptor))
+ }
+ destination16, err := windows.UTF16FromString(destination)
+ if err != nil {
+ return err
+ }
+ tmpDestination := randomFileName()
+ tmpDestination16, err := windows.UTF16PtrFromString(tmpDestination)
+ if err != nil {
+ return err
+ }
+ handle, err := windows.CreateFile(tmpDestination16, windows.GENERIC_WRITE|windows.DELETE, windows.FILE_SHARE_READ, sa, windows.CREATE_ALWAYS, windows.FILE_ATTRIBUTE_NORMAL, 0)
+ if err != nil {
+ return err
+ }
+ defer windows.CloseHandle(handle)
+ deleteIt := func() {
+ yes := byte(1)
+ windows.SetFileInformationByHandle(handle, windows.FileDispositionInfo, &yes, 1)
+ }
+ n, err := windows.Write(handle, contents)
+ if err != nil {
+ deleteIt()
+ return err
+ }
+ if n != len(contents) {
+ deleteIt()
+ return windows.ERROR_IO_INCOMPLETE
+ }
+ fileRenameInfo := &struct {
+ replaceIfExists byte
+ rootDirectory windows.Handle
+ fileNameLength uint32
+ fileName [windows.MAX_PATH]uint16
+ }{replaceIfExists: func() byte {
+ if overwrite {
+ return 1
+ } else {
+ return 0
+ }
+ }(), fileNameLength: uint32(len(destination16) - 1)}
+ if len(destination16) > len(fileRenameInfo.fileName) {
+ deleteIt()
+ return windows.ERROR_BUFFER_OVERFLOW
+ }
+ copy(fileRenameInfo.fileName[:], destination16[:])
+ err = windows.SetFileInformationByHandle(handle, windows.FileRenameInfo, (*byte)(unsafe.Pointer(fileRenameInfo)), uint32(unsafe.Sizeof(*fileRenameInfo)))
+ if err != nil {
+ deleteIt()
+ return err
+ }
+ return nil
+}
diff --git a/conf/migration_windows.go b/conf/migration_windows.go
index 72b298b6..4b68763f 100644
--- a/conf/migration_windows.go
+++ b/conf/migration_windows.go
@@ -1,51 +1,89 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package conf
import (
+ "errors"
+ "io/ioutil"
"log"
+ "os"
"path/filepath"
"strings"
+ "sync"
+ "time"
"golang.org/x/sys/windows"
)
-func maybeMigrate(c string) {
- if disableAutoMigration {
- return
- }
+var migrating sync.Mutex
+var lastMigrationTimer *time.Timer
- vol := filepath.VolumeName(c)
- withoutVol := strings.TrimPrefix(c, vol)
- oldRoot := filepath.Join(vol, "\\windows.old")
- oldC := filepath.Join(oldRoot, withoutVol)
+func MigrateUnencryptedConfigs() { migrateUnencryptedConfigs(3) }
- sd, err := windows.GetNamedSecurityInfo(oldRoot, windows.SE_FILE_OBJECT, windows.OWNER_SECURITY_INFORMATION)
- if err == windows.ERROR_PATH_NOT_FOUND || err == windows.ERROR_FILE_NOT_FOUND {
- return
- }
+func migrateUnencryptedConfigs(sharingBase int) {
+ migrating.Lock()
+ defer migrating.Unlock()
+ configFileDir, err := tunnelConfigurationsDirectory()
if err != nil {
- log.Printf("Not migrating configuration from ‘%s’ due to GetNamedSecurityInfo error: %v", oldRoot, err)
return
}
- owner, defaulted, err := sd.Owner()
+ files, err := ioutil.ReadDir(configFileDir)
if err != nil {
- log.Printf("Not migrating configuration from ‘%s’ due to GetSecurityDescriptorOwner error: %v", oldRoot, err)
return
}
- if defaulted || (!owner.IsWellKnown(windows.WinLocalSystemSid) && !owner.IsWellKnown(windows.WinBuiltinAdministratorsSid)) {
- log.Printf("Not migrating configuration from ‘%s’, as it is not explicitly owned by SYSTEM or Built-in Administrators, but rather ‘%v’", oldRoot, owner)
- return
- }
- err = windows.MoveFileEx(windows.StringToUTF16Ptr(oldC), windows.StringToUTF16Ptr(c), windows.MOVEFILE_COPY_ALLOWED)
- if err != nil {
- if err != windows.ERROR_FILE_NOT_FOUND && err != windows.ERROR_ALREADY_EXISTS {
- log.Printf("Not migrating configuration from ‘%s’ due to error when moving files: %v", oldRoot, err)
+ ignoreSharingViolations := false
+ for _, file := range files {
+ path := filepath.Join(configFileDir, file.Name())
+ name := filepath.Base(file.Name())
+ if len(name) <= len(configFileUnencryptedSuffix) || !strings.HasSuffix(name, configFileUnencryptedSuffix) {
+ continue
}
- return
+ if !file.Mode().IsRegular() || file.Mode().Perm()&0444 == 0 {
+ continue
+ }
+
+ var bytes []byte
+ var config *Config
+ // We don't use ioutil's ReadFile, because we actually want RDWR, so that we can take advantage
+ // of Windows file locking for ensuring the file is finished being written.
+ f, err := os.OpenFile(path, os.O_RDWR, 0)
+ if err != nil {
+ if errors.Is(err, windows.ERROR_SHARING_VIOLATION) {
+ if ignoreSharingViolations {
+ continue
+ } else if sharingBase > 0 {
+ if lastMigrationTimer != nil {
+ lastMigrationTimer.Stop()
+ }
+ lastMigrationTimer = time.AfterFunc(time.Second/time.Duration(sharingBase*sharingBase), func() { migrateUnencryptedConfigs(sharingBase - 1) })
+ ignoreSharingViolations = true
+ continue
+ }
+ }
+ goto error
+ }
+ bytes, err = ioutil.ReadAll(f)
+ f.Close()
+ if err != nil {
+ goto error
+ }
+ config, err = FromWgQuickWithUnknownEncoding(string(bytes), strings.TrimSuffix(name, configFileUnencryptedSuffix))
+ if err != nil {
+ goto error
+ }
+ err = config.Save(false)
+ if err != nil {
+ goto error
+ }
+ err = os.Remove(path)
+ if err != nil {
+ log.Printf("Unable to remove old path %#q: %v", path, err)
+ }
+ continue
+ error:
+ log.Printf("Unable to ingest and encrypt %#q: %v", path, err)
}
- log.Printf("Migrated configuration from ‘%s’", oldRoot)
}
diff --git a/conf/mksyscall.go b/conf/mksyscall.go
index 2706c304..4b0c5eb0 100644
--- a/conf/mksyscall.go
+++ b/conf/mksyscall.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package conf
diff --git a/conf/name.go b/conf/name.go
index 87c463af..6840bcaf 100644
--- a/conf/name.go
+++ b/conf/name.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package conf
diff --git a/conf/parser.go b/conf/parser.go
index da21e796..d26bdea4 100644
--- a/conf/parser.go
+++ b/conf/parser.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package conf
@@ -282,6 +282,14 @@ func FromWgQuick(s string, name string) (*Config, error) {
conf.Interface.DNS = append(conf.Interface.DNS, a)
}
}
+ case "preup":
+ conf.Interface.PreUp = val
+ case "postup":
+ conf.Interface.PostUp = val
+ case "predown":
+ conf.Interface.PreDown = val
+ case "postdown":
+ conf.Interface.PostDown = val
default:
return nil, &ParseError{l18n.Sprintf("Invalid key for [Interface] section"), key}
}
diff --git a/conf/parser_test.go b/conf/parser_test.go
index a6afbf53..5f806784 100644
--- a/conf/parser_test.go
+++ b/conf/parser_test.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package conf
diff --git a/conf/path_windows.go b/conf/path_windows.go
index a53968c5..9bda7918 100644
--- a/conf/path_windows.go
+++ b/conf/path_windows.go
@@ -1,33 +1,36 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package conf
import (
+ "debug/pe"
+ "errors"
"os"
"path/filepath"
+ "strings"
+ "unsafe"
"golang.org/x/sys/windows"
+ "golang.org/x/sys/windows/registry"
)
var cachedConfigFileDir string
var cachedRootDir string
-var disableAutoMigration bool
func tunnelConfigurationsDirectory() (string, error) {
if cachedConfigFileDir != "" {
return cachedConfigFileDir, nil
}
- root, err := RootDirectory()
+ root, err := RootDirectory(true)
if err != nil {
return "", err
}
c := filepath.Join(root, "Configurations")
- maybeMigrate(c)
- err = os.MkdirAll(c, os.ModeDir|0700)
- if err != nil {
+ err = os.Mkdir(c, os.ModeDir|0700)
+ if err != nil && !os.IsExist(err) {
return "", err
}
cachedConfigFileDir = c
@@ -39,22 +42,121 @@ func tunnelConfigurationsDirectory() (string, error) {
// consumers of our libraries who might want to do strange things.
func PresetRootDirectory(root string) {
cachedRootDir = root
- disableAutoMigration = true
}
-func RootDirectory() (string, error) {
+func RootDirectory(create bool) (string, error) {
if cachedRootDir != "" {
return cachedRootDir, nil
}
- root, err := windows.KnownFolderPath(windows.FOLDERID_LocalAppData, windows.KF_FLAG_CREATE)
+ //TODO: remove the wow detection logic when Go supports arm64
+ var isWow bool
+ var processMachine, nativeMachine uint16
+ err := windows.IsWow64Process2(windows.CurrentProcess(), &processMachine, &nativeMachine)
+ if err == nil {
+ isWow = processMachine != pe.IMAGE_FILE_MACHINE_UNKNOWN
+ } else {
+ if !errors.Is(err, windows.ERROR_PROC_NOT_FOUND) {
+ return "", err
+ }
+ err = windows.IsWow64Process(windows.CurrentProcess(), &isWow)
+ if err != nil {
+ return "", err
+ }
+ }
+ var root string
+ if !isWow {
+ root, err = windows.KnownFolderPath(windows.FOLDERID_ProgramFiles, windows.KF_FLAG_DEFAULT)
+ if err != nil {
+ return "", err
+ }
+ } else {
+ key, err := registry.OpenKey(windows.HKEY_LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows\CurrentVersion`, registry.READ|registry.WOW64_64KEY)
+ if err != nil {
+ return "", err
+ }
+ var typ uint32
+ root, typ, err = key.GetStringValue("ProgramFilesDir")
+ key.Close()
+ if err != nil {
+ return "", err
+ }
+ if typ != registry.SZ {
+ return "", registry.ErrUnexpectedType
+ }
+ }
+ root = filepath.Join(root, "WireGuard")
+ if !create {
+ return filepath.Join(root, "Data"), nil
+ }
+ root16, err := windows.UTF16PtrFromString(root)
+ if err != nil {
+ return "", err
+ }
+
+ // The root directory inherits its ACL from Program Files; we don't want to mess with that
+ err = windows.CreateDirectory(root16, nil)
+ if err != nil && err != windows.ERROR_ALREADY_EXISTS {
+ return "", err
+ }
+
+ dataDirectorySd, err := windows.SecurityDescriptorFromString("O:SYG:SYD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)")
+ if err != nil {
+ return "", err
+ }
+ dataDirectorySa := &windows.SecurityAttributes{
+ Length: uint32(unsafe.Sizeof(windows.SecurityAttributes{})),
+ SecurityDescriptor: dataDirectorySd,
+ }
+
+ data := filepath.Join(root, "Data")
+ data16, err := windows.UTF16PtrFromString(data)
+ if err != nil {
+ return "", err
+ }
+ var dataHandle windows.Handle
+ for {
+ err = windows.CreateDirectory(data16, dataDirectorySa)
+ if err != nil && err != windows.ERROR_ALREADY_EXISTS {
+ return "", err
+ }
+ dataHandle, err = windows.CreateFile(data16, windows.READ_CONTROL|windows.WRITE_OWNER|windows.WRITE_DAC, windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE, nil, windows.OPEN_EXISTING, windows.FILE_FLAG_BACKUP_SEMANTICS|windows.FILE_FLAG_OPEN_REPARSE_POINT|windows.FILE_ATTRIBUTE_DIRECTORY, 0)
+ if err != nil && err != windows.ERROR_FILE_NOT_FOUND {
+ return "", err
+ }
+ if err == nil {
+ break
+ }
+ }
+ defer windows.CloseHandle(dataHandle)
+ var fileInfo windows.ByHandleFileInformation
+ err = windows.GetFileInformationByHandle(dataHandle, &fileInfo)
if err != nil {
return "", err
}
- c := filepath.Join(root, "WireGuard")
- err = os.MkdirAll(c, os.ModeDir|0700)
+ if fileInfo.FileAttributes&windows.FILE_ATTRIBUTE_DIRECTORY == 0 {
+ return "", errors.New("Data directory is actually a file")
+ }
+ if fileInfo.FileAttributes&windows.FILE_ATTRIBUTE_REPARSE_POINT != 0 {
+ return "", errors.New("Data directory is reparse point")
+ }
+ buf := make([]uint16, windows.MAX_PATH+4)
+ for {
+ bufLen, err := windows.GetFinalPathNameByHandle(dataHandle, &buf[0], uint32(len(buf)), 0)
+ if err != nil {
+ return "", err
+ }
+ if bufLen < uint32(len(buf)) {
+ break
+ }
+ buf = make([]uint16, bufLen)
+ }
+ if !strings.EqualFold(`\\?\`+data, windows.UTF16ToString(buf[:])) {
+ return "", errors.New("Data directory jumped to unexpected location")
+ }
+ err = windows.SetKernelObjectSecurity(dataHandle, windows.DACL_SECURITY_INFORMATION|windows.GROUP_SECURITY_INFORMATION|windows.OWNER_SECURITY_INFORMATION|windows.PROTECTED_DACL_SECURITY_INFORMATION, dataDirectorySd)
if err != nil {
return "", err
}
- cachedRootDir = c
+ cachedRootDir = data
return cachedRootDir, nil
}
diff --git a/conf/store.go b/conf/store.go
index 21bd3a22..18f99f66 100644
--- a/conf/store.go
+++ b/conf/store.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package conf
@@ -47,79 +47,6 @@ func ListConfigNames() ([]string, error) {
return configs[:i], nil
}
-func MigrateUnencryptedConfigs() (int, []error) {
- configFileDir, err := tunnelConfigurationsDirectory()
- if err != nil {
- return 0, []error{err}
- }
- files, err := ioutil.ReadDir(configFileDir)
- if err != nil {
- return 0, []error{err}
- }
- errs := make([]error, len(files))
- i := 0
- e := 0
- for _, file := range files {
- path := filepath.Join(configFileDir, file.Name())
- name := filepath.Base(file.Name())
- if len(name) <= len(configFileUnencryptedSuffix) || !strings.HasSuffix(name, configFileUnencryptedSuffix) {
- continue
- }
- if !file.Mode().IsRegular() || file.Mode().Perm()&0444 == 0 {
- continue
- }
-
- // We don't use ioutil's ReadFile, because we actually want RDWR, so that we can take advantage
- // of Windows file locking for ensuring the file is finished being written.
- f, err := os.OpenFile(path, os.O_RDWR, 0)
- if err != nil {
- errs[e] = err
- e++
- continue
- }
- bytes, err := ioutil.ReadAll(f)
- f.Close()
- if err != nil {
- errs[e] = err
- e++
- continue
- }
- _, err = FromWgQuickWithUnknownEncoding(string(bytes), "input")
- if err != nil {
- errs[e] = err
- e++
- continue
- }
-
- bytes, err = dpapi.Encrypt(bytes, strings.TrimSuffix(name, configFileUnencryptedSuffix))
- if err != nil {
- errs[e] = err
- e++
- continue
- }
- dstFile := strings.TrimSuffix(path, configFileUnencryptedSuffix) + configFileSuffix
- if _, err = os.Stat(dstFile); err != nil && !os.IsNotExist(err) {
- errs[e] = errors.New("Unable to migrate to " + dstFile + " as it already exists")
- e++
- continue
- }
- err = ioutil.WriteFile(dstFile, bytes, 0600)
- if err != nil {
- errs[e] = err
- e++
- continue
- }
- err = os.Remove(path)
- if err != nil && os.Remove(dstFile) == nil {
- errs[e] = err
- e++
- continue
- }
- i++
- }
- return i, errs[:e]
-}
-
func LoadFromName(name string) (*Config, error) {
configFileDir, err := tunnelConfigurationsDirectory()
if err != nil {
@@ -129,10 +56,6 @@ func LoadFromName(name string) (*Config, error) {
}
func LoadFromPath(path string) (*Config, error) {
- if !disableAutoMigration {
- tunnelConfigurationsDirectory() // Provoke migrations, if needed.
- }
-
name, err := NameFromPath(path)
if err != nil {
return nil, err
@@ -171,7 +94,7 @@ func NameFromPath(path string) (string, error) {
return name, nil
}
-func (config *Config) Save() error {
+func (config *Config) Save(overwrite bool) error {
if !TunnelNameIsValid(config.Name) {
return errors.New("Tunnel name is not valid")
}
@@ -185,16 +108,7 @@ func (config *Config) Save() error {
if err != nil {
return err
}
- err = ioutil.WriteFile(filename+".tmp", bytes, 0600)
- if err != nil {
- return err
- }
- err = os.Rename(filename+".tmp", filename)
- if err != nil {
- os.Remove(filename + ".tmp")
- return err
- }
- return nil
+ return writeLockedDownFile(filename, overwrite, bytes)
}
func (config *Config) Path() (string, error) {
diff --git a/conf/store_test.go b/conf/store_test.go
index fdef7ea7..7335cd8c 100644
--- a/conf/store_test.go
+++ b/conf/store_test.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package conf
diff --git a/conf/storewatcher.go b/conf/storewatcher.go
index ffd20ee0..3e079865 100644
--- a/conf/storewatcher.go
+++ b/conf/storewatcher.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package conf
diff --git a/conf/storewatcher_windows.go b/conf/storewatcher_windows.go
index 19956263..44b4564e 100644
--- a/conf/storewatcher_windows.go
+++ b/conf/storewatcher_windows.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package conf
@@ -11,20 +11,6 @@ import (
"golang.org/x/sys/windows"
)
-const (
- fncFILE_NAME uint32 = 0x00000001
- fncDIR_NAME uint32 = 0x00000002
- fncATTRIBUTES uint32 = 0x00000004
- fncSIZE uint32 = 0x00000008
- fncLAST_WRITE uint32 = 0x00000010
- fncLAST_ACCESS uint32 = 0x00000020
- fncCREATION uint32 = 0x00000040
- fncSECURITY uint32 = 0x00000100
-)
-
-//sys findFirstChangeNotification(path *uint16, watchSubtree bool, filter uint32) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = kernel32.FindFirstChangeNotificationW
-//sys findNextChangeNotification(handle windows.Handle) (err error) = kernel32.FindNextChangeNotification
-
var haveStartedWatchingConfigDir bool
func startWatchingConfigDir() {
@@ -36,7 +22,7 @@ func startWatchingConfigDir() {
h := windows.InvalidHandle
defer func() {
if h != windows.InvalidHandle {
- windows.CloseHandle(h)
+ windows.FindCloseChangeNotification(h)
}
haveStartedWatchingConfigDir = false
}()
@@ -45,7 +31,7 @@ func startWatchingConfigDir() {
if err != nil {
return
}
- h, err = findFirstChangeNotification(windows.StringToUTF16Ptr(configFileDir), true, fncFILE_NAME|fncDIR_NAME|fncATTRIBUTES|fncSIZE|fncLAST_WRITE|fncLAST_ACCESS|fncCREATION|fncSECURITY)
+ h, err = windows.FindFirstChangeNotification(configFileDir, true, windows.FILE_NOTIFY_CHANGE_FILE_NAME|windows.FILE_NOTIFY_CHANGE_DIR_NAME|windows.FILE_NOTIFY_CHANGE_ATTRIBUTES|windows.FILE_NOTIFY_CHANGE_SIZE|windows.FILE_NOTIFY_CHANGE_LAST_WRITE|windows.FILE_NOTIFY_CHANGE_LAST_ACCESS|windows.FILE_NOTIFY_CHANGE_CREATION|windows.FILE_NOTIFY_CHANGE_SECURITY)
if err != nil {
log.Printf("Unable to monitor config directory: %v", err)
return
@@ -54,7 +40,7 @@ func startWatchingConfigDir() {
s, err := windows.WaitForSingleObject(h, windows.INFINITE)
if err != nil || s == windows.WAIT_FAILED {
log.Printf("Unable to wait on config directory watcher: %v", err)
- windows.CloseHandle(h)
+ windows.FindCloseChangeNotification(h)
h = windows.InvalidHandle
goto startover
}
@@ -63,10 +49,10 @@ func startWatchingConfigDir() {
cb.cb()
}
- err = findNextChangeNotification(h)
+ err = windows.FindNextChangeNotification(h)
if err != nil {
log.Printf("Unable to monitor config directory again: %v", err)
- windows.CloseHandle(h)
+ windows.FindCloseChangeNotification(h)
h = windows.InvalidHandle
goto startover
}
diff --git a/conf/writer.go b/conf/writer.go
index 365e65e9..f0b11e99 100644
--- a/conf/writer.go
+++ b/conf/writer.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package conf
@@ -41,6 +41,19 @@ func (conf *Config) ToWgQuick() string {
output.WriteString(fmt.Sprintf("MTU = %d\n", conf.Interface.MTU))
}
+ if len(conf.Interface.PreUp) > 0 {
+ output.WriteString(fmt.Sprintf("PreUp = %s\n", conf.Interface.PreUp))
+ }
+ if len(conf.Interface.PostUp) > 0 {
+ output.WriteString(fmt.Sprintf("PostUp = %s\n", conf.Interface.PostUp))
+ }
+ if len(conf.Interface.PreDown) > 0 {
+ output.WriteString(fmt.Sprintf("PreDown = %s\n", conf.Interface.PreDown))
+ }
+ if len(conf.Interface.PostDown) > 0 {
+ output.WriteString(fmt.Sprintf("PostDown = %s\n", conf.Interface.PostDown))
+ }
+
for _, peer := range conf.Peers {
output.WriteString("\n[Peer]\n")
diff --git a/conf/zsyscall_windows.go b/conf/zsyscall_windows.go
index 9dcf68fe..783411f6 100644
--- a/conf/zsyscall_windows.go
+++ b/conf/zsyscall_windows.go
@@ -19,6 +19,7 @@ const (
var (
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
+ errERROR_EINVAL error = syscall.EINVAL
)
// errnoErr returns common boxed Errno values, to prevent
@@ -26,7 +27,7 @@ var (
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
- return nil
+ return errERROR_EINVAL
case errnoERROR_IO_PENDING:
return errERROR_IO_PENDING
}
@@ -37,12 +38,9 @@ func errnoErr(e syscall.Errno) error {
}
var (
- modwininet = windows.NewLazySystemDLL("wininet.dll")
- modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
+ modwininet = windows.NewLazySystemDLL("wininet.dll")
- procInternetGetConnectedState = modwininet.NewProc("InternetGetConnectedState")
- procFindFirstChangeNotificationW = modkernel32.NewProc("FindFirstChangeNotificationW")
- procFindNextChangeNotification = modkernel32.NewProc("FindNextChangeNotification")
+ procInternetGetConnectedState = modwininet.NewProc("InternetGetConnectedState")
)
func internetGetConnectedState(flags *uint32, reserved uint32) (connected bool) {
@@ -50,34 +48,3 @@ func internetGetConnectedState(flags *uint32, reserved uint32) (connected bool)
connected = r0 != 0
return
}
-
-func findFirstChangeNotification(path *uint16, watchSubtree bool, filter uint32) (handle windows.Handle, err error) {
- var _p0 uint32
- if watchSubtree {
- _p0 = 1
- } else {
- _p0 = 0
- }
- r0, _, e1 := syscall.Syscall(procFindFirstChangeNotificationW.Addr(), 3, uintptr(unsafe.Pointer(path)), uintptr(_p0), uintptr(filter))
- handle = windows.Handle(r0)
- if handle == windows.InvalidHandle {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
- }
- return
-}
-
-func findNextChangeNotification(handle windows.Handle) (err error) {
- r1, _, e1 := syscall.Syscall(procFindNextChangeNotification.Addr(), 1, uintptr(handle), 0, 0)
- if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
- }
- return
-}
diff --git a/docs/adminregistry.md b/docs/adminregistry.md
new file mode 100644
index 00000000..f7d04409
--- /dev/null
+++ b/docs/adminregistry.md
@@ -0,0 +1,41 @@
+# Registry Keys for Admins
+
+These are advanced configuration knobs that admins can set to do unusual things
+that are not recommended. There is no UI to enable these, and no such thing is
+planned. These registry keys may also be removed at some point in the future.
+The uninstaller will clean up the entirety of `HKLM\Software\WireGuard`. Use
+at your own risk, and please make sure you know what you're doing.
+
+#### `HKLM\Software\WireGuard\LimitedOperatorUI`
+
+When this key is set to `DWORD(1)`, the UI will be launched on desktops of
+users belonging to the Network Configuration Operators builtin group
+(S-1-5-32-556), with the following limitations for members of that group:
+
+ - Configurations are stripped of all public, private, and pre-shared keys;
+ - No version update notifications are delivered;
+ - Adding, removing, editing, importing, or exporting configurations is forbidden; and
+ - Quitting the manager is forbidden.
+
+However, basic functionality such as starting and stopping tunnels remains intact.
+
+#### `HKLM\Software\WireGuard\DangerousScriptExecution`
+
+When this key is set to `DWORD(1)`, the tunnel service will execute the commands
+specified in the `PreUp`, `PostUp`, `PreDown`, and `PostDown` options of a
+tunnel configuration. Note that this execution is done as the Local System user,
+which runs with the highest permissions on the operating system, and is therefore
+a real target of malware. Therefore, you should enable this option only with the
+utmost trepidation. Rather than use `%i`, WireGuard for Windows instead sets the
+environment variable `WIREGUARD_TUNNEL_NAME` to the name of the tunnel when
+executing these scripts.
+
+#### `HKLM\Software\WireGuard\MultipleSimultaneousTunnels`
+
+When this key is set to `DWORD(1)`, the UI may start multiple tunnels at the
+same time; otherwise, an existing tunnel is stopped when a new one is started.
+Note that it is always possible, regardless of this key, to start multiple
+tunnels using `wireguard /installtunnelservice`; this controls only the semantics
+of tunnel start requests coming from the UI. If all goes well, this key will be
+removed and the logic of whether to stop existing tunnels will be based on
+overlapping routes, but for now, this key provides a manual override.
diff --git a/attacksurface.md b/docs/attacksurface.md
index 24fdbe02..6dca229f 100644
--- a/attacksurface.md
+++ b/docs/attacksurface.md
@@ -1,10 +1,10 @@
-### WireGuard for Windows Attack Surface
+# Attack Surface
_This is an evolving document, describing currently known attack surface, a few mitigations, and several open questions. This is a work in progress. We document our current understanding with the intent of improving both our understanding and our security posture over time._
WireGuard for Windows consists of four components: a kernel driver, and three separate interacting userspace parts.
-#### Wintun
+### Wintun
Wintun is a kernel driver. It exposes:
@@ -29,8 +29,10 @@ The manager service is a userspace service running as Local System, responsible
- Extensive IPC using unnamed pipes, inherited by the UI process.
- A readable `CreateFileMapping` handle to a binary ringlog shared by all services, inherited by the UI process.
- It listens for service changes in tunnel services according to the string prefix "WireGuardTunnel$".
- - It manages DPAPI-encrypted configuration files in Local System's local appdata directory, and makes some effort to enforce good configuration filenames.
- - It uses `WTSEnumerateSessions` and `WTSSESSION_NOTIFICATION` to walk through each available session. It then uses `WTSQueryUserToken`, and then calls `GetTokenInformation(TokenGroups)` on it. If one of the returned group's SIDs matches `IsWellKnownSid(WinBuiltinAdministratorsSid)`, and has attributes of either `SE_GROUP_ENABLED` or `SE_GROUP_USE_FOR_DENY_ONLY` and calling `GetTokenInformation(TokenElevation)` on it or its `TokenLinkedToken` indicates that either is elevated, then it spawns the UI process as that the elevated user token, passing it three unnamed pipe handles for IPC and the log mapping handle, as described above.
+ - It manages DPAPI-encrypted configuration files in `C:\Program Files\WireGuard\Data`, which is created with `O:SYG:SYD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)`, and makes some effort to enforce good configuration filenames.
+ - The actual DPAPI-encrypted configuration files are created with `O:SYG:SYD:PAI(A;;FA;;;SY)(A;;SD;;;BA)`.
+ - It uses `WTSEnumerateSessions` and `WTSSESSION_NOTIFICATION` to walk through each available session. It then uses `WTSQueryUserToken` to get the token belonging to each session and then determines whether or not it is an administrator token. To determine that, it calls `CheckTokenMembership(CreateWellKnownSid(WinBuiltinAdministratorsSid))` on a duplicated impersonation token, as well as and calling `GetTokenInformation(TokenElevation)` on it. If either of these are false, then it fetched the linked token using `GetTokenInformation(TokenLinkedToken)` and queries the same. Only then does it spawn the UI process as that the elevated user token, passing it three unnamed pipe handles for IPC and the log mapping handle, as described above.
+ - In the event that the administrator has set `HKLM\Software\WireGuard\LimitedOperatorUI` to 1, sessions are started for users that are a member of group S-1-5-32-556 (determined sing `CheckTokenMembership(CreateWellKnownSid(WinBuiltinNetworkConfigurationOperatorsSid))` on it and its linked token), with a more limited IPC interface, in which these non-admin users are denied private keys and tunnel editing rights. (This means users can potentially DoS the IPC server by draining notifications too slowly, or exhausting memory of the manager by spawning too many watcher go routines, or by sending garbage data that Go's `gob` decoder isn't expecting.)
### UI
diff --git a/docs/buildrun.md b/docs/buildrun.md
new file mode 100644
index 00000000..265c4d68
--- /dev/null
+++ b/docs/buildrun.md
@@ -0,0 +1,98 @@
+# Building, Running, and Developing
+
+### Building
+
+Windows 10 64-bit or Windows Server 2019, and Git for Windows is required. The build script will take care of downloading, verifying, and extracting the right versions of the various dependencies:
+
+```text
+C:\Projects> git clone https://git.zx2c4.com/wireguard-windows
+C:\Projects> cd wireguard-windows
+C:\Projects\wireguard-windows> build
+```
+
+### Running
+
+After you've built the application, run `amd64\wireguard.exe` or `x86\wireguard.exe` to install the manager service and show the UI.
+
+```text
+C:\Projects\wireguard-windows> amd64\wireguard.exe
+```
+
+Since WireGuard requires the Wintun driver to be installed, and this generally requires a valid Microsoft signature, you may benefit from first installing a release of WireGuard for Windows from the official [wireguard.com](https://www.wireguard.com/install/) builds, which bundles a Microsoft-signed Wintun, and then subsequently run your own wireguard.exe. Alternatively, you can craft your own installer using the `quickinstall.bat` script.
+
+### Optional: Localizing
+
+To translate WireGuard UI to your language:
+
+1. Upgrade `resources.rc` accordingly. Follow the pattern.
+
+2. Make a new directory in `locales\` containing the language ID:
+
+ ```text
+ C:\Projects\wireguard-windows> mkdir locales\<langID>
+ ```
+
+3. Configure and run `build` to prepare initial `locales\<langID>\messages.gotext.json` file:
+
+ ```text
+ C:\Projects\wireguard-windows> set GoGenerate=yes
+ C:\Projects\wireguard-windows> build
+ C:\Projects\wireguard-windows> copy locales\<langID>\out.gotext.json locales\<langID>\messages.gotext.json
+ ```
+
+4. Translate `locales\<langID>\messages.gotext.json`. See other language message files how to translate messages and how to tackle plural. For this step, the project is currently using [CrowdIn](https://crowdin.com/translate/WireGuard); please make sure your translations make it there in order to be added here.
+
+5. Run `build` from the step 3 again, and test.
+
+6. Repeat from step 4.
+
+### Optional: Creating the Installer
+
+The installer build script will take care of downloading, verifying, and extracting the right versions of the various dependencies:
+
+```text
+C:\Projects\wireguard-windows> cd installer
+C:\Projects\wireguard-windows\installer> build
+```
+
+### Optional: Signing Binaries
+
+Add a file called `sign.bat` in the root of this repository with these contents, or similar:
+
+```text
+set SigningCertificate=DF98E075A012ED8C86FBCF14854B8F9555CB3D45
+set TimestampServer=http://timestamp.digicert.com
+```
+
+After, run the above `build` commands as usual, from a shell that has [`signtool.exe`](https://docs.microsoft.com/en-us/windows/desktop/SecCrypto/signtool) in its `PATH`, such as the Visual Studio 2017 command prompt.
+
+### Alternative: Building from Linux
+
+You must first have Mingw and ImageMagick installed.
+
+```text
+$ sudo apt install mingw-w64 imagemagick
+$ git clone https://git.zx2c4.com/wireguard-windows
+$ cd wireguard-windows
+$ make
+```
+
+You can deploy the 64-bit build to an SSH host specified by the `DEPLOYMENT_HOST` environment variable (default "winvm") to the remote directory specified by the `DEPLOYMENT_PATH` environment variable (default "Desktop") by using the `deploy` target:
+
+```text
+$ make deploy
+```
+
+### [`wg(8)`](https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8) Support for Windows
+
+The command line utility [`wg(8)`](https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8) works well on Windows. Being a Unix-centric project, it compiles with a Makefile and MingW:
+
+```text
+$ git clone https://git.zx2c4.com/wireguard-tools
+$ PLATFORM=windows make -C wireguard-tools/src
+$ stat wireguard-tools/src/wg.exe
+```
+
+It interacts with WireGuard instances run by the main WireGuard for Windows program.
+
+When building on Windows, the aforementioned `build.bat` script takes care of building this.
diff --git a/docs/enterprise.md b/docs/enterprise.md
new file mode 100644
index 00000000..3f75ad14
--- /dev/null
+++ b/docs/enterprise.md
@@ -0,0 +1,86 @@
+# Enterprise Usage
+
+WireGuard for Windows has been designed from the ground-up to make full use of standard Windows service, ACL, and CLI capabilities, making WireGuard deployable in enterprise scenarios or as part of Active Directory domains.
+
+### Installation
+
+While consumer users are generally directed toward [wireguard-installer.exe](https://download.wireguard.com/windows-client/wireguard-installer.exe), this installer simply takes care of selecting the correct MSI for the architecture, validating signatures, and executing it. Enterprise admins can instead [download MSIs directly](https://download.wireguard.com/windows-client/) and deploy these using [Group Policy Objects](https://docs.microsoft.com/en-us/troubleshoot/windows-server/group-policy/use-group-policy-to-install-software). The installer makes use of standard MSI features and should be easily automatable. The additional MSI property of `DO_NOT_LAUNCH` suppresses launching WireGuard after its installation, should that be required.
+
+### Tunnel Service versus Manager Service and UI
+
+The "manager service" is responsible for displaying a UI on select users' desktops (in the system tray), and responding to requests from the UI to do things like add, remove, start, or stop tunnels. The "tunnel service" is a separate Windows service for each tunnel. These two services may be used together, or separately, as described below.
+
+### Tunnel Service
+
+A tunnel service may be installed or uninstalled using the commands:
+
+```text
+> wireguard /installtunnelservice C:\path\to\some\myconfname.conf
+> wireguard /uninstalltunnelservice myconfname
+```
+
+This creates a service called `WireGuardTunnel$myconfname`, which can be controlled using standard Windows service management utilites, such as `services.msc` or [`sc`](https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/sc-query).
+
+If the configuration filename ends in `.conf`, it is interpreted as a normal [`wg-quick(8)`](https://git.zx2c4.com/wireguard-tools/about/src/man/wg-quick.8) configuration file. If it ends in `.conf.dpapi`, it is considered to be that same configuration file, but encrypted using [`CryptProtectData(bytes, "myconfname")`](https://docs.microsoft.com/en-us/windows/win32/api/dpapi/nf-dpapi-cryptprotectdata).
+
+The tunnel service may be queried and modified at runtime using the standard [`wg(8)`](https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8) command line utility. If the configuration file is a `.conf.dpapi` one, then Local System or Administrator permissions is required to interact with it using `wg(8)`; otherwise users of `wg(8)` must have Local System or Administrator permissions, or permissions the same as the owner of the `.conf` file. Invocation of `wg(8)` follows usual patterns on other platforms. For example:
+
+```text
+> wg show myconfname
+interface: myconfname
+ public key: lfTRXEWxt8mZc8cjSvOWN3tqnTpWw4v2Eg3qF6WTklw=
+ private key: (hidden)
+ listening port: 53488
+
+peer: JRI8Xc0zKP9kXk8qP84NdUQA04h6DLfFbwJn4g+/PFs=
+ endpoint: 163.172.161.0:12912
+ allowed ips: 0.0.0.0/0
+ latest handshake: 3 seconds ago
+ transfer: 6.55 KiB received, 4.13 KiB sent
+```
+
+The `PreUp`, `PostUp`, `PreDown`, and `PostDown` configuration options may be specified to run custom commands at various points in the lifetime of a tunnel service, but only if the correct registry key is set. [See `adminregistry.md` for information.](adminregistry.md)
+
+### Manager Service
+
+The manager service may be installed or uninstalled using the commands:
+
+```text
+> wireguard /installmanagerservice
+> wireguard /uninstallmanagerservice
+```
+
+This creates a service called `WireGuardManager`, which can be controlled using standard Windows service management utilites, such as `services.msc` or [`sc`](https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/sc-query).
+
+When executing `wireguard` with no arguments, the command first attempts to show the UI if the manager service is already running; otherwise it starts the manager service, waits for it to create a UI in the system tray, and then shows the main manager window. Therefore, `wireguard /installmanagerservice` is suitable for silent installation, whereas `wireguard` alone is suitable for interactive startup.
+
+The manager service monitors `%ProgramFiles%\WireGuard\Data\Configurations\` for the addition of new `.conf` files. Upon seeing one, it encrypts the file to a `.conf.dpapi` file, makes it unreadable to users other than Local System, confers the administrator only the ability to remove it, and then deletes the original unencrypted file. (Configurations can always be _exported_ later using the export feature of the UI.) Using this, configurations can programmatically be added to the secure store of the manager service simply by copying them into that directory.
+
+The UI is started in the system tray of all builtin Administrators when the manager service is running. A limited UI may also be started in the system tray of all builtin Network Configuration Operators, if the correct registry key is set. [See `adminregistry.md` for information.](adminregistry.md)
+
+By default, the manager stops existing tunnels when starting new tunnels, so that only one tunnel service is running at a time. This behavior may be disabled if the correct registry key is set. [See `adminregistry.md` for information.](adminregistry.md)
+
+
+### Diagnostic Logs
+
+The manager and all tunnel services produce diagnostic logs in a shared ringbuffer-based log. This is shown in the UI, and also can be dumped to a file using the command:
+
+```text
+> wireguard /dumplog C:\path\to\diagnostic\log.txt
+```
+
+### Updates
+
+Administrators are notified of updates within the UI and can update from within the UI, but updates can also be invoked at the command line using the command:
+
+```text
+> wireguard /update C:\path\to\update\log.txt
+```
+
+### Wintun Adapters
+
+The tunnel service creates a Wintun adapter at startup and destroys it at shutdown. It may be desirable, however, to remove all Wintun adapters created in WireGuard's pool and uninstall the driver if no other applications are using Wintun. This can be accomplished using the command:
+
+```text
+> wireguard /removealladapters C:\path\to\removal\log.txt
+```
diff --git a/docs/netquirk.md b/docs/netquirk.md
new file mode 100644
index 00000000..1fe2ad72
--- /dev/null
+++ b/docs/netquirk.md
@@ -0,0 +1,33 @@
+# Network Configuration Quirks
+
+As part of setting up a WireGuard tunnel, the tunnel service also sets up various network configuration parameters that are in one way or another related to the original configuration.
+
+### Routing
+
+The tunnel service takes all the allowed IPs from each peer, deduplicates them, and adds them to the routes for the WireGuard interface. The service then monitors which interface on the system has a default route (a route with a `/0` CIDR) that is not the WireGuard interface itself, and it uses the [`IP_UNICAST_IF` socket option](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options) to bind WireGuard's UDP packets to that default route interface. If no MTU has been specified in the configuration, it also sets the MTU of the WireGuard interface to be 80 less than the MTU of that default route interface. If no default route interface can be found, then those outgoing packets are blackholed. If WireGuard packets would ordinarily be routed over a non-default route interface, the use of `IP_UNICAST_IF` forces the packets to, in fact, go over the default route interface. This is problematic, but at this moment, Microsoft has not provided a viable workaround. If any allowed IPs are a `/0`, then the interface is given a `0` metric and Windows' [automatic metric logic](https://docs.microsoft.com/en-us/troubleshoot/windows-server/networking/automatic-metric-for-ipv4-routes) is disabled.
+
+### Firewall Considerations for `/0` Allowed IPs
+
+If an interface has only one peer, and that peer contains an Allowed IP in `/0`, then WireGuard enables a so-called "kill-switch", which adds firewall rules to do the following:
+
+- Packets from the tunnel service itself are permitted, so that WireGuard packets can flow successfully.
+- If the configuration specifies DNS servers, then packets sent to port `53` are only permitted if they are to one of those DNS servers. This is to prevent Windows' [ordinary multihomed DNS resolution behavior](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd197552%28v%3Dws.10%29), so that DNS queries only go to the DNS server specified, rather than multiple DNS servers.
+- Loopback packets are permitted, and packets actually going through the WireGuard tunnel are permitted.
+- DHCP for IPv4 and IPv6 and NDP for IPv6 are permitted.
+- All other packets are blocked.
+
+This prevents traffic from leaking outside the tunnel.
+
+If you'd like to use a default route _without_ having these restrictive kill-switch semantics, one may use the routes `0.0.0.0/1` and `128.0.0.0/1` in place of `0.0.0.0/0`, as well as `::/1` and `8000::/1` in place of `::/0`. This achieves nearly the same thing, but does not activate the above firewalling semantics. (The UI's editor has a checkbox that toggles this.) And users without the need for a `/0` route at all do not have to worry about this, and instead fall back to ordinary Windows routing and DNS behavior.
+
+### Considerations for non-`/0` Allowed IPs
+
+When the above conditions do not apply, routing and DNS information is handed to Windows in the typical way for Windows to manage. This includes its [ordinary multihomed DNS resolution behavior](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd197552%28v%3Dws.10%29) as well as its ordinary routing table resolution. Users may make use of the normal Windows firewalling and network configuration capabilities to firewall this as needed. One firewall rule is added, however, which allows the tunnel service to send and receive WireGuard packets.
+
+### Network List Manager
+
+Windows assigns a unique GUID to each new WireGuard adapter. The application takes pains to make this GUID deterministic, so that firewall policy (such as "public" vs "private" network categorization) can be consistently applied to the tunnel's network. This determinism is based on the configuration of the tunnel. Therefore, if the WireGuard configuration changes, so too will the unique GUID. Technical details are described in [a mailing list post](https://lists.zx2c4.com/pipermail/wireguard/2019-June/004259.html).
+
+### Adapter Lifetime
+
+WireGuard's Wintun adapter is created dynamically when a tunnel is started and destroyed when a tunnel is stopped. This means that additional filters, address families, or protocols should be bound to the adapter programmatically, possibly through use of dangerous script execution in thet configuration file or by way of automatic NDIS layer binding.
diff --git a/elevate/doas.go b/elevate/doas.go
index 8322a5c9..7bb9d240 100644
--- a/elevate/doas.go
+++ b/elevate/doas.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package elevate
diff --git a/elevate/loader.go b/elevate/loader.go
index 0bb275da..e1843103 100644
--- a/elevate/loader.go
+++ b/elevate/loader.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package elevate
diff --git a/elevate/membership.go b/elevate/membership.go
index 07c2ef69..769652c1 100644
--- a/elevate/membership.go
+++ b/elevate/membership.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package elevate
@@ -37,12 +37,12 @@ func TokenIsElevatedOrElevatable(token windows.Token) bool {
}
func IsAdminDesktop() (bool, error) {
- hwnd := getShellWindow()
+ hwnd := windows.GetShellWindow()
if hwnd == 0 {
return false, windows.ERROR_INVALID_WINDOW_HANDLE
}
var pid uint32
- _, err := getWindowThreadProcessId(hwnd, &pid)
+ _, err := windows.GetWindowThreadProcessId(hwnd, &pid)
if err != nil {
return false, err
}
diff --git a/elevate/mksyscall.go b/elevate/mksyscall.go
index 8522c980..d8d2a997 100644
--- a/elevate/mksyscall.go
+++ b/elevate/mksyscall.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package elevate
diff --git a/elevate/privileges.go b/elevate/privileges.go
index 84fdb2bd..d1727150 100644
--- a/elevate/privileges.go
+++ b/elevate/privileges.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package elevate
diff --git a/elevate/shellexecute.go b/elevate/shellexecute.go
index 6290b4f8..84b5253e 100644
--- a/elevate/shellexecute.go
+++ b/elevate/shellexecute.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package elevate
diff --git a/elevate/syscall_windows.go b/elevate/syscall_windows.go
index 1957dea2..b854fe7e 100644
--- a/elevate/syscall_windows.go
+++ b/elevate/syscall_windows.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package elevate
@@ -84,6 +84,3 @@ const (
//sys coInitializeEx(reserved uintptr, coInit uint32) (ret error) = ole32.CoInitializeEx
//sys coUninitialize() = ole32.CoUninitialize
//sys coGetObject(name *uint16, bindOpts *cBIND_OPTS3, guid *windows.GUID, functionTable ***[0xffff]uintptr) (ret error) = ole32.CoGetObject
-
-//sys getWindowThreadProcessId(hwnd uintptr, pid *uint32) (tid uint32, err error) = user32.GetWindowThreadProcessId
-//sys getShellWindow() (hwnd uintptr) = user32.GetShellWindow
diff --git a/elevate/zsyscall_windows.go b/elevate/zsyscall_windows.go
index afff428f..c7f71819 100644
--- a/elevate/zsyscall_windows.go
+++ b/elevate/zsyscall_windows.go
@@ -19,6 +19,7 @@ const (
var (
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
+ errERROR_EINVAL error = syscall.EINVAL
)
// errnoErr returns common boxed Errno values, to prevent
@@ -26,7 +27,7 @@ var (
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
- return nil
+ return errERROR_EINVAL
case errnoERROR_IO_PENDING:
return errERROR_IO_PENDING
}
@@ -37,40 +38,24 @@ func errnoErr(e syscall.Errno) error {
}
var (
- modntdll = windows.NewLazySystemDLL("ntdll.dll")
- modole32 = windows.NewLazySystemDLL("ole32.dll")
- moduser32 = windows.NewLazySystemDLL("user32.dll")
-
- procRtlInitUnicodeString = modntdll.NewProc("RtlInitUnicodeString")
- procRtlGetCurrentPeb = modntdll.NewProc("RtlGetCurrentPeb")
- procCoInitializeEx = modole32.NewProc("CoInitializeEx")
- procCoUninitialize = modole32.NewProc("CoUninitialize")
- procCoGetObject = modole32.NewProc("CoGetObject")
- procGetWindowThreadProcessId = moduser32.NewProc("GetWindowThreadProcessId")
- procGetShellWindow = moduser32.NewProc("GetShellWindow")
+ modntdll = windows.NewLazySystemDLL("ntdll.dll")
+ modole32 = windows.NewLazySystemDLL("ole32.dll")
+
+ procRtlGetCurrentPeb = modntdll.NewProc("RtlGetCurrentPeb")
+ procRtlInitUnicodeString = modntdll.NewProc("RtlInitUnicodeString")
+ procCoGetObject = modole32.NewProc("CoGetObject")
+ procCoInitializeEx = modole32.NewProc("CoInitializeEx")
+ procCoUninitialize = modole32.NewProc("CoUninitialize")
)
-func rtlInitUnicodeString(destinationString *cUNICODE_STRING, sourceString *uint16) {
- syscall.Syscall(procRtlInitUnicodeString.Addr(), 2, uintptr(unsafe.Pointer(destinationString)), uintptr(unsafe.Pointer(sourceString)), 0)
- return
-}
-
func rtlGetCurrentPeb() (peb *cPEB) {
r0, _, _ := syscall.Syscall(procRtlGetCurrentPeb.Addr(), 0, 0, 0, 0)
peb = (*cPEB)(unsafe.Pointer(r0))
return
}
-func coInitializeEx(reserved uintptr, coInit uint32) (ret error) {
- r0, _, _ := syscall.Syscall(procCoInitializeEx.Addr(), 2, uintptr(reserved), uintptr(coInit), 0)
- if r0 != 0 {
- ret = syscall.Errno(r0)
- }
- return
-}
-
-func coUninitialize() {
- syscall.Syscall(procCoUninitialize.Addr(), 0, 0, 0, 0)
+func rtlInitUnicodeString(destinationString *cUNICODE_STRING, sourceString *uint16) {
+ syscall.Syscall(procRtlInitUnicodeString.Addr(), 2, uintptr(unsafe.Pointer(destinationString)), uintptr(unsafe.Pointer(sourceString)), 0)
return
}
@@ -82,21 +67,15 @@ func coGetObject(name *uint16, bindOpts *cBIND_OPTS3, guid *windows.GUID, functi
return
}
-func getWindowThreadProcessId(hwnd uintptr, pid *uint32) (tid uint32, err error) {
- r0, _, e1 := syscall.Syscall(procGetWindowThreadProcessId.Addr(), 2, uintptr(hwnd), uintptr(unsafe.Pointer(pid)), 0)
- tid = uint32(r0)
- if tid == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
+func coInitializeEx(reserved uintptr, coInit uint32) (ret error) {
+ r0, _, _ := syscall.Syscall(procCoInitializeEx.Addr(), 2, uintptr(reserved), uintptr(coInit), 0)
+ if r0 != 0 {
+ ret = syscall.Errno(r0)
}
return
}
-func getShellWindow() (hwnd uintptr) {
- r0, _, _ := syscall.Syscall(procGetShellWindow.Addr(), 0, 0, 0, 0)
- hwnd = uintptr(r0)
+func coUninitialize() {
+ syscall.Syscall(procCoUninitialize.Addr(), 0, 0, 0, 0)
return
}
diff --git a/embeddable-dll-service/README.md b/embeddable-dll-service/README.md
index 3ac0dbae..c93b4345 100644
--- a/embeddable-dll-service/README.md
+++ b/embeddable-dll-service/README.md
@@ -6,14 +6,16 @@ The basic setup to use `tunnel.dll` is:
##### 1. Install a service with these parameters:
- Service Name: "SomeServiceName"
- Display Name: "Some Service Name"
- Service Type: SERVICE_WIN32_OWN_PROCESS
- Start Type: StartAutomatic
- Error Control: ErrorNormal,
- Dependencies: [ "Nsi" ]
- Sid Type: SERVICE_SID_TYPE_UNRESTRICTED
- Executable: "C:\path\to\example\vpnclient.exe /service configfile.conf"
+```text
+Service Name: "WireGuardTunnel$SomeTunnelName"
+Display Name: "Some Service Name"
+Service Type: SERVICE_WIN32_OWN_PROCESS
+Start Type: StartAutomatic
+Error Control: ErrorNormal,
+Dependencies: [ "Nsi", "TcpIp" ]
+Sid Type: SERVICE_SID_TYPE_UNRESTRICTED
+Executable: "C:\path\to\example\vpnclient.exe /service configfile.conf"
+```
Some of these may have to be changed with `ChangeServiceConfig2` after the
initial call to `CreateService` The `SERVICE_SID_TYPE_UNRESTRICTED` parameter
@@ -21,19 +23,21 @@ is absolutely essential; do not forget it.
##### 2. Have your program's main function handle the `/service` switch:
- if (!strcmp(argv[1], "/service") && argc == 3) {
- HMODULE tunnel_lib = LoadLibrary("tunnel.dll");
- if (!tunnel_lib)
- abort();
- tunnel_proc_t tunnel_proc = (tunnel_proc_t)GetProcAddress(tunnel_lib, "WireGuardTunnelService");
- if (!tunnel_proc)
- abort();
- struct go_string conf_file = {
- .str = argv[2],
- .n = strlen(argv[2])
- };
- return tunnel_proc(conf_file);
- }
+```c
+if (!strcmp(argv[1], "/service") && argc == 3) {
+ HMODULE tunnel_lib = LoadLibrary("tunnel.dll");
+ if (!tunnel_lib)
+ abort();
+ tunnel_proc_t tunnel_proc = (tunnel_proc_t)GetProcAddress(tunnel_lib, "WireGuardTunnelService");
+ if (!tunnel_proc)
+ abort();
+ struct go_string conf_file = {
+ .str = argv[2],
+ .n = strlen(argv[2])
+ };
+ return tunnel_proc(conf_file);
+}
+```
##### 3. Scoop up logs by implementing a ringlogger format reader.
diff --git a/embeddable-dll-service/build.bat b/embeddable-dll-service/build.bat
index cae35f70..e0aa4c3b 100644
--- a/embeddable-dll-service/build.bat
+++ b/embeddable-dll-service/build.bat
@@ -1,6 +1,6 @@
@echo off
rem SPDX-License-Identifier: MIT
-rem Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+rem Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
setlocal
set BUILDDIR=%~dp0
@@ -8,9 +8,16 @@ set PATH=%BUILDDIR%..\.deps\go\bin;%BUILDDIR%..\.deps;%PATH%
set PATHEXT=.exe
cd /d %BUILDDIR% || exit /b 1
-if exist ..\.deps\prepared goto :build
+if exist .prepared goto :build
:installdeps
call ..\build.bat || goto :error
+ pushd ..\.deps || goto :error
+ rem Mirror of https://musl.cc/i686-w64-mingw32-native.zip
+ call :download mingw-x86.zip https://download.wireguard.com/windows-toolchain/distfiles/i686-w64-mingw32-native-20200907.zip c972c00993727ac9bff83c799f4df65662adb95bc871fa30cfa8857e744a7fbd || goto :error
+ rem Mirror of https://musl.cc/x86_64-w64-mingw32-native.zip
+ call :download mingw-amd64.zip https://download.wireguard.com/windows-toolchain/distfiles/x86_64-w64-mingw32-native-20200907.zip e34fbacbd25b007a8074fc96f7e08f886241e0473a055987ee57483c37567aa5 || goto :error
+ popd || goto :error
+ copy /y NUL .prepared > NUL || goto :error
:build
set GOOS=windows
@@ -18,15 +25,24 @@ if exist ..\.deps\prepared goto :build
set GOROOT=%BUILDDIR%..\.deps\go
set CGO_ENABLED=1
set CGO_CFLAGS=-O3 -Wall -Wno-unused-function -Wno-switch -std=gnu11 -DWINVER=0x0601
- set CGO_LDFLAGS=-Wl,--dynamicbase -Wl,--nxcompat -Wl,--export-all-symbols
call :build_plat x86 i686 386 || goto :error
- set CGO_LDFLAGS=%CGO_LDFLAGS% -Wl,--high-entropy-va
call :build_plat amd64 x86_64 amd64 || goto :error
:success
echo [+] Success
exit /b 0
+:download
+ echo [+] Downloading %1
+ curl -#fLo %1 %2 || exit /b 1
+ echo [+] Verifying %1
+ for /f %%a in ('CertUtil -hashfile %1 SHA256 ^| findstr /r "^[0-9a-f]*$"') do if not "%%a"=="%~3" exit /b 1
+ echo [+] Extracting %1
+ tar -xf %1 %~4 || exit /b 1
+ echo [+] Cleaning up %1
+ del %1 || exit /b 1
+ goto :eof
+
:build_plat
set PATH=%BUILDDIR%..\.deps\%~2-w64-mingw32-native\bin;%PATH%
set CC=%~2-w64-mingw32-gcc
diff --git a/embeddable-dll-service/csharp/DemoUI/MainWindow.Designer.cs b/embeddable-dll-service/csharp/DemoUI/MainWindow.Designer.cs
new file mode 100644
index 00000000..5fae35d3
--- /dev/null
+++ b/embeddable-dll-service/csharp/DemoUI/MainWindow.Designer.cs
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+namespace DemoUI
+{
+ partial class MainWindow
+ {
+ /// <summary>
+ /// Required designer variable.
+ /// </summary>
+ private System.ComponentModel.IContainer components = null;
+
+ /// <summary>
+ /// Clean up any resources being used.
+ /// </summary>
+ /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ /// <summary>
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ /// </summary>
+ private void InitializeComponent()
+ {
+ this.connectButton = new System.Windows.Forms.Button();
+ this.logBox = new System.Windows.Forms.TextBox();
+ this.SuspendLayout();
+ //
+ // connectButton
+ //
+ this.connectButton.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.connectButton.Location = new System.Drawing.Point(12, 12);
+ this.connectButton.Name = "connectButton";
+ this.connectButton.Size = new System.Drawing.Size(1137, 46);
+ this.connectButton.TabIndex = 5;
+ this.connectButton.Text = "&Connect";
+ this.connectButton.UseVisualStyleBackColor = true;
+ this.connectButton.Click += new System.EventHandler(this.connectButton_Click);
+ //
+ // logBox
+ //
+ this.logBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.logBox.Location = new System.Drawing.Point(12, 64);
+ this.logBox.Multiline = true;
+ this.logBox.Name = "logBox";
+ this.logBox.ReadOnly = true;
+ this.logBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
+ this.logBox.Size = new System.Drawing.Size(1137, 561);
+ this.logBox.TabIndex = 4;
+ this.logBox.TabStop = false;
+ //
+ // MainWindow
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(13F, 32F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(1161, 637);
+ this.Controls.Add(this.logBox);
+ this.Controls.Add(this.connectButton);
+ this.Name = "MainWindow";
+ this.Text = "WireGuard Demo Client";
+ this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainWindow_FormClosing);
+ this.Load += new System.EventHandler(this.MainWindow_Load);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+ private System.Windows.Forms.Button connectButton;
+ private System.Windows.Forms.TextBox logBox;
+ }
+}
+
diff --git a/embeddable-dll-service/csharp/DemoUI/MainWindow.cs b/embeddable-dll-service/csharp/DemoUI/MainWindow.cs
new file mode 100644
index 00000000..13499fcb
--- /dev/null
+++ b/embeddable-dll-service/csharp/DemoUI/MainWindow.cs
@@ -0,0 +1,213 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+using System;
+using System.Text;
+using System.Threading.Tasks;
+using System.IO;
+using System.Windows.Forms;
+using System.Threading;
+using System.IO.Pipes;
+using System.Diagnostics;
+using System.Net.Sockets;
+
+namespace DemoUI
+{
+ public partial class MainWindow : Form
+ {
+ private static readonly string userDirectory = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
+ private static readonly string configFile = Path.Combine(userDirectory, "demobox.conf");
+ private static readonly string logFile = Path.Combine(userDirectory, "log.bin");
+
+ private Tunnel.Ringlogger log;
+ private Thread logPrintingThread, transferUpdateThread;
+ private volatile bool threadsRunning;
+ private bool connected;
+
+ public MainWindow()
+ {
+ InitializeComponent();
+ Application.ApplicationExit += Application_ApplicationExit;
+
+ try { File.Delete(logFile); } catch { }
+ log = new Tunnel.Ringlogger(logFile, "GUI");
+ logPrintingThread = new Thread(new ThreadStart(tailLog));
+ transferUpdateThread = new Thread(new ThreadStart(tailTransfer));
+ }
+
+ private void tailLog()
+ {
+ var cursor = Tunnel.Ringlogger.CursorAll;
+ while (threadsRunning)
+ {
+ var lines = log.FollowFromCursor(ref cursor);
+ foreach (var line in lines)
+ logBox.Invoke(new Action<string>(logBox.AppendText), new object[] { line + "\r\n" });
+ try
+ {
+ Thread.Sleep(300);
+ }
+ catch
+ {
+ break;
+ }
+ }
+ }
+
+ private void tailTransfer()
+ {
+ NamedPipeClientStream stream = null;
+ try
+ {
+ while (threadsRunning)
+ {
+ while (threadsRunning)
+ {
+ try
+ {
+ stream = Tunnel.Service.GetPipe(configFile);
+ stream.Connect();
+ break;
+ }
+ catch { }
+ Thread.Sleep(1000);
+ }
+
+ var reader = new StreamReader(stream);
+ stream.Write(Encoding.UTF8.GetBytes("get=1\n\n"));
+ ulong rx = 0, tx = 0;
+ while (threadsRunning)
+ {
+ var line = reader.ReadLine();
+ if (line == null)
+ break;
+ line = line.Trim();
+ if (line.Length == 0)
+ break;
+ if (line.StartsWith("rx_bytes="))
+ rx += ulong.Parse(line.Substring(9));
+ else if (line.StartsWith("tx_bytes="))
+ tx += ulong.Parse(line.Substring(9));
+ }
+ Invoke(new Action<ulong, ulong>(updateTransferTitle), new object[] { rx, tx });
+ stream.Close();
+ Thread.Sleep(1000);
+ }
+ }
+ catch { }
+ finally
+ {
+ if (stream != null && stream.IsConnected)
+ stream.Close();
+ }
+ }
+
+ private void Application_ApplicationExit(object sender, EventArgs e)
+ {
+ Tunnel.Service.Remove(configFile, true);
+ try { File.Delete(logFile); } catch { }
+ try { File.Delete(configFile); } catch { }
+ }
+
+ private void MainWindow_Load(object sender, EventArgs e)
+ {
+ threadsRunning = true;
+ logPrintingThread.Start();
+ transferUpdateThread.Start();
+ }
+
+ private void MainWindow_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ threadsRunning = false;
+ logPrintingThread.Interrupt();
+ transferUpdateThread.Interrupt();
+ try { logPrintingThread.Join(); } catch { }
+ try { transferUpdateThread.Join(); } catch { }
+ }
+
+ private static string formatBytes(ulong bytes)
+ {
+ decimal d = bytes;
+ string selectedUnit = null;
+ foreach (string unit in new string[] { "B", "KiB", "MiB", "GiB", "TiB" })
+ {
+ selectedUnit = unit;
+ if (d < 1024)
+ break;
+ d /= 1024;
+ }
+ return string.Format("{0:0.##} {1}", d, selectedUnit);
+ }
+
+ private void updateTransferTitle(ulong rx, ulong tx)
+ {
+ var titleBase = Text;
+ var idx = titleBase.IndexOf(" - ");
+ if (idx != -1)
+ titleBase = titleBase.Substring(0, idx);
+ if (rx == 0 && tx == 0)
+ Text = titleBase;
+ else
+ Text = string.Format("{0} - rx: {1}, tx: {2}", titleBase, formatBytes(rx), formatBytes(tx));
+ }
+
+ private async Task<string> generateNewConfig()
+ {
+ log.Write("Generating keys");
+ var keys = Tunnel.Keypair.Generate();
+ log.Write("Exchanging keys with demo server");
+ var client = new TcpClient();
+ await client.ConnectAsync("demo.wireguard.com", 42912);
+ var stream = client.GetStream();
+ var reader = new StreamReader(stream, Encoding.UTF8);
+ var pubKeyBytes = Encoding.UTF8.GetBytes(keys.Public + "\n");
+ await stream.WriteAsync(pubKeyBytes, 0, pubKeyBytes.Length);
+ await stream.FlushAsync();
+ var ret = (await reader.ReadLineAsync()).Split(':');
+ client.Close();
+ var status = ret.Length >= 1 ? ret[0] : "";
+ var serverPubkey = ret.Length >= 2 ? ret[1] : "";
+ var serverPort = ret.Length >= 3 ? ret[2] : "";
+ var internalIP = ret.Length >= 4 ? ret[3] : "";
+ if (status != "OK")
+ throw new InvalidOperationException(string.Format("Server status is {0}", status));
+ return string.Format("[Interface]\nPrivateKey = {0}\nAddress = {1}/24\nDNS = 8.8.8.8, 8.8.4.4\n\n[Peer]\nPublicKey = {2}\nEndpoint = demo.wireguard.com:{3}\nAllowedIPs = 0.0.0.0/0\n", keys.Private, internalIP, serverPubkey, serverPort);
+ }
+
+ private async void connectButton_Click(object sender, EventArgs e)
+ {
+ if (connected)
+ {
+ connectButton.Enabled = false;
+ await Task.Run(() =>
+ {
+ Tunnel.Service.Remove(configFile, true);
+ try { File.Delete(configFile); } catch { }
+ });
+ updateTransferTitle(0, 0);
+ connectButton.Text = "&Connect";
+ connectButton.Enabled = true;
+ connected = false;
+ return;
+ }
+
+ connectButton.Enabled = false;
+ try
+ {
+ var config = await generateNewConfig();
+ await File.WriteAllBytesAsync(configFile, Encoding.UTF8.GetBytes(config));
+ await Task.Run(() => Tunnel.Service.Add(configFile, true));
+ connected = true;
+ connectButton.Text = "&Disconnect";
+ }
+ catch (Exception ex)
+ {
+ log.Write(ex.Message);
+ try { File.Delete(configFile); } catch { }
+ }
+ connectButton.Enabled = true;
+ }
+ }
+}
diff --git a/embeddable-dll-service/csharp/DemoUI/MainWindow.resx b/embeddable-dll-service/csharp/DemoUI/MainWindow.resx
new file mode 100644
index 00000000..f298a7be
--- /dev/null
+++ b/embeddable-dll-service/csharp/DemoUI/MainWindow.resx
@@ -0,0 +1,60 @@
+<root>
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+</root> \ No newline at end of file
diff --git a/embeddable-dll-service/csharp/DemoUI/Program.cs b/embeddable-dll-service/csharp/DemoUI/Program.cs
new file mode 100644
index 00000000..8c420973
--- /dev/null
+++ b/embeddable-dll-service/csharp/DemoUI/Program.cs
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+using System;
+using System.Threading;
+using System.Diagnostics;
+using System.Windows.Forms;
+
+namespace DemoUI
+{
+ static class Program
+ {
+ [STAThread]
+ static void Main(string[] args)
+ {
+ if (args.Length == 3 && args[0] == "/service")
+ {
+ var t = new Thread(() =>
+ {
+ try
+ {
+ var currentProcess = Process.GetCurrentProcess();
+ var uiProcess = Process.GetProcessById(int.Parse(args[2]));
+ if (uiProcess.MainModule.FileName != currentProcess.MainModule.FileName)
+ return;
+ uiProcess.WaitForExit();
+ Tunnel.Service.Remove(args[1], false);
+ }
+ catch { }
+ });
+ t.Start();
+ Tunnel.Service.Run(args[1]);
+ t.Interrupt();
+ return;
+ }
+ Application.SetHighDpiMode(HighDpiMode.SystemAware);
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new MainWindow());
+ }
+ }
+}
diff --git a/embeddable-dll-service/csharp/DemoUI/app.manifest b/embeddable-dll-service/csharp/DemoUI/app.manifest
new file mode 100644
index 00000000..616ab5b9
--- /dev/null
+++ b/embeddable-dll-service/csharp/DemoUI/app.manifest
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
+ <assemblyIdentity version="1.0.0.0" name="demo-client"/>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+ <security>
+ <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+ <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
+
+ <!-- Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
+
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
+
+ <!-- Windows 10 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
+
+ </application>
+ </compatibility>
+
+ <application xmlns="urn:schemas-microsoft-com:asm.v3">
+ <windowsSettings>
+ <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
+ </windowsSettings>
+ </application>
+
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0"
+ processorArchitecture="*"
+ publicKeyToken="6595b64144ccf1df"
+ language="*"
+ />
+ </dependentAssembly>
+ </dependency>
+
+</assembly>
diff --git a/embeddable-dll-service/csharp/Program.cs b/embeddable-dll-service/csharp/Program.cs
deleted file mode 100644
index d99e66fd..00000000
--- a/embeddable-dll-service/csharp/Program.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-/* SPDX-License-Identifier: MIT
- *
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
- */
-
-using System;
-using System.Net.Sockets;
-using System.IO;
-using System.Text;
-using System.Diagnostics;
-using System.Threading;
-using System.Runtime.InteropServices;
-
-namespace Tunnel
-{
- class Program
- {
-
- [DllImport("kernel32.dll")]
- private static extern bool SetConsoleCtrlHandler(SetConsoleCtrlEventHandler handler, bool add);
- private delegate bool SetConsoleCtrlEventHandler(UInt32 signal);
-
- public static void Main(string[] args)
- {
- if (args.Length == 2 && args[0] == "/service")
- {
- Service.Run(args[1]);
- return;
- }
-
- var baseDirectory = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
- var configFile = Path.Combine(baseDirectory, "demobox.conf");
- var logFile = Path.Combine(baseDirectory, "log.bin");
-
- try { File.Delete(logFile); } catch { }
- Ringlogger log = new Ringlogger(logFile, "GUI");
-
- var logPrintingThread = new Thread(() =>
- {
- var cursor = Ringlogger.CursorAll;
- while (Thread.CurrentThread.IsAlive)
- {
- var lines = log.FollowFromCursor(ref cursor);
- foreach (var line in lines)
- Console.WriteLine(line);
- Thread.Sleep(300);
- }
- });
- logPrintingThread.Start();
-
- log.Write("Generating keys");
- var keys = Keypair.Generate();
- log.Write("Exchanging keys with demo server");
- var client = new TcpClient("demo.wireguard.com", 42912);
- var stream = client.GetStream();
- var reader = new StreamReader(stream, Encoding.UTF8);
- var pubKeyBytes = Encoding.UTF8.GetBytes(keys.Public + "\n");
- stream.Write(pubKeyBytes, 0, pubKeyBytes.Length);
- stream.Flush();
- var ret = reader.ReadLine().Split(':');
- client.Close();
- var status = ret.Length >= 1 ? ret[0] : "";
- var serverPubkey = ret.Length >= 2 ? ret[1] : "";
- var serverPort = ret.Length >= 3 ? ret[2] : "";
- var internalIP = ret.Length >= 4 ? ret[3] : "";
-
- if (status != "OK")
- throw new InvalidOperationException(String.Format("Server status is {0}", status));
-
- SetConsoleCtrlHandler(delegate
- {
- Service.Remove(configFile);
- Environment.Exit(0);
- return true;
- }, true);
-
- log.Write("Writing config file to disk");
- var configFileContents = String.Format("[Interface]\nPrivateKey = {0}\nAddress = {1}/24\nDNS = 8.8.8.8, 8.8.4.4\n\n[Peer]\nPublicKey = {2}\nEndpoint = demo.wireguard.com:{3}\nAllowedIPs = 0.0.0.0/0\n", keys.Private, internalIP, serverPubkey, serverPort);
- File.WriteAllText(configFile, configFileContents);
-
- try
- {
- Service.Add(configFile);
- logPrintingThread.Join();
- }
- finally
- {
- Service.Remove(configFile);
- }
- }
- }
-}
diff --git a/embeddable-dll-service/csharp/README.md b/embeddable-dll-service/csharp/README.md
new file mode 100644
index 00000000..071d15f6
--- /dev/null
+++ b/embeddable-dll-service/csharp/README.md
@@ -0,0 +1,15 @@
+# Example WireGuard Demo Client for Windows
+
+This is a simple client for demo.wireguard.com, which brings up WireGuard tunnels using the [embeddable-dll-service](https://git.zx2c4.com/wireguard-windows/about/embeddable-dll-service/README.md).
+
+## Building
+
+The code in this repository can be built in Visual Studio 2019 by opening the .sln and pressing build. However, it requires `tunnel.dll` to be present in the run directory. That can be built by:
+
+```batch
+> git clone https://git.zx2c4.com/wireguard-windows
+> cd wireguard-windows\embeddable-dll-service
+> .\build.bat
+```
+
+In addition, `tunnel.dll` requires `wintun.dll`, which can be downloaded from [wintun.net](https://www.wintun.net).
diff --git a/embeddable-dll-service/csharp/Keypair.cs b/embeddable-dll-service/csharp/TunnelDll/Keypair.cs
index e5764fbd..146d56e0 100644
--- a/embeddable-dll-service/csharp/Keypair.cs
+++ b/embeddable-dll-service/csharp/TunnelDll/Keypair.cs
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
*/
using System;
@@ -13,7 +13,7 @@ namespace Tunnel
public readonly string Public;
public readonly string Private;
- private Keypair(string pub, string priv)
+ public Keypair(string pub, string priv)
{
Public = pub;
Private = priv;
diff --git a/embeddable-dll-service/csharp/Ringlogger.cs b/embeddable-dll-service/csharp/TunnelDll/Ringlogger.cs
index d0957926..469927c1 100644
--- a/embeddable-dll-service/csharp/Ringlogger.cs
+++ b/embeddable-dll-service/csharp/TunnelDll/Ringlogger.cs
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
*/
using System;
diff --git a/embeddable-dll-service/csharp/Service.cs b/embeddable-dll-service/csharp/TunnelDll/Service.cs
index db600819..7f2c622e 100644
--- a/embeddable-dll-service/csharp/Service.cs
+++ b/embeddable-dll-service/csharp/TunnelDll/Service.cs
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
*/
using System;
@@ -9,15 +9,14 @@ using System.IO.Pipes;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Diagnostics;
-using System.Security.Principal;
using System.Threading;
namespace Tunnel
{
public class Service
{
- private const string LongName = "Example WireGuard Tunnel Client";
- private const string Description = "A WireGuard tunnel created by example code.";
+ private const string LongName = "WireGuard Demo Box";
+ private const string Description = "Demonstration tunnel for testing WireGuard";
[DllImport("tunnel.dll", EntryPoint = "WireGuardTunnelService", CallingConvention = CallingConvention.Cdecl)]
public static extern bool Run([MarshalAs(UnmanagedType.LPWStr)] string configFile);
@@ -28,17 +27,13 @@ namespace Tunnel
return new NamedPipeClientStream(pipepath);
}
- public static void Add(string configFile)
+ public static void Add(string configFile, bool ephemeral)
{
var tunnelName = Path.GetFileNameWithoutExtension(configFile);
var shortName = String.Format("WireGuardTunnel${0}", tunnelName);
var longName = String.Format("{0}: {1}", LongName, tunnelName);
var exeName = Process.GetCurrentProcess().MainModule.FileName;
- var pathAndArgs = String.Format("\"{0}\" /service \"{1}\"", exeName, configFile); //TODO: This is not the proper way to escape file args.
-
- var accessControl = File.GetAccessControl(configFile); //TODO: TOCTOU!
- accessControl.SetOwner(new NTAccount(Environment.UserDomainName, Environment.UserName));
- File.SetAccessControl(configFile, accessControl);
+ var pathAndArgs = String.Format("\"{0}\" /service \"{1}\" {2}", exeName, configFile, Process.GetCurrentProcess().Id); //TODO: This is not the proper way to escape file args.
var scm = Win32.OpenSCManager(null, null, Win32.ScmAccessRights.AllAccess);
if (scm == IntPtr.Zero)
@@ -49,9 +44,9 @@ namespace Tunnel
if (service != IntPtr.Zero)
{
Win32.CloseServiceHandle(service);
- Remove(configFile);
+ Remove(configFile, true);
}
- service = Win32.CreateService(scm, shortName, longName, Win32.ServiceAccessRights.AllAccess, Win32.ServiceType.Win32OwnProcess, Win32.ServiceStartType.Demand, Win32.ServiceError.Normal, pathAndArgs, null, IntPtr.Zero, "Nsi", null, null);
+ service = Win32.CreateService(scm, shortName, longName, Win32.ServiceAccessRights.AllAccess, Win32.ServiceType.Win32OwnProcess, Win32.ServiceStartType.Demand, Win32.ServiceError.Normal, pathAndArgs, null, IntPtr.Zero, "Nsi\0TcpIp", null, null);
if (service == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
try
@@ -66,6 +61,9 @@ namespace Tunnel
if (!Win32.StartService(service, 0, null))
throw new Win32Exception(Marshal.GetLastWin32Error());
+
+ if (ephemeral && !Win32.DeleteService(service))
+ throw new Win32Exception(Marshal.GetLastWin32Error());
}
finally
{
@@ -78,7 +76,7 @@ namespace Tunnel
}
}
- public static void Remove(string configFile)
+ public static void Remove(string configFile, bool waitForStop)
{
var tunnelName = Path.GetFileNameWithoutExtension(configFile);
var shortName = String.Format("WireGuardTunnel${0}", tunnelName);
@@ -99,10 +97,10 @@ namespace Tunnel
var serviceStatus = new Win32.ServiceStatus();
Win32.ControlService(service, Win32.ServiceControl.Stop, serviceStatus);
- for (int i = 0; i < 180 && Win32.QueryServiceStatus(service, serviceStatus) && serviceStatus.dwCurrentState != Win32.ServiceState.Stopped; ++i)
+ for (int i = 0; waitForStop && i < 180 && Win32.QueryServiceStatus(service, serviceStatus) && serviceStatus.dwCurrentState != Win32.ServiceState.Stopped; ++i)
Thread.Sleep(1000);
- if (!Win32.DeleteService(service))
+ if (!Win32.DeleteService(service) && Marshal.GetLastWin32Error() != 0x00000430)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
finally
diff --git a/embeddable-dll-service/csharp/Win32.cs b/embeddable-dll-service/csharp/TunnelDll/Win32.cs
index 76395f7e..d8447f7f 100644
--- a/embeddable-dll-service/csharp/Win32.cs
+++ b/embeddable-dll-service/csharp/TunnelDll/Win32.cs
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
*/
using System;
diff --git a/embeddable-dll-service/csharp/demo-client.csproj b/embeddable-dll-service/csharp/demo-client.csproj
new file mode 100644
index 00000000..00339ee2
--- /dev/null
+++ b/embeddable-dll-service/csharp/demo-client.csproj
@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
+
+ <PropertyGroup>
+ <OutputType>WinExe</OutputType>
+ <TargetFramework>net5.0-windows</TargetFramework>
+ <RootNamespace>DemoUI</RootNamespace>
+ <UseWindowsForms>true</UseWindowsForms>
+ <AssemblyName>demo-client</AssemblyName>
+ <Platforms>x64</Platforms>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <ApplicationManifest>DemoUI\app.manifest</ApplicationManifest>
+ </PropertyGroup>
+
+</Project>
diff --git a/embeddable-dll-service/csharp/demo-client.csproj.user b/embeddable-dll-service/csharp/demo-client.csproj.user
new file mode 100644
index 00000000..27d1a581
--- /dev/null
+++ b/embeddable-dll-service/csharp/demo-client.csproj.user
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Compile Update="DemoUI\MainWindow.cs">
+ <SubType>Form</SubType>
+ </Compile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/embeddable-dll-service/csharp/demo-client.sln b/embeddable-dll-service/csharp/demo-client.sln
new file mode 100644
index 00000000..a7d8b0ee
--- /dev/null
+++ b/embeddable-dll-service/csharp/demo-client.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30804.86
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "demo-client", "demo-client.csproj", "{AADC81E1-0294-483B-ABAE-63DBE82436E9}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {AADC81E1-0294-483B-ABAE-63DBE82436E9}.Debug|x64.ActiveCfg = Debug|x64
+ {AADC81E1-0294-483B-ABAE-63DBE82436E9}.Debug|x64.Build.0 = Debug|x64
+ {AADC81E1-0294-483B-ABAE-63DBE82436E9}.Release|x64.ActiveCfg = Release|x64
+ {AADC81E1-0294-483B-ABAE-63DBE82436E9}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {E410FD53-4E4A-4299-B6BD-CE91685DF7BE}
+ EndGlobalSection
+EndGlobal
diff --git a/embeddable-dll-service/main.go b/embeddable-dll-service/main.go
index 27fca4ee..f4ba67ce 100644
--- a/embeddable-dll-service/main.go
+++ b/embeddable-dll-service/main.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package main
@@ -22,7 +22,7 @@ import (
//export WireGuardTunnelService
func WireGuardTunnelService(confFile16 *uint16) bool {
- confFile := windows.UTF16ToString((*[(1 << 30) - 1]uint16)(unsafe.Pointer(confFile16))[:])
+ confFile := windows.UTF16PtrToString(confFile16)
conf.PresetRootDirectory(filepath.Dir(confFile))
tunnel.UseFixedGUIDInsteadOfDeterministic = true
err := tunnel.Run(confFile)
diff --git a/go-patches/0001-runtime-allow-builtin-write-function-to-be-redirecte.patch b/go-patches/0001-runtime-allow-builtin-write-function-to-be-redirecte.patch
new file mode 100644
index 00000000..a22ec91f
--- /dev/null
+++ b/go-patches/0001-runtime-allow-builtin-write-function-to-be-redirecte.patch
@@ -0,0 +1,59 @@
+From 293b84bef3f6656a263248ff00c9d2fb82f4168d Mon Sep 17 00:00:00 2001
+From: "Jason A. Donenfeld" <Jason@zx2c4.com>
+Date: Thu, 3 Dec 2020 13:29:58 +0100
+Subject: [PATCH 1/2] runtime: allow builtin write function to be redirected
+ with function pointer
+
+The x/sys/windows package currently uses go:linkname for other facilities
+inside of runtime that are not suitable to be exposed as a public API
+due to their dangers but are still necessary for manipulating any
+low-level plumbing that the runtime controls.
+
+Logging, via the built-in println and panic handler, is one such
+low-level plumbing feature. In this case, x/sys/windows/svc needs to be
+able to redirect panics to the Windows event log. Because the event log
+is a complicated interface, this requires a bit more fiddling than the
+simple solution used on Android (baking it into runtime itself), and
+because Windows services are very diverse, the event log might not even
+always be a desirable destination.
+
+This commit accomplishes this by exposing a function pointer called
+"overrideWrite" that low-level runtime packages like x/sys/windows/svc
+can use to redirect output logs toward the event log or otherwise.
+
+It is not safe or acceptable to use as a generic mechanism, and for that
+reason, we wouldn't want to expose this as a real stable API, similar to
+the other instances of go:linkname in x/sys/windows. But for packages
+that must interoperate with low-level Go runtime fundamentals, this is a
+safety hatch for packages that are developed in tandem with the runtime.
+x/sys/windows is one such package.
+
+Fixes #42888.
+
+Change-Id: I77a32ff7e1494324e8cc38e792e007f86d32672d
+---
+ src/runtime/time_nofake.go | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/src/runtime/time_nofake.go b/src/runtime/time_nofake.go
+index 1912a94e87..0564448b2e 100644
+--- a/src/runtime/time_nofake.go
++++ b/src/runtime/time_nofake.go
+@@ -23,9 +23,14 @@ func walltime() (sec int64, nsec int32) {
+ return walltime1()
+ }
+
++var overrideWrite func(fd uintptr, p unsafe.Pointer, n int32) int32
++
+ // write must be nosplit on Windows (see write1)
+ //
+ //go:nosplit
+ func write(fd uintptr, p unsafe.Pointer, n int32) int32 {
++ if overrideWrite != nil {
++ return overrideWrite(fd, noescape(p), n)
++ }
+ return write1(fd, p, n)
+ }
+--
+2.29.2
+
diff --git a/go-patches/0002-cmd-link-handle-grouped-resource-sections.patch b/go-patches/0002-cmd-link-handle-grouped-resource-sections.patch
new file mode 100644
index 00000000..85fbc3e9
--- /dev/null
+++ b/go-patches/0002-cmd-link-handle-grouped-resource-sections.patch
@@ -0,0 +1,339 @@
+From f7465aae6e0e8bc4c90c3b8386f5a2a574a15de0 Mon Sep 17 00:00:00 2001
+From: "Jason A. Donenfeld" <Jason@zx2c4.com>
+Date: Sun, 8 Nov 2020 11:57:42 +0100
+Subject: [PATCH 2/2] cmd/link: handle grouped resource sections
+
+The Go PE linker does not support enough generalized PE logic to
+properly handle .rsrc sections gracefully. Instead a few things are
+special cased for these. The linker also does not support PE's "grouped
+sections" features, in which input objects have several named sections
+that are sorted, merged, and renamed in the output file. In the past,
+more sophisticated support for resources or for PE features like grouped
+sections have not been necessary, as Go's own object formats are pretty
+vanilla, and GNU binutils also produces pretty vanilla objects where all
+sections are already merged.
+
+However, GNU binutils is lagging with arm support, and here LLVM has
+picked up the slack. In particular, LLVM has its own rc/cvtres combo,
+which are glued together in mingw LLVM distributions as windres, a
+command line compatible tool with binutils' windres, which supports arm
+and arm64. But there's a key difference between binutils' windres and
+LLVM's windres: the LLVM one uses proper grouped sections.
+
+So, this commit adds grouped sections support for resource sections to
+the linker. We don't attempt to plumb generic support for grouped
+sections, just as there isn't generic support already for what resources
+require. Instead we augment the resource handling logic to deal with
+standard two-section resource objects.
+
+We also add a test for this, akin to the current test for more vanilla
+binutils resource objects, and make sure that the rsrc tests are always
+performed.
+
+Fixes #42866.
+Fixes #43182.
+
+Change-Id: I059450021405cdf2ef1c195ddbab3960764ad711
+Reviewed-on: https://go-review.googlesource.com/c/go/+/268337
+Run-TryBot: Jason A. Donenfeld <Jason@zx2c4.com>
+TryBot-Result: Go Bot <gobot@golang.org>
+Reviewed-by: Cherry Zhang <cherryyz@google.com>
+Trust: Alex Brainman <alex.brainman@gmail.com>
+Trust: Jason A. Donenfeld <Jason@zx2c4.com>
+---
+ src/cmd/link/internal/ld/lib.go | 2 +-
+ src/cmd/link/internal/ld/pe.go | 60 ++++++++++--------
+ src/cmd/link/internal/loadpe/ldpe.go | 49 +++++++-------
+ src/cmd/link/link_test.go | 19 ++++++
+ .../link/testdata/testPErsrc-complex/main.go | 43 +++++++++++++
+ .../testdata/testPErsrc-complex/rsrc.syso | Bin 0 -> 352 bytes
+ 6 files changed, 124 insertions(+), 49 deletions(-)
+ create mode 100644 src/cmd/link/testdata/testPErsrc-complex/main.go
+ create mode 100644 src/cmd/link/testdata/testPErsrc-complex/rsrc.syso
+
+diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
+index f3c301cc9b..9c2231a34e 100644
+--- a/src/cmd/link/internal/ld/lib.go
++++ b/src/cmd/link/internal/ld/lib.go
+@@ -1808,7 +1808,7 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string,
+ Errorf(nil, "%v", err)
+ return
+ }
+- if rsrc != 0 {
++ if len(rsrc) != 0 {
+ setpersrc(ctxt, rsrc)
+ }
+ ctxt.Textp = append(ctxt.Textp, textp...)
+diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go
+index adbf516d5c..5edaf54dd2 100644
+--- a/src/cmd/link/internal/ld/pe.go
++++ b/src/cmd/link/internal/ld/pe.go
+@@ -253,7 +253,7 @@ type Dll struct {
+ }
+
+ var (
+- rsrcsym loader.Sym
++ rsrcsyms []loader.Sym
+ PESECTHEADR int32
+ PEFILEHEADR int32
+ pe64 int
+@@ -1508,46 +1508,56 @@ func (ctxt *Link) dope() {
+ initdynexport(ctxt)
+ }
+
+-func setpersrc(ctxt *Link, sym loader.Sym) {
+- if rsrcsym != 0 {
++func setpersrc(ctxt *Link, syms []loader.Sym) {
++ if len(rsrcsyms) != 0 {
+ Errorf(nil, "too many .rsrc sections")
+ }
+-
+- rsrcsym = sym
++ rsrcsyms = syms
+ }
+
+ func addpersrc(ctxt *Link) {
+- if rsrcsym == 0 {
++ if len(rsrcsyms) == 0 {
+ return
+ }
+
+- data := ctxt.loader.Data(rsrcsym)
+- size := len(data)
+- h := pefile.addSection(".rsrc", size, size)
++ var size int64
++ for _, rsrcsym := range rsrcsyms {
++ size += ctxt.loader.SymSize(rsrcsym)
++ }
++ h := pefile.addSection(".rsrc", int(size), int(size))
+ h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA
+ h.checkOffset(ctxt.Out.Offset())
+
+- // relocation
+- relocs := ctxt.loader.Relocs(rsrcsym)
+- for i := 0; i < relocs.Count(); i++ {
+- r := relocs.At(i)
+- p := data[r.Off():]
+- val := uint32(int64(h.virtualAddress) + r.Add())
+-
+- // 32-bit little-endian
+- p[0] = byte(val)
+-
+- p[1] = byte(val >> 8)
+- p[2] = byte(val >> 16)
+- p[3] = byte(val >> 24)
++ for _, rsrcsym := range rsrcsyms {
++ // A split resource happens when the actual resource data and its relocations are
++ // split across multiple sections, denoted by a $01 or $02 at the end of the .rsrc
++ // section name.
++ splitResources := strings.Contains(ctxt.loader.SymName(rsrcsym), ".rsrc$")
++ relocs := ctxt.loader.Relocs(rsrcsym)
++ data := ctxt.loader.Data(rsrcsym)
++ for ri := 0; ri < relocs.Count(); ri++ {
++ r := relocs.At(ri)
++ p := data[r.Off():]
++ val := uint32(int64(h.virtualAddress) + r.Add())
++ if splitResources {
++ // If we're a split resource section, and that section has relocation
++ // symbols, then the data that it points to doesn't actually begin at
++ // the virtual address listed in this current section, but rather
++ // begins at the section immediately after this one. So, in order to
++ // calculate the proper virtual address of the data it's pointing to,
++ // we have to add the length of this section to the virtual address.
++ // This works because .rsrc sections are divided into two (but not more)
++ // of these sections.
++ val += uint32(len(data))
++ }
++ binary.LittleEndian.PutUint32(p, val)
++ }
++ ctxt.Out.Write(data)
+ }
+-
+- ctxt.Out.Write(data)
+ h.pad(ctxt.Out, uint32(size))
+
+ // update data directory
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h.virtualAddress
+-
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h.virtualSize
+ }
+
+diff --git a/src/cmd/link/internal/loadpe/ldpe.go b/src/cmd/link/internal/loadpe/ldpe.go
+index 1e6f978531..a5c025de8f 100644
+--- a/src/cmd/link/internal/loadpe/ldpe.go
++++ b/src/cmd/link/internal/loadpe/ldpe.go
+@@ -157,8 +157,9 @@ func makeUpdater(l *loader.Loader, bld *loader.SymbolBuilder, s loader.Sym) *loa
+
+ // Load loads the PE file pn from input.
+ // Symbols are written into syms, and a slice of the text symbols is returned.
+-// If an .rsrc section is found, its symbol is returned as rsrc.
+-func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, rsrc loader.Sym, err error) {
++// If an .rsrc section or set of .rsrc$xx sections is found, its symbols are
++// returned as rsrc.
++func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, rsrc []loader.Sym, err error) {
+ lookup := func(name string, version int) (*loader.SymbolBuilder, loader.Sym) {
+ s := l.LookupOrCreateSym(name, version)
+ sb := l.MakeSymbolUpdater(s)
+@@ -176,7 +177,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
+ // TODO: replace pe.NewFile with pe.Load (grep for "add Load function" in debug/pe for details)
+ f, err := pe.NewFile(sr)
+ if err != nil {
+- return nil, 0, err
++ return nil, nil, err
+ }
+ defer f.Close()
+
+@@ -211,21 +212,21 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
+ bld.SetType(sym.STEXT)
+
+ default:
+- return nil, 0, fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name)
++ return nil, nil, fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name)
+ }
+
+ if bld.Type() != sym.SNOPTRBSS {
+ data, err := sect.Data()
+ if err != nil {
+- return nil, 0, err
++ return nil, nil, err
+ }
+ sectdata[sect] = data
+ bld.SetData(data)
+ }
+ bld.SetSize(int64(sect.Size))
+ sectsyms[sect] = s
+- if sect.Name == ".rsrc" {
+- rsrc = s
++ if sect.Name == ".rsrc" || strings.HasPrefix(sect.Name, ".rsrc$") {
++ rsrc = append(rsrc, s)
+ }
+ }
+
+@@ -246,22 +247,23 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
+ continue
+ }
+
++ splitResources := strings.HasPrefix(rsect.Name, ".rsrc$")
+ sb := l.MakeSymbolUpdater(sectsyms[rsect])
+ for j, r := range rsect.Relocs {
+ if int(r.SymbolTableIndex) >= len(f.COFFSymbols) {
+- return nil, 0, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols))
++ return nil, nil, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols))
+ }
+ pesym := &f.COFFSymbols[r.SymbolTableIndex]
+ _, gosym, err := readpesym(l, arch, l.LookupOrCreateSym, f, pesym, sectsyms, localSymVersion)
+ if err != nil {
+- return nil, 0, err
++ return nil, nil, err
+ }
+ if gosym == 0 {
+ name, err := pesym.FullName(f.StringTable)
+ if err != nil {
+ name = string(pesym.Name[:])
+ }
+- return nil, 0, fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type)
++ return nil, nil, fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type)
+ }
+
+ rSym := gosym
+@@ -271,11 +273,11 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
+ var rType objabi.RelocType
+ switch arch.Family {
+ default:
+- return nil, 0, fmt.Errorf("%s: unsupported arch %v", pn, arch.Family)
++ return nil, nil, fmt.Errorf("%s: unsupported arch %v", pn, arch.Family)
+ case sys.I386, sys.AMD64:
+ switch r.Type {
+ default:
+- return nil, 0, fmt.Errorf("%s: %v: unknown relocation type %v", pn, sectsyms[rsect], r.Type)
++ return nil, nil, fmt.Errorf("%s: %v: unknown relocation type %v", pn, sectsyms[rsect], r.Type)
+
+ case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32,
+ IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32
+@@ -302,7 +304,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
+ case sys.ARM:
+ switch r.Type {
+ default:
+- return nil, 0, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, sectsyms[rsect], r.Type)
++ return nil, nil, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, sectsyms[rsect], r.Type)
+
+ case IMAGE_REL_ARM_SECREL:
+ rType = objabi.R_PCREL
+@@ -323,8 +325,9 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
+
+ // ld -r could generate multiple section symbols for the
+ // same section but with different values, we have to take
+- // that into account
+- if issect(pesym) {
++ // that into account, or in the case of split resources,
++ // the section and its symbols are split into two sections.
++ if issect(pesym) || splitResources {
+ rAdd += int64(pesym.Value)
+ }
+
+@@ -346,7 +349,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
+
+ name, err := pesym.FullName(f.StringTable)
+ if err != nil {
+- return nil, 0, err
++ return nil, nil, err
+ }
+ if name == "" {
+ continue
+@@ -384,7 +387,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
+
+ bld, s, err := readpesym(l, arch, l.LookupOrCreateSym, f, pesym, sectsyms, localSymVersion)
+ if err != nil {
+- return nil, 0, err
++ return nil, nil, err
+ }
+
+ if pesym.SectionNumber == 0 { // extern
+@@ -402,14 +405,14 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
+ } else if pesym.SectionNumber > 0 && int(pesym.SectionNumber) <= len(f.Sections) {
+ sect = f.Sections[pesym.SectionNumber-1]
+ if _, found := sectsyms[sect]; !found {
+- return nil, 0, fmt.Errorf("%s: %v: missing sect.sym", pn, s)
++ return nil, nil, fmt.Errorf("%s: %v: missing sect.sym", pn, s)
+ }
+ } else {
+- return nil, 0, fmt.Errorf("%s: %v: sectnum < 0!", pn, s)
++ return nil, nil, fmt.Errorf("%s: %v: sectnum < 0!", pn, s)
+ }
+
+ if sect == nil {
+- return nil, 0, nil
++ return nil, nil, nil
+ }
+
+ if l.OuterSym(s) != 0 {
+@@ -418,7 +421,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
+ }
+ outerName := l.SymName(l.OuterSym(s))
+ sectName := l.SymName(sectsyms[sect])
+- return nil, 0, fmt.Errorf("%s: duplicate symbol reference: %s in both %s and %s", pn, l.SymName(s), outerName, sectName)
++ return nil, nil, fmt.Errorf("%s: duplicate symbol reference: %s in both %s and %s", pn, l.SymName(s), outerName, sectName)
+ }
+
+ bld = makeUpdater(l, bld, s)
+@@ -429,7 +432,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
+ bld.SetSize(4)
+ if l.SymType(sectsym) == sym.STEXT {
+ if bld.External() && !bld.DuplicateOK() {
+- return nil, 0, fmt.Errorf("%s: duplicate symbol definition", l.SymName(s))
++ return nil, nil, fmt.Errorf("%s: duplicate symbol definition", l.SymName(s))
+ }
+ bld.SetExternal(true)
+ }
+@@ -446,7 +449,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
+ if l.SymType(s) == sym.STEXT {
+ for ; s != 0; s = l.SubSym(s) {
+ if l.AttrOnList(s) {
+- return nil, 0, fmt.Errorf("symbol %s listed multiple times", l.SymName(s))
++ return nil, nil, fmt.Errorf("symbol %s listed multiple times", l.SymName(s))
+ }
+ l.SetAttrOnList(s, true)
+ textp = append(textp, s)
+--
+2.29.2
+
diff --git a/go-patches/highres-timer.patch b/go-patches/highres-timer.patch
deleted file mode 100644
index 87dd6b17..00000000
--- a/go-patches/highres-timer.patch
+++ /dev/null
@@ -1,395 +0,0 @@
-From 2975b381dc3f559a2eef875582d73cec00ab6b17 Mon Sep 17 00:00:00 2001
-From: Alex Brainman <alex.brainman@gmail.com>
-Date: Sun, 19 Jul 2020 16:06:48 +1000
-Subject: [PATCH] runtime: use CreateWaitableTimerEx to implement usleep
-
-@jstarks suggested that recent versions of Windows provide access to high resolution timers. See
-
-https://github.com/golang/go/issues/8687#issuecomment-656259353
-
-for details.
-
-I tried to run this C program on my Windows 10 computer
-
-```
- #include <stdio.h>
- #include <Windows.h>
-
- #pragma comment(lib, "Winmm.lib")
-
-// Apparently this is already defined when I use msvc cl.
-//#define CREATE_WAITABLE_TIMER_HIGH_RESOLUTION = 0x00000002;
-
-int usleep(HANDLE timer, LONGLONG d) {
- LARGE_INTEGER liDueTime;
- DWORD ret;
- LARGE_INTEGER StartingTime, EndingTime, ElapsedMicroseconds;
- LARGE_INTEGER Frequency;
-
- QueryPerformanceFrequency(&Frequency);
- QueryPerformanceCounter(&StartingTime);
-
- liDueTime.QuadPart = d;
- liDueTime.QuadPart = liDueTime.QuadPart * 10; // us into 100 of ns units
- liDueTime.QuadPart = -liDueTime.QuadPart; // negative for relative dure time
-
- if (!SetWaitableTimer(timer, &liDueTime, 0, NULL, NULL, 0)) {
- printf("SetWaitableTimer failed: errno=%d\n", GetLastError());
- return 1;
- }
-
- ret = WaitForSingleObject(timer, INFINITE);
- if (ret != WAIT_OBJECT_0) {
- printf("WaitForSingleObject failed: ret=%d errno=%d\n", ret, GetLastError());
- return 1;
- }
-
- QueryPerformanceCounter(&EndingTime);
- ElapsedMicroseconds.QuadPart = EndingTime.QuadPart - StartingTime.QuadPart;
- ElapsedMicroseconds.QuadPart *= 1000000;
- ElapsedMicroseconds.QuadPart /= Frequency.QuadPart;
-
- printf("delay is %lld us - slept for %lld us\n", d, ElapsedMicroseconds.QuadPart);
-
- return 0;
-}
-
-int testTimer(DWORD createFlag)
-{
- HANDLE timer;
-
- timer = CreateWaitableTimerEx(NULL, NULL, createFlag, TIMER_ALL_ACCESS);
- if (timer == NULL) {
- printf("CreateWaitableTimerEx failed: errno=%d\n", GetLastError());
- return 1;
- }
-
- usleep(timer, 1000LL);
- usleep(timer, 100LL);
- usleep(timer, 10LL);
- usleep(timer, 1LL);
-
- CloseHandle(timer);
-
- return 0;
-}
-
-int main()
-{
- printf("\n1. CREATE_WAITABLE_TIMER_HIGH_RESOLUTION is off - timeBeginPeriod is off\n");
- testTimer(0);
-
- printf("\n2. CREATE_WAITABLE_TIMER_HIGH_RESOLUTION is on - timeBeginPeriod is off\n");
- testTimer(CREATE_WAITABLE_TIMER_HIGH_RESOLUTION);
-
- timeBeginPeriod(1);
-
- printf("\n3. CREATE_WAITABLE_TIMER_HIGH_RESOLUTION is off - timeBeginPeriod is on\n");
- testTimer(0);
-
- printf("\n4. CREATE_WAITABLE_TIMER_HIGH_RESOLUTION is on - timeBeginPeriod is on\n");
- testTimer(CREATE_WAITABLE_TIMER_HIGH_RESOLUTION);
-}
-```
-
-and I see this output
-
-```
-1. CREATE_WAITABLE_TIMER_HIGH_RESOLUTION is off - timeBeginPeriod is off
-delay is 1000 us - slept for 4045 us
-delay is 100 us - slept for 3915 us
-delay is 10 us - slept for 3291 us
-delay is 1 us - slept for 2234 us
-
-2. CREATE_WAITABLE_TIMER_HIGH_RESOLUTION is on - timeBeginPeriod is off
-delay is 1000 us - slept for 1076 us
-delay is 100 us - slept for 569 us
-delay is 10 us - slept for 585 us
-delay is 1 us - slept for 17 us
-
-3. CREATE_WAITABLE_TIMER_HIGH_RESOLUTION is off - timeBeginPeriod is on
-delay is 1000 us - slept for 742 us
-delay is 100 us - slept for 893 us
-delay is 10 us - slept for 414 us
-delay is 1 us - slept for 920 us
-
-4. CREATE_WAITABLE_TIMER_HIGH_RESOLUTION is on - timeBeginPeriod is on
-delay is 1000 us - slept for 1466 us
-delay is 100 us - slept for 559 us
-delay is 10 us - slept for 535 us
-delay is 1 us - slept for 5 us
-```
-
-That shows, that indeed using CREATE_WAITABLE_TIMER_HIGH_RESOLUTION
-will provide sleeps as low as about 500 microseconds, while our
-current approach provides about 1 millisecond sleep.
-
-New approach also does not require for timeBeginPeriod to be on,
-so this change solves long standing problem with go programs draining
-laptop battery, because it calls timeBeginPeriod.
-
-This change will only run on systems where
-CREATE_WAITABLE_TIMER_HIGH_RESOLUTION flag is available. If not
-available, the runtime will fallback to original code that uses
-timeBeginPeriod.
-
-This is how this change affects benchmark reported in issue #14790
-
-name               old time/op  new time/op  delta
-ChanToSyscallPing  1.05ms ± 2%  0.68ms ±11%  -35.43%  (p=0.000 n=10+10)
-
-The benchmark was run with GOMAXPROCS set to 1.
-
-Fixes #8687
-Updates #14790
-
-Change-Id: I5b97ba58289c088c17c05292e12e45285c467eae
----
-
-diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go
-index a62e941..46f4a23 100644
---- a/src/runtime/os_windows.go
-+++ b/src/runtime/os_windows.go
-@@ -21,6 +21,7 @@
- //go:cgo_import_dynamic runtime._CreateIoCompletionPort CreateIoCompletionPort%4 "kernel32.dll"
- //go:cgo_import_dynamic runtime._CreateThread CreateThread%6 "kernel32.dll"
- //go:cgo_import_dynamic runtime._CreateWaitableTimerA CreateWaitableTimerA%3 "kernel32.dll"
-+//go:cgo_import_dynamic runtime._CreateWaitableTimerExW CreateWaitableTimerExW%4 "kernel32.dll"
- //go:cgo_import_dynamic runtime._DuplicateHandle DuplicateHandle%7 "kernel32.dll"
- //go:cgo_import_dynamic runtime._ExitProcess ExitProcess%1 "kernel32.dll"
- //go:cgo_import_dynamic runtime._FreeEnvironmentStringsW FreeEnvironmentStringsW%1 "kernel32.dll"
-@@ -68,6 +69,7 @@
- _CreateIoCompletionPort,
- _CreateThread,
- _CreateWaitableTimerA,
-+ _CreateWaitableTimerExW,
- _DuplicateHandle,
- _ExitProcess,
- _FreeEnvironmentStringsW,
-@@ -151,6 +153,8 @@
- waitsema uintptr // semaphore for parking on locks
- resumesema uintptr // semaphore to indicate suspend/resume
-
-+ highResTimer uintptr // high resolution timer handle used in usleep
-+
- // preemptExtLock synchronizes preemptM with entry/exit from
- // external C code.
- //
-@@ -407,6 +411,12 @@
- // if we're already using the CPU, but if all Ps are idle there's no
- // need to consume extra power to drive the high-res timer.
- func osRelax(relax bool) uint32 {
-+ if haveHighResTimer {
-+ // Only call timeBeginPeriod/timeEndPeriod, if
-+ // high resolution timer is not available.
-+ return 0
-+ }
-+
- if relax {
- return uint32(stdcall1(_timeEndPeriod, 1))
- } else {
-@@ -414,6 +424,37 @@
- }
- }
-
-+// haveHighResTimer determines, if CreateWaitableTimerEx
-+// CREATE_WAITABLE_TIMER_HIGH_RESOLUTION flag is available.
-+var haveHighResTimer = false
-+
-+// createHighResTimer calls CreateWaitableTimerEx with
-+// CREATE_WAITABLE_TIMER_HIGH_RESOLUTION flag to create high
-+// resolution timer. createHighResTimer returns new timer
-+// handle or 0, if CreateWaitableTimerEx failed.
-+func createHighResTimer() uintptr {
-+ const (
-+ // As per @jstarks, see
-+ // https://github.com/golang/go/issues/8687#issuecomment-656259353
-+ _CREATE_WAITABLE_TIMER_HIGH_RESOLUTION = 0x00000002
-+ _TIMER_ALL_ACCESS = 0x1F0003
-+ )
-+ return stdcall4(_CreateWaitableTimerExW, 0, 0, _CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, _TIMER_ALL_ACCESS)
-+}
-+
-+func initHighResTimer() {
-+ if GOARCH == "arm" {
-+ // TODO: Not yet implemented.
-+ return
-+ }
-+ h := createHighResTimer()
-+ if h != 0 {
-+ haveHighResTimer = true
-+ usleep2Addr = unsafe.Pointer(funcPC(usleep2HighRes))
-+ stdcall1(_CloseHandle, h)
-+ }
-+}
-+
- func osinit() {
- asmstdcallAddr = unsafe.Pointer(funcPC(asmstdcall))
- usleep2Addr = unsafe.Pointer(funcPC(usleep2))
-@@ -429,6 +470,7 @@
-
- stdcall2(_SetConsoleCtrlHandler, funcPC(ctrlhandler), 1)
-
-+ initHighResTimer()
- timeBeginPeriodRetValue = osRelax(false)
-
- ncpu = getproccount()
-@@ -844,9 +886,20 @@
- var thandle uintptr
- stdcall7(_DuplicateHandle, currentProcess, currentThread, currentProcess, uintptr(unsafe.Pointer(&thandle)), 0, 0, _DUPLICATE_SAME_ACCESS)
-
-+ // Configure usleep timer, if possible.
-+ var timer uintptr
-+ if haveHighResTimer {
-+ timer = createHighResTimer()
-+ if timer == 0 {
-+ print("runtime: CreateWaitableTimerEx failed; errno=", getlasterror(), "\n")
-+ throw("CreateWaitableTimerEx when creating timer failed")
-+ }
-+ }
-+
- mp := getg().m
- lock(&mp.threadLock)
- mp.thread = thandle
-+ mp.highResTimer = timer
- unlock(&mp.threadLock)
-
- // Query the true stack base from the OS. Currently we're
-@@ -884,6 +937,10 @@
- lock(&mp.threadLock)
- stdcall1(_CloseHandle, mp.thread)
- mp.thread = 0
-+ if mp.highResTimer != 0 {
-+ stdcall1(_CloseHandle, mp.highResTimer)
-+ mp.highResTimer = 0
-+ }
- unlock(&mp.threadLock)
- }
-
-@@ -979,6 +1036,7 @@
- // in sys_windows_386.s and sys_windows_amd64.s
- func onosstack(fn unsafe.Pointer, arg uint32)
- func usleep2(usec uint32)
-+func usleep2HighRes(usec uint32)
- func switchtothread()
-
- var usleep2Addr unsafe.Pointer
-diff --git a/src/runtime/sys_windows_386.s b/src/runtime/sys_windows_386.s
-index 9e1f409..4ac1527 100644
---- a/src/runtime/sys_windows_386.s
-+++ b/src/runtime/sys_windows_386.s
-@@ -428,6 +428,42 @@
- MOVL BP, SP
- RET
-
-+// Runs on OS stack. duration (in 100ns units) is in BX.
-+TEXT runtime·usleep2HighRes(SB),NOSPLIT,$36
-+ // Want negative 100ns units.
-+ NEGL BX
-+ MOVL $-1, hi-4(SP)
-+ MOVL BX, lo-8(SP)
-+
-+ get_tls(CX)
-+ MOVL g(CX), CX
-+ MOVL g_m(CX), CX
-+ MOVL (m_mOS+mOS_highResTimer)(CX), CX
-+ MOVL CX, saved_timer-12(SP)
-+
-+ MOVL $0, fResume-16(SP)
-+ MOVL $0, lpArgToCompletionRoutine-20(SP)
-+ MOVL $0, pfnCompletionRoutine-24(SP)
-+ MOVL $0, lPeriod-28(SP)
-+ LEAL lo-8(SP), BX
-+ MOVL BX, lpDueTime-32(SP)
-+ MOVL CX, hTimer-36(SP)
-+ MOVL SP, BP
-+ MOVL runtime·_SetWaitableTimer(SB), AX
-+ CALL AX
-+ MOVL BP, SP
-+
-+ MOVL $0, ptime-28(SP)
-+ MOVL $0, alertable-32(SP)
-+ MOVL saved_timer-12(SP), CX
-+ MOVL CX, handle-36(SP)
-+ MOVL SP, BP
-+ MOVL runtime·_NtWaitForSingleObject(SB), AX
-+ CALL AX
-+ MOVL BP, SP
-+
-+ RET
-+
- // Runs on OS stack.
- TEXT runtime·switchtothread(SB),NOSPLIT,$0
- MOVL SP, BP
-diff --git a/src/runtime/sys_windows_amd64.s b/src/runtime/sys_windows_amd64.s
-index 6c8eecd..8475425 100644
---- a/src/runtime/sys_windows_amd64.s
-+++ b/src/runtime/sys_windows_amd64.s
-@@ -457,6 +457,38 @@
- MOVQ 40(SP), SP
- RET
-
-+// Runs on OS stack. duration (in 100ns units) is in BX.
-+TEXT runtime·usleep2HighRes(SB),NOSPLIT|NOFRAME,$72
-+ MOVQ SP, AX
-+ ANDQ $~15, SP // alignment as per Windows requirement
-+ MOVQ AX, 64(SP)
-+
-+ get_tls(CX)
-+ MOVQ g(CX), CX
-+ MOVQ g_m(CX), CX
-+ MOVQ (m_mOS+mOS_highResTimer)(CX), CX // hTimer
-+ MOVQ CX, 48(SP) // save hTimer for later
-+ // Want negative 100ns units.
-+ NEGQ BX
-+ LEAQ 56(SP), DX // lpDueTime
-+ MOVQ BX, (DX)
-+ MOVQ $0, R8 // lPeriod
-+ MOVQ $0, R9 // pfnCompletionRoutine
-+ MOVQ $0, AX
-+ MOVQ AX, 32(SP) // lpArgToCompletionRoutine
-+ MOVQ AX, 40(SP) // fResume
-+ MOVQ runtime·_SetWaitableTimer(SB), AX
-+ CALL AX
-+
-+ MOVQ 48(SP), CX // handle
-+ MOVQ $0, DX // alertable
-+ MOVQ $0, R8 // ptime
-+ MOVQ runtime·_NtWaitForSingleObject(SB), AX
-+ CALL AX
-+
-+ MOVQ 64(SP), SP
-+ RET
-+
- // Runs on OS stack.
- TEXT runtime·switchtothread(SB),NOSPLIT|NOFRAME,$0
- MOVQ SP, AX
-diff --git a/src/runtime/sys_windows_arm.s b/src/runtime/sys_windows_arm.s
-index 256b5ff..e6c61a4 100644
---- a/src/runtime/sys_windows_arm.s
-+++ b/src/runtime/sys_windows_arm.s
-@@ -468,6 +468,24 @@
- MOVW R4, R13 // Restore SP
- MOVM.IA.W (R13), [R4, R15] // pop {R4, pc}
-
-+// Runs on OS stack. Duration (in 100ns units) is in R0.
-+// TODO: neeeds to be implemented properly.
-+TEXT runtime·usleep2HighRes(SB),NOSPLIT|NOFRAME,$0
-+ MOVM.DB.W [R4, R14], (R13) // push {r4, lr}
-+ MOVW R13, R4 // Save SP
-+ SUB $8, R13 // R13 = R13 - 8
-+ BIC $0x7, R13 // Align SP for ABI
-+ RSB $0, R0, R3 // R3 = -R0
-+ MOVW $0, R1 // R1 = FALSE (alertable)
-+ MOVW $-1, R0 // R0 = handle
-+ MOVW R13, R2 // R2 = pTime
-+ MOVW R3, 0(R2) // time_lo
-+ MOVW R0, 4(R2) // time_hi
-+ MOVW runtime·_NtWaitForSingleObject(SB), R3
-+ BL (R3)
-+ MOVW R4, R13 // Restore SP
-+ MOVM.IA.W (R13), [R4, R15] // pop {R4, pc}
-+
- // Runs on OS stack.
- TEXT runtime·switchtothread(SB),NOSPLIT|NOFRAME,$0
- MOVM.DB.W [R4, R14], (R13) // push {R4, lr}
diff --git a/go-patches/no-ctrlc-handler.patch b/go-patches/no-ctrlc-handler.patch
deleted file mode 100644
index 157ed88f..00000000
--- a/go-patches/no-ctrlc-handler.patch
+++ /dev/null
@@ -1,184 +0,0 @@
-From 8bc7bff3bff8f61312f8d2307cfe2e433d356c31 Mon Sep 17 00:00:00 2001
-From: "Jason A. Donenfeld" <Jason@zx2c4.com>
-Date: Fri, 11 Sep 2020 13:04:11 +0200
-Subject: [PATCH 1/2] Revert "[release-branch.go1.15] runtime: detect services
- in signal handler"
-
-This reverts commit b1253d24e159129c778377c3a2a0bde15904a417.
----
- src/runtime/os_windows.go | 73 +++------------------------------------
- 1 file changed, 4 insertions(+), 69 deletions(-)
-
-diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go
-index 769197db46..a584ada702 100644
---- a/src/runtime/os_windows.go
-+++ b/src/runtime/os_windows.go
-@@ -36,10 +36,7 @@ const (
- //go:cgo_import_dynamic runtime._SetThreadContext SetThreadContext%2 "kernel32.dll"
- //go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll"
- //go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 "kernel32.dll"
--//go:cgo_import_dynamic runtime._OpenProcess OpenProcess%3 "kernel32.dll"
- //go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 "kernel32.dll"
--//go:cgo_import_dynamic runtime._ProcessIdToSessionId ProcessIdToSessionId%2 "kernel32.dll"
--//go:cgo_import_dynamic runtime._QueryFullProcessImageNameA QueryFullProcessImageNameA%4 "kernel32.dll"
- //go:cgo_import_dynamic runtime._ResumeThread ResumeThread%1 "kernel32.dll"
- //go:cgo_import_dynamic runtime._SetConsoleCtrlHandler SetConsoleCtrlHandler%2 "kernel32.dll"
- //go:cgo_import_dynamic runtime._SetErrorMode SetErrorMode%1 "kernel32.dll"
-@@ -87,10 +84,7 @@ var (
- _SetThreadContext,
- _LoadLibraryW,
- _LoadLibraryA,
-- _OpenProcess,
- _PostQueuedCompletionStatus,
-- _ProcessIdToSessionId,
-- _QueryFullProcessImageNameA,
- _QueryPerformanceCounter,
- _QueryPerformanceFrequency,
- _ResumeThread,
-@@ -134,8 +128,7 @@ var (
- // Load ntdll.dll manually during startup, otherwise Mingw
- // links wrong printf function to cgo executable (see issue
- // 12030 for details).
-- _NtWaitForSingleObject stdFunction
-- _NtQueryInformationProcess stdFunction
-+ _NtWaitForSingleObject stdFunction
-
- // These are from non-kernel32.dll, so we prefer to LoadLibraryEx them.
- _timeBeginPeriod,
-@@ -262,7 +255,6 @@ func loadOptionalSyscalls() {
- throw("ntdll.dll not found")
- }
- _NtWaitForSingleObject = windowsFindfunc(n32, []byte("NtWaitForSingleObject\000"))
-- _NtQueryInformationProcess = windowsFindfunc(n32, []byte("NtQueryInformationProcess\000"))
-
- if GOARCH == "arm" {
- _QueryPerformanceCounter = windowsFindfunc(k32, []byte("QueryPerformanceCounter\000"))
-@@ -1003,63 +995,6 @@ func usleep(us uint32) {
- onosstack(usleep2Addr, 10*us)
- }
-
--// isWindowsService returns whether the process is currently executing as a
--// Windows service. The below technique looks a bit hairy, but it's actually
--// exactly what the .NET framework does for the similarly named function:
--// https://github.com/dotnet/extensions/blob/f4066026ca06984b07e90e61a6390ac38152ba93/src/Hosting/WindowsServices/src/WindowsServiceHelpers.cs#L26-L31
--// Specifically, it looks up whether the parent process has session ID zero
--// and is called "services".
--func isWindowsService() bool {
-- const (
-- _CURRENT_PROCESS = ^uintptr(0)
-- _PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
-- )
-- // pbi is a PROCESS_BASIC_INFORMATION struct, where we just care about
-- // the 6th pointer inside of it, which contains the pid of the process
-- // parent:
-- // https://github.com/wine-mirror/wine/blob/42cb7d2ad1caba08de235e6319b9967296b5d554/include/winternl.h#L1294
-- var pbi [6]uintptr
-- var pbiLen uint32
-- err := stdcall5(_NtQueryInformationProcess, _CURRENT_PROCESS, 0, uintptr(unsafe.Pointer(&pbi[0])), uintptr(unsafe.Sizeof(pbi)), uintptr(unsafe.Pointer(&pbiLen)))
-- if err != 0 {
-- return false
-- }
-- var psid uint32
-- err = stdcall2(_ProcessIdToSessionId, pbi[5], uintptr(unsafe.Pointer(&psid)))
-- if err == 0 || psid != 0 {
-- return false
-- }
-- pproc := stdcall3(_OpenProcess, _PROCESS_QUERY_LIMITED_INFORMATION, 0, pbi[5])
-- if pproc == 0 {
-- return false
-- }
-- defer stdcall1(_CloseHandle, pproc)
-- // exeName gets the path to the executable image of the parent process
-- var exeName [261]byte
-- exeNameLen := uint32(len(exeName) - 1)
-- err = stdcall4(_QueryFullProcessImageNameA, pproc, 0, uintptr(unsafe.Pointer(&exeName[0])), uintptr(unsafe.Pointer(&exeNameLen)))
-- if err == 0 || exeNameLen == 0 {
-- return false
-- }
-- servicesLower := "services.exe"
-- servicesUpper := "SERVICES.EXE"
-- i := int(exeNameLen) - 1
-- j := len(servicesLower) - 1
-- if i < j {
-- return false
-- }
-- for {
-- if j == -1 {
-- return i == -1 || exeName[i] == '\\'
-- }
-- if exeName[i] != servicesLower[j] && exeName[i] != servicesUpper[j] {
-- return false
-- }
-- i--
-- j--
-- }
--}
--
- func ctrlhandler1(_type uint32) uint32 {
- var s uint32
-
-@@ -1075,9 +1010,9 @@ func ctrlhandler1(_type uint32) uint32 {
- if sigsend(s) {
- return 1
- }
-- if !islibrary && !isarchive && !isWindowsService() {
-- // Only exit the program if we don't have a DLL or service.
-- // See https://golang.org/issues/35965 and https://golang.org/issues/40167
-+ if !islibrary && !isarchive {
-+ // Only exit the program if we don't have a DLL.
-+ // See https://golang.org/issues/35965.
- exit(2) // SIGINT, SIGTERM, etc
- }
- return 0
---
-2.28.0
-
-From 84cc2046962e754af08f99327561be2e979eaf16 Mon Sep 17 00:00:00 2001
-From: "Jason A. Donenfeld" <Jason@zx2c4.com>
-Date: Tue, 14 Jul 2020 01:41:03 -0600
-Subject: [PATCH 2/2] runtime: do not explicitly exit on ctrl handler
-
-The default ctrl+c handler should process exits in situations where it
-makes sense, like console apps, but not in situations where it doesn't,
-like libraries or services. Therefore, we should remove the exit(2) so
-that the default handler is used for this. This also uses the more
-proper windows exit code of STATUS_CONTROL_C_EXIT, with the base case
-handler installed by KernelBase.dll. In particular, this helps in the
-case of services, which previously would terminate when receiving
-shutdown signals, instead of passing them onward to the service program.
-In this CL, contrary to CL 244959, we do not need to special case
-services with expensive detection algorithms, or rely on hard-coded
-library/archive flags.
-
-Fixes #40167.
-Fixes #40074.
-
-Change-Id: I9bf6ed6f65cefeff754d270aa33fa4df8d0b451f
-Reviewed-on: https://go-review.googlesource.com/c/go/+/243597
-Run-TryBot: Jason A. Donenfeld <Jason@zx2c4.com>
-TryBot-Result: Gobot Gobot <gobot@golang.org>
-Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
-Reviewed-by: Jason A. Donenfeld <Jason@zx2c4.com>
----
- src/runtime/os_windows.go | 5 -----
- 1 file changed, 5 deletions(-)
-
-diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go
-index a584ada702..a62e941229 100644
---- a/src/runtime/os_windows.go
-+++ b/src/runtime/os_windows.go
-@@ -1010,11 +1010,6 @@ func ctrlhandler1(_type uint32) uint32 {
- if sigsend(s) {
- return 1
- }
-- if !islibrary && !isarchive {
-- // Only exit the program if we don't have a DLL.
-- // See https://golang.org/issues/35965.
-- exit(2) // SIGINT, SIGTERM, etc
-- }
- return 0
- }
-
---
-2.28.0
-
diff --git a/go.mod b/go.mod
index e791b9a4..3c51cd3d 100644
--- a/go.mod
+++ b/go.mod
@@ -1,18 +1,18 @@
module golang.zx2c4.com/wireguard/windows
-go 1.14
+go 1.15
require (
- github.com/lxn/walk v0.0.0-20191128110447-55ccb3a9f5c1
- github.com/lxn/win v0.0.0-20191128105842-2da648fda5b4
- golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899
- golang.org/x/net v0.0.0-20200707034311-ab3426394381
- golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1
- golang.org/x/text v0.3.3
- golang.zx2c4.com/wireguard v0.0.20200321-0.20200715051853-507f148e1c42
+ github.com/lxn/walk v0.0.0-20210112085537-c389da54e794
+ github.com/lxn/win v0.0.0-20201111105847-2a20daff6a55
+ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
+ golang.org/x/net v0.0.0-20210119194325-5f4716e94777
+ golang.org/x/sys v0.0.0-20210123231150-1d476976d117
+ golang.org/x/text v0.3.5
+ golang.zx2c4.com/wireguard v0.0.20201119-0.20210120232502-fcc8ad05df75
)
replace (
- github.com/lxn/walk => golang.zx2c4.com/wireguard/windows v0.0.0-20200319192453-d35a18df246f
- github.com/lxn/win => golang.zx2c4.com/wireguard/windows v0.0.0-20191128151145-b4e4933852d5
+ github.com/lxn/walk => golang.zx2c4.com/wireguard/windows v0.0.0-20210121140954-e7fc19d483bd
+ github.com/lxn/win => golang.zx2c4.com/wireguard/windows v0.0.0-20201107183008-659a4e955570
)
diff --git a/go.mod.master b/go.mod.master
index f10687d3..6993a3ac 100644
--- a/go.mod.master
+++ b/go.mod.master
@@ -1,5 +1,7 @@
module golang.zx2c4.com/wireguard/windows
+go 1.15
+
require (
github.com/lxn/walk latest
github.com/lxn/win latest
diff --git a/go.sum b/go.sum
index 916b67a1..b34d78b3 100644
--- a/go.sum
+++ b/go.sum
@@ -1,28 +1,27 @@
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg=
-golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
+golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
-golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
+golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1 h1:sIky/MyNRSHTrdxfsiUSS4WIAMvInbeXljJz+jDjeYE=
-golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210105210732-16f7687f5001/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210123231150-1d476976d117 h1:M1sK0uTIn2x3HD5sySUPBg7ml5hmlQ/t7n7cIM6My9w=
+golang.org/x/sys v0.0.0-20210123231150-1d476976d117/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc=
+golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.zx2c4.com/wireguard v0.0.20200321-0.20200715051853-507f148e1c42 h1:SrR1hmxGKKarHEEDvaHxatwnqE3uT+7jvMcin6SHOkw=
-golang.zx2c4.com/wireguard v0.0.20200321-0.20200715051853-507f148e1c42/go.mod h1:GJvYs5O24/ASlwPiRklVnjMx2xQzrOic0DuU6GvYJL4=
-golang.zx2c4.com/wireguard/windows v0.0.0-20191128151145-b4e4933852d5 h1:tkrF3cHqbnFQ068q0cUbu9nJMXKChFT2rGL9sSAMlrI=
-golang.zx2c4.com/wireguard/windows v0.0.0-20191128151145-b4e4933852d5/go.mod h1:ouWl4wViUNh8tPSIwxTVMuS014WakR1hqvBc2I0bMoA=
-golang.zx2c4.com/wireguard/windows v0.0.0-20200319192453-d35a18df246f h1:55sAe6dUkYZS7m8s/+eoEAdggzZjNkku8lj/W3vHu+U=
-golang.zx2c4.com/wireguard/windows v0.0.0-20200319192453-d35a18df246f/go.mod h1:Y+FYqVFaQO6a+1uigm0N0GiuaZrLEaBxEiJ8tfH9sMQ=
+golang.zx2c4.com/wireguard v0.0.20201119-0.20210120232502-fcc8ad05df75 h1:Q8bLImgHajG+re7SSJQstC0ySZtGYx2KH6wVg8BG5YU=
+golang.zx2c4.com/wireguard v0.0.20201119-0.20210120232502-fcc8ad05df75/go.mod h1:r0ExowOoGFfDoLDxx+M9SYbNVsoZ0xviLL+K4f2mt+A=
+golang.zx2c4.com/wireguard/windows v0.0.0-20201107183008-659a4e955570 h1:sbXpfRwl+7YQY72KBuzyacG7ucr6w4OueJiEb7+pvSk=
+golang.zx2c4.com/wireguard/windows v0.0.0-20201107183008-659a4e955570/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
+golang.zx2c4.com/wireguard/windows v0.0.0-20210121140954-e7fc19d483bd h1:kAUzMAITME2MCtrXBaUa9P4tndiXGWO674k9gn6ZR28=
+golang.zx2c4.com/wireguard/windows v0.0.0-20210121140954-e7fc19d483bd/go.mod h1:Y+FYqVFaQO6a+1uigm0N0GiuaZrLEaBxEiJ8tfH9sMQ=
diff --git a/gotext.go b/gotext.go
index d61ce393..a1af724c 100644
--- a/gotext.go
+++ b/gotext.go
@@ -3,7 +3,7 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package main
@@ -65,7 +65,7 @@ func main() {
panic(err)
}
cmd = exec.Command(gotextFilename, "-srclang=en", "update", "-out=zgotext.go", "-lang="+strings.Join(langs, ","))
- cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64", "CGO_ENABLED=1", "CC=x86_64-w64-mingw32-gcc")
+ cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
diff --git a/installer/.gitignore b/installer/.gitignore
index 2933d030..fc4d2127 100644
--- a/installer/.gitignore
+++ b/installer/.gitignore
@@ -5,3 +5,5 @@
/dist
/x86
/amd64
+/arm
+/arm64
diff --git a/installer/build.bat b/installer/build.bat
index 864987d8..d0507f51 100644
--- a/installer/build.bat
+++ b/installer/build.bat
@@ -1,30 +1,27 @@
@echo off
rem SPDX-License-Identifier: MIT
-rem Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+rem Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
setlocal
set PATHEXT=.exe
set BUILDDIR=%~dp0
cd /d %BUILDDIR% || exit /b 1
-for /f "tokens=3" %%a in ('findstr /r "WIREGUARD_WINDOWS_VERSION_STRING.*[0-9.]*" ..\version\version.h') do set WIREGUARD_VERSION=%%a
+for /f "tokens=3" %%a in ('findstr /r "Number.*=.*[0-9.]*" ..\version\version.go') do set WIREGUARD_VERSION=%%a
set WIREGUARD_VERSION=%WIREGUARD_VERSION:"=%
set WIX_CANDLE_FLAGS=-nologo -dWIREGUARD_VERSION="%WIREGUARD_VERSION%"
set WIX_LIGHT_FLAGS=-nologo -spdb
-set WIX_LIGHT_FLAGS=%WIX_LIGHT_FLAGS% -sw1056
-set WIX_LIGHT_FLAGS=%WIX_LIGHT_FLAGS% -sice:ICE30
+set WIX_LIGHT_FLAGS=%WIX_LIGHT_FLAGS% -sice:ICE39
set WIX_LIGHT_FLAGS=%WIX_LIGHT_FLAGS% -sice:ICE61
-set WIX_LIGHT_FLAGS=%WIX_LIGHT_FLAGS% -sice:ICE09
+set WIX_LIGHT_FLAGS=%WIX_LIGHT_FLAGS% -sice:ICE03
if exist .deps\prepared goto :build
:installdeps
rmdir /s /q .deps 2> NUL
mkdir .deps || goto :error
cd .deps || goto :error
- call :download wintun-x86.msm https://www.wintun.net/builds/wintun-x86-0.8.1.msm 5b47f83ffa9c361a360196d692f64755183e82c65f4753accc92087e6736af10 || goto :error
- call :download wintun-amd64.msm https://www.wintun.net/builds/wintun-amd64-0.8.1.msm af9644438a716f5a022052e3574ee0404c3e3309daff84889d656178fbc6b168 || goto :error
- call :download wix-binaries.zip https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip 2c1888d5d1dba377fc7fa14444cf556963747ff9a0a289a3599cf09da03b9e2e || goto :error
+ call :download wix-binaries.zip https://wixtoolset.org/downloads/v3.14.0.4118/wix314-binaries.zip 34dcbba9952902bfb710161bd45ee2e721ffa878db99f738285a21c9b09c6edb || goto :error
echo [+] Extracting wix-binaries.zip
mkdir wix\bin || goto :error
tar -xf wix-binaries.zip -C wix\bin || goto :error
@@ -34,13 +31,15 @@ if exist .deps\prepared goto :build
cd .. || goto :error
:build
+ set PATH=%BUILDDIR%..\.deps\llvm-mingw\bin;%PATH%
set WIX=%BUILDDIR%.deps\wix\
set CFLAGS=-O3 -Wall -std=gnu11 -DWINVER=0x0601 -D_WIN32_WINNT=0x0601 -municode -DUNICODE -D_UNICODE -DNDEBUG
set LDFLAGS=-shared -s -Wl,--kill-at -Wl,--major-os-version=6 -Wl,--minor-os-version=1 -Wl,--major-subsystem-version=6 -Wl,--minor-subsystem-version=1 -Wl,--tsaware -Wl,--dynamicbase -Wl,--nxcompat -Wl,--export-all-symbols
- set LDLIBS=-lmsi -lole32 -lshlwapi -lshell32 -luuid
+ set LDLIBS=-lmsi -lole32 -lshlwapi -lshell32 -luuid -lntdll
call :msi x86 i686 x86 || goto :error
- set CGO_LDFLAGS=%CGO_LDFLAGS% -Wl,--high-entropy-va
call :msi amd64 x86_64 x64 || goto :error
+ call :msi arm armv7 arm || goto :error
+ call :msi arm64 aarch64 arm64 || goto :error
if exist ..\sign.bat call ..\sign.bat
if "%SigningCertificate%"=="" goto :success
if "%TimestampServer%"=="" goto :success
@@ -59,7 +58,6 @@ if exist .deps\prepared goto :build
goto :eof
:msi
- set PATH=%BUILDDIR%..\.deps\%~2-w64-mingw32-native\bin;%PATH%
set CC=%~2-w64-mingw32-gcc
if not exist "%~1" mkdir "%~1"
echo [+] Compiling %1
diff --git a/installer/customactions.c b/installer/customactions.c
index 496d80b1..e2611356 100644
--- a/installer/customactions.c
+++ b/installer/customactions.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
/*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
#include <windows.h>
@@ -17,7 +17,7 @@
#define MANAGER_SERVICE_NAME TEXT("WireGuardManager")
#define TUNNEL_SERVICE_PREFIX TEXT("WireGuardTunnel$")
-enum log_level { LOG_LEVEL_INFO, LOG_LEVEL_WARN, LOG_LEVEL_ERR };
+enum log_level { LOG_LEVEL_INFO, LOG_LEVEL_WARN, LOG_LEVEL_ERR, LOG_LEVEL_MSIERR };
static void log_messagef(MSIHANDLE installer, enum log_level level, const TCHAR *format, ...)
{
@@ -49,6 +49,10 @@ static void log_messagef(MSIHANDLE installer, enum log_level level, const TCHAR
template = TEXT("WireGuard error: [1]");
type = INSTALLMESSAGE_ERROR;
break;
+ case LOG_LEVEL_MSIERR:
+ template = TEXT("[1]");
+ type = INSTALLMESSAGE_ERROR;
+ break;
default:
goto out;
}
@@ -78,6 +82,134 @@ static void log_errorf(MSIHANDLE installer, enum log_level level, DWORD error_co
LocalFree(system_message);
}
+__declspec(dllexport) UINT __stdcall CheckWow64(MSIHANDLE installer)
+{
+ UINT ret = ERROR_SUCCESS;
+ bool is_com_initialized = SUCCEEDED(CoInitialize(NULL));
+ HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
+ BOOL(WINAPI *IsWow64Process2)(HANDLE hProcess, USHORT *pProcessMachine, USHORT *pNativeMachine);
+ USHORT process_machine, native_machine;
+ BOOL is_wow64_process;
+
+ if (!kernel32) {
+ ret = GetLastError();
+ log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("Failed to get kernel32.dll handle"));
+ goto out;
+ }
+ IsWow64Process2 = (void *)GetProcAddress(kernel32, "IsWow64Process2");
+ if (IsWow64Process2) {
+ if (!IsWow64Process2(GetCurrentProcess(), &process_machine, &native_machine)) {
+ ret = GetLastError();
+ log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("Failed to determine Wow64 status from IsWow64Process2"));
+ goto out;
+ }
+ if (process_machine == IMAGE_FILE_MACHINE_UNKNOWN)
+ goto out;
+ } else {
+ if (!IsWow64Process(GetCurrentProcess(), &is_wow64_process)) {
+ ret = GetLastError();
+ log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("Failed to determine Wow64 status from IsWow64Process"));
+ goto out;
+ }
+ if (!is_wow64_process)
+ goto out;
+ }
+ log_messagef(installer, LOG_LEVEL_MSIERR, TEXT("You must use the native version of WireGuard on this computer."));
+ ret = ERROR_INSTALL_FAILURE;
+out:
+ if (is_com_initialized)
+ CoUninitialize();
+ return ret;
+}
+
+extern NTAPI __declspec(dllimport) void RtlGetNtVersionNumbers(DWORD *MajorVersion, DWORD *MinorVersion, DWORD *BuildNumber);
+
+static int message_box(MSIHANDLE installer, TCHAR *text, UINT type)
+{
+ TCHAR progressOnly[2];
+ DWORD len;
+ MSIHANDLE record;
+ int ret;
+
+ len = _countof(progressOnly);
+ if (MsiGetProperty(installer, TEXT("MsiUIProgressOnly"), progressOnly, &len) == ERROR_SUCCESS && _tcstoul(progressOnly, NULL, 10) == 1)
+ return MessageBox(GetForegroundWindow(), text, TEXT("WireGuard"), type);
+ record = MsiCreateRecord(2);
+ MsiRecordSetString(record, 0, TEXT("[1]"));
+ MsiRecordSetString(record, 1, text);
+ ret = MsiProcessMessage(installer, INSTALLMESSAGE_USER | type, record);
+ MsiCloseHandle(record);
+ return ret;
+}
+
+__declspec(dllexport) UINT __stdcall CheckKB2921916(MSIHANDLE installer)
+{
+ bool is_com_initialized = SUCCEEDED(CoInitialize(NULL));
+ UINT ret = ERROR_SUCCESS;
+ DWORD maj, min, build, len;
+ TCHAR uiLevel[10];
+ TCHAR setupapi_path[MAX_PATH];
+ HANDLE setupapi_handle = INVALID_HANDLE_VALUE;
+ HANDLE setupapi_filemapping = NULL;
+ const char *setupapi_bytes = NULL;
+ MEMORY_BASIC_INFORMATION setupapi_meminfo;
+ static const char setupapi_marker[] = "Signature Hash";
+
+ RtlGetNtVersionNumbers(&maj, &min, &build);
+ if (maj != 6 || min != 1)
+ goto out;
+
+ ret = ERROR_INSTALL_FAILURE;
+ if (!GetSystemDirectory(setupapi_path, _countof(setupapi_path)))
+ goto out;
+ if (!PathAppend(setupapi_path, TEXT("setupapi.dll")))
+ goto out;
+ setupapi_handle = CreateFile(setupapi_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (setupapi_handle == INVALID_HANDLE_VALUE)
+ goto out;
+ setupapi_filemapping = CreateFileMapping(setupapi_handle, NULL, PAGE_READONLY, 0, 0, NULL);
+ if (!setupapi_filemapping)
+ goto out;
+ setupapi_bytes = MapViewOfFile(setupapi_filemapping, FILE_MAP_READ, 0, 0, 0);
+ if (!setupapi_bytes)
+ goto out;
+ if (VirtualQuery(setupapi_bytes, &setupapi_meminfo, sizeof(setupapi_meminfo)) != sizeof(setupapi_meminfo))
+ goto out;
+ if (setupapi_meminfo.RegionSize > strlen(setupapi_marker)) {
+ for (const char *p = setupapi_bytes + strlen(setupapi_marker) - 1; (p = memchr(p, 'h', setupapi_meminfo.RegionSize - (p - setupapi_bytes))); ++p) {
+ if (!memcmp(p - strlen(setupapi_marker) + 1, setupapi_marker, strlen(setupapi_marker))) {
+ ret = ERROR_SUCCESS;
+ goto out;
+ }
+ }
+ }
+
+ len = _countof(uiLevel);
+ if (MsiGetProperty(installer, TEXT("UILevel"), uiLevel, &len) != ERROR_SUCCESS || _tcstoul(uiLevel, NULL, 10) < INSTALLUILEVEL_BASIC) {
+ log_messagef(installer, LOG_LEVEL_MSIERR, TEXT("Use of WireGuard on Windows 7 requires KB2921916."));
+ goto out;
+ }
+#ifdef _WIN64
+ static const TCHAR url[] = TEXT("https://download.wireguard.com/windows-toolchain/distfiles/Windows6.1-KB2921916-x64.msu");
+#else
+ static const TCHAR url[] = TEXT("https://download.wireguard.com/windows-toolchain/distfiles/Windows6.1-KB2921916-x86.msu");
+#endif
+ if (message_box(installer, TEXT("Missing Windows Hotfix\n\nUse of WireGuard on Windows 7 requires KB2921916. Would you like to download the hotfix in your web browser?"), MB_ICONWARNING | MB_YESNO) == IDYES)
+ ShellExecute(GetForegroundWindow(), NULL, url, NULL, NULL, SW_SHOWNORMAL);
+ ret = ERROR_INSTALL_USEREXIT;
+
+out:
+ if (setupapi_bytes)
+ UnmapViewOfFile(setupapi_bytes);
+ if (setupapi_filemapping)
+ CloseHandle(setupapi_filemapping);
+ if (setupapi_handle != INVALID_HANDLE_VALUE)
+ CloseHandle(setupapi_handle);
+ if (is_com_initialized)
+ CoUninitialize();
+ return ret;
+}
+
static UINT insert_service_control(MSIHANDLE installer, MSIHANDLE view, const TCHAR *service_name, bool start)
{
static unsigned int index = 0;
@@ -126,62 +258,6 @@ out:
return ret;
}
-static bool remove_directory_recursive(MSIHANDLE installer, TCHAR path[MAX_PATH], unsigned int max_depth)
-{
- HANDLE find_handle;
- WIN32_FIND_DATA find_data;
- TCHAR *path_end;
-
- if (!max_depth) {
- log_messagef(installer, LOG_LEVEL_WARN, TEXT("Too many levels of nesting at \"%1\""), path);
- return false;
- }
-
- path_end = path + _tcsnlen(path, MAX_PATH);
- if (!PathAppend(path, TEXT("*.*"))) {
- log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("PathAppend(\"%1\", \"*.*\") failed"), path);
- return false;
- }
- find_handle = FindFirstFileEx(path, FindExInfoBasic, &find_data, FindExSearchNameMatch, NULL, 0);
- if (find_handle == INVALID_HANDLE_VALUE) {
- log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("FindFirstFileEx(\"%1\") failed"), path);
- return false;
- }
- do {
- if (find_data.cFileName[0] == TEXT('.') && (find_data.cFileName[1] == TEXT('\0') || (find_data.cFileName[1] == TEXT('.') && find_data.cFileName[2] == TEXT('\0'))))
- continue;
-
- path_end[0] = TEXT('\0');
- if (!PathAppend(path, find_data.cFileName)) {
- log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("PathAppend(\"%1\", \"%2\") failed"), path, find_data.cFileName);
- continue;
- }
-
- if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
- remove_directory_recursive(installer, path, max_depth - 1);
- continue;
- }
-
- if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) && !SetFileAttributes(path, find_data.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY))
- log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("SetFileAttributes(\"%1\") failed"), path);
-
- if (DeleteFile(path))
- log_messagef(installer, LOG_LEVEL_INFO, TEXT("Deleted \"%1\""), path);
- else
- log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("DeleteFile(\"%1\") failed"), path);
- } while (FindNextFile(find_handle, &find_data));
- FindClose(find_handle);
-
- path_end[0] = TEXT('\0');
- if (RemoveDirectory(path)) {
- log_messagef(installer, LOG_LEVEL_INFO, TEXT("Removed \"%1\""), path);
- return true;
- } else {
- log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("RemoveDirectory(\"%1\") failed"), path);
- return false;
- }
-}
-
__declspec(dllexport) UINT __stdcall EvaluateWireGuardServices(MSIHANDLE installer)
{
UINT ret = ERROR_INSTALL_FAILURE;
@@ -255,27 +331,82 @@ out:
return ret == ERROR_SUCCESS ? ret : ERROR_INSTALL_FAILURE;
}
-__declspec(dllexport) UINT __stdcall RemoveConfigFolder(MSIHANDLE installer)
+__declspec(dllexport) UINT __stdcall LaunchApplicationAndAbort(MSIHANDLE installer)
{
- LSTATUS ret;
+ UINT ret = ERROR_INSTALL_FAILURE;
TCHAR path[MAX_PATH];
+ DWORD path_len = _countof(path);
+ PROCESS_INFORMATION pi;
+ STARTUPINFO si = { .cb = sizeof(STARTUPINFO) };
+
+ ret = MsiGetProperty(installer, TEXT("WireGuardFolder"), path, &path_len);
+ if (ret != ERROR_SUCCESS) {
+ log_errorf(installer, LOG_LEVEL_WARN, ret, TEXT("MsiGetProperty(\"WireGuardFolder\") failed"));
+ goto out;
+ }
+ if (!path[0] || !PathAppend(path, TEXT("wireguard.exe")))
+ goto out;
+ log_messagef(installer, LOG_LEVEL_INFO, TEXT("Launching %1"), path);
+ if (!CreateProcess(path, TEXT("wireguard"), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
+ log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("Failed to create \"%1\" process"), path);
+ goto out;
+ }
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+out:
+ return ERROR_INSTALL_USEREXIT;
+}
+
+__declspec(dllexport) UINT __stdcall EvaluateWireGuardComponents(MSIHANDLE installer)
+{
+ UINT ret = ERROR_INSTALL_FAILURE;
bool is_com_initialized = SUCCEEDED(CoInitialize(NULL));
+ INSTALLSTATE component_installed, component_action;
+ TCHAR path[MAX_PATH];
+ DWORD path_len = _countof(path);
- ret = SHRegGetPath(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\S-1-5-18"),
- TEXT("ProfileImagePath"), path, 0);
+ ret = MsiGetComponentState(installer, TEXT("WireGuardExecutable"), &component_installed, &component_action);
if (ret != ERROR_SUCCESS) {
- log_errorf(installer, LOG_LEVEL_WARN, ret, TEXT("SHRegGetPath failed"));
+ log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("MsiGetComponentState(\"WireGuardExecutable\") failed"));
goto out;
}
- if (!PathAppend(path, TEXT("AppData\\Local\\WireGuard"))) {
- log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("PathAppend(\"%1\", \"AppData\\Local\\WireGuard\") failed"), path);
+ ret = MsiGetProperty(installer, TEXT("WireGuardFolder"), path, &path_len);
+ if (ret != ERROR_SUCCESS) {
+ log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("MsiGetProperty(\"WireGuardFolder\") failed"));
goto out;
}
- remove_directory_recursive(installer, path, 10);
+
+ if (component_action >= INSTALLSTATE_LOCAL) {
+ /* WireGuardExecutable component shall be installed. */
+ ret = MsiSetProperty(installer, TEXT("KillWireGuardProcesses"), path);
+ if (ret != ERROR_SUCCESS) {
+ log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("MsiSetProperty(\"KillWireGuardProcesses\") failed"));
+ goto out;
+ }
+ } else if (component_action >= INSTALLSTATE_REMOVED) {
+ /* WireGuardExecutable component shall be uninstalled. */
+ ret = MsiSetProperty(installer, TEXT("KillWireGuardProcesses"), path);
+ if (ret != ERROR_SUCCESS) {
+ log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("MsiSetProperty(\"KillWireGuardProcesses\") failed"));
+ goto out;
+ }
+ ret = MsiSetProperty(installer, TEXT("RemoveConfigFolder"), path);
+ if (ret != ERROR_SUCCESS) {
+ log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("MsiSetProperty(\"RemoveConfigFolder\") failed"));
+ goto out;
+ }
+ ret = MsiSetProperty(installer, TEXT("RemoveAdapters"), path);
+ if (ret != ERROR_SUCCESS) {
+ log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("MsiSetProperty(\"RemoveAdapters\") failed"));
+ goto out;
+ }
+ }
+ ret = ERROR_SUCCESS;
+
out:
if (is_com_initialized)
CoUninitialize();
- return ERROR_SUCCESS;
+ return ret == ERROR_SUCCESS ? ret : ERROR_INSTALL_FAILURE;
}
struct file_id { DWORD volume, index_high, index_low; };
@@ -299,36 +430,30 @@ static bool calculate_file_id(const TCHAR *path, struct file_id *id)
return true;
}
-static bool calculate_known_file_id(const KNOWNFOLDERID *known_folder, const TCHAR *file, struct file_id *id)
-{
- TCHAR *folder_path, process_path[MAX_PATH + 1];
- bool ret = false;
-
- if (SHGetKnownFolderPath(known_folder, KF_FLAG_DEFAULT, NULL, &folder_path) == S_OK) {
- if (PathCombine(process_path, folder_path, file)) {
- if (calculate_file_id(process_path, id))
- ret = true;
- }
- CoTaskMemFree(folder_path);
- }
- return ret;
-}
-
__declspec(dllexport) UINT __stdcall KillWireGuardProcesses(MSIHANDLE installer)
{
HANDLE snapshot, process;
PROCESSENTRY32 entry = { .dwSize = sizeof(PROCESSENTRY32) };
- TCHAR process_path[MAX_PATH + 1];
- DWORD process_path_len;
+ TCHAR process_path[MAX_PATH], executable[MAX_PATH];
+ DWORD process_path_len = _countof(process_path);
struct file_id file_ids[3], file_id;
size_t file_ids_len = 0;
bool is_com_initialized = SUCCEEDED(CoInitialize(NULL));
+ LSTATUS mret;
- if (calculate_known_file_id(&FOLDERID_System, TEXT("wg.exe"), &file_ids[file_ids_len]))
- ++file_ids_len;
- if (calculate_known_file_id(&FOLDERID_SystemX86, TEXT("wg.exe"), &file_ids[file_ids_len]))
+ mret = MsiGetProperty(installer, TEXT("CustomActionData"), process_path, &process_path_len);
+ if (mret != ERROR_SUCCESS) {
+ log_errorf(installer, LOG_LEVEL_WARN, mret, TEXT("MsiGetProperty(\"CustomActionData\") failed"));
+ goto out;
+ }
+ if (!process_path[0])
+ goto out;
+
+ log_messagef(installer, LOG_LEVEL_INFO, TEXT("Detecting running processes"));
+
+ if (PathCombine(executable, process_path, TEXT("wg.exe")) && calculate_file_id(executable, &file_ids[file_ids_len]))
++file_ids_len;
- if (calculate_known_file_id(&FOLDERID_ProgramFiles, TEXT("WireGuard\\wireguard.exe"), &file_ids[file_ids_len]))
+ if (PathCombine(executable, process_path, TEXT("wireguard.exe")) && calculate_file_id(executable, &file_ids[file_ids_len]))
++file_ids_len;
if (!file_ids_len)
goto out;
@@ -371,3 +496,147 @@ out:
CoUninitialize();
return ERROR_SUCCESS;
}
+
+static bool remove_directory_recursive(MSIHANDLE installer, TCHAR path[MAX_PATH], unsigned int max_depth)
+{
+ HANDLE find_handle;
+ WIN32_FIND_DATA find_data;
+ TCHAR *path_end;
+
+ if (!max_depth) {
+ log_messagef(installer, LOG_LEVEL_WARN, TEXT("Too many levels of nesting at \"%1\""), path);
+ return false;
+ }
+
+ path_end = path + _tcsnlen(path, MAX_PATH);
+ if (!PathAppend(path, TEXT("*.*"))) {
+ log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("PathAppend(\"%1\", \"*.*\") failed"), path);
+ return false;
+ }
+ find_handle = FindFirstFileEx(path, FindExInfoBasic, &find_data, FindExSearchNameMatch, NULL, 0);
+ if (find_handle == INVALID_HANDLE_VALUE) {
+ log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("FindFirstFileEx(\"%1\") failed"), path);
+ return false;
+ }
+ do {
+ if (find_data.cFileName[0] == TEXT('.') && (find_data.cFileName[1] == TEXT('\0') || (find_data.cFileName[1] == TEXT('.') && find_data.cFileName[2] == TEXT('\0'))))
+ continue;
+
+ path_end[0] = TEXT('\0');
+ if (!PathAppend(path, find_data.cFileName)) {
+ log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("PathAppend(\"%1\", \"%2\") failed"), path, find_data.cFileName);
+ continue;
+ }
+
+ if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ remove_directory_recursive(installer, path, max_depth - 1);
+ continue;
+ }
+
+ if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) && !SetFileAttributes(path, find_data.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY))
+ log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("SetFileAttributes(\"%1\") failed"), path);
+
+ if (DeleteFile(path))
+ log_messagef(installer, LOG_LEVEL_INFO, TEXT("Deleted \"%1\""), path);
+ else
+ log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("DeleteFile(\"%1\") failed"), path);
+ } while (FindNextFile(find_handle, &find_data));
+ FindClose(find_handle);
+
+ path_end[0] = TEXT('\0');
+ if (RemoveDirectory(path)) {
+ log_messagef(installer, LOG_LEVEL_INFO, TEXT("Removed \"%1\""), path);
+ return true;
+ } else {
+ log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("RemoveDirectory(\"%1\") failed"), path);
+ return false;
+ }
+}
+
+__declspec(dllexport) UINT __stdcall RemoveConfigFolder(MSIHANDLE installer)
+{
+ LSTATUS ret;
+ TCHAR path[MAX_PATH];
+ DWORD path_len = _countof(path);
+ bool is_com_initialized = SUCCEEDED(CoInitialize(NULL));
+
+ ret = MsiGetProperty(installer, TEXT("CustomActionData"), path, &path_len);
+ if (ret != ERROR_SUCCESS) {
+ log_errorf(installer, LOG_LEVEL_WARN, ret, TEXT("MsiGetProperty(\"CustomActionData\") failed"));
+ goto out;
+ }
+ if (!path[0] || !PathAppend(path, TEXT("Data")))
+ goto out;
+ remove_directory_recursive(installer, path, 10);
+ RegDeleteKey(HKEY_LOCAL_MACHINE, TEXT("Software\\WireGuard")); // Assumes no WOW.
+out:
+ if (is_com_initialized)
+ CoUninitialize();
+ return ERROR_SUCCESS;
+}
+
+__declspec(dllexport) UINT __stdcall RemoveAdapters(MSIHANDLE installer)
+{
+ UINT ret;
+ bool is_com_initialized = SUCCEEDED(CoInitialize(NULL));
+ TCHAR path[MAX_PATH];
+ DWORD path_len = _countof(path);
+ HANDLE pipe;
+ char buf[0x200];
+ DWORD offset = 0, size_read;
+ PROCESS_INFORMATION pi;
+ STARTUPINFO si = {
+ .cb = sizeof(STARTUPINFO),
+ .dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES,
+ .wShowWindow = SW_HIDE
+ };
+
+ ret = MsiGetProperty(installer, TEXT("CustomActionData"), path, &path_len);
+ if (ret != ERROR_SUCCESS) {
+ log_errorf(installer, LOG_LEVEL_WARN, ret, TEXT("MsiGetProperty(\"CustomActionData\") failed"));
+ goto out;
+ }
+ if (!path[0] || !PathAppend(path, TEXT("wireguard.exe")))
+ goto out;
+
+ if (!CreatePipe(&pipe, &si.hStdOutput, NULL, 0)) {
+ log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("CreatePipe failed"));
+ goto out;
+ }
+ if (!SetHandleInformation(si.hStdOutput, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) {
+ log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("SetHandleInformation failed"));
+ goto cleanup_pipe_w;
+ }
+ if (!CreateProcess(path, TEXT("wireguard /removealladapters"), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
+ log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("Failed to create \"%1\" process"), path);
+ goto cleanup_pipe_w;
+ }
+ CloseHandle(si.hStdOutput);
+ buf[sizeof(buf) - 1] = '\0';
+ while (ReadFile(pipe, buf + offset, sizeof(buf) - offset - 1, &size_read, NULL)) {
+ char *nl;
+ buf[offset + size_read] = '\0';
+ nl = strchr(buf, '\n');
+ if (!nl) {
+ offset = size_read;
+ continue;
+ }
+ nl[0] = '\0';
+ log_messagef(installer, LOG_LEVEL_INFO, TEXT("%1!hs!"), buf);
+ offset = strlen(&nl[1]);
+ memmove(buf, &nl[1], offset);
+ }
+ WaitForSingleObject(pi.hProcess, INFINITE);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ goto cleanup_pipe_r;
+
+cleanup_pipe_w:
+ CloseHandle(si.hStdOutput);
+cleanup_pipe_r:
+ CloseHandle(pipe);
+out:
+ if (is_com_initialized)
+ CoUninitialize();
+ return ERROR_SUCCESS;
+}
diff --git a/installer/fetcher/.gitignore b/installer/fetcher/.gitignore
new file mode 100644
index 00000000..9e4c427d
--- /dev/null
+++ b/installer/fetcher/.gitignore
@@ -0,0 +1,6 @@
+*.ico
+*.o
+*.d
+*.exe
+*.pro
+*.pro.user
diff --git a/installer/fetcher/Makefile b/installer/fetcher/Makefile
new file mode 100644
index 00000000..051e4a6b
--- /dev/null
+++ b/installer/fetcher/Makefile
@@ -0,0 +1,42 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+
+CFLAGS ?= -Os
+DEPLOYMENT_HOST ?= winvm
+DEPLOYMENT_PATH ?= Desktop
+
+CFLAGS += -std=gnu11 -DWINVER=0x0601 -D_WIN32_WINNT=0x0601 -flto
+CFLAGS += -Wall -Wextra
+CFLAGS += -MMD -MP
+LDLIBS += -lkernel32 -lwinhttp -lntdll -lshlwapi -lmsi -lcomctl32 -luser32 -lshell32 -lwintrust -lbcrypt
+LDFLAGS += -s -flto -Wl,--dynamicbase -Wl,--nxcompat -Wl,--tsaware -mwindows
+LDFLAGS += -Wl,--major-os-version=6 -Wl,--minor-os-version=1 -Wl,--major-subsystem-version=6 -Wl,--minor-subsystem-version=1
+# The use of -Wl,/delayload: here implies we're using llvm-mingw
+LDFLAGS += -Wl,/delayload:winhttp.dll -Wl,/delayload:msi.dll -Wl,/delayload:wintrust.dll -Wl,/delayload:advapi32.dll -Wl,/delayload:shell32.dll -Wl,/delayload:shlwapi.dll -Wl,/delayload:gdi32.dll -Wl,/delayload:user32.dll -Wl,/delayload:comctl32.dll -Wl,/delayload:bcrypt.dll
+TARGET := wireguard-installer.exe
+CC := i686-w64-mingw32-clang
+WINDRES := i686-w64-mingw32-windres
+
+$(TARGET): $(sort $(patsubst %.c,%.o,$(wildcard *.c))) resources.o
+ $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
+
+%.ico: %.svg
+ convert -background none $< -define icon:auto-resize="64,32,16" -compress zip $@
+
+resources.o: resources.rc icon.ico manifest.xml
+ $(WINDRES) -O coff -c 65001 -i $< -o $@
+
+clean:
+ $(RM) $(TARGET) *.o *.d *.ico
+
+deploy: $(TARGET)
+ scp $< $(DEPLOYMENT_HOST):$(DEPLOYMENT_PATH)
+
+sign: deploy
+ ssh $(DEPLOYMENT_HOST) '"C:\Program Files (x86)\Windows Kits\10\App Certification Kit\signtool.exe"' sign /sha1 $(SIGNING_CERTIFICATE) /fd sha256 /tr $(TIMESTAMP_SERVER) /td sha256 /d '"WireGuard Installer"' '$(DEPLOYMENT_PATH)\$(TARGET)'
+ scp -T '$(DEPLOYMENT_HOST):$(DEPLOYMENT_PATH)\$(TARGET)' .
+
+.PHONY: clean deploy sign
+
+-include *.d
diff --git a/installer/fetcher/constants.h b/installer/fetcher/constants.h
new file mode 100644
index 00000000..57a28a20
--- /dev/null
+++ b/installer/fetcher/constants.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2020 Jason A. Donenfeld. All Rights Reserved.
+ */
+
+#ifndef _CONSTANTS_H
+#define _CONSTANTS_H
+
+#define release_public_key_base64 "RWRNqGKtBXftKTKPpBPGDMe8jHLnFQ0EdRy8Wg0apV6vTDFLAODD83G4"
+#define msi_arch_prefix "wireguard-%s-"
+#define msi_suffix ".msi"
+#define server "download.wireguard.com"
+#define port (443)
+#define latest_version_file "latest.sig"
+#define msi_path "/windows-client/"
+
+#endif
diff --git a/installer/fetcher/crypto.c b/installer/fetcher/crypto.c
new file mode 100644
index 00000000..154eac37
--- /dev/null
+++ b/installer/fetcher/crypto.c
@@ -0,0 +1,2252 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Jason A. Donenfeld. All Rights Reserved.
+ * Copyright (c) 2020, Google Inc.
+ */
+
+#include "crypto.h"
+#include <stdint.h>
+#include <string.h>
+#include <winternl.h>
+#include <bcrypt.h>
+
+#if REG_DWORD == REG_DWORD_LITTLE_ENDIAN
+#define swap_le64(x) (x)
+#define swap_le32(x) (x)
+#elif REG_DWORD == REG_DWORD_BIG_ENDIAN
+#define swap_le64(x) __builtin_bswap64(x)
+#define swap_le32(x) __builtin_bswap32(x)
+#endif
+
+static void store_le64(uint8_t *dst, uint64_t src)
+{
+ src = swap_le64(src);
+ __builtin_memcpy(dst, &src, sizeof(src));
+}
+
+static uint64_t load_le64(const uint8_t *src)
+{
+ uint64_t dst;
+ __builtin_memcpy(&dst, src, sizeof(dst));
+ return swap_le64(dst);
+}
+
+static uint32_t load_le24(const uint8_t *in)
+{
+ uint32_t dst;
+ dst = (uint32_t)in[0];
+ dst |= ((uint32_t)in[1]) << 8;
+ dst |= ((uint32_t)in[2]) << 16;
+ return dst;
+}
+
+static uint32_t load_le32(const uint8_t *src)
+{
+ uint32_t dst;
+ __builtin_memcpy(&dst, src, sizeof(dst));
+ return swap_le32(dst);
+}
+
+static uint64_t ror64(uint64_t i, unsigned int s)
+{
+ return (i >> (s & 63)) | (i << ((-s) & 63));
+}
+
+static inline uint32_t value_barrier_u32(uint32_t a)
+{
+ __asm__("" : "+r"(a) : /* no inputs */);
+ return a;
+}
+
+static int memcmp_ct(const void *first, const void *second, size_t len)
+{
+ const uint8_t *a = first;
+ const uint8_t *b = second;
+ uint8_t diff = 0;
+
+ for (size_t i = 0; i < len; ++i) {
+ diff |= a[i] ^ b[i];
+ __asm__("" : "+r"(diff) : /* no inputs */);
+ }
+
+ return diff;
+}
+
+/*
+ * The function fiat_25519_addcarryx_u26 is an addition with carry.
+ * Postconditions:
+ * out1 = (arg1 + arg2 + arg3) mod 2^26
+ * out2 = ⌊(arg1 + arg2 + arg3) / 2^26⌋
+ *
+ * Input Bounds:
+ * arg1: [0x0 ~> 0x1]
+ * arg2: [0x0 ~> 0x3ffffff]
+ * arg3: [0x0 ~> 0x3ffffff]
+ * Output Bounds:
+ * out1: [0x0 ~> 0x3ffffff]
+ * out2: [0x0 ~> 0x1]
+ */
+static void fiat_25519_addcarryx_u26(uint32_t *out1, uint8_t *out2,
+ uint8_t arg1, uint32_t arg2, uint32_t arg3)
+{
+ uint32_t x1 = ((arg1 + arg2) + arg3);
+ uint32_t x2 = (x1 & UINT32_C(0x3ffffff));
+ uint8_t x3 = (uint8_t)(x1 >> 26);
+ *out1 = x2;
+ *out2 = x3;
+}
+
+/*
+ * The function fiat_25519_subborrowx_u26 is a subtraction with borrow.
+ * Postconditions:
+ * out1 = (-arg1 + arg2 + -arg3) mod 2^26
+ * out2 = -⌊(-arg1 + arg2 + -arg3) / 2^26⌋
+ *
+ * Input Bounds:
+ * arg1: [0x0 ~> 0x1]
+ * arg2: [0x0 ~> 0x3ffffff]
+ * arg3: [0x0 ~> 0x3ffffff]
+ * Output Bounds:
+ * out1: [0x0 ~> 0x3ffffff]
+ * out2: [0x0 ~> 0x1]
+ */
+static void fiat_25519_subborrowx_u26(uint32_t *out1, uint8_t *out2,
+ uint8_t arg1, uint32_t arg2,
+ uint32_t arg3)
+{
+ int32_t x1 = ((int32_t)(arg2 - arg1) - (int32_t)arg3);
+ int8_t x2 = (int8_t)(x1 >> 26);
+ uint32_t x3 = (x1 & UINT32_C(0x3ffffff));
+ *out1 = x3;
+ *out2 = (uint8_t)(0x0 - x2);
+}
+
+/*
+ * The function fiat_25519_addcarryx_u25 is an addition with carry.
+ * Postconditions:
+ * out1 = (arg1 + arg2 + arg3) mod 2^25
+ * out2 = ⌊(arg1 + arg2 + arg3) / 2^25⌋
+ *
+ * Input Bounds:
+ * arg1: [0x0 ~> 0x1]
+ * arg2: [0x0 ~> 0x1ffffff]
+ * arg3: [0x0 ~> 0x1ffffff]
+ * Output Bounds:
+ * out1: [0x0 ~> 0x1ffffff]
+ * out2: [0x0 ~> 0x1]
+ */
+static void fiat_25519_addcarryx_u25(uint32_t *out1, uint8_t *out2,
+ uint8_t arg1, uint32_t arg2, uint32_t arg3)
+{
+ uint32_t x1 = ((arg1 + arg2) + arg3);
+ uint32_t x2 = (x1 & UINT32_C(0x1ffffff));
+ uint8_t x3 = (uint8_t)(x1 >> 25);
+ *out1 = x2;
+ *out2 = x3;
+}
+
+/*
+ * The function fiat_25519_subborrowx_u25 is a subtraction with borrow.
+ * Postconditions:
+ * out1 = (-arg1 + arg2 + -arg3) mod 2^25
+ * out2 = -⌊(-arg1 + arg2 + -arg3) / 2^25⌋
+ *
+ * Input Bounds:
+ * arg1: [0x0 ~> 0x1]
+ * arg2: [0x0 ~> 0x1ffffff]
+ * arg3: [0x0 ~> 0x1ffffff]
+ * Output Bounds:
+ * out1: [0x0 ~> 0x1ffffff]
+ * out2: [0x0 ~> 0x1]
+ */
+static void fiat_25519_subborrowx_u25(uint32_t *out1, uint8_t *out2,
+ uint8_t arg1, uint32_t arg2,
+ uint32_t arg3)
+{
+ int32_t x1 = ((int32_t)(arg2 - arg1) - (int32_t)arg3);
+ int8_t x2 = (int8_t)(x1 >> 25);
+ uint32_t x3 = (x1 & UINT32_C(0x1ffffff));
+ *out1 = x3;
+ *out2 = (uint8_t)(0x0 - x2);
+}
+
+/*
+ * The function fiat_25519_cmovznz_u32 is a single-word conditional move.
+ * Postconditions:
+ * out1 = (if arg1 = 0 then arg2 else arg3)
+ *
+ * Input Bounds:
+ * arg1: [0x0 ~> 0x1]
+ * arg2: [0x0 ~> 0xffffffff]
+ * arg3: [0x0 ~> 0xffffffff]
+ * Output Bounds:
+ * out1: [0x0 ~> 0xffffffff]
+ */
+static void fiat_25519_cmovznz_u32(uint32_t *out1, uint8_t arg1, uint32_t arg2,
+ uint32_t arg3)
+{
+ uint8_t x1 = (!(!arg1));
+ uint32_t x2 = ((int8_t)(0x0 - x1) & UINT32_C(0xffffffff));
+ // Note this line has been patched from the synthesized code to add value
+ // barriers.
+ //
+ // Clang recognizes this pattern as a select. While it usually transforms it
+ // to a cmov, it sometimes further transforms it into a branch, which we do
+ // not want.
+ uint32_t x3 = ((value_barrier_u32(x2) & arg3) |
+ (value_barrier_u32(~x2) & arg2));
+ *out1 = x3;
+}
+
+/*
+ * The function fiat_25519_carry_mul multiplies two field elements and reduces the result.
+ * Postconditions:
+ * eval out1 mod m = (eval arg1 * eval arg2) mod m
+ *
+ * Input Bounds:
+ * arg1: [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]]
+ * arg2: [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]]
+ * Output Bounds:
+ * out1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]]
+ */
+static void fiat_25519_carry_mul(uint32_t out1[10], const uint32_t arg1[10],
+ const uint32_t arg2[10])
+{
+ uint64_t x1 = ((uint64_t)(arg1[9]) * ((arg2[9]) * UINT8_C(0x26)));
+ uint64_t x2 = ((uint64_t)(arg1[9]) * ((arg2[8]) * UINT8_C(0x13)));
+ uint64_t x3 = ((uint64_t)(arg1[9]) * ((arg2[7]) * UINT8_C(0x26)));
+ uint64_t x4 = ((uint64_t)(arg1[9]) * ((arg2[6]) * UINT8_C(0x13)));
+ uint64_t x5 = ((uint64_t)(arg1[9]) * ((arg2[5]) * UINT8_C(0x26)));
+ uint64_t x6 = ((uint64_t)(arg1[9]) * ((arg2[4]) * UINT8_C(0x13)));
+ uint64_t x7 = ((uint64_t)(arg1[9]) * ((arg2[3]) * UINT8_C(0x26)));
+ uint64_t x8 = ((uint64_t)(arg1[9]) * ((arg2[2]) * UINT8_C(0x13)));
+ uint64_t x9 = ((uint64_t)(arg1[9]) * ((arg2[1]) * UINT8_C(0x26)));
+ uint64_t x10 = ((uint64_t)(arg1[8]) * ((arg2[9]) * UINT8_C(0x13)));
+ uint64_t x11 = ((uint64_t)(arg1[8]) * ((arg2[8]) * UINT8_C(0x13)));
+ uint64_t x12 = ((uint64_t)(arg1[8]) * ((arg2[7]) * UINT8_C(0x13)));
+ uint64_t x13 = ((uint64_t)(arg1[8]) * ((arg2[6]) * UINT8_C(0x13)));
+ uint64_t x14 = ((uint64_t)(arg1[8]) * ((arg2[5]) * UINT8_C(0x13)));
+ uint64_t x15 = ((uint64_t)(arg1[8]) * ((arg2[4]) * UINT8_C(0x13)));
+ uint64_t x16 = ((uint64_t)(arg1[8]) * ((arg2[3]) * UINT8_C(0x13)));
+ uint64_t x17 = ((uint64_t)(arg1[8]) * ((arg2[2]) * UINT8_C(0x13)));
+ uint64_t x18 = ((uint64_t)(arg1[7]) * ((arg2[9]) * UINT8_C(0x26)));
+ uint64_t x19 = ((uint64_t)(arg1[7]) * ((arg2[8]) * UINT8_C(0x13)));
+ uint64_t x20 = ((uint64_t)(arg1[7]) * ((arg2[7]) * UINT8_C(0x26)));
+ uint64_t x21 = ((uint64_t)(arg1[7]) * ((arg2[6]) * UINT8_C(0x13)));
+ uint64_t x22 = ((uint64_t)(arg1[7]) * ((arg2[5]) * UINT8_C(0x26)));
+ uint64_t x23 = ((uint64_t)(arg1[7]) * ((arg2[4]) * UINT8_C(0x13)));
+ uint64_t x24 = ((uint64_t)(arg1[7]) * ((arg2[3]) * UINT8_C(0x26)));
+ uint64_t x25 = ((uint64_t)(arg1[6]) * ((arg2[9]) * UINT8_C(0x13)));
+ uint64_t x26 = ((uint64_t)(arg1[6]) * ((arg2[8]) * UINT8_C(0x13)));
+ uint64_t x27 = ((uint64_t)(arg1[6]) * ((arg2[7]) * UINT8_C(0x13)));
+ uint64_t x28 = ((uint64_t)(arg1[6]) * ((arg2[6]) * UINT8_C(0x13)));
+ uint64_t x29 = ((uint64_t)(arg1[6]) * ((arg2[5]) * UINT8_C(0x13)));
+ uint64_t x30 = ((uint64_t)(arg1[6]) * ((arg2[4]) * UINT8_C(0x13)));
+ uint64_t x31 = ((uint64_t)(arg1[5]) * ((arg2[9]) * UINT8_C(0x26)));
+ uint64_t x32 = ((uint64_t)(arg1[5]) * ((arg2[8]) * UINT8_C(0x13)));
+ uint64_t x33 = ((uint64_t)(arg1[5]) * ((arg2[7]) * UINT8_C(0x26)));
+ uint64_t x34 = ((uint64_t)(arg1[5]) * ((arg2[6]) * UINT8_C(0x13)));
+ uint64_t x35 = ((uint64_t)(arg1[5]) * ((arg2[5]) * UINT8_C(0x26)));
+ uint64_t x36 = ((uint64_t)(arg1[4]) * ((arg2[9]) * UINT8_C(0x13)));
+ uint64_t x37 = ((uint64_t)(arg1[4]) * ((arg2[8]) * UINT8_C(0x13)));
+ uint64_t x38 = ((uint64_t)(arg1[4]) * ((arg2[7]) * UINT8_C(0x13)));
+ uint64_t x39 = ((uint64_t)(arg1[4]) * ((arg2[6]) * UINT8_C(0x13)));
+ uint64_t x40 = ((uint64_t)(arg1[3]) * ((arg2[9]) * UINT8_C(0x26)));
+ uint64_t x41 = ((uint64_t)(arg1[3]) * ((arg2[8]) * UINT8_C(0x13)));
+ uint64_t x42 = ((uint64_t)(arg1[3]) * ((arg2[7]) * UINT8_C(0x26)));
+ uint64_t x43 = ((uint64_t)(arg1[2]) * ((arg2[9]) * UINT8_C(0x13)));
+ uint64_t x44 = ((uint64_t)(arg1[2]) * ((arg2[8]) * UINT8_C(0x13)));
+ uint64_t x45 = ((uint64_t)(arg1[1]) * ((arg2[9]) * UINT8_C(0x26)));
+ uint64_t x46 = ((uint64_t)(arg1[9]) * (arg2[0]));
+ uint64_t x47 = ((uint64_t)(arg1[8]) * (arg2[1]));
+ uint64_t x48 = ((uint64_t)(arg1[8]) * (arg2[0]));
+ uint64_t x49 = ((uint64_t)(arg1[7]) * (arg2[2]));
+ uint64_t x50 = ((uint64_t)(arg1[7]) * ((arg2[1]) * 0x2));
+ uint64_t x51 = ((uint64_t)(arg1[7]) * (arg2[0]));
+ uint64_t x52 = ((uint64_t)(arg1[6]) * (arg2[3]));
+ uint64_t x53 = ((uint64_t)(arg1[6]) * (arg2[2]));
+ uint64_t x54 = ((uint64_t)(arg1[6]) * (arg2[1]));
+ uint64_t x55 = ((uint64_t)(arg1[6]) * (arg2[0]));
+ uint64_t x56 = ((uint64_t)(arg1[5]) * (arg2[4]));
+ uint64_t x57 = ((uint64_t)(arg1[5]) * ((arg2[3]) * 0x2));
+ uint64_t x58 = ((uint64_t)(arg1[5]) * (arg2[2]));
+ uint64_t x59 = ((uint64_t)(arg1[5]) * ((arg2[1]) * 0x2));
+ uint64_t x60 = ((uint64_t)(arg1[5]) * (arg2[0]));
+ uint64_t x61 = ((uint64_t)(arg1[4]) * (arg2[5]));
+ uint64_t x62 = ((uint64_t)(arg1[4]) * (arg2[4]));
+ uint64_t x63 = ((uint64_t)(arg1[4]) * (arg2[3]));
+ uint64_t x64 = ((uint64_t)(arg1[4]) * (arg2[2]));
+ uint64_t x65 = ((uint64_t)(arg1[4]) * (arg2[1]));
+ uint64_t x66 = ((uint64_t)(arg1[4]) * (arg2[0]));
+ uint64_t x67 = ((uint64_t)(arg1[3]) * (arg2[6]));
+ uint64_t x68 = ((uint64_t)(arg1[3]) * ((arg2[5]) * 0x2));
+ uint64_t x69 = ((uint64_t)(arg1[3]) * (arg2[4]));
+ uint64_t x70 = ((uint64_t)(arg1[3]) * ((arg2[3]) * 0x2));
+ uint64_t x71 = ((uint64_t)(arg1[3]) * (arg2[2]));
+ uint64_t x72 = ((uint64_t)(arg1[3]) * ((arg2[1]) * 0x2));
+ uint64_t x73 = ((uint64_t)(arg1[3]) * (arg2[0]));
+ uint64_t x74 = ((uint64_t)(arg1[2]) * (arg2[7]));
+ uint64_t x75 = ((uint64_t)(arg1[2]) * (arg2[6]));
+ uint64_t x76 = ((uint64_t)(arg1[2]) * (arg2[5]));
+ uint64_t x77 = ((uint64_t)(arg1[2]) * (arg2[4]));
+ uint64_t x78 = ((uint64_t)(arg1[2]) * (arg2[3]));
+ uint64_t x79 = ((uint64_t)(arg1[2]) * (arg2[2]));
+ uint64_t x80 = ((uint64_t)(arg1[2]) * (arg2[1]));
+ uint64_t x81 = ((uint64_t)(arg1[2]) * (arg2[0]));
+ uint64_t x82 = ((uint64_t)(arg1[1]) * (arg2[8]));
+ uint64_t x83 = ((uint64_t)(arg1[1]) * ((arg2[7]) * 0x2));
+ uint64_t x84 = ((uint64_t)(arg1[1]) * (arg2[6]));
+ uint64_t x85 = ((uint64_t)(arg1[1]) * ((arg2[5]) * 0x2));
+ uint64_t x86 = ((uint64_t)(arg1[1]) * (arg2[4]));
+ uint64_t x87 = ((uint64_t)(arg1[1]) * ((arg2[3]) * 0x2));
+ uint64_t x88 = ((uint64_t)(arg1[1]) * (arg2[2]));
+ uint64_t x89 = ((uint64_t)(arg1[1]) * ((arg2[1]) * 0x2));
+ uint64_t x90 = ((uint64_t)(arg1[1]) * (arg2[0]));
+ uint64_t x91 = ((uint64_t)(arg1[0]) * (arg2[9]));
+ uint64_t x92 = ((uint64_t)(arg1[0]) * (arg2[8]));
+ uint64_t x93 = ((uint64_t)(arg1[0]) * (arg2[7]));
+ uint64_t x94 = ((uint64_t)(arg1[0]) * (arg2[6]));
+ uint64_t x95 = ((uint64_t)(arg1[0]) * (arg2[5]));
+ uint64_t x96 = ((uint64_t)(arg1[0]) * (arg2[4]));
+ uint64_t x97 = ((uint64_t)(arg1[0]) * (arg2[3]));
+ uint64_t x98 = ((uint64_t)(arg1[0]) * (arg2[2]));
+ uint64_t x99 = ((uint64_t)(arg1[0]) * (arg2[1]));
+ uint64_t x100 = ((uint64_t)(arg1[0]) * (arg2[0]));
+ uint64_t x101 =
+ (x100 +
+ (x45 +
+ (x44 + (x42 + (x39 + (x35 + (x30 + (x24 + (x17 + x9)))))))));
+ uint64_t x102 = (x101 >> 26);
+ uint32_t x103 = (uint32_t)(x101 & UINT32_C(0x3ffffff));
+ uint64_t x104 =
+ (x91 +
+ (x82 +
+ (x74 + (x67 + (x61 + (x56 + (x52 + (x49 + (x47 + x46)))))))));
+ uint64_t x105 =
+ (x92 +
+ (x83 +
+ (x75 + (x68 + (x62 + (x57 + (x53 + (x50 + (x48 + x1)))))))));
+ uint64_t x106 =
+ (x93 +
+ (x84 +
+ (x76 + (x69 + (x63 + (x58 + (x54 + (x51 + (x10 + x2)))))))));
+ uint64_t x107 =
+ (x94 +
+ (x85 +
+ (x77 + (x70 + (x64 + (x59 + (x55 + (x18 + (x11 + x3)))))))));
+ uint64_t x108 =
+ (x95 +
+ (x86 +
+ (x78 + (x71 + (x65 + (x60 + (x25 + (x19 + (x12 + x4)))))))));
+ uint64_t x109 =
+ (x96 +
+ (x87 +
+ (x79 + (x72 + (x66 + (x31 + (x26 + (x20 + (x13 + x5)))))))));
+ uint64_t x110 =
+ (x97 +
+ (x88 +
+ (x80 + (x73 + (x36 + (x32 + (x27 + (x21 + (x14 + x6)))))))));
+ uint64_t x111 =
+ (x98 +
+ (x89 +
+ (x81 + (x40 + (x37 + (x33 + (x28 + (x22 + (x15 + x7)))))))));
+ uint64_t x112 =
+ (x99 +
+ (x90 +
+ (x43 + (x41 + (x38 + (x34 + (x29 + (x23 + (x16 + x8)))))))));
+ uint64_t x113 = (x102 + x112);
+ uint64_t x114 = (x113 >> 25);
+ uint32_t x115 = (uint32_t)(x113 & UINT32_C(0x1ffffff));
+ uint64_t x116 = (x114 + x111);
+ uint64_t x117 = (x116 >> 26);
+ uint32_t x118 = (uint32_t)(x116 & UINT32_C(0x3ffffff));
+ uint64_t x119 = (x117 + x110);
+ uint64_t x120 = (x119 >> 25);
+ uint32_t x121 = (uint32_t)(x119 & UINT32_C(0x1ffffff));
+ uint64_t x122 = (x120 + x109);
+ uint64_t x123 = (x122 >> 26);
+ uint32_t x124 = (uint32_t)(x122 & UINT32_C(0x3ffffff));
+ uint64_t x125 = (x123 + x108);
+ uint64_t x126 = (x125 >> 25);
+ uint32_t x127 = (uint32_t)(x125 & UINT32_C(0x1ffffff));
+ uint64_t x128 = (x126 + x107);
+ uint64_t x129 = (x128 >> 26);
+ uint32_t x130 = (uint32_t)(x128 & UINT32_C(0x3ffffff));
+ uint64_t x131 = (x129 + x106);
+ uint64_t x132 = (x131 >> 25);
+ uint32_t x133 = (uint32_t)(x131 & UINT32_C(0x1ffffff));
+ uint64_t x134 = (x132 + x105);
+ uint64_t x135 = (x134 >> 26);
+ uint32_t x136 = (uint32_t)(x134 & UINT32_C(0x3ffffff));
+ uint64_t x137 = (x135 + x104);
+ uint64_t x138 = (x137 >> 25);
+ uint32_t x139 = (uint32_t)(x137 & UINT32_C(0x1ffffff));
+ uint64_t x140 = (x138 * UINT8_C(0x13));
+ uint64_t x141 = (x103 + x140);
+ uint32_t x142 = (uint32_t)(x141 >> 26);
+ uint32_t x143 = (uint32_t)(x141 & UINT32_C(0x3ffffff));
+ uint32_t x144 = (x142 + x115);
+ uint8_t x145 = (uint8_t)(x144 >> 25);
+ uint32_t x146 = (x144 & UINT32_C(0x1ffffff));
+ uint32_t x147 = (x145 + x118);
+ out1[0] = x143;
+ out1[1] = x146;
+ out1[2] = x147;
+ out1[3] = x121;
+ out1[4] = x124;
+ out1[5] = x127;
+ out1[6] = x130;
+ out1[7] = x133;
+ out1[8] = x136;
+ out1[9] = x139;
+}
+
+/*
+ * The function fiat_25519_carry_square squares a field element and reduces the result.
+ * Postconditions:
+ * eval out1 mod m = (eval arg1 * eval arg1) mod m
+ *
+ * Input Bounds:
+ * arg1: [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]]
+ * Output Bounds:
+ * out1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]]
+ */
+static void fiat_25519_carry_square(uint32_t out1[10], const uint32_t arg1[10])
+{
+ uint32_t x1 = ((arg1[9]) * UINT8_C(0x13));
+ uint32_t x2 = (x1 * 0x2);
+ uint32_t x3 = ((arg1[9]) * 0x2);
+ uint32_t x4 = ((arg1[8]) * UINT8_C(0x13));
+ uint64_t x5 = ((uint64_t)x4 * 0x2);
+ uint32_t x6 = ((arg1[8]) * 0x2);
+ uint32_t x7 = ((arg1[7]) * UINT8_C(0x13));
+ uint32_t x8 = (x7 * 0x2);
+ uint32_t x9 = ((arg1[7]) * 0x2);
+ uint32_t x10 = ((arg1[6]) * UINT8_C(0x13));
+ uint64_t x11 = ((uint64_t)x10 * 0x2);
+ uint32_t x12 = ((arg1[6]) * 0x2);
+ uint32_t x13 = ((arg1[5]) * UINT8_C(0x13));
+ uint32_t x14 = ((arg1[5]) * 0x2);
+ uint32_t x15 = ((arg1[4]) * 0x2);
+ uint32_t x16 = ((arg1[3]) * 0x2);
+ uint32_t x17 = ((arg1[2]) * 0x2);
+ uint32_t x18 = ((arg1[1]) * 0x2);
+ uint64_t x19 = ((uint64_t)(arg1[9]) * (x1 * 0x2));
+ uint64_t x20 = ((uint64_t)(arg1[8]) * x2);
+ uint64_t x21 = ((uint64_t)(arg1[8]) * x4);
+ uint64_t x22 = ((arg1[7]) * ((uint64_t)x2 * 0x2));
+ uint64_t x23 = ((arg1[7]) * x5);
+ uint64_t x24 = ((uint64_t)(arg1[7]) * (x7 * 0x2));
+ uint64_t x25 = ((uint64_t)(arg1[6]) * x2);
+ uint64_t x26 = ((arg1[6]) * x5);
+ uint64_t x27 = ((uint64_t)(arg1[6]) * x8);
+ uint64_t x28 = ((uint64_t)(arg1[6]) * x10);
+ uint64_t x29 = ((arg1[5]) * ((uint64_t)x2 * 0x2));
+ uint64_t x30 = ((arg1[5]) * x5);
+ uint64_t x31 = ((arg1[5]) * ((uint64_t)x8 * 0x2));
+ uint64_t x32 = ((arg1[5]) * x11);
+ uint64_t x33 = ((uint64_t)(arg1[5]) * (x13 * 0x2));
+ uint64_t x34 = ((uint64_t)(arg1[4]) * x2);
+ uint64_t x35 = ((arg1[4]) * x5);
+ uint64_t x36 = ((uint64_t)(arg1[4]) * x8);
+ uint64_t x37 = ((arg1[4]) * x11);
+ uint64_t x38 = ((uint64_t)(arg1[4]) * x14);
+ uint64_t x39 = ((uint64_t)(arg1[4]) * (arg1[4]));
+ uint64_t x40 = ((arg1[3]) * ((uint64_t)x2 * 0x2));
+ uint64_t x41 = ((arg1[3]) * x5);
+ uint64_t x42 = ((arg1[3]) * ((uint64_t)x8 * 0x2));
+ uint64_t x43 = ((uint64_t)(arg1[3]) * x12);
+ uint64_t x44 = ((uint64_t)(arg1[3]) * (x14 * 0x2));
+ uint64_t x45 = ((uint64_t)(arg1[3]) * x15);
+ uint64_t x46 = ((uint64_t)(arg1[3]) * ((arg1[3]) * 0x2));
+ uint64_t x47 = ((uint64_t)(arg1[2]) * x2);
+ uint64_t x48 = ((arg1[2]) * x5);
+ uint64_t x49 = ((uint64_t)(arg1[2]) * x9);
+ uint64_t x50 = ((uint64_t)(arg1[2]) * x12);
+ uint64_t x51 = ((uint64_t)(arg1[2]) * x14);
+ uint64_t x52 = ((uint64_t)(arg1[2]) * x15);
+ uint64_t x53 = ((uint64_t)(arg1[2]) * x16);
+ uint64_t x54 = ((uint64_t)(arg1[2]) * (arg1[2]));
+ uint64_t x55 = ((arg1[1]) * ((uint64_t)x2 * 0x2));
+ uint64_t x56 = ((uint64_t)(arg1[1]) * x6);
+ uint64_t x57 = ((uint64_t)(arg1[1]) * (x9 * 0x2));
+ uint64_t x58 = ((uint64_t)(arg1[1]) * x12);
+ uint64_t x59 = ((uint64_t)(arg1[1]) * (x14 * 0x2));
+ uint64_t x60 = ((uint64_t)(arg1[1]) * x15);
+ uint64_t x61 = ((uint64_t)(arg1[1]) * (x16 * 0x2));
+ uint64_t x62 = ((uint64_t)(arg1[1]) * x17);
+ uint64_t x63 = ((uint64_t)(arg1[1]) * ((arg1[1]) * 0x2));
+ uint64_t x64 = ((uint64_t)(arg1[0]) * x3);
+ uint64_t x65 = ((uint64_t)(arg1[0]) * x6);
+ uint64_t x66 = ((uint64_t)(arg1[0]) * x9);
+ uint64_t x67 = ((uint64_t)(arg1[0]) * x12);
+ uint64_t x68 = ((uint64_t)(arg1[0]) * x14);
+ uint64_t x69 = ((uint64_t)(arg1[0]) * x15);
+ uint64_t x70 = ((uint64_t)(arg1[0]) * x16);
+ uint64_t x71 = ((uint64_t)(arg1[0]) * x17);
+ uint64_t x72 = ((uint64_t)(arg1[0]) * x18);
+ uint64_t x73 = ((uint64_t)(arg1[0]) * (arg1[0]));
+ uint64_t x74 = (x73 + (x55 + (x48 + (x42 + (x37 + x33)))));
+ uint64_t x75 = (x74 >> 26);
+ uint32_t x76 = (uint32_t)(x74 & UINT32_C(0x3ffffff));
+ uint64_t x77 = (x64 + (x56 + (x49 + (x43 + x38))));
+ uint64_t x78 = (x65 + (x57 + (x50 + (x44 + (x39 + x19)))));
+ uint64_t x79 = (x66 + (x58 + (x51 + (x45 + x20))));
+ uint64_t x80 = (x67 + (x59 + (x52 + (x46 + (x22 + x21)))));
+ uint64_t x81 = (x68 + (x60 + (x53 + (x25 + x23))));
+ uint64_t x82 = (x69 + (x61 + (x54 + (x29 + (x26 + x24)))));
+ uint64_t x83 = (x70 + (x62 + (x34 + (x30 + x27))));
+ uint64_t x84 = (x71 + (x63 + (x40 + (x35 + (x31 + x28)))));
+ uint64_t x85 = (x72 + (x47 + (x41 + (x36 + x32))));
+ uint64_t x86 = (x75 + x85);
+ uint64_t x87 = (x86 >> 25);
+ uint32_t x88 = (uint32_t)(x86 & UINT32_C(0x1ffffff));
+ uint64_t x89 = (x87 + x84);
+ uint64_t x90 = (x89 >> 26);
+ uint32_t x91 = (uint32_t)(x89 & UINT32_C(0x3ffffff));
+ uint64_t x92 = (x90 + x83);
+ uint64_t x93 = (x92 >> 25);
+ uint32_t x94 = (uint32_t)(x92 & UINT32_C(0x1ffffff));
+ uint64_t x95 = (x93 + x82);
+ uint64_t x96 = (x95 >> 26);
+ uint32_t x97 = (uint32_t)(x95 & UINT32_C(0x3ffffff));
+ uint64_t x98 = (x96 + x81);
+ uint64_t x99 = (x98 >> 25);
+ uint32_t x100 = (uint32_t)(x98 & UINT32_C(0x1ffffff));
+ uint64_t x101 = (x99 + x80);
+ uint64_t x102 = (x101 >> 26);
+ uint32_t x103 = (uint32_t)(x101 & UINT32_C(0x3ffffff));
+ uint64_t x104 = (x102 + x79);
+ uint64_t x105 = (x104 >> 25);
+ uint32_t x106 = (uint32_t)(x104 & UINT32_C(0x1ffffff));
+ uint64_t x107 = (x105 + x78);
+ uint64_t x108 = (x107 >> 26);
+ uint32_t x109 = (uint32_t)(x107 & UINT32_C(0x3ffffff));
+ uint64_t x110 = (x108 + x77);
+ uint64_t x111 = (x110 >> 25);
+ uint32_t x112 = (uint32_t)(x110 & UINT32_C(0x1ffffff));
+ uint64_t x113 = (x111 * UINT8_C(0x13));
+ uint64_t x114 = (x76 + x113);
+ uint32_t x115 = (uint32_t)(x114 >> 26);
+ uint32_t x116 = (uint32_t)(x114 & UINT32_C(0x3ffffff));
+ uint32_t x117 = (x115 + x88);
+ uint8_t x118 = (uint8_t)(x117 >> 25);
+ uint32_t x119 = (x117 & UINT32_C(0x1ffffff));
+ uint32_t x120 = (x118 + x91);
+ out1[0] = x116;
+ out1[1] = x119;
+ out1[2] = x120;
+ out1[3] = x94;
+ out1[4] = x97;
+ out1[5] = x100;
+ out1[6] = x103;
+ out1[7] = x106;
+ out1[8] = x109;
+ out1[9] = x112;
+}
+
+/*
+ * The function fiat_25519_carry reduces a field element.
+ * Postconditions:
+ * eval out1 mod m = eval arg1 mod m
+ *
+ * Input Bounds:
+ * arg1: [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]]
+ * Output Bounds:
+ * out1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]]
+ */
+static void fiat_25519_carry(uint32_t out1[10], const uint32_t arg1[10])
+{
+ uint32_t x1 = (arg1[0]);
+ uint32_t x2 = ((x1 >> 26) + (arg1[1]));
+ uint32_t x3 = ((x2 >> 25) + (arg1[2]));
+ uint32_t x4 = ((x3 >> 26) + (arg1[3]));
+ uint32_t x5 = ((x4 >> 25) + (arg1[4]));
+ uint32_t x6 = ((x5 >> 26) + (arg1[5]));
+ uint32_t x7 = ((x6 >> 25) + (arg1[6]));
+ uint32_t x8 = ((x7 >> 26) + (arg1[7]));
+ uint32_t x9 = ((x8 >> 25) + (arg1[8]));
+ uint32_t x10 = ((x9 >> 26) + (arg1[9]));
+ uint32_t x11 =
+ ((x1 & UINT32_C(0x3ffffff)) + ((x10 >> 25) * UINT8_C(0x13)));
+ uint32_t x12 = ((uint8_t)(x11 >> 26) + (x2 & UINT32_C(0x1ffffff)));
+ uint32_t x13 = (x11 & UINT32_C(0x3ffffff));
+ uint32_t x14 = (x12 & UINT32_C(0x1ffffff));
+ uint32_t x15 = ((uint8_t)(x12 >> 25) + (x3 & UINT32_C(0x3ffffff)));
+ uint32_t x16 = (x4 & UINT32_C(0x1ffffff));
+ uint32_t x17 = (x5 & UINT32_C(0x3ffffff));
+ uint32_t x18 = (x6 & UINT32_C(0x1ffffff));
+ uint32_t x19 = (x7 & UINT32_C(0x3ffffff));
+ uint32_t x20 = (x8 & UINT32_C(0x1ffffff));
+ uint32_t x21 = (x9 & UINT32_C(0x3ffffff));
+ uint32_t x22 = (x10 & UINT32_C(0x1ffffff));
+ out1[0] = x13;
+ out1[1] = x14;
+ out1[2] = x15;
+ out1[3] = x16;
+ out1[4] = x17;
+ out1[5] = x18;
+ out1[6] = x19;
+ out1[7] = x20;
+ out1[8] = x21;
+ out1[9] = x22;
+}
+
+/*
+ * The function fiat_25519_add adds two field elements.
+ * Postconditions:
+ * eval out1 mod m = (eval arg1 + eval arg2) mod m
+ *
+ * Input Bounds:
+ * arg1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]]
+ * arg2: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]]
+ * Output Bounds:
+ * out1: [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]]
+ */
+static void fiat_25519_add(uint32_t out1[10], const uint32_t arg1[10],
+ const uint32_t arg2[10])
+{
+ uint32_t x1 = ((arg1[0]) + (arg2[0]));
+ uint32_t x2 = ((arg1[1]) + (arg2[1]));
+ uint32_t x3 = ((arg1[2]) + (arg2[2]));
+ uint32_t x4 = ((arg1[3]) + (arg2[3]));
+ uint32_t x5 = ((arg1[4]) + (arg2[4]));
+ uint32_t x6 = ((arg1[5]) + (arg2[5]));
+ uint32_t x7 = ((arg1[6]) + (arg2[6]));
+ uint32_t x8 = ((arg1[7]) + (arg2[7]));
+ uint32_t x9 = ((arg1[8]) + (arg2[8]));
+ uint32_t x10 = ((arg1[9]) + (arg2[9]));
+ out1[0] = x1;
+ out1[1] = x2;
+ out1[2] = x3;
+ out1[3] = x4;
+ out1[4] = x5;
+ out1[5] = x6;
+ out1[6] = x7;
+ out1[7] = x8;
+ out1[8] = x9;
+ out1[9] = x10;
+}
+
+/*
+ * The function fiat_25519_sub subtracts two field elements.
+ * Postconditions:
+ * eval out1 mod m = (eval arg1 - eval arg2) mod m
+ *
+ * Input Bounds:
+ * arg1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]]
+ * arg2: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]]
+ * Output Bounds:
+ * out1: [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]]
+ */
+static void fiat_25519_sub(uint32_t out1[10], const uint32_t arg1[10],
+ const uint32_t arg2[10])
+{
+ uint32_t x1 = ((UINT32_C(0x7ffffda) + (arg1[0])) - (arg2[0]));
+ uint32_t x2 = ((UINT32_C(0x3fffffe) + (arg1[1])) - (arg2[1]));
+ uint32_t x3 = ((UINT32_C(0x7fffffe) + (arg1[2])) - (arg2[2]));
+ uint32_t x4 = ((UINT32_C(0x3fffffe) + (arg1[3])) - (arg2[3]));
+ uint32_t x5 = ((UINT32_C(0x7fffffe) + (arg1[4])) - (arg2[4]));
+ uint32_t x6 = ((UINT32_C(0x3fffffe) + (arg1[5])) - (arg2[5]));
+ uint32_t x7 = ((UINT32_C(0x7fffffe) + (arg1[6])) - (arg2[6]));
+ uint32_t x8 = ((UINT32_C(0x3fffffe) + (arg1[7])) - (arg2[7]));
+ uint32_t x9 = ((UINT32_C(0x7fffffe) + (arg1[8])) - (arg2[8]));
+ uint32_t x10 = ((UINT32_C(0x3fffffe) + (arg1[9])) - (arg2[9]));
+ out1[0] = x1;
+ out1[1] = x2;
+ out1[2] = x3;
+ out1[3] = x4;
+ out1[4] = x5;
+ out1[5] = x6;
+ out1[6] = x7;
+ out1[7] = x8;
+ out1[8] = x9;
+ out1[9] = x10;
+}
+
+/*
+ * The function fiat_25519_opp negates a field element.
+ * Postconditions:
+ * eval out1 mod m = -eval arg1 mod m
+ *
+ * Input Bounds:
+ * arg1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]]
+ * Output Bounds:
+ * out1: [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]]
+ */
+static void fiat_25519_opp(uint32_t out1[10], const uint32_t arg1[10])
+{
+ uint32_t x1 = (UINT32_C(0x7ffffda) - (arg1[0]));
+ uint32_t x2 = (UINT32_C(0x3fffffe) - (arg1[1]));
+ uint32_t x3 = (UINT32_C(0x7fffffe) - (arg1[2]));
+ uint32_t x4 = (UINT32_C(0x3fffffe) - (arg1[3]));
+ uint32_t x5 = (UINT32_C(0x7fffffe) - (arg1[4]));
+ uint32_t x6 = (UINT32_C(0x3fffffe) - (arg1[5]));
+ uint32_t x7 = (UINT32_C(0x7fffffe) - (arg1[6]));
+ uint32_t x8 = (UINT32_C(0x3fffffe) - (arg1[7]));
+ uint32_t x9 = (UINT32_C(0x7fffffe) - (arg1[8]));
+ uint32_t x10 = (UINT32_C(0x3fffffe) - (arg1[9]));
+ out1[0] = x1;
+ out1[1] = x2;
+ out1[2] = x3;
+ out1[3] = x4;
+ out1[4] = x5;
+ out1[5] = x6;
+ out1[6] = x7;
+ out1[7] = x8;
+ out1[8] = x9;
+ out1[9] = x10;
+}
+
+/*
+ * The function fiat_25519_to_bytes serializes a field element to bytes in little-endian order.
+ * Postconditions:
+ * out1 = map (λ x, ⌊((eval arg1 mod m) mod 2^(8 * (x + 1))) / 2^(8 * x)⌋) [0..31]
+ *
+ * Input Bounds:
+ * arg1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]]
+ * Output Bounds:
+ * out1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0x7f]]
+ */
+static void fiat_25519_to_bytes(uint8_t out1[32], const uint32_t arg1[10])
+{
+ uint32_t x1;
+ uint8_t x2;
+ fiat_25519_subborrowx_u26(&x1, &x2, 0x0, (arg1[0]),
+ UINT32_C(0x3ffffed));
+ uint32_t x3;
+ uint8_t x4;
+ fiat_25519_subborrowx_u25(&x3, &x4, x2, (arg1[1]), UINT32_C(0x1ffffff));
+ uint32_t x5;
+ uint8_t x6;
+ fiat_25519_subborrowx_u26(&x5, &x6, x4, (arg1[2]), UINT32_C(0x3ffffff));
+ uint32_t x7;
+ uint8_t x8;
+ fiat_25519_subborrowx_u25(&x7, &x8, x6, (arg1[3]), UINT32_C(0x1ffffff));
+ uint32_t x9;
+ uint8_t x10;
+ fiat_25519_subborrowx_u26(&x9, &x10, x8, (arg1[4]),
+ UINT32_C(0x3ffffff));
+ uint32_t x11;
+ uint8_t x12;
+ fiat_25519_subborrowx_u25(&x11, &x12, x10, (arg1[5]),
+ UINT32_C(0x1ffffff));
+ uint32_t x13;
+ uint8_t x14;
+ fiat_25519_subborrowx_u26(&x13, &x14, x12, (arg1[6]),
+ UINT32_C(0x3ffffff));
+ uint32_t x15;
+ uint8_t x16;
+ fiat_25519_subborrowx_u25(&x15, &x16, x14, (arg1[7]),
+ UINT32_C(0x1ffffff));
+ uint32_t x17;
+ uint8_t x18;
+ fiat_25519_subborrowx_u26(&x17, &x18, x16, (arg1[8]),
+ UINT32_C(0x3ffffff));
+ uint32_t x19;
+ uint8_t x20;
+ fiat_25519_subborrowx_u25(&x19, &x20, x18, (arg1[9]),
+ UINT32_C(0x1ffffff));
+ uint32_t x21;
+ fiat_25519_cmovznz_u32(&x21, x20, 0x0, UINT32_C(0xffffffff));
+ uint32_t x22;
+ uint8_t x23;
+ fiat_25519_addcarryx_u26(&x22, &x23, 0x0, x1,
+ (x21 & UINT32_C(0x3ffffed)));
+ uint32_t x24;
+ uint8_t x25;
+ fiat_25519_addcarryx_u25(&x24, &x25, x23, x3,
+ (x21 & UINT32_C(0x1ffffff)));
+ uint32_t x26;
+ uint8_t x27;
+ fiat_25519_addcarryx_u26(&x26, &x27, x25, x5,
+ (x21 & UINT32_C(0x3ffffff)));
+ uint32_t x28;
+ uint8_t x29;
+ fiat_25519_addcarryx_u25(&x28, &x29, x27, x7,
+ (x21 & UINT32_C(0x1ffffff)));
+ uint32_t x30;
+ uint8_t x31;
+ fiat_25519_addcarryx_u26(&x30, &x31, x29, x9,
+ (x21 & UINT32_C(0x3ffffff)));
+ uint32_t x32;
+ uint8_t x33;
+ fiat_25519_addcarryx_u25(&x32, &x33, x31, x11,
+ (x21 & UINT32_C(0x1ffffff)));
+ uint32_t x34;
+ uint8_t x35;
+ fiat_25519_addcarryx_u26(&x34, &x35, x33, x13,
+ (x21 & UINT32_C(0x3ffffff)));
+ uint32_t x36;
+ uint8_t x37;
+ fiat_25519_addcarryx_u25(&x36, &x37, x35, x15,
+ (x21 & UINT32_C(0x1ffffff)));
+ uint32_t x38;
+ uint8_t x39;
+ fiat_25519_addcarryx_u26(&x38, &x39, x37, x17,
+ (x21 & UINT32_C(0x3ffffff)));
+ uint32_t x40;
+ uint8_t x41;
+ fiat_25519_addcarryx_u25(&x40, &x41, x39, x19,
+ (x21 & UINT32_C(0x1ffffff)));
+ uint32_t x42 = (x40 << 6);
+ uint32_t x43 = (x38 << 4);
+ uint32_t x44 = (x36 << 3);
+ uint32_t x45 = (x34 * (uint32_t)0x2);
+ uint32_t x46 = (x30 << 6);
+ uint32_t x47 = (x28 << 5);
+ uint32_t x48 = (x26 << 3);
+ uint32_t x49 = (x24 << 2);
+ uint32_t x50 = (x22 >> 8);
+ uint8_t x51 = (uint8_t)(x22 & UINT8_C(0xff));
+ uint32_t x52 = (x50 >> 8);
+ uint8_t x53 = (uint8_t)(x50 & UINT8_C(0xff));
+ uint8_t x54 = (uint8_t)(x52 >> 8);
+ uint8_t x55 = (uint8_t)(x52 & UINT8_C(0xff));
+ uint32_t x56 = (x54 + x49);
+ uint32_t x57 = (x56 >> 8);
+ uint8_t x58 = (uint8_t)(x56 & UINT8_C(0xff));
+ uint32_t x59 = (x57 >> 8);
+ uint8_t x60 = (uint8_t)(x57 & UINT8_C(0xff));
+ uint8_t x61 = (uint8_t)(x59 >> 8);
+ uint8_t x62 = (uint8_t)(x59 & UINT8_C(0xff));
+ uint32_t x63 = (x61 + x48);
+ uint32_t x64 = (x63 >> 8);
+ uint8_t x65 = (uint8_t)(x63 & UINT8_C(0xff));
+ uint32_t x66 = (x64 >> 8);
+ uint8_t x67 = (uint8_t)(x64 & UINT8_C(0xff));
+ uint8_t x68 = (uint8_t)(x66 >> 8);
+ uint8_t x69 = (uint8_t)(x66 & UINT8_C(0xff));
+ uint32_t x70 = (x68 + x47);
+ uint32_t x71 = (x70 >> 8);
+ uint8_t x72 = (uint8_t)(x70 & UINT8_C(0xff));
+ uint32_t x73 = (x71 >> 8);
+ uint8_t x74 = (uint8_t)(x71 & UINT8_C(0xff));
+ uint8_t x75 = (uint8_t)(x73 >> 8);
+ uint8_t x76 = (uint8_t)(x73 & UINT8_C(0xff));
+ uint32_t x77 = (x75 + x46);
+ uint32_t x78 = (x77 >> 8);
+ uint8_t x79 = (uint8_t)(x77 & UINT8_C(0xff));
+ uint32_t x80 = (x78 >> 8);
+ uint8_t x81 = (uint8_t)(x78 & UINT8_C(0xff));
+ uint8_t x82 = (uint8_t)(x80 >> 8);
+ uint8_t x83 = (uint8_t)(x80 & UINT8_C(0xff));
+ uint8_t x84 = (uint8_t)(x82 & UINT8_C(0xff));
+ uint32_t x85 = (x32 >> 8);
+ uint8_t x86 = (uint8_t)(x32 & UINT8_C(0xff));
+ uint32_t x87 = (x85 >> 8);
+ uint8_t x88 = (uint8_t)(x85 & UINT8_C(0xff));
+ uint8_t x89 = (uint8_t)(x87 >> 8);
+ uint8_t x90 = (uint8_t)(x87 & UINT8_C(0xff));
+ uint32_t x91 = (x89 + x45);
+ uint32_t x92 = (x91 >> 8);
+ uint8_t x93 = (uint8_t)(x91 & UINT8_C(0xff));
+ uint32_t x94 = (x92 >> 8);
+ uint8_t x95 = (uint8_t)(x92 & UINT8_C(0xff));
+ uint8_t x96 = (uint8_t)(x94 >> 8);
+ uint8_t x97 = (uint8_t)(x94 & UINT8_C(0xff));
+ uint32_t x98 = (x96 + x44);
+ uint32_t x99 = (x98 >> 8);
+ uint8_t x100 = (uint8_t)(x98 & UINT8_C(0xff));
+ uint32_t x101 = (x99 >> 8);
+ uint8_t x102 = (uint8_t)(x99 & UINT8_C(0xff));
+ uint8_t x103 = (uint8_t)(x101 >> 8);
+ uint8_t x104 = (uint8_t)(x101 & UINT8_C(0xff));
+ uint32_t x105 = (x103 + x43);
+ uint32_t x106 = (x105 >> 8);
+ uint8_t x107 = (uint8_t)(x105 & UINT8_C(0xff));
+ uint32_t x108 = (x106 >> 8);
+ uint8_t x109 = (uint8_t)(x106 & UINT8_C(0xff));
+ uint8_t x110 = (uint8_t)(x108 >> 8);
+ uint8_t x111 = (uint8_t)(x108 & UINT8_C(0xff));
+ uint32_t x112 = (x110 + x42);
+ uint32_t x113 = (x112 >> 8);
+ uint8_t x114 = (uint8_t)(x112 & UINT8_C(0xff));
+ uint32_t x115 = (x113 >> 8);
+ uint8_t x116 = (uint8_t)(x113 & UINT8_C(0xff));
+ uint8_t x117 = (uint8_t)(x115 >> 8);
+ uint8_t x118 = (uint8_t)(x115 & UINT8_C(0xff));
+ out1[0] = x51;
+ out1[1] = x53;
+ out1[2] = x55;
+ out1[3] = x58;
+ out1[4] = x60;
+ out1[5] = x62;
+ out1[6] = x65;
+ out1[7] = x67;
+ out1[8] = x69;
+ out1[9] = x72;
+ out1[10] = x74;
+ out1[11] = x76;
+ out1[12] = x79;
+ out1[13] = x81;
+ out1[14] = x83;
+ out1[15] = x84;
+ out1[16] = x86;
+ out1[17] = x88;
+ out1[18] = x90;
+ out1[19] = x93;
+ out1[20] = x95;
+ out1[21] = x97;
+ out1[22] = x100;
+ out1[23] = x102;
+ out1[24] = x104;
+ out1[25] = x107;
+ out1[26] = x109;
+ out1[27] = x111;
+ out1[28] = x114;
+ out1[29] = x116;
+ out1[30] = x118;
+ out1[31] = x117;
+}
+
+/*
+ * The function fiat_25519_from_bytes deserializes a field element from bytes in little-endian order.
+ * Postconditions:
+ * eval out1 mod m = bytes_eval arg1 mod m
+ *
+ * Input Bounds:
+ * arg1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0x7f]]
+ * Output Bounds:
+ * out1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]]
+ */
+static void fiat_25519_from_bytes(uint32_t out1[10], const uint8_t arg1[32])
+{
+ uint32_t x1 = ((uint32_t)(arg1[31]) << 18);
+ uint32_t x2 = ((uint32_t)(arg1[30]) << 10);
+ uint32_t x3 = ((uint32_t)(arg1[29]) << 2);
+ uint32_t x4 = ((uint32_t)(arg1[28]) << 20);
+ uint32_t x5 = ((uint32_t)(arg1[27]) << 12);
+ uint32_t x6 = ((uint32_t)(arg1[26]) << 4);
+ uint32_t x7 = ((uint32_t)(arg1[25]) << 21);
+ uint32_t x8 = ((uint32_t)(arg1[24]) << 13);
+ uint32_t x9 = ((uint32_t)(arg1[23]) << 5);
+ uint32_t x10 = ((uint32_t)(arg1[22]) << 23);
+ uint32_t x11 = ((uint32_t)(arg1[21]) << 15);
+ uint32_t x12 = ((uint32_t)(arg1[20]) << 7);
+ uint32_t x13 = ((uint32_t)(arg1[19]) << 24);
+ uint32_t x14 = ((uint32_t)(arg1[18]) << 16);
+ uint32_t x15 = ((uint32_t)(arg1[17]) << 8);
+ uint8_t x16 = (arg1[16]);
+ uint32_t x17 = ((uint32_t)(arg1[15]) << 18);
+ uint32_t x18 = ((uint32_t)(arg1[14]) << 10);
+ uint32_t x19 = ((uint32_t)(arg1[13]) << 2);
+ uint32_t x20 = ((uint32_t)(arg1[12]) << 19);
+ uint32_t x21 = ((uint32_t)(arg1[11]) << 11);
+ uint32_t x22 = ((uint32_t)(arg1[10]) << 3);
+ uint32_t x23 = ((uint32_t)(arg1[9]) << 21);
+ uint32_t x24 = ((uint32_t)(arg1[8]) << 13);
+ uint32_t x25 = ((uint32_t)(arg1[7]) << 5);
+ uint32_t x26 = ((uint32_t)(arg1[6]) << 22);
+ uint32_t x27 = ((uint32_t)(arg1[5]) << 14);
+ uint32_t x28 = ((uint32_t)(arg1[4]) << 6);
+ uint32_t x29 = ((uint32_t)(arg1[3]) << 24);
+ uint32_t x30 = ((uint32_t)(arg1[2]) << 16);
+ uint32_t x31 = ((uint32_t)(arg1[1]) << 8);
+ uint8_t x32 = (arg1[0]);
+ uint32_t x33 = (x32 + (x31 + (x30 + x29)));
+ uint8_t x34 = (uint8_t)(x33 >> 26);
+ uint32_t x35 = (x33 & UINT32_C(0x3ffffff));
+ uint32_t x36 = (x3 + (x2 + x1));
+ uint32_t x37 = (x6 + (x5 + x4));
+ uint32_t x38 = (x9 + (x8 + x7));
+ uint32_t x39 = (x12 + (x11 + x10));
+ uint32_t x40 = (x16 + (x15 + (x14 + x13)));
+ uint32_t x41 = (x19 + (x18 + x17));
+ uint32_t x42 = (x22 + (x21 + x20));
+ uint32_t x43 = (x25 + (x24 + x23));
+ uint32_t x44 = (x28 + (x27 + x26));
+ uint32_t x45 = (x34 + x44);
+ uint8_t x46 = (uint8_t)(x45 >> 25);
+ uint32_t x47 = (x45 & UINT32_C(0x1ffffff));
+ uint32_t x48 = (x46 + x43);
+ uint8_t x49 = (uint8_t)(x48 >> 26);
+ uint32_t x50 = (x48 & UINT32_C(0x3ffffff));
+ uint32_t x51 = (x49 + x42);
+ uint8_t x52 = (uint8_t)(x51 >> 25);
+ uint32_t x53 = (x51 & UINT32_C(0x1ffffff));
+ uint32_t x54 = (x52 + x41);
+ uint32_t x55 = (x54 & UINT32_C(0x3ffffff));
+ uint8_t x56 = (uint8_t)(x40 >> 25);
+ uint32_t x57 = (x40 & UINT32_C(0x1ffffff));
+ uint32_t x58 = (x56 + x39);
+ uint8_t x59 = (uint8_t)(x58 >> 26);
+ uint32_t x60 = (x58 & UINT32_C(0x3ffffff));
+ uint32_t x61 = (x59 + x38);
+ uint8_t x62 = (uint8_t)(x61 >> 25);
+ uint32_t x63 = (x61 & UINT32_C(0x1ffffff));
+ uint32_t x64 = (x62 + x37);
+ uint8_t x65 = (uint8_t)(x64 >> 26);
+ uint32_t x66 = (x64 & UINT32_C(0x3ffffff));
+ uint32_t x67 = (x65 + x36);
+ out1[0] = x35;
+ out1[1] = x47;
+ out1[2] = x50;
+ out1[3] = x53;
+ out1[4] = x55;
+ out1[5] = x57;
+ out1[6] = x60;
+ out1[7] = x63;
+ out1[8] = x66;
+ out1[9] = x67;
+}
+
+// Definitions
+
+// fe means field element. Here the field is \Z/(2^255-19). An element t,
+// entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77
+// t[3]+2^102 t[4]+...+2^230 t[9].
+// fe limbs are bounded by 1.125*2^26,1.125*2^25,1.125*2^26,1.125*2^25,etc.
+// Multiplication and carrying produce fe from fe_loose.
+typedef struct fe {
+ uint32_t v[10];
+} fe;
+
+// fe_loose limbs are bounded by 3.375*2^26,3.375*2^25,3.375*2^26,3.375*2^25,etc.
+// Addition and subtraction produce fe_loose from (fe, fe).
+typedef struct fe_loose {
+ uint32_t v[10];
+} fe_loose;
+
+// ge means group element.
+//
+// Here the group is the set of pairs (x,y) of field elements (see fe.h)
+// satisfying -x^2 + y^2 = 1 + d x^2y^2
+// where d = -121665/121666.
+//
+// Representations:
+// ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z
+// ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
+// ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
+// ge_precomp (Duif): (y+x,y-x,2dxy)
+
+typedef struct {
+ fe X;
+ fe Y;
+ fe Z;
+} ge_p2;
+
+typedef struct {
+ fe X;
+ fe Y;
+ fe Z;
+ fe T;
+} ge_p3;
+
+typedef struct {
+ fe_loose X;
+ fe_loose Y;
+ fe_loose Z;
+ fe_loose T;
+} ge_p1p1;
+
+typedef struct {
+ fe_loose yplusx;
+ fe_loose yminusx;
+ fe_loose xy2d;
+} ge_precomp;
+
+typedef struct {
+ fe_loose YplusX;
+ fe_loose YminusX;
+ fe_loose Z;
+ fe_loose T2d;
+} ge_cached;
+
+// Constants.
+
+static const fe d = { { 56195235, 13857412, 51736253, 6949390, 114729, 24766616,
+ 60832955, 30306712, 48412415, 21499315 } };
+
+static const fe sqrtm1 = { { 34513072, 25610706, 9377949, 3500415, 12389472,
+ 33281959, 41962654, 31548777, 326685, 11406482 } };
+
+static const fe d2 = { { 45281625, 27714825, 36363642, 13898781, 229458,
+ 15978800, 54557047, 27058993, 29715967, 9444199 } };
+
+// Bi[i] = (2*i+1)*B
+static const ge_precomp Bi[8] = {
+ {
+ { { 25967493, 19198397, 29566455, 3660896, 54414519, 4014786,
+ 27544626, 21800161, 61029707, 2047604
+
+ } },
+ { { 54563134, 934261, 64385954, 3049989, 66381436, 9406985,
+ 12720692, 5043384, 19500929, 18085054
+
+ } },
+ { { 58370664, 4489569, 9688441, 18769238, 10184608, 21191052,
+ 29287918, 11864899, 42594502, 29115885 } },
+ },
+ {
+ { { 15636272, 23865875, 24204772, 25642034, 616976, 16869170,
+ 27787599, 18782243, 28944399, 32004408 } },
+ { { 16568933, 4717097, 55552716, 32452109, 15682895, 21747389,
+ 16354576, 21778470, 7689661, 11199574 } },
+ { { 30464137, 27578307, 55329429, 17883566, 23220364, 15915852,
+ 7512774, 10017326, 49359771, 23634074 } },
+ },
+ {
+ { { 10861363, 11473154, 27284546, 1981175, 37044515, 12577860,
+ 32867885, 14515107, 51670560, 10819379 } },
+ { { 4708026, 6336745, 20377586, 9066809, 55836755, 6594695,
+ 41455196, 12483687, 54440373, 5581305 } },
+ { { 19563141, 16186464, 37722007, 4097518, 10237984, 29206317,
+ 28542349, 13850243, 43430843, 17738489 } },
+ },
+ {
+ { { 5153727, 9909285, 1723747, 30776558, 30523604, 5516873,
+ 19480852, 5230134, 43156425, 18378665 } },
+ { { 36839857, 30090922, 7665485, 10083793, 28475525, 1649722,
+ 20654025, 16520125, 30598449, 7715701 } },
+ { { 28881826, 14381568, 9657904, 3680757, 46927229, 7843315,
+ 35708204, 1370707, 29794553, 32145132 } },
+ },
+ {
+ { { 44589871, 26862249, 14201701, 24808930, 43598457, 8844725,
+ 18474211, 32192982, 54046167, 13821876 } },
+ { { 60653668, 25714560, 3374701, 28813570, 40010246, 22982724,
+ 31655027, 26342105, 18853321, 19333481 } },
+ { { 4566811, 20590564, 38133974, 21313742, 59506191, 30723862,
+ 58594505, 23123294, 2207752, 30344648 } },
+ },
+ {
+ { { 41954014, 29368610, 29681143, 7868801, 60254203, 24130566,
+ 54671499, 32891431, 35997400, 17421995 } },
+ { { 25576264, 30851218, 7349803, 21739588, 16472781, 9300885,
+ 3844789, 15725684, 171356, 6466918 } },
+ { { 23103977, 13316479, 9739013, 17404951, 817874, 18515490,
+ 8965338, 19466374, 36393951, 16193876 } },
+ },
+ {
+ { { 33587053, 3180712, 64714734, 14003686, 50205390, 17283591,
+ 17238397, 4729455, 49034351, 9256799 } },
+ { { 41926547, 29380300, 32336397, 5036987, 45872047, 11360616,
+ 22616405, 9761698, 47281666, 630304 } },
+ { { 53388152, 2639452, 42871404, 26147950, 9494426, 27780403,
+ 60554312, 17593437, 64659607, 19263131 } },
+ },
+ {
+ { { 63957664, 28508356, 9282713, 6866145, 35201802, 32691408,
+ 48168288, 15033783, 25105118, 25659556 } },
+ { { 42782475, 15950225, 35307649, 18961608, 55446126, 28463506,
+ 1573891, 30928545, 2198789, 17749813 } },
+ { { 64009494, 10324966, 64867251, 7453182, 61661885, 30818928,
+ 53296841, 17317989, 34647629, 21263748 } },
+ },
+};
+
+static void fe_frombytes_strict(fe *h, const uint8_t s[32])
+{
+ // |fiat_25519_from_bytes| requires the top-most bit be clear.
+ fiat_25519_from_bytes(h->v, s);
+}
+
+static void fe_frombytes(fe *h, const uint8_t s[32])
+{
+ uint8_t s_copy[32];
+ memcpy(s_copy, s, 32);
+ s_copy[31] &= 0x7f;
+ fe_frombytes_strict(h, s_copy);
+}
+
+static void fe_tobytes(uint8_t s[32], const fe *f)
+{
+ fiat_25519_to_bytes(s, f->v);
+}
+
+// h = 0
+static void fe_0(fe *h)
+{
+ memset(h, 0, sizeof(fe));
+}
+
+// h = 1
+static void fe_1(fe *h)
+{
+ memset(h, 0, sizeof(fe));
+ h->v[0] = 1;
+}
+
+// h = f + g
+// Can overlap h with f or g.
+static void fe_add(fe_loose *h, const fe *f, const fe *g)
+{
+ fiat_25519_add(h->v, f->v, g->v);
+}
+
+// h = f - g
+// Can overlap h with f or g.
+static void fe_sub(fe_loose *h, const fe *f, const fe *g)
+{
+ fiat_25519_sub(h->v, f->v, g->v);
+}
+
+static void fe_carry(fe *h, const fe_loose *f)
+{
+ fiat_25519_carry(h->v, f->v);
+}
+
+static void fe_mul_impl(uint32_t out[10], const uint32_t in1[10],
+ const uint32_t in2[10])
+{
+ fiat_25519_carry_mul(out, in1, in2);
+}
+
+static void fe_mul_ltt(fe_loose *h, const fe *f, const fe *g)
+{
+ fe_mul_impl(h->v, f->v, g->v);
+}
+
+static void fe_mul_ttt(fe *h, const fe *f, const fe *g)
+{
+ fe_mul_impl(h->v, f->v, g->v);
+}
+
+static void fe_mul_tlt(fe *h, const fe_loose *f, const fe *g)
+{
+ fe_mul_impl(h->v, f->v, g->v);
+}
+
+static void fe_mul_ttl(fe *h, const fe *f, const fe_loose *g)
+{
+ fe_mul_impl(h->v, f->v, g->v);
+}
+
+static void fe_mul_tll(fe *h, const fe_loose *f, const fe_loose *g)
+{
+ fe_mul_impl(h->v, f->v, g->v);
+}
+
+static void fe_sq_tl(fe *h, const fe_loose *f)
+{
+ fiat_25519_carry_square(h->v, f->v);
+}
+
+static void fe_sq_tt(fe *h, const fe *f)
+{
+ fiat_25519_carry_square(h->v, f->v);
+}
+
+// h = -f
+static void fe_neg(fe_loose *h, const fe *f)
+{
+ fiat_25519_opp(h->v, f->v);
+}
+
+// h = f
+static void fe_copy(fe *h, const fe *f)
+{
+ memmove(h, f, sizeof(fe));
+}
+
+static void fe_copy_lt(fe_loose *h, const fe *f)
+{
+ memmove(h, f, sizeof(fe));
+}
+
+static void fe_loose_invert(fe *out, const fe_loose *z)
+{
+ fe t0;
+ fe t1;
+ fe t2;
+ fe t3;
+ int i;
+
+ fe_sq_tl(&t0, z);
+ fe_sq_tt(&t1, &t0);
+ for (i = 1; i < 2; ++i) {
+ fe_sq_tt(&t1, &t1);
+ }
+ fe_mul_tlt(&t1, z, &t1);
+ fe_mul_ttt(&t0, &t0, &t1);
+ fe_sq_tt(&t2, &t0);
+ fe_mul_ttt(&t1, &t1, &t2);
+ fe_sq_tt(&t2, &t1);
+ for (i = 1; i < 5; ++i) {
+ fe_sq_tt(&t2, &t2);
+ }
+ fe_mul_ttt(&t1, &t2, &t1);
+ fe_sq_tt(&t2, &t1);
+ for (i = 1; i < 10; ++i) {
+ fe_sq_tt(&t2, &t2);
+ }
+ fe_mul_ttt(&t2, &t2, &t1);
+ fe_sq_tt(&t3, &t2);
+ for (i = 1; i < 20; ++i) {
+ fe_sq_tt(&t3, &t3);
+ }
+ fe_mul_ttt(&t2, &t3, &t2);
+ fe_sq_tt(&t2, &t2);
+ for (i = 1; i < 10; ++i) {
+ fe_sq_tt(&t2, &t2);
+ }
+ fe_mul_ttt(&t1, &t2, &t1);
+ fe_sq_tt(&t2, &t1);
+ for (i = 1; i < 50; ++i) {
+ fe_sq_tt(&t2, &t2);
+ }
+ fe_mul_ttt(&t2, &t2, &t1);
+ fe_sq_tt(&t3, &t2);
+ for (i = 1; i < 100; ++i) {
+ fe_sq_tt(&t3, &t3);
+ }
+ fe_mul_ttt(&t2, &t3, &t2);
+ fe_sq_tt(&t2, &t2);
+ for (i = 1; i < 50; ++i) {
+ fe_sq_tt(&t2, &t2);
+ }
+ fe_mul_ttt(&t1, &t2, &t1);
+ fe_sq_tt(&t1, &t1);
+ for (i = 1; i < 5; ++i) {
+ fe_sq_tt(&t1, &t1);
+ }
+ fe_mul_ttt(out, &t1, &t0);
+}
+
+static void fe_invert(fe *out, const fe *z)
+{
+ fe_loose l;
+ fe_copy_lt(&l, z);
+ fe_loose_invert(out, &l);
+}
+
+// return 0 if f == 0
+// return 1 if f != 0
+static int fe_isnonzero(const fe_loose *f)
+{
+ fe tight;
+ fe_carry(&tight, f);
+ uint8_t s[32];
+ fe_tobytes(s, &tight);
+
+ static const uint8_t zero[32] = { 0 };
+ return memcmp_ct(s, zero, sizeof(zero)) != 0;
+}
+
+// return 1 if f is in {1,3,5,...,q-2}
+// return 0 if f is in {0,2,4,...,q-1}
+static int fe_isnegative(const fe *f)
+{
+ uint8_t s[32];
+ fe_tobytes(s, f);
+ return s[0] & 1;
+}
+
+static void fe_sq2_tt(fe *h, const fe *f)
+{
+ // h = f^2
+ fe_sq_tt(h, f);
+
+ // h = h + h
+ fe_loose tmp;
+ fe_add(&tmp, h, h);
+ fe_carry(h, &tmp);
+}
+
+static void fe_pow22523(fe *out, const fe *z)
+{
+ fe t0;
+ fe t1;
+ fe t2;
+ int i;
+
+ fe_sq_tt(&t0, z);
+ fe_sq_tt(&t1, &t0);
+ for (i = 1; i < 2; ++i) {
+ fe_sq_tt(&t1, &t1);
+ }
+ fe_mul_ttt(&t1, z, &t1);
+ fe_mul_ttt(&t0, &t0, &t1);
+ fe_sq_tt(&t0, &t0);
+ fe_mul_ttt(&t0, &t1, &t0);
+ fe_sq_tt(&t1, &t0);
+ for (i = 1; i < 5; ++i) {
+ fe_sq_tt(&t1, &t1);
+ }
+ fe_mul_ttt(&t0, &t1, &t0);
+ fe_sq_tt(&t1, &t0);
+ for (i = 1; i < 10; ++i) {
+ fe_sq_tt(&t1, &t1);
+ }
+ fe_mul_ttt(&t1, &t1, &t0);
+ fe_sq_tt(&t2, &t1);
+ for (i = 1; i < 20; ++i) {
+ fe_sq_tt(&t2, &t2);
+ }
+ fe_mul_ttt(&t1, &t2, &t1);
+ fe_sq_tt(&t1, &t1);
+ for (i = 1; i < 10; ++i) {
+ fe_sq_tt(&t1, &t1);
+ }
+ fe_mul_ttt(&t0, &t1, &t0);
+ fe_sq_tt(&t1, &t0);
+ for (i = 1; i < 50; ++i) {
+ fe_sq_tt(&t1, &t1);
+ }
+ fe_mul_ttt(&t1, &t1, &t0);
+ fe_sq_tt(&t2, &t1);
+ for (i = 1; i < 100; ++i) {
+ fe_sq_tt(&t2, &t2);
+ }
+ fe_mul_ttt(&t1, &t2, &t1);
+ fe_sq_tt(&t1, &t1);
+ for (i = 1; i < 50; ++i) {
+ fe_sq_tt(&t1, &t1);
+ }
+ fe_mul_ttt(&t0, &t1, &t0);
+ fe_sq_tt(&t0, &t0);
+ for (i = 1; i < 2; ++i) {
+ fe_sq_tt(&t0, &t0);
+ }
+ fe_mul_ttt(out, &t0, z);
+}
+
+static void x25519_ge_tobytes(uint8_t s[32], const ge_p2 *h)
+{
+ fe recip;
+ fe x;
+ fe y;
+
+ fe_invert(&recip, &h->Z);
+ fe_mul_ttt(&x, &h->X, &recip);
+ fe_mul_ttt(&y, &h->Y, &recip);
+ fe_tobytes(s, &y);
+ s[31] ^= fe_isnegative(&x) << 7;
+}
+
+static int x25519_ge_frombytes_vartime(ge_p3 *h, const uint8_t s[32])
+{
+ fe u;
+ fe_loose v;
+ fe v3;
+ fe vxx;
+ fe_loose check;
+
+ fe_frombytes(&h->Y, s);
+ fe_1(&h->Z);
+ fe_sq_tt(&v3, &h->Y);
+ fe_mul_ttt(&vxx, &v3, &d);
+ fe_sub(&v, &v3, &h->Z); // u = y^2-1
+ fe_carry(&u, &v);
+ fe_add(&v, &vxx, &h->Z); // v = dy^2+1
+
+ fe_sq_tl(&v3, &v);
+ fe_mul_ttl(&v3, &v3, &v); // v3 = v^3
+ fe_sq_tt(&h->X, &v3);
+ fe_mul_ttl(&h->X, &h->X, &v);
+ fe_mul_ttt(&h->X, &h->X, &u); // x = uv^7
+
+ fe_pow22523(&h->X, &h->X); // x = (uv^7)^((q-5)/8)
+ fe_mul_ttt(&h->X, &h->X, &v3);
+ fe_mul_ttt(&h->X, &h->X, &u); // x = uv^3(uv^7)^((q-5)/8)
+
+ fe_sq_tt(&vxx, &h->X);
+ fe_mul_ttl(&vxx, &vxx, &v);
+ fe_sub(&check, &vxx, &u);
+ if (fe_isnonzero(&check)) {
+ fe_add(&check, &vxx, &u);
+ if (fe_isnonzero(&check)) {
+ return 0;
+ }
+ fe_mul_ttt(&h->X, &h->X, &sqrtm1);
+ }
+
+ if (fe_isnegative(&h->X) != (s[31] >> 7)) {
+ fe_loose t;
+ fe_neg(&t, &h->X);
+ fe_carry(&h->X, &t);
+ }
+
+ fe_mul_ttt(&h->T, &h->X, &h->Y);
+ return 1;
+}
+
+static void ge_p2_0(ge_p2 *h)
+{
+ fe_0(&h->X);
+ fe_1(&h->Y);
+ fe_1(&h->Z);
+}
+
+// r = p
+static void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p)
+{
+ fe_copy(&r->X, &p->X);
+ fe_copy(&r->Y, &p->Y);
+ fe_copy(&r->Z, &p->Z);
+}
+
+// r = p
+static void x25519_ge_p3_to_cached(ge_cached *r, const ge_p3 *p)
+{
+ fe_add(&r->YplusX, &p->Y, &p->X);
+ fe_sub(&r->YminusX, &p->Y, &p->X);
+ fe_copy_lt(&r->Z, &p->Z);
+ fe_mul_ltt(&r->T2d, &p->T, &d2);
+}
+
+// r = p
+static void x25519_ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p)
+{
+ fe_mul_tll(&r->X, &p->X, &p->T);
+ fe_mul_tll(&r->Y, &p->Y, &p->Z);
+ fe_mul_tll(&r->Z, &p->Z, &p->T);
+}
+
+// r = p
+static void x25519_ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p)
+{
+ fe_mul_tll(&r->X, &p->X, &p->T);
+ fe_mul_tll(&r->Y, &p->Y, &p->Z);
+ fe_mul_tll(&r->Z, &p->Z, &p->T);
+ fe_mul_tll(&r->T, &p->X, &p->Y);
+}
+
+// r = 2 * p
+static void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p)
+{
+ fe trX, trZ, trT;
+ fe t0;
+
+ fe_sq_tt(&trX, &p->X);
+ fe_sq_tt(&trZ, &p->Y);
+ fe_sq2_tt(&trT, &p->Z);
+ fe_add(&r->Y, &p->X, &p->Y);
+ fe_sq_tl(&t0, &r->Y);
+
+ fe_add(&r->Y, &trZ, &trX);
+ fe_sub(&r->Z, &trZ, &trX);
+ fe_carry(&trZ, &r->Y);
+ fe_sub(&r->X, &t0, &trZ);
+ fe_carry(&trZ, &r->Z);
+ fe_sub(&r->T, &trT, &trZ);
+}
+
+// r = 2 * p
+static void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p)
+{
+ ge_p2 q;
+ ge_p3_to_p2(&q, p);
+ ge_p2_dbl(r, &q);
+}
+
+// r = p + q
+static void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q)
+{
+ fe trY, trZ, trT;
+
+ fe_add(&r->X, &p->Y, &p->X);
+ fe_sub(&r->Y, &p->Y, &p->X);
+ fe_mul_tll(&trZ, &r->X, &q->yplusx);
+ fe_mul_tll(&trY, &r->Y, &q->yminusx);
+ fe_mul_tlt(&trT, &q->xy2d, &p->T);
+ fe_add(&r->T, &p->Z, &p->Z);
+ fe_sub(&r->X, &trZ, &trY);
+ fe_add(&r->Y, &trZ, &trY);
+ fe_carry(&trZ, &r->T);
+ fe_add(&r->Z, &trZ, &trT);
+ fe_sub(&r->T, &trZ, &trT);
+}
+
+// r = p - q
+static void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q)
+{
+ fe trY, trZ, trT;
+
+ fe_add(&r->X, &p->Y, &p->X);
+ fe_sub(&r->Y, &p->Y, &p->X);
+ fe_mul_tll(&trZ, &r->X, &q->yminusx);
+ fe_mul_tll(&trY, &r->Y, &q->yplusx);
+ fe_mul_tlt(&trT, &q->xy2d, &p->T);
+ fe_add(&r->T, &p->Z, &p->Z);
+ fe_sub(&r->X, &trZ, &trY);
+ fe_add(&r->Y, &trZ, &trY);
+ fe_carry(&trZ, &r->T);
+ fe_sub(&r->Z, &trZ, &trT);
+ fe_add(&r->T, &trZ, &trT);
+}
+
+// r = p + q
+static void x25519_ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q)
+{
+ fe trX, trY, trZ, trT;
+
+ fe_add(&r->X, &p->Y, &p->X);
+ fe_sub(&r->Y, &p->Y, &p->X);
+ fe_mul_tll(&trZ, &r->X, &q->YplusX);
+ fe_mul_tll(&trY, &r->Y, &q->YminusX);
+ fe_mul_tlt(&trT, &q->T2d, &p->T);
+ fe_mul_ttl(&trX, &p->Z, &q->Z);
+ fe_add(&r->T, &trX, &trX);
+ fe_sub(&r->X, &trZ, &trY);
+ fe_add(&r->Y, &trZ, &trY);
+ fe_carry(&trZ, &r->T);
+ fe_add(&r->Z, &trZ, &trT);
+ fe_sub(&r->T, &trZ, &trT);
+}
+
+// r = p - q
+static void x25519_ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q)
+{
+ fe trX, trY, trZ, trT;
+
+ fe_add(&r->X, &p->Y, &p->X);
+ fe_sub(&r->Y, &p->Y, &p->X);
+ fe_mul_tll(&trZ, &r->X, &q->YminusX);
+ fe_mul_tll(&trY, &r->Y, &q->YplusX);
+ fe_mul_tlt(&trT, &q->T2d, &p->T);
+ fe_mul_ttl(&trX, &p->Z, &q->Z);
+ fe_add(&r->T, &trX, &trX);
+ fe_sub(&r->X, &trZ, &trY);
+ fe_add(&r->Y, &trZ, &trY);
+ fe_carry(&trZ, &r->T);
+ fe_sub(&r->Z, &trZ, &trT);
+ fe_add(&r->T, &trZ, &trT);
+}
+
+static void slide(signed char *r, const uint8_t *a)
+{
+ int i;
+ int b;
+ int k;
+
+ for (i = 0; i < 256; ++i) {
+ r[i] = 1 & (a[i >> 3] >> (i & 7));
+ }
+
+ for (i = 0; i < 256; ++i) {
+ if (r[i]) {
+ for (b = 1; b <= 6 && i + b < 256; ++b) {
+ if (r[i + b]) {
+ if (r[i] + (r[i + b] << b) <= 15) {
+ r[i] += r[i + b] << b;
+ r[i + b] = 0;
+ } else if (r[i] - (r[i + b] << b) >=
+ -15) {
+ r[i] -= r[i + b] << b;
+ for (k = i + b; k < 256; ++k) {
+ if (!r[k]) {
+ r[k] = 1;
+ break;
+ }
+ r[k] = 0;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+// r = a * A + b * B
+// where a = a[0]+256*a[1]+...+256^31 a[31].
+// and b = b[0]+256*b[1]+...+256^31 b[31].
+// B is the Ed25519 base point (x,4/5) with x positive.
+static void ge_double_scalarmult_vartime(ge_p2 *r, const uint8_t *a,
+ const ge_p3 *A, const uint8_t *b)
+{
+ signed char aslide[256];
+ signed char bslide[256];
+ ge_cached Ai[8]; // A,3A,5A,7A,9A,11A,13A,15A
+ ge_p1p1 t;
+ ge_p3 u;
+ ge_p3 A2;
+ int i;
+
+ slide(aslide, a);
+ slide(bslide, b);
+
+ x25519_ge_p3_to_cached(&Ai[0], A);
+ ge_p3_dbl(&t, A);
+ x25519_ge_p1p1_to_p3(&A2, &t);
+ x25519_ge_add(&t, &A2, &Ai[0]);
+ x25519_ge_p1p1_to_p3(&u, &t);
+ x25519_ge_p3_to_cached(&Ai[1], &u);
+ x25519_ge_add(&t, &A2, &Ai[1]);
+ x25519_ge_p1p1_to_p3(&u, &t);
+ x25519_ge_p3_to_cached(&Ai[2], &u);
+ x25519_ge_add(&t, &A2, &Ai[2]);
+ x25519_ge_p1p1_to_p3(&u, &t);
+ x25519_ge_p3_to_cached(&Ai[3], &u);
+ x25519_ge_add(&t, &A2, &Ai[3]);
+ x25519_ge_p1p1_to_p3(&u, &t);
+ x25519_ge_p3_to_cached(&Ai[4], &u);
+ x25519_ge_add(&t, &A2, &Ai[4]);
+ x25519_ge_p1p1_to_p3(&u, &t);
+ x25519_ge_p3_to_cached(&Ai[5], &u);
+ x25519_ge_add(&t, &A2, &Ai[5]);
+ x25519_ge_p1p1_to_p3(&u, &t);
+ x25519_ge_p3_to_cached(&Ai[6], &u);
+ x25519_ge_add(&t, &A2, &Ai[6]);
+ x25519_ge_p1p1_to_p3(&u, &t);
+ x25519_ge_p3_to_cached(&Ai[7], &u);
+
+ ge_p2_0(r);
+
+ for (i = 255; i >= 0; --i) {
+ if (aslide[i] || bslide[i]) {
+ break;
+ }
+ }
+
+ for (; i >= 0; --i) {
+ ge_p2_dbl(&t, r);
+
+ if (aslide[i] > 0) {
+ x25519_ge_p1p1_to_p3(&u, &t);
+ x25519_ge_add(&t, &u, &Ai[aslide[i] / 2]);
+ } else if (aslide[i] < 0) {
+ x25519_ge_p1p1_to_p3(&u, &t);
+ x25519_ge_sub(&t, &u, &Ai[(-aslide[i]) / 2]);
+ }
+
+ if (bslide[i] > 0) {
+ x25519_ge_p1p1_to_p3(&u, &t);
+ ge_madd(&t, &u, &Bi[bslide[i] / 2]);
+ } else if (bslide[i] < 0) {
+ x25519_ge_p1p1_to_p3(&u, &t);
+ ge_msub(&t, &u, &Bi[(-bslide[i]) / 2]);
+ }
+
+ x25519_ge_p1p1_to_p2(r, &t);
+ }
+}
+
+// int64_lshift21 returns |a << 21| but is defined when shifting bits into the
+// sign bit. This works around a language flaw in C.
+static inline int64_t int64_lshift21(int64_t a)
+{
+ return (int64_t)((uint64_t)a << 21);
+}
+
+// The set of scalars is \Z/l
+// where l = 2^252 + 27742317777372353535851937790883648493.
+
+// Input:
+// s[0]+256*s[1]+...+256^63*s[63] = s
+//
+// Output:
+// s[0]+256*s[1]+...+256^31*s[31] = s mod l
+// where l = 2^252 + 27742317777372353535851937790883648493.
+// Overwrites s in place.
+static void x25519_sc_reduce(uint8_t s[64])
+{
+ int64_t s0 = 2097151 & load_le24(s);
+ int64_t s1 = 2097151 & (load_le32(s + 2) >> 5);
+ int64_t s2 = 2097151 & (load_le24(s + 5) >> 2);
+ int64_t s3 = 2097151 & (load_le32(s + 7) >> 7);
+ int64_t s4 = 2097151 & (load_le32(s + 10) >> 4);
+ int64_t s5 = 2097151 & (load_le24(s + 13) >> 1);
+ int64_t s6 = 2097151 & (load_le32(s + 15) >> 6);
+ int64_t s7 = 2097151 & (load_le24(s + 18) >> 3);
+ int64_t s8 = 2097151 & load_le24(s + 21);
+ int64_t s9 = 2097151 & (load_le32(s + 23) >> 5);
+ int64_t s10 = 2097151 & (load_le24(s + 26) >> 2);
+ int64_t s11 = 2097151 & (load_le32(s + 28) >> 7);
+ int64_t s12 = 2097151 & (load_le32(s + 31) >> 4);
+ int64_t s13 = 2097151 & (load_le24(s + 34) >> 1);
+ int64_t s14 = 2097151 & (load_le32(s + 36) >> 6);
+ int64_t s15 = 2097151 & (load_le24(s + 39) >> 3);
+ int64_t s16 = 2097151 & load_le24(s + 42);
+ int64_t s17 = 2097151 & (load_le32(s + 44) >> 5);
+ int64_t s18 = 2097151 & (load_le24(s + 47) >> 2);
+ int64_t s19 = 2097151 & (load_le32(s + 49) >> 7);
+ int64_t s20 = 2097151 & (load_le32(s + 52) >> 4);
+ int64_t s21 = 2097151 & (load_le24(s + 55) >> 1);
+ int64_t s22 = 2097151 & (load_le32(s + 57) >> 6);
+ int64_t s23 = (load_le32(s + 60) >> 3);
+ int64_t carry0;
+ int64_t carry1;
+ int64_t carry2;
+ int64_t carry3;
+ int64_t carry4;
+ int64_t carry5;
+ int64_t carry6;
+ int64_t carry7;
+ int64_t carry8;
+ int64_t carry9;
+ int64_t carry10;
+ int64_t carry11;
+ int64_t carry12;
+ int64_t carry13;
+ int64_t carry14;
+ int64_t carry15;
+ int64_t carry16;
+
+ s11 += s23 * 666643;
+ s12 += s23 * 470296;
+ s13 += s23 * 654183;
+ s14 -= s23 * 997805;
+ s15 += s23 * 136657;
+ s16 -= s23 * 683901;
+ s23 = 0;
+
+ s10 += s22 * 666643;
+ s11 += s22 * 470296;
+ s12 += s22 * 654183;
+ s13 -= s22 * 997805;
+ s14 += s22 * 136657;
+ s15 -= s22 * 683901;
+ s22 = 0;
+
+ s9 += s21 * 666643;
+ s10 += s21 * 470296;
+ s11 += s21 * 654183;
+ s12 -= s21 * 997805;
+ s13 += s21 * 136657;
+ s14 -= s21 * 683901;
+ s21 = 0;
+
+ s8 += s20 * 666643;
+ s9 += s20 * 470296;
+ s10 += s20 * 654183;
+ s11 -= s20 * 997805;
+ s12 += s20 * 136657;
+ s13 -= s20 * 683901;
+ s20 = 0;
+
+ s7 += s19 * 666643;
+ s8 += s19 * 470296;
+ s9 += s19 * 654183;
+ s10 -= s19 * 997805;
+ s11 += s19 * 136657;
+ s12 -= s19 * 683901;
+ s19 = 0;
+
+ s6 += s18 * 666643;
+ s7 += s18 * 470296;
+ s8 += s18 * 654183;
+ s9 -= s18 * 997805;
+ s10 += s18 * 136657;
+ s11 -= s18 * 683901;
+ s18 = 0;
+
+ carry6 = (s6 + (1 << 20)) >> 21;
+ s7 += carry6;
+ s6 -= int64_lshift21(carry6);
+ carry8 = (s8 + (1 << 20)) >> 21;
+ s9 += carry8;
+ s8 -= int64_lshift21(carry8);
+ carry10 = (s10 + (1 << 20)) >> 21;
+ s11 += carry10;
+ s10 -= int64_lshift21(carry10);
+ carry12 = (s12 + (1 << 20)) >> 21;
+ s13 += carry12;
+ s12 -= int64_lshift21(carry12);
+ carry14 = (s14 + (1 << 20)) >> 21;
+ s15 += carry14;
+ s14 -= int64_lshift21(carry14);
+ carry16 = (s16 + (1 << 20)) >> 21;
+ s17 += carry16;
+ s16 -= int64_lshift21(carry16);
+
+ carry7 = (s7 + (1 << 20)) >> 21;
+ s8 += carry7;
+ s7 -= int64_lshift21(carry7);
+ carry9 = (s9 + (1 << 20)) >> 21;
+ s10 += carry9;
+ s9 -= int64_lshift21(carry9);
+ carry11 = (s11 + (1 << 20)) >> 21;
+ s12 += carry11;
+ s11 -= int64_lshift21(carry11);
+ carry13 = (s13 + (1 << 20)) >> 21;
+ s14 += carry13;
+ s13 -= int64_lshift21(carry13);
+ carry15 = (s15 + (1 << 20)) >> 21;
+ s16 += carry15;
+ s15 -= int64_lshift21(carry15);
+
+ s5 += s17 * 666643;
+ s6 += s17 * 470296;
+ s7 += s17 * 654183;
+ s8 -= s17 * 997805;
+ s9 += s17 * 136657;
+ s10 -= s17 * 683901;
+ s17 = 0;
+
+ s4 += s16 * 666643;
+ s5 += s16 * 470296;
+ s6 += s16 * 654183;
+ s7 -= s16 * 997805;
+ s8 += s16 * 136657;
+ s9 -= s16 * 683901;
+ s16 = 0;
+
+ s3 += s15 * 666643;
+ s4 += s15 * 470296;
+ s5 += s15 * 654183;
+ s6 -= s15 * 997805;
+ s7 += s15 * 136657;
+ s8 -= s15 * 683901;
+ s15 = 0;
+
+ s2 += s14 * 666643;
+ s3 += s14 * 470296;
+ s4 += s14 * 654183;
+ s5 -= s14 * 997805;
+ s6 += s14 * 136657;
+ s7 -= s14 * 683901;
+ s14 = 0;
+
+ s1 += s13 * 666643;
+ s2 += s13 * 470296;
+ s3 += s13 * 654183;
+ s4 -= s13 * 997805;
+ s5 += s13 * 136657;
+ s6 -= s13 * 683901;
+ s13 = 0;
+
+ s0 += s12 * 666643;
+ s1 += s12 * 470296;
+ s2 += s12 * 654183;
+ s3 -= s12 * 997805;
+ s4 += s12 * 136657;
+ s5 -= s12 * 683901;
+ s12 = 0;
+
+ carry0 = (s0 + (1 << 20)) >> 21;
+ s1 += carry0;
+ s0 -= int64_lshift21(carry0);
+ carry2 = (s2 + (1 << 20)) >> 21;
+ s3 += carry2;
+ s2 -= int64_lshift21(carry2);
+ carry4 = (s4 + (1 << 20)) >> 21;
+ s5 += carry4;
+ s4 -= int64_lshift21(carry4);
+ carry6 = (s6 + (1 << 20)) >> 21;
+ s7 += carry6;
+ s6 -= int64_lshift21(carry6);
+ carry8 = (s8 + (1 << 20)) >> 21;
+ s9 += carry8;
+ s8 -= int64_lshift21(carry8);
+ carry10 = (s10 + (1 << 20)) >> 21;
+ s11 += carry10;
+ s10 -= int64_lshift21(carry10);
+
+ carry1 = (s1 + (1 << 20)) >> 21;
+ s2 += carry1;
+ s1 -= int64_lshift21(carry1);
+ carry3 = (s3 + (1 << 20)) >> 21;
+ s4 += carry3;
+ s3 -= int64_lshift21(carry3);
+ carry5 = (s5 + (1 << 20)) >> 21;
+ s6 += carry5;
+ s5 -= int64_lshift21(carry5);
+ carry7 = (s7 + (1 << 20)) >> 21;
+ s8 += carry7;
+ s7 -= int64_lshift21(carry7);
+ carry9 = (s9 + (1 << 20)) >> 21;
+ s10 += carry9;
+ s9 -= int64_lshift21(carry9);
+ carry11 = (s11 + (1 << 20)) >> 21;
+ s12 += carry11;
+ s11 -= int64_lshift21(carry11);
+
+ s0 += s12 * 666643;
+ s1 += s12 * 470296;
+ s2 += s12 * 654183;
+ s3 -= s12 * 997805;
+ s4 += s12 * 136657;
+ s5 -= s12 * 683901;
+ s12 = 0;
+
+ carry0 = s0 >> 21;
+ s1 += carry0;
+ s0 -= int64_lshift21(carry0);
+ carry1 = s1 >> 21;
+ s2 += carry1;
+ s1 -= int64_lshift21(carry1);
+ carry2 = s2 >> 21;
+ s3 += carry2;
+ s2 -= int64_lshift21(carry2);
+ carry3 = s3 >> 21;
+ s4 += carry3;
+ s3 -= int64_lshift21(carry3);
+ carry4 = s4 >> 21;
+ s5 += carry4;
+ s4 -= int64_lshift21(carry4);
+ carry5 = s5 >> 21;
+ s6 += carry5;
+ s5 -= int64_lshift21(carry5);
+ carry6 = s6 >> 21;
+ s7 += carry6;
+ s6 -= int64_lshift21(carry6);
+ carry7 = s7 >> 21;
+ s8 += carry7;
+ s7 -= int64_lshift21(carry7);
+ carry8 = s8 >> 21;
+ s9 += carry8;
+ s8 -= int64_lshift21(carry8);
+ carry9 = s9 >> 21;
+ s10 += carry9;
+ s9 -= int64_lshift21(carry9);
+ carry10 = s10 >> 21;
+ s11 += carry10;
+ s10 -= int64_lshift21(carry10);
+ carry11 = s11 >> 21;
+ s12 += carry11;
+ s11 -= int64_lshift21(carry11);
+
+ s0 += s12 * 666643;
+ s1 += s12 * 470296;
+ s2 += s12 * 654183;
+ s3 -= s12 * 997805;
+ s4 += s12 * 136657;
+ s5 -= s12 * 683901;
+ s12 = 0;
+
+ carry0 = s0 >> 21;
+ s1 += carry0;
+ s0 -= int64_lshift21(carry0);
+ carry1 = s1 >> 21;
+ s2 += carry1;
+ s1 -= int64_lshift21(carry1);
+ carry2 = s2 >> 21;
+ s3 += carry2;
+ s2 -= int64_lshift21(carry2);
+ carry3 = s3 >> 21;
+ s4 += carry3;
+ s3 -= int64_lshift21(carry3);
+ carry4 = s4 >> 21;
+ s5 += carry4;
+ s4 -= int64_lshift21(carry4);
+ carry5 = s5 >> 21;
+ s6 += carry5;
+ s5 -= int64_lshift21(carry5);
+ carry6 = s6 >> 21;
+ s7 += carry6;
+ s6 -= int64_lshift21(carry6);
+ carry7 = s7 >> 21;
+ s8 += carry7;
+ s7 -= int64_lshift21(carry7);
+ carry8 = s8 >> 21;
+ s9 += carry8;
+ s8 -= int64_lshift21(carry8);
+ carry9 = s9 >> 21;
+ s10 += carry9;
+ s9 -= int64_lshift21(carry9);
+ carry10 = s10 >> 21;
+ s11 += carry10;
+ s10 -= int64_lshift21(carry10);
+
+ s[0] = s0 >> 0;
+ s[1] = s0 >> 8;
+ s[2] = (s0 >> 16) | (s1 << 5);
+ s[3] = s1 >> 3;
+ s[4] = s1 >> 11;
+ s[5] = (s1 >> 19) | (s2 << 2);
+ s[6] = s2 >> 6;
+ s[7] = (s2 >> 14) | (s3 << 7);
+ s[8] = s3 >> 1;
+ s[9] = s3 >> 9;
+ s[10] = (s3 >> 17) | (s4 << 4);
+ s[11] = s4 >> 4;
+ s[12] = s4 >> 12;
+ s[13] = (s4 >> 20) | (s5 << 1);
+ s[14] = s5 >> 7;
+ s[15] = (s5 >> 15) | (s6 << 6);
+ s[16] = s6 >> 2;
+ s[17] = s6 >> 10;
+ s[18] = (s6 >> 18) | (s7 << 3);
+ s[19] = s7 >> 5;
+ s[20] = s7 >> 13;
+ s[21] = s8 >> 0;
+ s[22] = s8 >> 8;
+ s[23] = (s8 >> 16) | (s9 << 5);
+ s[24] = s9 >> 3;
+ s[25] = s9 >> 11;
+ s[26] = (s9 >> 19) | (s10 << 2);
+ s[27] = s10 >> 6;
+ s[28] = (s10 >> 14) | (s11 << 7);
+ s[29] = s11 >> 1;
+ s[30] = s11 >> 9;
+ s[31] = s11 >> 17;
+}
+
+bool ed25519_verify(const uint8_t signature[64], const uint8_t public_key[32],
+ const void *message, size_t message_size)
+{
+ ge_p3 A;
+ if ((signature[63] & 224) != 0 ||
+ !x25519_ge_frombytes_vartime(&A, public_key))
+ return false;
+
+ fe_loose t;
+ fe_neg(&t, &A.X);
+ fe_carry(&A.X, &t);
+ fe_neg(&t, &A.T);
+ fe_carry(&A.T, &t);
+
+ uint8_t pkcopy[32];
+ memcpy(pkcopy, public_key, 32);
+ uint8_t rcopy[32];
+ memcpy(rcopy, signature, 32);
+ union {
+ uint64_t u64[4];
+ uint8_t u8[32];
+ } scopy;
+ memcpy(&scopy.u8[0], signature + 32, 32);
+
+ // https://tools.ietf.org/html/rfc8032#section-5.1.7 requires that s be in
+ // the range [0, order) in order to prevent signature malleability.
+
+ // kOrder is the order of Curve25519 in little-endian form.
+ static const uint64_t kOrder[4] = {
+ UINT64_C(0x5812631a5cf5d3ed),
+ UINT64_C(0x14def9dea2f79cd6),
+ 0,
+ UINT64_C(0x1000000000000000),
+ };
+ for (size_t i = 3;; --i) {
+ uint64_t le = swap_le64(scopy.u64[i]);
+ if (le > kOrder[i]) {
+ return false;
+ } else if (le < kOrder[i]) {
+ break;
+ } else if (i == 0) {
+ return false;
+ }
+ }
+
+ uint8_t h[64];
+ BCRYPT_ALG_HANDLE alg, hash;
+ if (!NT_SUCCESS(BCryptOpenAlgorithmProvider(&alg, BCRYPT_SHA512_ALGORITHM, NULL, 0)) ||
+ !NT_SUCCESS(BCryptCreateHash(alg, &hash, NULL, 0, NULL, 0, 0)) ||
+ !NT_SUCCESS(BCryptHashData(hash, (PUCHAR)signature, 32, 0)) ||
+ !NT_SUCCESS(BCryptHashData(hash, (PUCHAR)public_key, 32, 0)) ||
+ !NT_SUCCESS(BCryptHashData(hash, (PUCHAR)message, message_size, 0)) ||
+ !NT_SUCCESS(BCryptFinishHash(hash, h, 64, 0)) ||
+ !NT_SUCCESS(BCryptDestroyHash(hash)) ||
+ !NT_SUCCESS(BCryptCloseAlgorithmProvider(alg, 0)))
+ return false;
+
+ x25519_sc_reduce(h);
+
+ ge_p2 R;
+ ge_double_scalarmult_vartime(&R, h, &A, scopy.u8);
+
+ uint8_t rcheck[32];
+ x25519_ge_tobytes(rcheck, &R);
+
+ return memcmp_ct(rcheck, rcopy, sizeof(rcheck)) == 0;
+}
+
+static const uint64_t blake2b_iv[8] = {
+ 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 0x3c6ef372fe94f82bULL,
+ 0xa54ff53a5f1d36f1ULL, 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL,
+ 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
+};
+
+static const uint8_t blake2b_sigma[12][16] = {
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
+ { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
+ { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
+ { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
+ { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
+ { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
+ { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
+ { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
+ { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }
+};
+
+#define G(r, i, a, b, c, d) \
+ do { \
+ a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \
+ d = ror64(d ^ a, 32); \
+ c = c + d; \
+ b = ror64(b ^ c, 24); \
+ a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \
+ d = ror64(d ^ a, 16); \
+ c = c + d; \
+ b = ror64(b ^ c, 63); \
+ } while (0)
+
+#define ROUND(r) \
+ do { \
+ G(r, 0, v[0], v[4], v[8], v[12]); \
+ G(r, 1, v[1], v[5], v[9], v[13]); \
+ G(r, 2, v[2], v[6], v[10], v[14]); \
+ G(r, 3, v[3], v[7], v[11], v[15]); \
+ G(r, 4, v[0], v[5], v[10], v[15]); \
+ G(r, 5, v[1], v[6], v[11], v[12]); \
+ G(r, 6, v[2], v[7], v[8], v[13]); \
+ G(r, 7, v[3], v[4], v[9], v[14]); \
+ } while (0)
+
+static void blake2b256_compress(struct blake2b256_state *state,
+ const uint8_t block[128])
+{
+ uint64_t m[16];
+ uint64_t v[16];
+
+ for (int i = 0; i < 16; ++i)
+ m[i] = load_le64(block + i * sizeof(m[i]));
+
+ for (int i = 0; i < 8; ++i)
+ v[i] = state->h[i];
+
+ memcpy(v + 8, blake2b_iv, sizeof(blake2b_iv));
+ v[12] ^= state->t[0];
+ v[13] ^= state->t[1];
+ v[14] ^= state->f[0];
+ v[15] ^= state->f[1];
+
+ for (int i = 0; i < 12; ++i)
+ ROUND(i);
+ for (int i = 0; i < 8; ++i)
+ state->h[i] = state->h[i] ^ v[i] ^ v[i + 8];
+}
+
+void blake2b256_init(struct blake2b256_state *state)
+{
+ memset(state, 0, sizeof(*state));
+ memcpy(state->h, blake2b_iv, sizeof(state->h));
+ state->h[0] ^= 0x01010000 | 32;
+}
+
+void blake2b256_update(struct blake2b256_state *state, const uint8_t *in,
+ unsigned int inlen)
+{
+ const size_t left = state->buflen;
+ const size_t fill = 128 - left;
+
+ if (!inlen)
+ return;
+
+ if (inlen > fill) {
+ state->buflen = 0;
+ memcpy(state->buf + left, in, fill);
+ state->t[0] += 128;
+ state->t[1] += (state->t[0] < 128);
+ blake2b256_compress(state, state->buf);
+ in += fill;
+ inlen -= fill;
+ while (inlen > 128) {
+ state->t[0] += 128;
+ state->t[1] += (state->t[0] < 128);
+ blake2b256_compress(state, in);
+ in += 128;
+ inlen -= 128;
+ }
+ }
+ memcpy(state->buf + state->buflen, in, inlen);
+ state->buflen += inlen;
+}
+
+void blake2b256_final(struct blake2b256_state *state, uint8_t *out)
+{
+ state->t[0] += state->buflen;
+ state->t[1] += (state->t[0] < state->buflen);
+ state->f[0] = (uint64_t)-1;
+ memset(state->buf + state->buflen, 0, 128 - state->buflen);
+ blake2b256_compress(state, state->buf);
+
+ for (int i = 0; i < 8; ++i)
+ store_le64(out + i * sizeof(state->h[i]), state->h[i]);
+}
diff --git a/installer/fetcher/crypto.h b/installer/fetcher/crypto.h
new file mode 100644
index 00000000..5d76dcab
--- /dev/null
+++ b/installer/fetcher/crypto.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2020 Jason A. Donenfeld. All Rights Reserved.
+ */
+
+#ifndef _CRYPTO_H
+#define _CRYPTO_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+struct blake2b256_state {
+ uint64_t h[8];
+ uint64_t t[2];
+ uint64_t f[2];
+ uint8_t buf[128];
+ size_t buflen;
+};
+
+void blake2b256_init(struct blake2b256_state *state);
+void blake2b256_update(struct blake2b256_state *state, const uint8_t *in,
+ unsigned int inlen);
+void blake2b256_final(struct blake2b256_state *state, uint8_t *out);
+
+bool ed25519_verify(const uint8_t signature[64], const uint8_t public_key[32],
+ const void *message, size_t message_size);
+
+#endif
diff --git a/installer/fetcher/fetcher.c b/installer/fetcher/fetcher.c
new file mode 100644
index 00000000..986d9da3
--- /dev/null
+++ b/installer/fetcher/fetcher.c
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Jason A. Donenfeld. All Rights Reserved.
+ */
+
+#include <windows.h>
+#include <delayimp.h>
+#include <commctrl.h>
+#include <shlwapi.h>
+#include <ntsecapi.h>
+#include <sddl.h>
+#include <winhttp.h>
+#include <wintrust.h>
+#include <softpub.h>
+#include <msi.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <wchar.h>
+#include "filelist.h"
+#include "crypto.h"
+#include "systeminfo.h"
+#include "constants.h"
+
+static char msi_filename[MAX_PATH];
+static volatile bool msi_filename_is_set;
+static volatile size_t g_current, g_total;
+static HWND progress;
+static HANDLE filehandle = INVALID_HANDLE_VALUE;
+
+static wchar_t *L(const char *a)
+{
+ static wchar_t w[0x2000];
+ if (!MultiByteToWideChar(CP_UTF8, 0, a, -1, w, sizeof(w)))
+ abort();
+ return w;
+}
+
+static bool random_string(char hex[static 65])
+{
+ uint8_t bytes[32];
+ if (!RtlGenRandom(bytes, sizeof(bytes)))
+ return false;
+ for (int i = 0; i < 32; ++i) {
+ hex[i * 2] = 87U + (bytes[i] >> 4) + ((((bytes[i] >> 4) - 10U) >> 8) & ~38U);
+ hex[i * 2 + 1] = 87U + (bytes[i] & 0xf) + ((((bytes[i] & 0xf) - 10U) >> 8) & ~38U);
+ }
+ hex[64] = '\0';
+ return true;
+}
+
+static void set_status(HWND progress, const char *status)
+{
+ LONG_PTR current_style = GetWindowLongPtrA(progress, GWL_STYLE);
+ char buf[0x1000];
+ g_total = 0;
+ _snprintf_s(buf, sizeof(buf), _TRUNCATE, "WireGuard: %s...", status);
+ SetWindowTextA(progress, buf);
+ if (!(current_style & PBS_MARQUEE)) {
+ SendMessageA(progress, PBM_SETRANGE32, 0, 100);
+ SendMessageA(progress, PBM_SETPOS, 0, 0);
+ SetWindowLongPtrA(progress, GWL_STYLE, current_style | PBS_MARQUEE);
+ SendMessageA(progress, PBM_SETMARQUEE, TRUE, 0);
+ }
+}
+
+static void set_progress(HWND progress, size_t current, size_t total)
+{
+ g_current = current;
+ g_total = total;
+ PostMessageA(progress, WM_APP, 0, 0);
+}
+
+static DWORD __stdcall download_thread(void *param)
+{
+ DWORD ret = 1, bytes_read, bytes_written, enable_http2 = WINHTTP_PROTOCOL_FLAG_HTTP2;
+ HINTERNET session = NULL, connection = NULL, request = NULL;
+ uint8_t hash[32], computed_hash[32], buf[512 * 1024];
+ char download_path[MAX_FILENAME_LEN + sizeof(msi_path)], random_filename[65];
+ wchar_t total_bytes_str[22];
+ size_t total_bytes, current_bytes;
+ const char *arch;
+ struct blake2b256_state hasher;
+ SECURITY_ATTRIBUTES security_attributes = { .nLength = sizeof(security_attributes) };
+ WINTRUST_FILE_INFO wintrust_fileinfo = { .cbStruct = sizeof(wintrust_fileinfo) };
+ WINTRUST_DATA wintrust_data = {
+ .cbStruct = sizeof(wintrust_data),
+ .dwUIChoice = WTD_UI_NONE,
+ .fdwRevocationChecks = WTD_REVOKE_WHOLECHAIN,
+ .dwUnionChoice = WTD_CHOICE_FILE,
+ .dwStateAction = WTD_STATEACTION_VERIFY,
+ .pFile = &wintrust_fileinfo
+ };
+
+ (void)param;
+
+ set_status(progress, "determining paths");
+ if (!ConvertStringSecurityDescriptorToSecurityDescriptorA("O:BAD:PAI(A;;FA;;;BA)", SDDL_REVISION_1, &security_attributes.lpSecurityDescriptor, NULL))
+ goto out;
+ if (!GetWindowsDirectoryA(msi_filename, sizeof(msi_filename)) || !PathAppendA(msi_filename, "Temp"))
+ goto out;
+ if (!random_string(random_filename))
+ goto out;
+ if (!PathAppendA(msi_filename, random_filename))
+ goto out;
+
+ set_status(progress, "determining architecture");
+ arch = architecture();
+ if (!arch)
+ goto out;
+
+ set_status(progress, "connecting to server");
+ session = WinHttpOpen(L(useragent()), is_win7() ? WINHTTP_ACCESS_TYPE_DEFAULT_PROXY : WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, NULL, NULL, 0);
+ if (!session)
+ goto out;
+ WinHttpSetOption(session, WINHTTP_OPTION_ENABLE_HTTP_PROTOCOL, &enable_http2, sizeof(enable_http2)); // Don't check return value, in case of old Windows
+ connection = WinHttpConnect(session, L(server), port, 0);
+ if (!connection)
+ goto out;
+
+ set_status(progress, "downloading installer list");
+ request = WinHttpOpenRequest(connection, L"GET", L(msi_path latest_version_file), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_REFRESH | WINHTTP_FLAG_SECURE);
+ if (!request)
+ goto out;
+ if (!WinHttpSendRequest(request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
+ goto out;
+ if (!WinHttpReceiveResponse(request, NULL))
+ goto out;
+ if (!WinHttpReadData(request, buf, sizeof(buf), &bytes_read))
+ goto out;
+ WinHttpCloseHandle(request);
+ request = NULL;
+ if (bytes_read <= 0 || bytes_read >= sizeof(buf))
+ goto out;
+
+ set_status(progress, "verifying installer list");
+ memcpy(download_path, msi_path, strlen(msi_path));
+ if (!extract_newest_file(download_path + strlen(msi_path), hash, (const char *)buf, bytes_read, arch))
+ goto out;
+
+ set_status(progress, "creating temporary file");
+ filehandle = CreateFileA(msi_filename, GENERIC_WRITE | DELETE, 0, &security_attributes, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY, NULL);
+ if (filehandle == INVALID_HANDLE_VALUE)
+ goto out;
+ msi_filename_is_set = true;
+
+ set_status(progress, "downloading installer");
+ request = WinHttpOpenRequest(connection, L"GET", L(download_path), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
+ if (!request)
+ goto out;
+ if (!WinHttpSendRequest(request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
+ goto out;
+ if (!WinHttpReceiveResponse(request, NULL))
+ goto out;
+ bytes_read = sizeof(total_bytes_str);
+ if (!WinHttpQueryHeaders(request, WINHTTP_QUERY_CONTENT_LENGTH, WINHTTP_HEADER_NAME_BY_INDEX, total_bytes_str, &bytes_read, WINHTTP_NO_HEADER_INDEX))
+ goto out;
+ total_bytes = wcstoul(total_bytes_str, NULL, 10);
+ if (total_bytes > 100 * 1024 * 1024)
+ goto out;
+ blake2b256_init(&hasher);
+ set_progress(progress, 0, total_bytes);
+ for (current_bytes = 0;;) {
+ if (!WinHttpReadData(request, buf, 8192, &bytes_read))
+ goto out;
+ if (!bytes_read)
+ break;
+ current_bytes += bytes_read;
+ if (current_bytes > 100 * 1024 * 1024)
+ goto out;
+ blake2b256_update(&hasher, buf, bytes_read);
+ if (!WriteFile(filehandle, buf, bytes_read, &bytes_written, NULL) || bytes_read != bytes_written)
+ goto out;
+ set_progress(progress, current_bytes, total_bytes);
+ }
+
+ set_status(progress, "verifying installer");
+ blake2b256_final(&hasher, computed_hash);
+ if (memcmp(hash, computed_hash, sizeof(hash)))
+ goto out;
+ CloseHandle(filehandle); //TODO: I wish this wasn't required.
+ filehandle = INVALID_HANDLE_VALUE;
+ wintrust_fileinfo.pcwszFilePath = L(msi_filename);
+ if (WinVerifyTrust(INVALID_HANDLE_VALUE, &(GUID)WINTRUST_ACTION_GENERIC_VERIFY_V2, &wintrust_data))
+ goto out;
+
+ set_status(progress, "launching installer");
+ ShowWindow(progress, SW_HIDE);
+ ret = MsiInstallProductA(msi_filename, NULL);
+ ret = ret == ERROR_INSTALL_USEREXIT ? ERROR_SUCCESS : ret;
+
+out:
+ if (request)
+ WinHttpCloseHandle(request);
+ if (connection)
+ WinHttpCloseHandle(connection);
+ if (session)
+ WinHttpCloseHandle(session);
+ if (security_attributes.lpSecurityDescriptor)
+ LocalFree(security_attributes.lpSecurityDescriptor);
+
+ if (ret) {
+ ShowWindow(progress, SW_SHOWDEFAULT);
+ if (MessageBoxA(progress, "Something went wrong when downloading the WireGuard installer. Would you like to open your web browser to the MSI download page?", "Download Error", MB_YESNO | MB_ICONWARNING) == IDYES)
+ ShellExecuteA(progress, NULL, "https://" server msi_path, NULL, NULL, SW_SHOWNORMAL);
+ }
+ exit(ret);
+ return ret;
+}
+
+static int cleanup(void)
+{
+ BOOL did_delete_via_handle = FALSE;
+ FILE_DISPOSITION_INFO disposition = { TRUE };
+ if (filehandle != INVALID_HANDLE_VALUE) {
+ did_delete_via_handle = SetFileInformationByHandle(filehandle, FileDispositionInfo, &disposition, sizeof(disposition));
+ CloseHandle(filehandle);
+ filehandle = INVALID_HANDLE_VALUE;
+ }
+ if (msi_filename_is_set && !did_delete_via_handle) {
+ //TODO: how does DeleteFile deal with reparse points?
+ for (int i = 0; i < 200 && !DeleteFileA(msi_filename) && GetLastError() != ERROR_FILE_NOT_FOUND; ++i)
+ Sleep(200);
+ }
+ return 0;
+}
+
+static FARPROC WINAPI delayed_load_library_hook(unsigned dliNotify, PDelayLoadInfo pdli)
+{
+ HMODULE library;
+ if (dliNotify != dliNotePreLoadLibrary)
+ return NULL;
+ library = LoadLibraryExA(pdli->szDll, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
+ if (!library)
+ abort();
+ return (FARPROC)library;
+}
+
+PfnDliHook __pfnDliNotifyHook2 = delayed_load_library_hook;
+
+static LRESULT CALLBACK wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
+{
+ (void)uIdSubclass; (void)dwRefData;
+
+ switch (uMsg) {
+ case WM_CLOSE:
+ case WM_DESTROY: {
+ LRESULT ret = DefSubclassProc(hWnd, uMsg, wParam, lParam);
+ exit(0);
+ return ret;
+ }
+ case WM_APP: if (g_total) {
+ char buf[0x1000], *start, *paren;
+ LONG_PTR current_style;
+ int chars = GetWindowTextA(progress, buf, sizeof(buf));
+ if (chars) {
+ start = buf + chars;
+ if (start[-1] == '.' && start[-2] == '.' && start[-3] == '.')
+ start -= 3;
+ else if ((paren = memchr(buf, '(', chars)) && paren > buf)
+ start = paren - 1;
+ *start = '\0';
+ _snprintf_s(start, sizeof(buf) - (start - buf), _TRUNCATE, " (%.2f%%)", g_current * 100.0f / g_total);
+ SetWindowTextA(progress, buf);
+ }
+ current_style = GetWindowLongPtrA(progress, GWL_STYLE);
+ if (current_style & PBS_MARQUEE) {
+ SetWindowLongPtrA(progress, GWL_STYLE, current_style & ~PBS_MARQUEE);
+ SendMessageA(progress, PBM_SETMARQUEE, FALSE, 0);
+ }
+ SendMessageA(progress, PBM_SETRANGE32, 0, (LPARAM)g_total);
+ SendMessageA(progress, PBM_SETPOS, (WPARAM)g_current, 0);
+ break;
+ }
+ }
+ return DefSubclassProc(hWnd, uMsg, wParam, lParam);
+}
+
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pCmdLine, int nCmdShow)
+{
+ MSG msg;
+ HICON icon;
+ HDC dc;
+ float scale;
+
+ (void)hPrevInstance; (void)pCmdLine; (void)nCmdShow;
+
+ if (!SetDllDirectoryA("") || !SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32))
+ return 1;
+
+ InitCommonControlsEx(&(INITCOMMONCONTROLSEX){ .dwSize = sizeof(INITCOMMONCONTROLSEX), .dwICC = ICC_PROGRESS_CLASS });
+
+ progress = CreateWindowExA(0, PROGRESS_CLASS, "WireGuard Installer",
+ (WS_OVERLAPPEDWINDOW & ~(WS_BORDER | WS_THICKFRAME | WS_MAXIMIZEBOX)) | PBS_MARQUEE | PBS_SMOOTH,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ NULL, NULL, hInstance, NULL);
+ SetWindowSubclass(progress, wndproc, 0, 0);
+ dc = GetDC(progress);
+ scale = GetDeviceCaps(dc, LOGPIXELSY) / 96.0f;
+ ReleaseDC(progress, dc);
+ icon = LoadIconA(hInstance, MAKEINTRESOURCE(7));
+ SendMessageA(progress, WM_SETICON, ICON_BIG, (LPARAM)icon);
+ SendMessageA(progress, WM_SETICON, ICON_SMALL, (LPARAM)icon);
+ SendMessageA(progress, PBM_SETMARQUEE, TRUE, 0);
+ SetWindowPos(progress, HWND_TOPMOST, -1, -1, 500 * scale, 80 * scale, SWP_NOMOVE | SWP_SHOWWINDOW);
+
+ _onexit(cleanup);
+ CreateThread(NULL, 0, download_thread, NULL, 0, NULL);
+
+ while (GetMessage(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ return 0;
+}
diff --git a/installer/fetcher/filelist.c b/installer/fetcher/filelist.c
new file mode 100644
index 00000000..ea4410d0
--- /dev/null
+++ b/installer/fetcher/filelist.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Jason A. Donenfeld. All Rights Reserved.
+ */
+
+#include "constants.h"
+#include "crypto.h"
+#include "filelist.h"
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static inline int decode_base64(const char src[static 4])
+{
+ int val = 0;
+
+ for (unsigned int i = 0; i < 4; ++i)
+ val |= (-1
+ + ((((('A' - 1) - src[i]) & (src[i] - ('Z' + 1))) >> 8) & (src[i] - 64))
+ + ((((('a' - 1) - src[i]) & (src[i] - ('z' + 1))) >> 8) & (src[i] - 70))
+ + ((((('0' - 1) - src[i]) & (src[i] - ('9' + 1))) >> 8) & (src[i] + 5))
+ + ((((('+' - 1) - src[i]) & (src[i] - ('+' + 1))) >> 8) & 63)
+ + ((((('/' - 1) - src[i]) & (src[i] - ('/' + 1))) >> 8) & 64)
+ ) << (18 - 6 * i);
+ return val;
+}
+
+bool signify_pubkey_from_base64(uint8_t key[static 42], const char base64[static 56])
+{
+ unsigned int i;
+ volatile uint8_t ret = 0;
+ int val;
+
+ for (i = 0; i < 42 / 3; ++i) {
+ val = decode_base64(&base64[i * 4]);
+ ret |= (uint32_t)val >> 31;
+ key[i * 3 + 0] = (val >> 16) & 0xff;
+ key[i * 3 + 1] = (val >> 8) & 0xff;
+ key[i * 3 + 2] = val & 0xff;
+ }
+
+ return 1 & ((ret - 1) >> 8);
+}
+
+bool signify_signature_from_base64(uint8_t sig[static 74], const char base64[static 100])
+{
+ unsigned int i;
+ volatile uint8_t ret = 0;
+ int val;
+
+ if (base64[99] != '=')
+ return false;
+
+ for (i = 0; i < 74 / 3; ++i) {
+ val = decode_base64(&base64[i * 4]);
+ ret |= (uint32_t)val >> 31;
+ sig[i * 3 + 0] = (val >> 16) & 0xff;
+ sig[i * 3 + 1] = (val >> 8) & 0xff;
+ sig[i * 3 + 2] = val & 0xff;
+ }
+ val = decode_base64((const char[]){ base64[i * 4 + 0], base64[i * 4 + 1], base64[i * 4 + 2], 'A' });
+ ret |= ((uint32_t)val >> 31) | (val & 0xff);
+ sig[i * 3 + 0] = (val >> 16) & 0xff;
+ sig[i * 3 + 1] = (val >> 8) & 0xff;
+
+ return 1 & ((ret - 1) >> 8);
+}
+
+bool hash_from_hex(uint8_t hash[static 32], const char hex[static 64])
+{
+ uint8_t c, c_acc, c_alpha0, c_alpha, c_num0, c_num, c_val;
+ volatile uint8_t ret = 0;
+
+ for (unsigned int i = 0; i < 64; i += 2) {
+ c = (uint8_t)hex[i];
+ c_num = c ^ 48U;
+ c_num0 = (c_num - 10U) >> 8;
+ c_alpha = (c & ~32U) - 55U;
+ c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8;
+ ret |= ((c_num0 | c_alpha0) - 1) >> 8;
+ c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha);
+ c_acc = c_val * 16U;
+
+ c = (uint8_t)hex[i + 1];
+ c_num = c ^ 48U;
+ c_num0 = (c_num - 10U) >> 8;
+ c_alpha = (c & ~32U) - 55U;
+ c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8;
+ ret |= ((c_num0 | c_alpha0) - 1) >> 8;
+ c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha);
+ hash[i / 2] = c_acc | c_val;
+ }
+
+ return 1 & ((ret - 1) >> 8);
+}
+
+static uint64_t parse_version(const char *str, size_t len)
+{
+ uint64_t version = 0;
+ unsigned long nibble;
+ const char *limit = str + len;
+ char *end;
+
+ for (int shift = 64 - 16; shift >= 0; shift -= 16, str = end + 1) {
+ if (str[0] == '-' || str[0] == '+')
+ return 0;
+ nibble = strtoul(str, &end, 10);
+ if (nibble > UINT16_MAX)
+ return 0;
+ version |= (uint64_t)nibble << shift;
+ if (end >= limit)
+ break;
+ if (end[0] != '.')
+ return 0;
+ }
+ return version;
+}
+
+bool extract_newest_file(char filename[static MAX_FILENAME_LEN], uint8_t hash[static 32], const char *list, size_t len, const char *arch)
+{
+ const char *first_nl, *second_nl, *line_start, *line_end;
+ char msi_prefix[sizeof(msi_arch_prefix) + 10];
+ int msi_prefix_len;
+ uint8_t pubkey[42], signature[74];
+ uint64_t biggest_version = 0, version;
+
+ if ((msi_prefix_len = _snprintf_s(msi_prefix, sizeof(msi_prefix), _TRUNCATE, msi_arch_prefix, arch)) < 0)
+ return false;
+ if (!signify_pubkey_from_base64(pubkey, release_public_key_base64))
+ return false;
+ first_nl = memchr(list, '\n', len);
+ if (!first_nl)
+ return false;
+ second_nl = memchr(first_nl + 1, '\n', len - (first_nl + 1 - list));
+ if (!second_nl)
+ return false;
+ if (len < 19 || memcmp(list, "untrusted comment: ", 19))
+ return false;
+ if (second_nl - first_nl != 101)
+ return false;
+ if (!signify_signature_from_base64(signature, first_nl + 1))
+ return false;
+ if (memcmp(pubkey, signature, 10))
+ return false;
+ if (!ed25519_verify(signature + 10, pubkey + 10, second_nl + 1, len - (second_nl + 1 - list)))
+ return false;
+ for (line_start = second_nl + 1; line_start < list + len; line_start = line_end + 1) {
+ line_end = memchr(line_start + 1, '\n', len - (line_start + 1 - list));
+ if (!line_end)
+ break;
+ if ((size_t)(line_end - line_start) < (64 + 2 + msi_prefix_len + strlen(msi_suffix) + 1) || line_start[64] != ' ' || line_start[65] != ' ')
+ continue;
+ if (memcmp(msi_prefix, line_start + 66, msi_prefix_len) || memcmp(msi_suffix, line_end - strlen(msi_suffix), strlen(msi_suffix)))
+ continue;
+ if (line_end - line_start - 66 > MAX_FILENAME_LEN - 1)
+ continue;
+ version = parse_version(line_start + 66 + msi_prefix_len, line_end - strlen(msi_suffix) - line_start - 66 - msi_prefix_len);
+ if (version < biggest_version)
+ continue;
+ if (!hash_from_hex(hash, line_start))
+ continue;
+ memcpy(filename, line_start + 66, line_end - line_start - 66);
+ filename[line_end - line_start - 66] = '\0';
+ biggest_version = version;
+ }
+ return biggest_version > 0;
+}
diff --git a/installer/fetcher/filelist.h b/installer/fetcher/filelist.h
new file mode 100644
index 00000000..e0588837
--- /dev/null
+++ b/installer/fetcher/filelist.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2020 Jason A. Donenfeld. All Rights Reserved.
+ */
+
+#ifndef _FILELIST_H
+#define _FILELIST_H
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+enum { MAX_FILENAME_LEN = 0x400 };
+
+bool extract_newest_file(char filename[static MAX_FILENAME_LEN], uint8_t hash[static 32], const char *list, size_t len, const char *arch);
+
+#endif
diff --git a/installer/fetcher/icon.svg b/installer/fetcher/icon.svg
new file mode 100644
index 00000000..06a2db5c
--- /dev/null
+++ b/installer/fetcher/icon.svg
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="478.99808"
+ height="478.99808"
+ version="1.1"
+ viewBox="0 0 478.99808 478.99808"
+ xml:space="preserve"
+ id="svg35"
+ sodipodi:docname="wireguard-install.svg"
+ inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"><metadata
+ id="metadata39"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1023"
+ id="namedview37"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:zoom="1.2639534"
+ inkscape:cx="246.80998"
+ inkscape:cy="256.18157"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg35" /><defs
+ id="defs5"><clipPath
+ id="a"><path
+ d="M 0,300 H 300 V 0 H 0 Z"
+ id="path2" /></clipPath></defs><g
+ id="g2416"
+ transform="scale(1.3897777)"><g
+ transform="matrix(1.1488658,0,0,-1.1488658,0,344.65974)"
+ id="g33"><g
+ clip-path="url(#a)"
+ id="g31"><g
+ transform="translate(177.57,268.56)"
+ id="g9"><path
+ d="M 0,0 C 0.969,-0.066 2.097,0.81 3.987,1.635 2.125,2.437 1.014,3.29 0.048,3.228 -0.948,3.165 -2.544,2.197 -2.519,1.65 -2.492,1.047 -0.987,0.067 0,0"
+ fill="#871719"
+ id="path7" /></g><g
+ transform="translate(179.32,268.11)"
+ id="g13"><path
+ d="M 0,0 C 0.969,-0.066 2.097,0.81 3.987,1.635 2.125,2.437 1.014,3.29 0.048,3.228 -0.948,3.165 -2.544,2.197 -2.519,1.65 -2.492,1.047 -0.987,0.067 0,0"
+ fill="#871719"
+ id="path11" /></g><g
+ transform="translate(299.74,154.44)"
+ id="g17"><path
+ d="m 0,0 c 0,0 6.94,145.56 -153.04,145.56 -141.48,0 -145.9,-139.63 -145.9,-139.63 0,0 -20.811,-160.37 149.16,-160.37 C 13.24,-154.44 0,0 0,0"
+ fill="#871719"
+ id="path15" /></g><g
+ transform="translate(133.86,128.17)"
+ id="g21"><path
+ d="m 0,0 c -2.627,-1.39 -4.65,-2.414 -6.63,-3.517 -8.1,-4.512 -15.026,-10.419 -20.544,-17.868 -1.784,-2.409 -3.01,-2.603 -5.727,-0.941 -35.338,21.61 -37.609,75.843 0.983,99.453 C -1.901,95.491 36.447,84.267 50.817,56.65 53.54,51.416 53.886,43.359 52.162,37.868 46.207,18.913 32.147,8.282 12.849,3.766 18.538,8.636 23.067,14.159 24.508,21.791 25.96,29.478 24.424,36.429 19.966,42.747 13.193,52.343 0.098,56.291 -10.845,52.136 -22.726,47.625 -29.235,36.782 -28.061,23.453 -26.971,11.072 -17.577,3.048 0,0"
+ fill="#ffffff"
+ id="path19" /></g><g
+ transform="translate(58.513,66.293)"
+ id="g25"><path
+ d="M 0,0 C 2.838,19.152 25.265,36.788 44.23,34.776 38.356,26.832 35.643,17.846 34.988,8.883 28.686,7.722 22.747,6.941 16.981,5.478 11.304,4.037 5.803,1.903 0,0"
+ fill="#ffffff"
+ id="path23" /></g><g
+ transform="translate(183.79,273.09)"
+ id="g29"><path
+ d="M 0,0 C 1.061,0.812 2.155,1.494 3.472,0.408 4.222,-0.209 4.95,-0.849 5.858,-1.624 4.731,-2.219 3.816,-2.72 2.883,-3.191 1.577,-3.849 0.601,-3.409 -0.189,-2.369 -0.831,-1.525 -0.946,-0.724 0,0 m 15.447,-157.8 c -1.598,1.382 -2.611,1.381 -4.485,0.182 -6.359,-4.068 -12.867,-7.922 -19.481,-11.562 -3.792,-2.086 -7.898,-3.599 -12.653,-5.724 1.633,-0.421 2.418,-0.619 3.201,-0.827 17.776,-4.73 27.272,-20.335 23.065,-37.813 -3.741,-15.544 -19.52,-25.482 -34.812,-22.86 -12.748,2.186 -23.877,12.772 -25.735,25.456 -2.026,13.824 4.859,27.119 17.108,32.689 6.794,3.089 13.771,5.778 20.549,8.9 7.706,3.551 16.038,6.355 22.766,11.296 16.7,12.262 27.012,29.145 31.033,49.523 2.408,12.207 2.245,24.36 -3.339,35.95 -4.286,8.895 -11.319,15.357 -18.875,21.253 -7.775,6.068 -16.007,11.554 -23.747,17.664 -2.095,1.653 -3.509,4.505 -4.478,7.09 -0.411,1.095 0.925,4.066 1.819,4.227 4.746,0.852 9.596,1.29 14.425,1.473 5.574,0.21 11.164,0.032 16.746,-0.042 1.21,-0.015 2.853,0.141 3.549,-0.542 2.891,-2.843 5.159,-1.014 7.166,0.856 1.689,1.573 2.893,3.668 4.236,5.433 -0.815,0.12 -2.487,0.541 -4.168,0.581 -5.613,0.133 -11.233,0.047 -16.843,0.253 -1,0.037 -1.963,1.066 -2.942,1.637 1.031,0.409 2.058,1.165 3.093,1.175 9.682,0.091 19.366,0.054 29.057,0.054 C 41.713,-6.44 34.98,0.458 28.998,2.328 28.953,1.646 28.911,1.011 28.867,0.334 22.923,0.193 17.089,0.304 11.789,3.122 10.393,3.865 9.48,5.516 8.343,6.749 6.912,8.3 5.738,10.296 3.994,11.308 c -3.576,2.076 -7.48,3.58 -11.211,5.397 -13.259,6.458 -27.262,6.231 -42.302,4.854 8.991,-2.092 17.11,-3.982 25.23,-5.872 -0.093,-0.494 -0.185,-0.987 -0.278,-1.481 -10.86,-1.455 -21.134,2.528 -31.756,4.003 3.849,-2.254 7.749,-4.35 11.778,-6.158 4.095,-1.837 8.316,-3.39 12.538,-5.091 -5.364,-4.583 -10.746,-5.588 -17.488,-4.048 -3.686,0.842 -7.585,1.29 -11.348,1.106 -3.887,-0.19 -7.802,-1.147 -11.332,-3.506 3.78,-1.916 7.263,-3.506 10.549,-5.432 1.355,-0.795 2.909,-2.144 3.287,-3.536 0.904,-3.333 1.166,-6.841 1.687,-10.281 -6.188,-0.701 -17.071,-6.994 -19.27,-11.09 9.512,-1.831 19.868,0.383 28.942,-5.746 -2.989,-2.262 -9.949,-5.075 -12.502,-7.007 3.156,-0.827 10.469,-0.423 13.33,-0.229 2.409,0.164 3.521,0.223 4.508,-0.59 l 28.001,-21.921 c 2.944,-2.374 14.835,-13.629 17.939,-20.704 2.643,-6.023 2.966,-11.148 2.965,-12.398 -0.002,-3.355 -0.413,-8.609 -2.721,-14.469 -0.969,-2.461 -3.812,-7.912 -9.677,-14.267 -9.09,-9.847 -20.783,-15.17 -33.57,-17.807 -29.732,-6.13 -54.436,-37.881 -47.462,-72.884 8.142,-40.866 53.247,-62.991 90.107,-43.552 23.824,12.564 36.456,37.078 33.072,63.762 -2.045,16.12 -9.338,29.269 -21.563,39.839"
+ fill="#ffffff"
+ id="path27" /></g></g></g><image
+ width="292.86456"
+ height="292.86456"
+ preserveAspectRatio="none"
+ style="image-rendering:optimizeQuality"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAABGdBTUEAALGPC/xhBQAAACBjSFJN AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QA/wD/AP+gvaeTAACA AElEQVR42uz9d7xl2VnfCX+ftdbe+8Qb694KXVVd1Tm3pFZGiSwQYMwYp9cYDzjwGgcMg2EMJth4 7Hk9tsf2MGN7xtiYDxjLwGCShBAogKRuhVZnde6qrurKN5+0917h/WOtfe6pUrfULbUQtO/Tn+p7 7sn3nPXk3/N7YE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2 ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2 ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2 ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE9epMiX+w3syZdOQgjT71dEwpXX/VGV5r1e+bc83/V78sWJ +nK/gT354qVR6tmfzeVZxf/joPwv9PfsKf+XRv5YHIg9efFypdd/mZRegPAC13PFbS903y/8xdPf MWvM9gzCyyPmy/0G9uSly6x3/1wK/hKVv1HcWQWeVfDne64r7/f5rpvK+94Pw6+B1wGHfeChT98j cBCRqy97Ve8f4dH7Py1AeDgEeQL4xB/2B/4Klr0I4I+hNB7wZfDuL0XZX+SbQ9Kjw+lnnuH9J09y 5B3vkDdPCJ86iegbCRsnT/AxuxLm6nU5wJFQmZNy5tJFRN3OcH0o9XgQCIFibim054Ign+HY0vFQ 16t8183R8//rJ4P8zetmooAQhL2o4CXLngH4YyxfpAF4MQovV9z+Wd7dpuu1LWV9s+A9z3je8Ton Zx7S4eKx4L+pb6ZK+Y//YF2rVVVcunip3Ue36mCyatLNLGvGZqKd0tqi1AKirNQyFpE8qCBOghtY r9sdmxWlbVmx1cTVstydDDYH9Wpbxj/6FdeMvtzfxx9H2TMAf0zk+bz+S4gCns/Tf677fc7nHG7C dgv6k0ucWtiHs6i1hx5jePSYfddyKwD8w48/k1eDyQFX6v2Vby0Hq5fWd3aOjDd2rrEM9itll7V2 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 me8hWHTJHr9/WifF/vpvYw3D6aHRB650ArDYKQzlyBbxDuZvG943b/Bm7jn13HvdKyVIa2gM6f75 MHne8GHWuJm7voi/v6gJZ7hTL3I286+dXheu3OWHty26Pv8Z59dnZPzpi91f/+0utcfv85oDcOUu vYh3YBbclm5n7jEwG1XArANYFA0MnRfMOoR54wlXuW2R8Q8fu2iHn08xFoXqV3sMzDoABo+df/8s uL7o99/32ncA/20vdQ337XV5hd4AVzqF4eP0Ho/b67nS7WHB61+tjXZ4W7p8vt11L3BtkUHPP254 /6L79nrtvX6/2tp3APvrD2Vdy3lwNYGL+VB9eH2eoPR8j12EAXw67zPhBH7utkWGlvLsa2HOhWu4 np5zr9edf324NqP+Azf8T+cL3V/7C65ujMnoFjmHvUBHrvL4vW6fxwAWrWvJkfeKFJ7vvs/k/qsZ 7x+aYV/r2ncA++szWc+XOswDjVf7m+dzBs/3Pj7THfTTAdOezwFcy3M83337a3/tr/21v/bX/tpf +2t/7a/9tb/21/7aX/trf+2v/bW/9tf+2l/7a3/tr/21v/bX/tpf+2t/7a/9tb/21/7aX/trf+2v /bW/9tf+2l/7a3/tr/21v/bX/tpf+2t/7a/9tb/21/7aX/trf+2v/bW/9tf+2l/7a3/tr/21v/bX /tpf+2t/7a/9tb/21/7aX/trf+2v/bW/9tf+2l/7a3/tr/21v/bX/tpf+2t/7a/9tb/21/7aX/tr f+2v/bW/9tf+2l/7a3/tr8/u9f8Hy0CzRRKiMyQAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjAtMTEt MTJUMTU6MjQ6MjcrMDA6MDDoOkHhAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIwLTExLTEyVDE1OjI0 OjI3KzAwOjAwmWf5XQAAAABJRU5ErkJggg== "
+ id="image1086"
+ x="47.787258"
+ y="85.720528" /></g></svg>
diff --git a/installer/fetcher/manifest.xml b/installer/fetcher/manifest.xml
new file mode 100644
index 00000000..92205f98
--- /dev/null
+++ b/installer/fetcher/manifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="wireguard-installer" type="win32" />
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows 10 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
+ <!-- Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
+ <!-- Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
+ </application>
+ </compatibility>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" />
+ </dependentAssembly>
+ </dependency>
+ <application xmlns="urn:schemas-microsoft-com:asm.v3">
+ <windowsSettings>
+ <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
+ <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True</dpiAware>
+ </windowsSettings>
+ </application>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+ <security>
+ <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+ <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+</assembly>
diff --git a/installer/fetcher/resources.rc b/installer/fetcher/resources.rc
new file mode 100644
index 00000000..574dce97
--- /dev/null
+++ b/installer/fetcher/resources.rc
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2020 Jason A. Donenfeld. All Rights Reserved.
+ */
+
+#include <windows.h>
+#include "version.h"
+
+#pragma code_page(65001) // UTF-8
+
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST manifest.xml
+7 ICON icon.ico
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION VERSION_ARRAY
+PRODUCTVERSION VERSION_ARRAY
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_APP
+FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "WireGuard LLC"
+ VALUE "FileDescription", "WireGuard Installer: Fast, Modern, Secure VPN Tunnel"
+ VALUE "FileVersion", VERSION_STR
+ VALUE "InternalName", "wireguard-installer"
+ VALUE "LegalCopyright", "Copyright © 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved."
+ VALUE "OriginalFilename", "wireguard-installer.exe"
+ VALUE "ProductName", "WireGuard"
+ VALUE "ProductVersion", VERSION_STR
+ VALUE "Comments", "https://www.wireguard.com/"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 0x4b0
+ END
+END
diff --git a/installer/fetcher/systeminfo.c b/installer/fetcher/systeminfo.c
new file mode 100644
index 00000000..39f34389
--- /dev/null
+++ b/installer/fetcher/systeminfo.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Jason A. Donenfeld. All Rights Reserved.
+ */
+
+#include "systeminfo.h"
+#include "version.h"
+#include <windows.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+extern NTAPI __declspec(dllimport) void RtlGetNtVersionNumbers(DWORD *MajorVersion, DWORD *MinorVersion, DWORD *BuildNumber);
+
+const char *architecture(void)
+{
+ static const char *cached_arch;
+ HMODULE kernel32;
+ BOOL(WINAPI *IsWow64Process2)(HANDLE hProcess, USHORT *pProcessMachine, USHORT *pNativeMachine);
+ USHORT process_machine, native_machine;
+ BOOL is_wow64_process;
+
+ if (cached_arch)
+ return cached_arch;
+
+ kernel32 = GetModuleHandleA("kernel32.dll");
+ if (!kernel32)
+ return NULL;
+ IsWow64Process2 = (void *)GetProcAddress(kernel32, "IsWow64Process2");
+ if (IsWow64Process2) {
+ if (!IsWow64Process2(GetCurrentProcess(), &process_machine, &native_machine))
+ return NULL;
+ switch (native_machine) {
+ case IMAGE_FILE_MACHINE_I386:
+ return cached_arch = "x86";
+ case IMAGE_FILE_MACHINE_AMD64:
+ return cached_arch = "amd64";
+ case IMAGE_FILE_MACHINE_ARMNT:
+ return cached_arch = "arm";
+ case IMAGE_FILE_MACHINE_ARM64:
+ return cached_arch = "arm64";
+ }
+ } else {
+ if (!IsWow64Process(GetCurrentProcess(), &is_wow64_process))
+ return NULL;
+ return cached_arch = is_wow64_process ? "amd64" : "x86";
+ }
+ return NULL;
+}
+
+const char *useragent(void)
+{
+ static char useragent[0x200];
+ DWORD maj, min, build;
+
+ if (useragent[0])
+ return useragent;
+ RtlGetNtVersionNumbers(&maj, &min, &build);
+ _snprintf_s(useragent, sizeof(useragent), _TRUNCATE, "WireGuard-Fetcher/" VERSION_STR " (Windows %lu.%lu.%lu; %s)", maj, min, build & 0xffff, architecture());
+ return useragent;
+}
+
+bool is_win7(void)
+{
+ DWORD maj, min, build;
+ RtlGetNtVersionNumbers(&maj, &min, &build);
+ return maj == 6 && min == 1;
+}
diff --git a/installer/fetcher/systeminfo.h b/installer/fetcher/systeminfo.h
new file mode 100644
index 00000000..d2bcb6c7
--- /dev/null
+++ b/installer/fetcher/systeminfo.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2020 Jason A. Donenfeld. All Rights Reserved.
+ */
+
+#ifndef _SYSTEMINFO_H
+#define _SYSTEMINFO_H
+
+#include <stdbool.h>
+
+const char *architecture(void);
+const char *useragent(void);
+bool is_win7(void);
+
+#endif
diff --git a/installer/fetcher/version.h b/installer/fetcher/version.h
new file mode 100644
index 00000000..b78fe94b
--- /dev/null
+++ b/installer/fetcher/version.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2020 Jason A. Donenfeld. All Rights Reserved.
+ */
+
+#ifndef _VERSION_H
+#define _VERSION_H
+
+#define VERSION_STR "1.0"
+#define VERSION_ARRAY 1,0,0,0
+
+#endif
diff --git a/installer/wireguard.wxs b/installer/wireguard.wxs
index 5bbb1ebb..e51dc11e 100644
--- a/installer/wireguard.wxs
+++ b/installer/wireguard.wxs
@@ -2,18 +2,22 @@
<!--
SPDX-License-Identifier: GPL-2.0
- Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
-->
-<?if $(var.WIREGUARD_PLATFORM) = "x86"?>
- <?define PlatformProgramFilesFolder = "ProgramFilesFolder"?>
-<?else?>
+<?if $(var.WIREGUARD_PLATFORM) = "amd64" Or $(var.WIREGUARD_PLATFORM) = "arm64"?>
<?define PlatformProgramFilesFolder = "ProgramFiles64Folder"?>
+<?else?>
+ <?define PlatformProgramFilesFolder = "ProgramFilesFolder"?>
<?endif?>
<?if $(var.WIREGUARD_PLATFORM) = "amd64"?>
<?define UpgradeCode = "5e5a1da5-ba36-404d-92ec-41050d1c799c"?>
<?elseif $(var.WIREGUARD_PLATFORM) = "x86"?>
<?define UpgradeCode = "62754a0a-fee9-4412-b739-e8da2e7c9405"?>
+<?elseif $(var.WIREGUARD_PLATFORM) = "arm"?>
+ <?define UpgradeCode = "f90bca59-9627-431d-92b4-a5c2d9a529ff"?>
+<?elseif $(var.WIREGUARD_PLATFORM) = "arm64"?>
+ <?define UpgradeCode = "7ff76099-8940-4d3e-99b9-50a3b3ca1ee9"?>
<?else?>
<?error Unknown platform ?>
<?endif?>
@@ -27,7 +31,7 @@
Manufacturer="WireGuard LLC"
UpgradeCode="$(var.UpgradeCode)">
<Package
- InstallerVersion="400"
+ InstallerVersion="500"
Compressed="yes"
InstallScope="perMachine"
Description="WireGuard: Fast, Modern, Secure VPN Tunnel"
@@ -53,7 +57,8 @@
AllowDowngrades="no"
AllowSameVersionUpgrades="yes"
DowngradeErrorMessage="A newer version of [ProductName] is already installed."
- Schedule="afterInstallExecute" />
+ Schedule="afterInstallExecute"
+ IgnoreRemoveFailure="yes" />
<!--
Folders
@@ -63,10 +68,6 @@
<Directory Id="WireGuardFolder" Name="WireGuard" />
</Directory>
<Directory Id="ProgramMenuFolder" />
- <Directory Id="SystemFolder" />
- <?if $(var.WIREGUARD_PLATFORM) != "x86"?>
- <Directory Id="System64Folder" />
- <?endif?>
</Directory>
<!--
@@ -79,32 +80,34 @@
</File>
<ServiceControl Id="DummyService.3AA0C492_29F4_4342_B608_DB95B2DECB13" Name="DummyService.3AA0C492_29F4_4342_B608_DB95B2DECB13" /><!-- A dummy to make WiX create ServiceControl table for us. -->
</Component>
- <Component Directory="SystemFolder" Win64="no" Id="Wg32Executable" Guid="5ca31841-97d8-4614-a318-f1e268135ba7">
- <File Source="..\x86\wg.exe" Id="Wg32Executable" />
- </Component>
- <?if $(var.WIREGUARD_PLATFORM) != "x86"?>
- <Component Directory="System64Folder" Win64="yes" Id="Wg64Executable" Guid="d9b494ec-0959-442c-89ad-6aa175acfd03">
- <File Source="..\$(var.WIREGUARD_PLATFORM)\wg.exe" Id="Wg64Executable" />
+ <Component Directory="WireGuardFolder" Id="WgExecutable" Guid="540cf446-fcc3-4452-b9fb-eb4c02780251">
+ <File Source="..\$(var.WIREGUARD_PLATFORM)\wg.exe" KeyPath="yes" />
+ <Environment Id="PATH" Name="PATH" System="yes" Action="set" Part="last" Permanent="no" Value="[WireGuardFolder]" />
</Component>
- <?endif?>
</ComponentGroup>
<!--
- Merge modules
- -->
- <DirectoryRef Id="WireGuardFolder">
- <Merge Id="WintunMergeModule" Language="0" DiskId="1" SourceFile=".deps\wintun-$(var.WIREGUARD_PLATFORM).msm" />
- </DirectoryRef>
-
- <!--
Features
-->
<Feature Id="WireGuardFeature" Title="WireGuard" Level="1">
<ComponentGroupRef Id="WireGuardComponents" />
</Feature>
- <Feature Id="WintunFeature" Title="Wintun" Level="1">
- <MergeRef Id="WintunMergeModule" />
- </Feature>
+
+ <!--
+ Abort early if running under Wow64
+ -->
+ <CustomAction Id="CheckWow64" BinaryKey="customactions.dll" DllEntry="CheckWow64" />
+ <InstallExecuteSequence>
+ <Custom Action="CheckWow64" After="FindRelatedProducts">NOT REMOVE</Custom>
+ </InstallExecuteSequence>
+
+ <!--
+ Abort early if running without KB2921916 on Windows 7
+ -->
+ <CustomAction Id="CheckKB2921916" BinaryKey="customactions.dll" DllEntry="CheckKB2921916" />
+ <InstallExecuteSequence>
+ <Custom Action="CheckKB2921916" After="CheckWow64">NOT REMOVE</Custom>
+ </InstallExecuteSequence>
<!--
Evaluate WireGuard services and populate ServiceControl table
@@ -115,11 +118,19 @@
</InstallExecuteSequence>
<!--
- Clear out our config folder on uninstall
+ Launch wireguard.exe on product reconfiguration (starting same MSI again)
-->
- <CustomAction Id="RemoveConfigFolder" BinaryKey="customactions.dll" DllEntry="RemoveConfigFolder" Execute="deferred" Impersonate="no" />
+ <CustomAction Id="LaunchApplicationAndAbort" BinaryKey="customactions.dll" DllEntry="LaunchApplicationAndAbort" />
+ <InstallExecuteSequence>
+ <Custom Action="LaunchApplicationAndAbort" After="CostFinalize">ProductState=5 AND NOT REMOVE AND NOT DO_NOT_LAUNCH</Custom>
+ </InstallExecuteSequence>
+
+ <!--
+ Evaluate WireGuard components
+ -->
+ <CustomAction Id="EvaluateWireGuardComponents" BinaryKey="customactions.dll" DllEntry="EvaluateWireGuardComponents" />
<InstallExecuteSequence>
- <Custom Action="RemoveConfigFolder" After="DeleteServices">(NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")</Custom>
+ <Custom Action="EvaluateWireGuardComponents" After="ProcessComponents" />
</InstallExecuteSequence>
<!--
@@ -131,19 +142,27 @@
</InstallExecuteSequence>
<!--
- Launch wireguard.exe after setup complete
+ Clear out our config folder on uninstall
-->
- <CustomAction Id="LaunchApplication" HideTarget="yes" Impersonate="no" Execute="deferred" FileKey="wireguard.exe" ExeCommand="" Return="asyncNoWait" />
+ <CustomAction Id="RemoveConfigFolder" BinaryKey="customactions.dll" DllEntry="RemoveConfigFolder" Execute="deferred" Impersonate="no" />
<InstallExecuteSequence>
- <Custom Action="LaunchApplication" Before="InstallFinalize">(&amp;WireGuardFeature = 3) AND NOT DO_NOT_LAUNCH</Custom>
+ <Custom Action="RemoveConfigFolder" After="DeleteServices" />
</InstallExecuteSequence>
<!--
- Launch wireguard.exe on product reconfiguration (starting same MSI again)
+ Clear out our adapters on uninstall
+ -->
+ <CustomAction Id="RemoveAdapters" BinaryKey="customactions.dll" DllEntry="RemoveAdapters" Execute="deferred" Impersonate="no" />
+ <InstallExecuteSequence>
+ <Custom Action="RemoveAdapters" Before="RemoveFiles" />
+ </InstallExecuteSequence>
+
+ <!--
+ Launch wireguard.exe after setup complete
-->
- <CustomAction Id="LaunchApplicationAsOrdinaryUser" HideTarget="yes" FileKey="wireguard.exe" ExeCommand="" Return="asyncNoWait" />
+ <CustomAction Id="LaunchApplication" HideTarget="yes" Impersonate="no" Execute="deferred" FileKey="wireguard.exe" ExeCommand="" Return="asyncNoWait" />
<InstallExecuteSequence>
- <Custom Action="LaunchApplicationAsOrdinaryUser" After="InstallFinalize">(&amp;WireGuardFeature = -1) AND (!WireGuardFeature = 3) AND NOT DO_NOT_LAUNCH</Custom>
+ <Custom Action="LaunchApplication" Before="InstallFinalize">(&amp;WireGuardFeature = 3) AND NOT DO_NOT_LAUNCH</Custom>
</InstallExecuteSequence>
</Product>
</Wix>
diff --git a/l18n/l18n.go b/l18n/l18n.go
index 76c52ccf..0028098b 100644
--- a/l18n/l18n.go
+++ b/l18n/l18n.go
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved.
*/
package l18n
diff --git a/locales/de/messages.gotext.json b/locales/de/messages.gotext.json
index fa49c849..cae1860e 100644
--- a/locales/de/messages.gotext.json
+++ b/locales/de/messages.gotext.json
@@ -60,9 +60,9 @@
]
},
{
- "id": "You must use the 64-bit version of WireGuard on this computer.",
- "message": "You must use the 64-bit version of WireGuard on this computer.",
- "translation": "Sie müssen die 64-Bit-Version von WireGuard auf diesem Computer verwenden.",
+ "id": "You must use the native version of WireGuard on this computer.",
+ "message": "You must use the native version of WireGuard on this computer.",
+ "translation": "Sie müssen die Version von Wireguard benutzen, die für ihren Computer bestimmt ist.",
"translatorComment": "Copied from source."
},
{
@@ -586,9 +586,9 @@
"translatorComment": "Copied from source."
},
{
- "id": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}\nOperating system: {OsName}\nArchitecture: {GOARCH}",
- "message": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}\nOperating system: {OsName}\nArchitecture: {GOARCH}",
- "translation": "App Version: {Number}\nGo Backend Version: {WireGuardGoVersion}\nGo Version: {Version_go}\nBetriebssystem: {OsName}\nArchitektur: {GOARCH}",
+ "id": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}-{GOARCH}\nOperating system: {OsName}\nArchitecture: {NativeArch}",
+ "message": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}-{GOARCH}\nOperating system: {OsName}\nArchitecture: {NativeArch}",
+ "translation": "App Version: {Number}\nGo backend Version: {WireGuardGoVersion}\nGo Version: {Version_go}-{GOARCH}\nBetriebssystem: {OsName}\nArchitektur: {NativeArch}",
"translatorComment": "Copied from source.",
"placeholders": [
{
@@ -616,20 +616,28 @@
"expr": "strings.TrimPrefix(runtime.Version(), \"go\")"
},
{
- "id": "OsName",
+ "id": "GOARCH",
"string": "%[4]s",
"type": "string",
"underlyingType": "string",
"argNum": 4,
- "expr": "version.OsName()"
+ "expr": "runtime.GOARCH"
},
{
- "id": "GOARCH",
+ "id": "OsName",
"string": "%[5]s",
"type": "string",
"underlyingType": "string",
"argNum": 5,
- "expr": "runtime.GOARCH"
+ "expr": "version.OsName()"
+ },
+ {
+ "id": "NativeArch",
+ "string": "%[6]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 6,
+ "expr": "version.NativeArch()"
}
]
},
@@ -694,6 +702,12 @@
"translatorComment": "Copied from source."
},
{
+ "id": "Scripts:",
+ "message": "Scripts:",
+ "translation": "Skripte:",
+ "translatorComment": "Copied from source."
+ },
+ {
"id": "Preshared key:",
"message": "Preshared key:",
"translation": "Geteilter Schlüssel:",
@@ -730,6 +744,12 @@
"translatorComment": "Copied from source."
},
{
+ "id": "disabled, per policy",
+ "message": "disabled, per policy",
+ "translation": "deaktiviert, per Richtlinie",
+ "translatorComment": "Copied from source."
+ },
+ {
"id": "enabled",
"message": "enabled",
"translation": "aktiviert",
@@ -738,7 +758,7 @@
{
"id": "{String} received, {String_1} sent",
"message": "{String} received, {String_1} sent",
- "translation": "{String} empfangen, {String} gesendet",
+ "translation": "{String} empfangen, {String_1} gesendet",
"translatorComment": "Copied from source.",
"placeholders": [
{
@@ -836,9 +856,9 @@
"translatorComment": "Copied from source."
},
{
- "id": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface, with special exceptions for DHCP and NDP.",
- "message": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface, with special exceptions for DHCP and NDP.",
- "translation": "Wenn die Konfiguration genau einen Teilnehmer enthält und dieser einen der Einträge 0.0.0.0/0 oder ::/0 in den Erlaubten IPs enthält, so wird ein Firewall-Regelsatz erstellt, der allen Verkehr blockiert, der weder aus dem Tunnel stammt noch in diesen geht, mit Ausnahmen für DHCP- und NDP-Verkehr.",
+ "id": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface or is to the wrong DNS server, with special exceptions for DHCP and NDP.",
+ "message": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface or is to the wrong DNS server, with special exceptions for DHCP and NDP.",
+ "translation": "Wenn eine Konfiguration genau einen Teilnehmer enthält und dieser einen der Einträge 0.0.0.0/0 oder ::/0 in den erlaubten IPs enthält, dann erstellt der Tunneldienst ein Firewall-Regelsatz, der allen Verkehr (insbesondere zum falschen DNS-Server) blockiert, der weder aus dem Tunnel stammt noch in den Tunnel geht, mit Ausnahmen für DHCP- und NDP-Verkehr.",
"translatorComment": "Copied from source."
},
{
@@ -1124,100 +1144,100 @@
"translatorComment": "Copied from source."
},
{
- "id": "WireGuard Tunnel Error",
- "message": "WireGuard Tunnel Error",
- "translation": "WireGuard Tunnel Fehler",
+ "id": "WireGuard Activated",
+ "message": "WireGuard Activated",
+ "translation": "WireGuard aktiviert",
"translatorComment": "Copied from source."
},
{
- "id": "WireGuard: {TextForStateglobalState_true}",
- "message": "WireGuard: {TextForStateglobalState_true}",
- "translation": "WireGuard: {TextForStateglobalState_true}",
+ "id": "The {Name} tunnel has been activated.",
+ "message": "The {Name} tunnel has been activated.",
+ "translation": "Der Tunnel {Name} wurde aktiviert.",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "TextForStateglobalState_true",
+ "id": "Name",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "textForState(globalState, true)"
+ "expr": "tunnel.Name"
}
]
},
{
- "id": "Status: {StateText}",
- "message": "Status: {StateText}",
- "translation": "Status: {StateText}",
+ "id": "WireGuard Deactivated",
+ "message": "WireGuard Deactivated",
+ "translation": "WireGuard deaktiviert",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "The {Name} tunnel has been deactivated.",
+ "message": "The {Name} tunnel has been deactivated.",
+ "translation": "Der Tunnel {Name} wurde deaktiviert.",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "StateText",
+ "id": "Name",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "stateText"
+ "expr": "tunnel.Name"
}
]
},
{
- "id": "Addresses: {String}",
- "message": "Addresses: {String}",
- "translation": "Adressen: {String}",
+ "id": "WireGuard Tunnel Error",
+ "message": "WireGuard Tunnel Error",
+ "translation": "WireGuard Tunnel Fehler",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "WireGuard: {TextForStateglobalState_true}",
+ "message": "WireGuard: {TextForStateglobalState_true}",
+ "translation": "WireGuard: {TextForStateglobalState_true}",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "String",
+ "id": "TextForStateglobalState_true",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "sb.String()"
+ "expr": "textForState(globalState, true)"
}
]
},
{
- "id": "WireGuard Activated",
- "message": "WireGuard Activated",
- "translation": "WireGuard aktiviert",
- "translatorComment": "Copied from source."
- },
- {
- "id": "The {Name} tunnel has been activated.",
- "message": "The {Name} tunnel has been activated.",
- "translation": "Der Tunnel {Name} wurde aktiviert.",
+ "id": "Status: {StateText}",
+ "message": "Status: {StateText}",
+ "translation": "Status: {StateText}",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "Name",
+ "id": "StateText",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "tunnel.Name"
+ "expr": "stateText"
}
]
},
{
- "id": "WireGuard Deactivated",
- "message": "WireGuard Deactivated",
- "translation": "WireGuard deaktiviert",
- "translatorComment": "Copied from source."
- },
- {
- "id": "The {Name} tunnel has been deactivated.",
- "message": "The {Name} tunnel has been deactivated.",
- "translation": "Der Tunnel {Name} wurde deaktiviert.",
+ "id": "Addresses: {EnumerationSeparator}",
+ "message": "Addresses: {EnumerationSeparator}",
+ "translation": "Adressen: {EnumerationSeparator}",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "Name",
+ "id": "EnumerationSeparator",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "tunnel.Name"
+ "expr": "strings.Join(addrs, l18n.EnumerationSeparator())"
}
]
},
@@ -1300,6 +1320,12 @@
"translatorComment": "Copied from source."
},
{
+ "id": "no configuration files were found",
+ "message": "no configuration files were found",
+ "translation": "keine Konfigurationsdateien gefunden",
+ "translatorComment": "Copied from source."
+ },
+ {
"id": "Could not import selected configuration: {LastErr}",
"message": "Could not import selected configuration: {LastErr}",
"translation": "Ausgewählte Konfiguration konnte nicht importiert werden: {LastErr}",
diff --git a/locales/es-ES/messages.gotext.json b/locales/es-ES/messages.gotext.json
new file mode 100644
index 00000000..273022d4
--- /dev/null
+++ b/locales/es-ES/messages.gotext.json
@@ -0,0 +1,41 @@
+{
+ "language": "es-ES",
+ "messages": [
+ {
+ "id": "Error",
+ "message": "Error",
+ "translation": "Error",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "(no argument): elevate and install manager service",
+ "message": "(no argument): elevate and install manager service",
+ "translation": "(sin argumento): eleva e instala el servicio de administrador",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Usage: {Args0} [\n{String}]",
+ "message": "Usage: {Args0} [\n{String}]",
+ "translation": "Uso: {Args0} [\n{String}]",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Args0",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "os.Args[0]"
+ },
+ {
+ "id": "String",
+ "string": "%[2]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 2,
+ "expr": "builder.String()"
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/locales/fa/messages.gotext.json b/locales/fa/messages.gotext.json
index 5933b60c..f0918cc7 100644
--- a/locales/fa/messages.gotext.json
+++ b/locales/fa/messages.gotext.json
@@ -14,12 +14,6 @@
"translatorComment": "Copied from source."
},
{
- "id": "You must use the 64-bit version of WireGuard on this computer.",
- "message": "You must use the 64-bit version of WireGuard on this computer.",
- "translation": "شما باید نگارش ۶۴-بیت WireGuard را در این رایانه استفاده کنید.",
- "translatorComment": "Copied from source."
- },
- {
"id": "Now",
"message": "Now",
"translation": "هم اکنون",
@@ -544,72 +538,56 @@
"translatorComment": "Copied from source."
},
{
- "id": "WireGuard Tunnel Error",
- "message": "WireGuard Tunnel Error",
- "translation": "خطای تونل WireGuard",
+ "id": "WireGuard Activated",
+ "message": "WireGuard Activated",
+ "translation": "WireGuard فعال‌شد",
"translatorComment": "Copied from source."
},
{
- "id": "Status: {StateText}",
- "message": "Status: {StateText}",
- "translation": "وضعیت: {StateText}",
+ "id": "The {Name} tunnel has been activated.",
+ "message": "The {Name} tunnel has been activated.",
+ "translation": "تونل {Name} فعال‌شده.",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "StateText",
+ "id": "Name",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "stateText"
+ "expr": "tunnel.Name"
}
]
},
{
- "id": "Addresses: {String}",
- "message": "Addresses: {String}",
- "translation": "نشانی‌ها: {String}",
- "translatorComment": "Copied from source.",
- "placeholders": [
- {
- "id": "String",
- "string": "%[1]s",
- "type": "string",
- "underlyingType": "string",
- "argNum": 1,
- "expr": "sb.String()"
- }
- ]
+ "id": "WireGuard Deactivated",
+ "message": "WireGuard Deactivated",
+ "translation": "WireGuard غیرفعال شد",
+ "translatorComment": "Copied from source."
},
{
- "id": "WireGuard Activated",
- "message": "WireGuard Activated",
- "translation": "WireGuard فعال‌شد",
+ "id": "WireGuard Tunnel Error",
+ "message": "WireGuard Tunnel Error",
+ "translation": "خطای تونل WireGuard",
"translatorComment": "Copied from source."
},
{
- "id": "The {Name} tunnel has been activated.",
- "message": "The {Name} tunnel has been activated.",
- "translation": "تونل {Name} فعال‌شده.",
+ "id": "Status: {StateText}",
+ "message": "Status: {StateText}",
+ "translation": "وضعیت: {StateText}",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "Name",
+ "id": "StateText",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "tunnel.Name"
+ "expr": "stateText"
}
]
},
{
- "id": "WireGuard Deactivated",
- "message": "WireGuard Deactivated",
- "translation": "WireGuard غیرفعال شد",
- "translatorComment": "Copied from source."
- },
- {
"id": "An Update is Available!",
"message": "An Update is Available!",
"translation": "یک به‌روزرسانی در دسترس است!",
diff --git a/locales/fi/messages.gotext.json b/locales/fi/messages.gotext.json
new file mode 100644
index 00000000..cca09e32
--- /dev/null
+++ b/locales/fi/messages.gotext.json
@@ -0,0 +1,303 @@
+{
+ "language": "fi",
+ "messages": [
+ {
+ "id": "Error",
+ "message": "Error",
+ "translation": "Virhe",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Now",
+ "message": "Now",
+ "translation": "Nyt",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "System clock wound backward!",
+ "message": "System clock wound backward!",
+ "translation": "Järjestelmän kello jättää!",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Invalid persistent keepalive",
+ "message": "Invalid persistent keepalive",
+ "translation": "Virheellinen jatkuva keepalive",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "[EnumerationSeparator]",
+ "message": "[EnumerationSeparator]",
+ "translation": ", ",
+ "comment": "Text to insert between items when listing - most western languages will translate ‘[EnumerationSeparator]’ into ‘, ’ to produce lists like ‘apple, orange, strawberry’. Eastern languages might translate into ‘、’ to produce lists like ‘リンゴ、オレンジ、イチゴ’."
+ },
+ {
+ "id": "About WireGuard",
+ "message": "About WireGuard",
+ "translation": "Tietoa WireGuardista",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "WireGuard logo image",
+ "message": "WireGuard logo image",
+ "translation": "WireGuard logon kuva",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Close",
+ "message": "Close",
+ "translation": "Sulje",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "♥ &Donate!",
+ "message": "♥ &Donate!",
+ "translation": "♥ &Lahjoita!",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Status:",
+ "message": "Status:",
+ "translation": "Tila:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "MTU:",
+ "message": "MTU:",
+ "translation": "MTU:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Addresses:",
+ "message": "Addresses:",
+ "translation": "Osoitteet:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "DNS servers:",
+ "message": "DNS servers:",
+ "translation": "DNS palvelimet:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Scripts:",
+ "message": "Scripts:",
+ "translation": "Komentosarjat:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Preshared key:",
+ "message": "Preshared key:",
+ "translation": "Jaettu avain:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Allowed IPs:",
+ "message": "Allowed IPs:",
+ "translation": "Sallitut IP-osoitteet:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Endpoint:",
+ "message": "Endpoint:",
+ "translation": "Päätepiste:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Persistent keepalive:",
+ "message": "Persistent keepalive:",
+ "translation": "Jatkuva keepalive:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Latest handshake:",
+ "message": "Latest handshake:",
+ "translation": "Viimeisin kättely:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Transfer:",
+ "message": "Transfer:",
+ "translation": "Siirrot:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Peer",
+ "message": "Peer",
+ "translation": "Osapuoli",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Create new tunnel",
+ "message": "Create new tunnel",
+ "translation": "Luo uusi tunneli",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Edit tunnel",
+ "message": "Edit tunnel",
+ "translation": "Muokkaa tunnelia",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Name:",
+ "message": "&Name:",
+ "translation": "&Nimi:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Public key:",
+ "message": "&Public key:",
+ "translation": "&Julkinen avain:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "(unknown)",
+ "message": "(unknown)",
+ "translation": "(tuntematon)",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Block untunneled traffic (kill-switch)",
+ "message": "&Block untunneled traffic (kill-switch)",
+ "translation": "&Estä tunneloimaton liikenne (pääkatkaisija)",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Save",
+ "message": "&Save",
+ "translation": "&Tallenna",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Configuration:",
+ "message": "&Configuration:",
+ "translation": "&Konfiguraatio:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Invalid name",
+ "message": "Invalid name",
+ "translation": "Virheellinen nimi",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "A name is required.",
+ "message": "A name is required.",
+ "translation": "Nimi on pakollinen.",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Tunnel already exists",
+ "message": "Tunnel already exists",
+ "translation": "Tunneli on jo olemassa",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Log",
+ "message": "Log",
+ "translation": "Loki",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Copy",
+ "message": "&Copy",
+ "translation": "&Kopioi",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "An Update is Available!",
+ "message": "An Update is Available!",
+ "translation": "Päivitys on saatavilla!",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "WireGuard Update Available",
+ "message": "WireGuard Update Available",
+ "translation": "WireGuard 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.",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Tunnels",
+ "message": "Tunnels",
+ "translation": "Tunneli",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Edit",
+ "message": "&Edit",
+ "translation": "&Muokkaa",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Imported tunnels",
+ "message": "Imported tunnels",
+ "translation": "Tuodut tunnelit",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Imported {M} tunnels",
+ "message": "Imported {M} tunnels",
+ "translation": {
+ "select": {
+ "feature": "plural",
+ "arg": "M",
+ "cases": {
+ "one": {
+ "msg": "Tuotu {M} tunneli"
+ },
+ "other": {
+ "msg": "Tuotu {M} tunnelia"
+ }
+ }
+ }
+ },
+ "placeholders": [
+ {
+ "id": "M",
+ "string": "%[1]d",
+ "type": "int",
+ "underlyingType": "int",
+ "argNum": 1,
+ "expr": "m"
+ }
+ ]
+ },
+ {
+ "id": "Unable to delete tunnels",
+ "message": "Unable to delete tunnels",
+ "translation": "Tunnelia ei voitu poistaa",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Import tunnel(s) from file",
+ "message": "Import tunnel(s) from file",
+ "translation": "Tuo tunneli(t) tiedostosta",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Status: Waiting for user",
+ "message": "Status: Waiting for user",
+ "translation": "Tila: Odotetaan käyttäjää",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Update Now",
+ "message": "Update Now",
+ "translation": "Päivitä nyt",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Status: Complete!",
+ "message": "Status: Complete!",
+ "translation": "Tila: Valmis!",
+ "translatorComment": "Copied from source."
+ }
+ ]
+} \ No newline at end of file
diff --git a/locales/fr/messages.gotext.json b/locales/fr/messages.gotext.json
index 8c4b44f5..95f02eec 100644
--- a/locales/fr/messages.gotext.json
+++ b/locales/fr/messages.gotext.json
@@ -60,15 +60,15 @@
]
},
{
- "id": "You must use the 64-bit version of WireGuard on this computer.",
- "message": "You must use the 64-bit version of WireGuard on this computer.",
- "translation": "Utilisez la version 64 bits du WireGuard sur cet ordinateur.",
+ "id": "You must use the native version of WireGuard on this computer.",
+ "message": "You must use the native version of WireGuard on this computer.",
+ "translation": "Vous devez utiliser la version native de WireGuard sur cet ordinateur.",
"translatorComment": "Copied from source."
},
{
"id": "Unable to open current process token: {Err}",
"message": "Unable to open current process token: {Err}",
- "translation": "Impossible d'ouvrir le jeton de processus actuel : {Err}",
+ "translation": "Impossible d'ouvrir le jeton du processus actuel : {Err}",
"translatorComment": "Copied from source.",
"placeholders": [
{
@@ -418,7 +418,7 @@
{
"id": "Brackets must contain an IPv6 address",
"message": "Brackets must contain an IPv6 address",
- "translation": "Il faut mettre un adresse IPv6 entre les crochets",
+ "translation": "L’adresse IPv6 doit être contenue entre des crochets",
"translatorComment": "Copied from source."
},
{
@@ -436,7 +436,7 @@
{
"id": "Invalid persistent keepalive",
"message": "Invalid persistent keepalive",
- "translation": "Conservation de connexion active permanente non valide",
+ "translation": "Keepalive non valide",
"translatorComment": "Copied from source."
},
{
@@ -464,7 +464,7 @@
{
"id": "Number must be a number between 0 and 2^64-1: {Err}",
"message": "Number must be a number between 0 and 2^64-1: {Err}",
- "translation": "Le numéro doit être un numéro entre 0 et 2^64-1 : {Err}",
+ "translation": "Le numéro doit être compris entre 0 et 2^64-1 : {Err}",
"translatorComment": "Copied from source.",
"placeholders": [
{
@@ -534,7 +534,7 @@
{
"id": "All peers must have public keys",
"message": "All peers must have public keys",
- "translation": "Tous les homologues doivent avoirs les clés publiques.",
+ "translation": "Toutes les pairs doivent contenir une clé publique",
"translatorComment": "Copied from source."
},
{
@@ -586,9 +586,9 @@
"translatorComment": "Copied from source."
},
{
- "id": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}\nOperating system: {OsName}\nArchitecture: {GOARCH}",
- "message": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}\nOperating system: {OsName}\nArchitecture: {GOARCH}",
- "translation": "Version de l'application : {Number}\nVersion wireguard-go : {WireGuardGoVersion}\nVersion Go : {Version_go}\nSystème d'exploitation : {OsName}\nArchitecture : {GOARCH}",
+ "id": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}-{GOARCH}\nOperating system: {OsName}\nArchitecture: {NativeArch}",
+ "message": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}-{GOARCH}\nOperating system: {OsName}\nArchitecture: {NativeArch}",
+ "translation": "Version de l'application : {Number}\nVersion backend Go : {WireGuardGoVersion}\nVersion Go : {Version_go}-{GOARCH}\nSystème d'exploitation : {OsName}\nArchitecture : {NativeArch}",
"translatorComment": "Copied from source.",
"placeholders": [
{
@@ -616,20 +616,28 @@
"expr": "strings.TrimPrefix(runtime.Version(), \"go\")"
},
{
- "id": "OsName",
+ "id": "GOARCH",
"string": "%[4]s",
"type": "string",
"underlyingType": "string",
"argNum": 4,
- "expr": "version.OsName()"
+ "expr": "runtime.GOARCH"
},
{
- "id": "GOARCH",
+ "id": "OsName",
"string": "%[5]s",
"type": "string",
"underlyingType": "string",
"argNum": 5,
- "expr": "runtime.GOARCH"
+ "expr": "version.OsName()"
+ },
+ {
+ "id": "NativeArch",
+ "string": "%[6]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 6,
+ "expr": "version.NativeArch()"
}
]
},
@@ -694,6 +702,12 @@
"translatorComment": "Copied from source."
},
{
+ "id": "Scripts:",
+ "message": "Scripts:",
+ "translation": "Scripts :",
+ "translatorComment": "Copied from source."
+ },
+ {
"id": "Preshared key:",
"message": "Preshared key:",
"translation": "Clé pré-partagée :",
@@ -730,6 +744,36 @@
"translatorComment": "Copied from source."
},
{
+ "id": "pre-up",
+ "message": "pre-up",
+ "translation": "pré-activation",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "post-up",
+ "message": "post-up",
+ "translation": "post-activation",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "pre-down",
+ "message": "pre-down",
+ "translation": "pré-désactivation",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "post-down",
+ "message": "post-down",
+ "translation": "post-désactivation",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "disabled, per policy",
+ "message": "disabled, per policy",
+ "translation": "désactivé, par préférence",
+ "translatorComment": "Copied from source."
+ },
+ {
"id": "enabled",
"message": "enabled",
"translation": "activé(e)",
@@ -836,9 +880,9 @@
"translatorComment": "Copied from source."
},
{
- "id": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface, with special exceptions for DHCP and NDP.",
- "message": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface, with special exceptions for DHCP and NDP.",
- "translation": "Si la configuration a exactement un homologue et si cet homologue a une addresse IP approvée contenant au moins un de 0.0.0.0/0 ou ::/0, le service de tunnelisation utilise l'ensemble de règles du pare-feu afin de blocquer tout le traffic, sauf le traffic vers ou dépuis l'interface de tunnel et l'exceptions speciales liées à DHCP et NDP.",
+ "id": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface or is to the wrong DNS server, with special exceptions for DHCP and NDP.",
+ "message": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface or is to the wrong DNS server, with special exceptions for DHCP and NDP.",
+ "translation": "Si la configuration a exactement un homologue et si cet homologue a une adresse IP autorisée contenant au moins un de 0.0.0.0/0 ou ::/0, le service de tunnel utilise un ensemble de règles du pare-feu afin de bloquer tout le trafic qui n'est ni vers ni depuis l'interface de tunnel ou qui est vers le mauvais serveur DNS, avec des exceptions spéciales pour les DHCP et NDP.",
"translatorComment": "Copied from source."
},
{
@@ -946,7 +990,7 @@
{
"id": "Active",
"message": "Active",
- "translation": "Activé(e)",
+ "translation": "Activée",
"translatorComment": "Copied from source."
},
{
@@ -958,7 +1002,7 @@
{
"id": "Inactive",
"message": "Inactive",
- "translation": "Inactivé(e)",
+ "translation": "Éteinte",
"translatorComment": "Copied from source."
},
{
@@ -1124,100 +1168,106 @@
"translatorComment": "Copied from source."
},
{
- "id": "WireGuard Tunnel Error",
- "message": "WireGuard Tunnel Error",
- "translation": "Erreur du tunnel WireGuard",
+ "id": "&Tunnels",
+ "message": "&Tunnels",
+ "translation": "& Tunnels",
"translatorComment": "Copied from source."
},
{
- "id": "WireGuard: {TextForStateglobalState_true}",
- "message": "WireGuard: {TextForStateglobalState_true}",
- "translation": "WireGuard : {TextForStateglobalState_true}",
+ "id": "WireGuard Activated",
+ "message": "WireGuard Activated",
+ "translation": "WireGuard activé",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "The {Name} tunnel has been activated.",
+ "message": "The {Name} tunnel has been activated.",
+ "translation": "Tunnel {Name} a été activé.",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "TextForStateglobalState_true",
+ "id": "Name",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "textForState(globalState, true)"
+ "expr": "tunnel.Name"
}
]
},
{
- "id": "Status: {StateText}",
- "message": "Status: {StateText}",
- "translation": "État : {StateText}",
+ "id": "WireGuard Deactivated",
+ "message": "WireGuard Deactivated",
+ "translation": "WireGuard désactivé",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "The {Name} tunnel has been deactivated.",
+ "message": "The {Name} tunnel has been deactivated.",
+ "translation": "Tunnel {Name} a été désactivé.",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "StateText",
+ "id": "Name",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "stateText"
+ "expr": "tunnel.Name"
}
]
},
{
- "id": "Addresses: {String}",
- "message": "Addresses: {String}",
- "translation": "Adresses : {String}",
+ "id": "WireGuard Tunnel Error",
+ "message": "WireGuard Tunnel Error",
+ "translation": "Erreur du tunnel WireGuard",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "WireGuard: {TextForStateglobalState_true}",
+ "message": "WireGuard: {TextForStateglobalState_true}",
+ "translation": "WireGuard : {TextForStateglobalState_true}",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "String",
+ "id": "TextForStateglobalState_true",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "sb.String()"
+ "expr": "textForState(globalState, true)"
}
]
},
{
- "id": "WireGuard Activated",
- "message": "WireGuard Activated",
- "translation": "WireGuard activé",
- "translatorComment": "Copied from source."
- },
- {
- "id": "The {Name} tunnel has been activated.",
- "message": "The {Name} tunnel has been activated.",
- "translation": "Tunnel {Name} a été activé.",
+ "id": "Status: {StateText}",
+ "message": "Status: {StateText}",
+ "translation": "État : {StateText}",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "Name",
+ "id": "StateText",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "tunnel.Name"
+ "expr": "stateText"
}
]
},
{
- "id": "WireGuard Deactivated",
- "message": "WireGuard Deactivated",
- "translation": "WireGuard désactivé",
- "translatorComment": "Copied from source."
- },
- {
- "id": "The {Name} tunnel has been deactivated.",
- "message": "The {Name} tunnel has been deactivated.",
- "translation": "Tunnel {Name} a été désactivé.",
+ "id": "Addresses: {EnumerationSeparator}",
+ "message": "Addresses: {EnumerationSeparator}",
+ "translation": "Adresses : {EnumerationSeparator}",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "Name",
+ "id": "EnumerationSeparator",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "tunnel.Name"
+ "expr": "strings.Join(addrs, l18n.EnumerationSeparator())"
}
]
},
@@ -1300,6 +1350,12 @@
"translatorComment": "Copied from source."
},
{
+ "id": "no configuration files were found",
+ "message": "no configuration files were found",
+ "translation": "aucun fichier de configuration trouvé",
+ "translatorComment": "Copied from source."
+ },
+ {
"id": "Could not import selected configuration: {LastErr}",
"message": "Could not import selected configuration: {LastErr}",
"translation": "Impossible d'importer la configuration sélectionnée : {LastErr}",
diff --git a/locales/id/messages.gotext.json b/locales/id/messages.gotext.json
index e3ebdd87..07fad080 100644
--- a/locales/id/messages.gotext.json
+++ b/locales/id/messages.gotext.json
@@ -60,12 +60,6 @@
]
},
{
- "id": "You must use the 64-bit version of WireGuard on this computer.",
- "message": "You must use the 64-bit version of WireGuard on this computer.",
- "translation": "Anda harus menggunakan WireGuard versi 64-bit di komputer ini.",
- "translatorComment": "Copied from source."
- },
- {
"id": "Unable to open current process token: {Err}",
"message": "Unable to open current process token: {Err}",
"translation": "Tidak dapat membuka token proses saat ini: {Err}",
@@ -553,54 +547,6 @@
"translatorComment": "Copied from source."
},
{
- "id": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}\nOperating system: {OsName}\nArchitecture: {GOARCH}",
- "message": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}\nOperating system: {OsName}\nArchitecture: {GOARCH}",
- "translation": "Versi Aplikasi: {Number}\nVersi back-end Go: {WireGuardGoVersion}\nVersi Go: {Version_go}\nSistem Operasi: {OsName}\nArsitektur: {GOARCH}",
- "translatorComment": "Copied from source.",
- "placeholders": [
- {
- "id": "Number",
- "string": "%[1]s",
- "type": "string",
- "underlyingType": "string",
- "argNum": 1,
- "expr": "version.Number"
- },
- {
- "id": "WireGuardGoVersion",
- "string": "%[2]s",
- "type": "string",
- "underlyingType": "string",
- "argNum": 2,
- "expr": "device.WireGuardGoVersion"
- },
- {
- "id": "Version_go",
- "string": "%[3]s",
- "type": "string",
- "underlyingType": "string",
- "argNum": 3,
- "expr": "strings.TrimPrefix(runtime.Version(), \"go\")"
- },
- {
- "id": "OsName",
- "string": "%[4]s",
- "type": "string",
- "underlyingType": "string",
- "argNum": 4,
- "expr": "version.OsName()"
- },
- {
- "id": "GOARCH",
- "string": "%[5]s",
- "type": "string",
- "underlyingType": "string",
- "argNum": 5,
- "expr": "runtime.GOARCH"
- }
- ]
- },
- {
"id": "Close",
"message": "Close",
"translation": "Tutup",
diff --git a/locales/it/messages.gotext.json b/locales/it/messages.gotext.json
index 7943e5da..f1626e8b 100644
--- a/locales/it/messages.gotext.json
+++ b/locales/it/messages.gotext.json
@@ -60,12 +60,6 @@
]
},
{
- "id": "You must use the 64-bit version of WireGuard on this computer.",
- "message": "You must use the 64-bit version of WireGuard on this computer.",
- "translation": "Devi utilizzare la versione a 64 bit di WireGuard su questo computer.",
- "translatorComment": "Copied from source."
- },
- {
"id": "Unable to open current process token: {Err}",
"message": "Unable to open current process token: {Err}",
"translation": "Impossibile aprire il token del processo corrente: {Err}",
@@ -580,54 +574,6 @@
"translatorComment": "Copied from source."
},
{
- "id": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}\nOperating system: {OsName}\nArchitecture: {GOARCH}",
- "message": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}\nOperating system: {OsName}\nArchitecture: {GOARCH}",
- "translation": "Versione applicazione: {Number}\nVersione backend Go: {WireGuardGoVersion}\nVersione Go: {Version_go}\nSistema operativo: {OsName}\nArchitettura: {GOARCH}",
- "translatorComment": "Copied from source.",
- "placeholders": [
- {
- "id": "Number",
- "string": "%[1]s",
- "type": "string",
- "underlyingType": "string",
- "argNum": 1,
- "expr": "version.Number"
- },
- {
- "id": "WireGuardGoVersion",
- "string": "%[2]s",
- "type": "string",
- "underlyingType": "string",
- "argNum": 2,
- "expr": "device.WireGuardGoVersion"
- },
- {
- "id": "Version_go",
- "string": "%[3]s",
- "type": "string",
- "underlyingType": "string",
- "argNum": 3,
- "expr": "strings.TrimPrefix(runtime.Version(), \"go\")"
- },
- {
- "id": "OsName",
- "string": "%[4]s",
- "type": "string",
- "underlyingType": "string",
- "argNum": 4,
- "expr": "version.OsName()"
- },
- {
- "id": "GOARCH",
- "string": "%[5]s",
- "type": "string",
- "underlyingType": "string",
- "argNum": 5,
- "expr": "runtime.GOARCH"
- }
- ]
- },
- {
"id": "Close",
"message": "Close",
"translation": "Chiudi",
@@ -830,12 +776,6 @@
"translatorComment": "Copied from source."
},
{
- "id": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface, with special exceptions for DHCP and NDP.",
- "message": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface, with special exceptions for DHCP and NDP.",
- "translation": "Quando una configurazione ha esattamente un peer e quel peer ha un IP consentito contenente almeno uno di 0.0.0.0/0 o ::/0, il servizio tunnel impegna un insieme di regole del firewall per bloccare tutto il traffico che non proviene dall'interfaccia tunnel, con speciali eccezioni per DHCP e NDP.",
- "translatorComment": "Copied from source."
- },
- {
"id": "&Save",
"message": "&Save",
"translation": "&Salva",
@@ -1118,100 +1058,84 @@
"translatorComment": "Copied from source."
},
{
- "id": "WireGuard Tunnel Error",
- "message": "WireGuard Tunnel Error",
- "translation": "Errore tunnel di WireGuard",
+ "id": "WireGuard Activated",
+ "message": "WireGuard Activated",
+ "translation": "WireGuard attivato",
"translatorComment": "Copied from source."
},
{
- "id": "WireGuard: {TextForStateglobalState_true}",
- "message": "WireGuard: {TextForStateglobalState_true}",
- "translation": "WireGuard: {TextForStateglobalState_true}",
+ "id": "The {Name} tunnel has been activated.",
+ "message": "The {Name} tunnel has been activated.",
+ "translation": "Il tunnel {Name} è stato attivato.",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "TextForStateglobalState_true",
+ "id": "Name",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "textForState(globalState, true)"
+ "expr": "tunnel.Name"
}
]
},
{
- "id": "Status: {StateText}",
- "message": "Status: {StateText}",
- "translation": "Stato: {StateText}",
- "translatorComment": "Copied from source.",
- "placeholders": [
- {
- "id": "StateText",
- "string": "%[1]s",
- "type": "string",
- "underlyingType": "string",
- "argNum": 1,
- "expr": "stateText"
- }
- ]
+ "id": "WireGuard Deactivated",
+ "message": "WireGuard Deactivated",
+ "translation": "WireGuard disattivato",
+ "translatorComment": "Copied from source."
},
{
- "id": "Addresses: {String}",
- "message": "Addresses: {String}",
- "translation": "Indirizzi: {String}",
+ "id": "The {Name} tunnel has been deactivated.",
+ "message": "The {Name} tunnel has been deactivated.",
+ "translation": "Il tunnel {Name} è stato disattivato.",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "String",
+ "id": "Name",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "sb.String()"
+ "expr": "tunnel.Name"
}
]
},
{
- "id": "WireGuard Activated",
- "message": "WireGuard Activated",
- "translation": "WireGuard attivato",
+ "id": "WireGuard Tunnel Error",
+ "message": "WireGuard Tunnel Error",
+ "translation": "Errore tunnel di WireGuard",
"translatorComment": "Copied from source."
},
{
- "id": "The {Name} tunnel has been activated.",
- "message": "The {Name} tunnel has been activated.",
- "translation": "Il tunnel {Name} è stato attivato.",
+ "id": "WireGuard: {TextForStateglobalState_true}",
+ "message": "WireGuard: {TextForStateglobalState_true}",
+ "translation": "WireGuard: {TextForStateglobalState_true}",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "Name",
+ "id": "TextForStateglobalState_true",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "tunnel.Name"
+ "expr": "textForState(globalState, true)"
}
]
},
{
- "id": "WireGuard Deactivated",
- "message": "WireGuard Deactivated",
- "translation": "WireGuard disattivato",
- "translatorComment": "Copied from source."
- },
- {
- "id": "The {Name} tunnel has been deactivated.",
- "message": "The {Name} tunnel has been deactivated.",
- "translation": "Il tunnel {Name} è stato disattivato.",
+ "id": "Status: {StateText}",
+ "message": "Status: {StateText}",
+ "translation": "Stato: {StateText}",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "Name",
+ "id": "StateText",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "tunnel.Name"
+ "expr": "stateText"
}
]
},
diff --git a/locales/ja/messages.gotext.json b/locales/ja/messages.gotext.json
index 5d29fcd8..61751200 100644
--- a/locales/ja/messages.gotext.json
+++ b/locales/ja/messages.gotext.json
@@ -60,12 +60,6 @@
]
},
{
- "id": "You must use the 64-bit version of WireGuard on this computer.",
- "message": "You must use the 64-bit version of WireGuard on this computer.",
- "translation": "このコンピュータでは64ビット版の WireGuard を使ってください。",
- "translatorComment": "Copied from source."
- },
- {
"id": "Unable to open current process token: {Err}",
"message": "Unable to open current process token: {Err}",
"translation": "現在のプロセスのトークンを開けません: {Err}",
@@ -571,54 +565,6 @@
"translatorComment": "Copied from source."
},
{
- "id": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}\nOperating system: {OsName}\nArchitecture: {GOARCH}",
- "message": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}\nOperating system: {OsName}\nArchitecture: {GOARCH}",
- "translation": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}\nOperating system: {OsName}\nArchitecture: {GOARCH}",
- "translatorComment": "Copied from source.",
- "placeholders": [
- {
- "id": "Number",
- "string": "%[1]s",
- "type": "string",
- "underlyingType": "string",
- "argNum": 1,
- "expr": "version.Number"
- },
- {
- "id": "WireGuardGoVersion",
- "string": "%[2]s",
- "type": "string",
- "underlyingType": "string",
- "argNum": 2,
- "expr": "device.WireGuardGoVersion"
- },
- {
- "id": "Version_go",
- "string": "%[3]s",
- "type": "string",
- "underlyingType": "string",
- "argNum": 3,
- "expr": "strings.TrimPrefix(runtime.Version(), \"go\")"
- },
- {
- "id": "OsName",
- "string": "%[4]s",
- "type": "string",
- "underlyingType": "string",
- "argNum": 4,
- "expr": "version.OsName()"
- },
- {
- "id": "GOARCH",
- "string": "%[5]s",
- "type": "string",
- "underlyingType": "string",
- "argNum": 5,
- "expr": "runtime.GOARCH"
- }
- ]
- },
- {
"id": "Close",
"message": "Close",
"translation": "閉じる",
@@ -821,12 +767,6 @@
"translatorComment": "Copied from source."
},
{
- "id": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface, with special exceptions for DHCP and NDP.",
- "message": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface, with special exceptions for DHCP and NDP.",
- "translation": "ピアが1つだけ設定されていて、さらに Allowed IPs に 0.0.0.0/0 または ::/0 が含まれている場合、トンネルサービスはトンネルインターフェースを通らないすべてのトラフィックをブロックするファイアウォールルールを追加します。",
- "translatorComment": "Copied from source."
- },
- {
"id": "&Save",
"message": "&Save",
"translation": "保存(&S)",
@@ -1109,100 +1049,84 @@
"translatorComment": "Copied from source."
},
{
- "id": "WireGuard Tunnel Error",
- "message": "WireGuard Tunnel Error",
- "translation": "WireGuard トンネルエラー",
+ "id": "WireGuard Activated",
+ "message": "WireGuard Activated",
+ "translation": "WireGuard 有効化済み",
"translatorComment": "Copied from source."
},
{
- "id": "WireGuard: {TextForStateglobalState_true}",
- "message": "WireGuard: {TextForStateglobalState_true}",
- "translation": "WireGuard: {TextForStateglobalState_true}",
+ "id": "The {Name} tunnel has been activated.",
+ "message": "The {Name} tunnel has been activated.",
+ "translation": "トンネル {Name} は有効になりました。",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "TextForStateglobalState_true",
+ "id": "Name",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "textForState(globalState, true)"
+ "expr": "tunnel.Name"
}
]
},
{
- "id": "Status: {StateText}",
- "message": "Status: {StateText}",
- "translation": "状態: {StateText}",
- "translatorComment": "Copied from source.",
- "placeholders": [
- {
- "id": "StateText",
- "string": "%[1]s",
- "type": "string",
- "underlyingType": "string",
- "argNum": 1,
- "expr": "stateText"
- }
- ]
+ "id": "WireGuard Deactivated",
+ "message": "WireGuard Deactivated",
+ "translation": "WireGuard 無効化済み",
+ "translatorComment": "Copied from source."
},
{
- "id": "Addresses: {String}",
- "message": "Addresses: {String}",
- "translation": "アドレス: {String}",
+ "id": "The {Name} tunnel has been deactivated.",
+ "message": "The {Name} tunnel has been deactivated.",
+ "translation": "トンネル {Name} は無効になりました。",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "String",
+ "id": "Name",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "sb.String()"
+ "expr": "tunnel.Name"
}
]
},
{
- "id": "WireGuard Activated",
- "message": "WireGuard Activated",
- "translation": "WireGuard 有効化済み",
+ "id": "WireGuard Tunnel Error",
+ "message": "WireGuard Tunnel Error",
+ "translation": "WireGuard トンネルエラー",
"translatorComment": "Copied from source."
},
{
- "id": "The {Name} tunnel has been activated.",
- "message": "The {Name} tunnel has been activated.",
- "translation": "トンネル {Name} は有効になりました。",
+ "id": "WireGuard: {TextForStateglobalState_true}",
+ "message": "WireGuard: {TextForStateglobalState_true}",
+ "translation": "WireGuard: {TextForStateglobalState_true}",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "Name",
+ "id": "TextForStateglobalState_true",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "tunnel.Name"
+ "expr": "textForState(globalState, true)"
}
]
},
{
- "id": "WireGuard Deactivated",
- "message": "WireGuard Deactivated",
- "translation": "WireGuard 無効化済み",
- "translatorComment": "Copied from source."
- },
- {
- "id": "The {Name} tunnel has been deactivated.",
- "message": "The {Name} tunnel has been deactivated.",
- "translation": "トンネル {Name} は無効になりました。",
+ "id": "Status: {StateText}",
+ "message": "Status: {StateText}",
+ "translation": "状態: {StateText}",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "Name",
+ "id": "StateText",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "tunnel.Name"
+ "expr": "stateText"
}
]
},
diff --git a/locales/pa-IN/messages.gotext.json b/locales/pa-IN/messages.gotext.json
new file mode 100644
index 00000000..1fe5c65d
--- /dev/null
+++ b/locales/pa-IN/messages.gotext.json
@@ -0,0 +1,1223 @@
+{
+ "language": "pa-IN",
+ "messages": [
+ {
+ "id": "Error",
+ "message": "Error",
+ "translation": "ਗ਼ਲਤੀ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Usage: {Args0} [\n{String}]",
+ "message": "Usage: {Args0} [\n{String}]",
+ "translation": "ਵਰਤੋਂ: {Args0} [\n{String}]",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Args0",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "os.Args[0]"
+ },
+ {
+ "id": "String",
+ "string": "%[2]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 2,
+ "expr": "builder.String()"
+ }
+ ]
+ },
+ {
+ "id": "Command Line Options",
+ "message": "Command Line Options",
+ "translation": "ਕਮਾਂਡ ਲਾਈਨ ਚੋਣਾਂ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Unable to determine whether the process is running under WOW64: {Err}",
+ "message": "Unable to determine whether the process is running under WOW64: {Err}",
+ "translation": "ਪਤਾ ਲਗਾਉਣ ਲਈ ਅਸਮਰੱਥ ਹੈ ਕਿ ਪਰੋਸੈਸ WOW64 ਅਧੀਨ ਚੱਲ ਰਿਹਾ ਹੈ: {Err}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Err",
+ "string": "%[1]v",
+ "type": "error",
+ "underlyingType": "interface{Error() string}",
+ "argNum": 1,
+ "expr": "err"
+ }
+ ]
+ },
+ {
+ "id": "You must use the native version of WireGuard on this computer.",
+ "message": "You must use the native version of WireGuard on this computer.",
+ "translation": "ਤੁਹਾਨੂੰ ਇਸ ਕੰਪਿਊਟਰ ਉੱਤੇ WireGuard ਦਾ ਮੂਲ ਵਰਜ਼ਨ ਵਰਤਣਾ ਚਾਹੀਦਾ ਹੈ।",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Unable to open current process token: {Err}",
+ "message": "Unable to open current process token: {Err}",
+ "translation": "ਮੌਜੂਦਾ ਪਰੋਸੈਸ ਟੋਕਨ ਖੋਲ੍ਹਣ ਲਈ ਅਸਮਰੱਥ: {Err}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Err",
+ "string": "%[1]v",
+ "type": "error",
+ "underlyingType": "interface{Error() string}",
+ "argNum": 1,
+ "expr": "err"
+ }
+ ]
+ },
+ {
+ "id": "WireGuard may only be used by users who are a member of the Builtin {AdminGroupName} group.",
+ "message": "WireGuard may only be used by users who are a member of the Builtin {AdminGroupName} group.",
+ "translation": "WireGuard ਨੂੰ ਸਿਰਫ਼ ਉਹੀ ਵਰਤੋਂਕਾਰ ਵਰਤ ਸਕਦੇ ਹਨ, ਜੋ ਕਿ ਪਹਿਲਾਂ ਮੌਜੂਦ {AdminGroupName} ਗਰੁੱਪ ਦੇ ਮੈਂਬਰ ਹਨ।",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "AdminGroupName",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "elevate.AdminGroupName()"
+ }
+ ]
+ },
+ {
+ "id": "WireGuard is running, but the UI is only accessible from desktops of the Builtin {AdminGroupName} group.",
+ "message": "WireGuard is running, but the UI is only accessible from desktops of the Builtin {AdminGroupName} group.",
+ "translation": "WireGuard ਚੱਲ ਰਿਹਾ ਹੈ, ਪਰ UI ਨੂੰ ਸਿਰਫ਼ ਪਹਿਲਾਂ ਮੌਜੂਦ {AdminGroupName} ਗਰੁੱਪ ਦੇ ਡੈਸਕਟਾਪ ਰਾਹੀਂ ਹੀ ਵਰਤਿਆ ਜਾ ਸਕਦਾ ਹੈ।",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "AdminGroupName",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "elevate.AdminGroupName()"
+ }
+ ]
+ },
+ {
+ "id": "WireGuard system tray icon did not appear after 30 seconds.",
+ "message": "WireGuard system tray icon did not appear after 30 seconds.",
+ "translation": "WireGuard ਸਿਸਟਮ ਟਰੇ ਆਈਕਾਨ 30 ਸਕਿੰਟਾਂ ਬਾਅਦ ਦਿਖਾਈ ਨਹੀਂ ਦਿੱਤਾ ਹੈ।",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Now",
+ "message": "Now",
+ "translation": "ਹੁਣ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "System clock wound backward!",
+ "message": "System clock wound backward!",
+ "translation": "ਸਿਸਟਮ ਘੜੀ ਪੁ਼ੱਠੀ ਮੋੜੀ ਗਈ!",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "{Years} year(s)",
+ "message": "{Years} year(s)",
+ "translation": {
+ "select": {
+ "feature": "plural",
+ "arg": "Years",
+ "cases": {
+ "one": {
+ "msg": "{Years} ਸਾਲ"
+ },
+ "other": {
+ "msg": "{Years} ਸਾਲ"
+ }
+ }
+ }
+ },
+ "placeholders": [
+ {
+ "id": "Years",
+ "string": "%[1]d",
+ "type": "int64",
+ "underlyingType": "int64",
+ "argNum": 1,
+ "expr": "years"
+ }
+ ]
+ },
+ {
+ "id": "{Days} day(s)",
+ "message": "{Days} day(s)",
+ "translation": {
+ "select": {
+ "feature": "plural",
+ "arg": "Days",
+ "cases": {
+ "one": {
+ "msg": "{Days} ਦਿਨ"
+ },
+ "other": {
+ "msg": "{Days} ਦਿਨ"
+ }
+ }
+ }
+ },
+ "placeholders": [
+ {
+ "id": "Days",
+ "string": "%[1]d",
+ "type": "int64",
+ "underlyingType": "int64",
+ "argNum": 1,
+ "expr": "days"
+ }
+ ]
+ },
+ {
+ "id": "{Hours} hour(s)",
+ "message": "{Hours} hour(s)",
+ "translation": {
+ "select": {
+ "feature": "plural",
+ "arg": "Hours",
+ "cases": {
+ "one": {
+ "msg": "{Hours} ਘੰਟਾ"
+ },
+ "other": {
+ "msg": "{Hours} ਘੰਟੇ"
+ }
+ }
+ }
+ },
+ "placeholders": [
+ {
+ "id": "Hours",
+ "string": "%[1]d",
+ "type": "int64",
+ "underlyingType": "int64",
+ "argNum": 1,
+ "expr": "hours"
+ }
+ ]
+ },
+ {
+ "id": "{Minutes} minute(s)",
+ "message": "{Minutes} minute(s)",
+ "translation": {
+ "select": {
+ "feature": "plural",
+ "arg": "Minutes",
+ "cases": {
+ "one": {
+ "msg": "{Minutes} ਮਿੰਟ"
+ },
+ "other": {
+ "msg": "{Minutes} ਮਿੰਟ"
+ }
+ }
+ }
+ },
+ "placeholders": [
+ {
+ "id": "Minutes",
+ "string": "%[1]d",
+ "type": "int64",
+ "underlyingType": "int64",
+ "argNum": 1,
+ "expr": "minutes"
+ }
+ ]
+ },
+ {
+ "id": "{Seconds} second(s)",
+ "message": "{Seconds} second(s)",
+ "translation": {
+ "select": {
+ "feature": "plural",
+ "arg": "Seconds",
+ "cases": {
+ "one": {
+ "msg": "{Seconds} ਸਕਿੰਟ"
+ },
+ "other": {
+ "msg": "{Seconds} ਸਕਿੰਟ"
+ }
+ }
+ }
+ },
+ "placeholders": [
+ {
+ "id": "Seconds",
+ "string": "%[1]d",
+ "type": "int64",
+ "underlyingType": "int64",
+ "argNum": 1,
+ "expr": "seconds"
+ }
+ ]
+ },
+ {
+ "id": "{Timestamp} ago",
+ "message": "{Timestamp} ago",
+ "translation": "{Timestamp} ਪਹਿਲਾਂ",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Timestamp",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "timestamp"
+ }
+ ]
+ },
+ {
+ "id": "{Bytes} B",
+ "message": "{Bytes} B",
+ "translation": "{Bytes} B",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Bytes",
+ "string": "%[1]d",
+ "type": "golang.zx2c4.com/wireguard/windows/conf.Bytes",
+ "underlyingType": "uint64",
+ "argNum": 1,
+ "expr": "b"
+ }
+ ]
+ },
+ {
+ "id": "{Float64b__1024} KiB",
+ "message": "{Float64b__1024} KiB",
+ "translation": "{Float64b__1024} KiB",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Float64b__1024",
+ "string": "%.2[1]f",
+ "type": "float64",
+ "underlyingType": "float64",
+ "argNum": 1,
+ "expr": "float64(b) / 1024"
+ }
+ ]
+ },
+ {
+ "id": "{Float64b__1024__1024} MiB",
+ "message": "{Float64b__1024__1024} MiB",
+ "translation": "{Float64b__1024__1024} MiB",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Float64b__1024__1024",
+ "string": "%.2[1]f",
+ "type": "float64",
+ "underlyingType": "float64",
+ "argNum": 1,
+ "expr": "float64(b) / (1024 * 1024)"
+ }
+ ]
+ },
+ {
+ "id": "{Float64b__1024__1024__1024} GiB",
+ "message": "{Float64b__1024__1024__1024} GiB",
+ "translation": "{Float64b__1024__1024__1024} GiB",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Float64b__1024__1024__1024",
+ "string": "%.2[1]f",
+ "type": "float64",
+ "underlyingType": "float64",
+ "argNum": 1,
+ "expr": "float64(b) / (1024 * 1024 * 1024)"
+ }
+ ]
+ },
+ {
+ "id": "{Float64b__1024__1024__1024__1024} TiB",
+ "message": "{Float64b__1024__1024__1024__1024} TiB",
+ "translation": "{Float64b__1024__1024__1024__1024} TiB",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Float64b__1024__1024__1024__1024",
+ "string": "%.2[1]f",
+ "type": "float64",
+ "underlyingType": "float64",
+ "argNum": 1,
+ "expr": "float64(b) / (1024 * 1024 * 1024) / 1024"
+ }
+ ]
+ },
+ {
+ "id": "{Why}: {Offender}",
+ "message": "{Why}: {Offender}",
+ "translation": "{Why}: {Offender}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Why",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "e.why"
+ },
+ {
+ "id": "Offender",
+ "string": "%[2]q",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 2,
+ "expr": "e.offender"
+ }
+ ]
+ },
+ {
+ "id": "Invalid IP address",
+ "message": "Invalid IP address",
+ "translation": "ਅਵੈਧ IP ਸਿਰਨਾਵਾਂ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Invalid network prefix length",
+ "message": "Invalid network prefix length",
+ "translation": "ਗਲਤ ਨੈੱਟਵਰਕ ਅਗੇਤਰ ਲੰਬਾਈ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Missing port from endpoint",
+ "message": "Missing port from endpoint",
+ "translation": "ਐਂਡਪੁਆਇੰਟ ਤੋਂ ਪੋਰਟ ਗੁੰਮ ਹੈ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Invalid MTU",
+ "message": "Invalid MTU",
+ "translation": "ਗ਼ੈਰ-ਵਾਜਬ MTU",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Invalid port",
+ "message": "Invalid port",
+ "translation": "ਗ਼ੈਰ-ਵਾਜਬ ਪੋਰਟ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Invalid persistent keepalive",
+ "message": "Invalid persistent keepalive",
+ "translation": "ਗ਼ੈਰ-ਵਾਜਬ persistent keepalive",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "[EnumerationSeparator]",
+ "message": "[EnumerationSeparator]",
+ "translation": ", ",
+ "comment": "Text to insert between items when listing - most western languages will translate ‘[EnumerationSeparator]’ into ‘, ’ to produce lists like ‘apple, orange, strawberry’. Eastern languages might translate into ‘、’ to produce lists like ‘リンゴ、オレンジ、イチゴ’."
+ },
+ {
+ "id": "[UnitSeparator]",
+ "message": "[UnitSeparator]",
+ "translation": ", ",
+ "comment": "Text to insert when combining units of a measure - most languages will translate ‘[UnitSeparator]’ into ‘ ’ (space) to produce lists like ‘2 minuti 30 sekund’, or empty string ‘’ to produce ‘2分30秒’."
+ },
+ {
+ "id": "About WireGuard",
+ "message": "About WireGuard",
+ "translation": "ਵਾਇਰਗਾਰਡ ਬਾਰੇ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}-{GOARCH}\nOperating system: {OsName}\nArchitecture: {NativeArch}",
+ "message": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}-{GOARCH}\nOperating system: {OsName}\nArchitecture: {NativeArch}",
+ "translation": "ਐਪ ਦਾ ਵਰਜ਼ਨ: {Number}\nਗੋ ਬੈਕਐਂਡ ਦਾ ਵਰਜ਼ਨ: {WireGuardGoVersion}\nਗੋ ਦਾ ਵਰਜ਼ਨ: {Version_go}-{GOARCH}\nਓਪਰੇਟਿੰਗ ਸਿਸਟਮ: {OsName}\nਢਾਂਚਾ: {NativeArch}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Number",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "version.Number"
+ },
+ {
+ "id": "WireGuardGoVersion",
+ "string": "%[2]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 2,
+ "expr": "device.WireGuardGoVersion"
+ },
+ {
+ "id": "Version_go",
+ "string": "%[3]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 3,
+ "expr": "strings.TrimPrefix(runtime.Version(), \"go\")"
+ },
+ {
+ "id": "GOARCH",
+ "string": "%[4]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 4,
+ "expr": "runtime.GOARCH"
+ },
+ {
+ "id": "OsName",
+ "string": "%[5]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 5,
+ "expr": "version.OsName()"
+ },
+ {
+ "id": "NativeArch",
+ "string": "%[6]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 6,
+ "expr": "version.NativeArch()"
+ }
+ ]
+ },
+ {
+ "id": "Close",
+ "message": "Close",
+ "translation": "ਬੰਦ ਕਰੋ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "♥ &Donate!",
+ "message": "♥ &Donate!",
+ "translation": "♥ ਦਾਨ ਦਿਓ(&D)!",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Status:",
+ "message": "Status:",
+ "translation": "ਸਥਿਤੀ:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Deactivate",
+ "message": "&Deactivate",
+ "translation": "ਨਾ-ਸਰਗਰਮ ਕਰੋ(&D)",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Activate",
+ "message": "&Activate",
+ "translation": "ਸਰਗਰਮ ਕਰੋ(&A)",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Public key:",
+ "message": "Public key:",
+ "translation": "ਪਬਲਿਕ ਕੁੰਜੀ:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Listen port:",
+ "message": "Listen port:",
+ "translation": "ਸੁਣਨ ਵਾਲੀ ਪੋਰਟ:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "MTU:",
+ "message": "MTU:",
+ "translation": "MTU:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Addresses:",
+ "message": "Addresses:",
+ "translation": "ਸਿਰਨਾਵੇ:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "DNS servers:",
+ "message": "DNS servers:",
+ "translation": "DNS ਸਰਵਰ:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Scripts:",
+ "message": "Scripts:",
+ "translation": "ਸਕ੍ਰਿਪਟਾਂ:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Allowed IPs:",
+ "message": "Allowed IPs:",
+ "translation": "ਮਨਜ਼ੂਰ ਕੀਤੇ IP:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Endpoint:",
+ "message": "Endpoint:",
+ "translation": "ਐਂਡ-ਪੁਆਇੰਟ:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Transfer:",
+ "message": "Transfer:",
+ "translation": "ਟਰਾਂਸਫਰ:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "enabled",
+ "message": "enabled",
+ "translation": "ਸਮਰੱਥ ਹੈ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "{String} received, {String_1} sent",
+ "message": "{String} received, {String_1} sent",
+ "translation": "{String} ਮਿਲੇ, {String_1} ਭੇਜੇ",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "String",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "c.RxBytes.String()"
+ },
+ {
+ "id": "String_1",
+ "string": "%[2]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 2,
+ "expr": "c.TxBytes.String()"
+ }
+ ]
+ },
+ {
+ "id": "Failed to determine tunnel state",
+ "message": "Failed to determine tunnel state",
+ "translation": "ਟਨਲ ਸਥਿਤੀ ਪਤਾ ਲਗਾਉਣ ਲਈ ਅਸਫ਼ਲ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Failed to activate tunnel",
+ "message": "Failed to activate tunnel",
+ "translation": "ਟਨਲ ਸਰਗਰਮ ਕਰਨ ਲਈ ਅਸਫ਼ਲ ਹੈ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Failed to deactivate tunnel",
+ "message": "Failed to deactivate tunnel",
+ "translation": "ਟਨਲ ਨਾ-ਸਰਗਰਮ ਕਰਨ ਲਈ ਅਸਫ਼ਲ ਹੈ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Interface: {Name}",
+ "message": "Interface: {Name}",
+ "translation": "ਇੰਟਰਫੇਸ: {Name}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Name",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "config.Name"
+ }
+ ]
+ },
+ {
+ "id": "Peer",
+ "message": "Peer",
+ "translation": "ਪੀਅਰ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Create new tunnel",
+ "message": "Create new tunnel",
+ "translation": "ਨਵੀਂ ਟਨਲ ਬਣਾਓ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Edit tunnel",
+ "message": "Edit tunnel",
+ "translation": "ਟਨਲ ਨੂੰ ਸੋਧੋ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Name:",
+ "message": "&Name:",
+ "translation": "ਨਾਂ(&N):",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Public key:",
+ "message": "&Public key:",
+ "translation": "ਪਬਲਿਕ ਕੁੰਜੀ(&P):",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "(unknown)",
+ "message": "(unknown)",
+ "translation": "(ਅਣਪਛਾਤਾ)",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Save",
+ "message": "&Save",
+ "translation": "ਸੰਭਾਲੋ(&S)",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Cancel",
+ "message": "Cancel",
+ "translation": "ਰੱਦ ਕਰੋ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Configuration:",
+ "message": "&Configuration:",
+ "translation": "ਸੰਰਚਨਾ(&C):",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Invalid name",
+ "message": "Invalid name",
+ "translation": "ਅਯੋਗ ਨਾਂ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "A name is required.",
+ "message": "A name is required.",
+ "translation": "ਨਾਂ ਚਾਹੀਦਾ ਹੈ।",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Tunnel already exists",
+ "message": "Tunnel already exists",
+ "translation": "ਟਨਲ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹੈ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Another tunnel already exists with the name ‘{NewName}’.",
+ "message": "Another tunnel already exists with the name ‘{NewName}’.",
+ "translation": "ਨਾਂ ‘{NewName}’ ਨਾਲ ਟਨਲ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹੈ।",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "NewName",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "newName"
+ }
+ ]
+ },
+ {
+ "id": "Unable to create new configuration",
+ "message": "Unable to create new configuration",
+ "translation": "ਨਵੀਂ ਸੰਰਚਨਾ ਬਣਾਉਣ ਲਈ ਅਸਮਰੱਥ ਹੈ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Writing file failed",
+ "message": "Writing file failed",
+ "translation": "ਫ਼ਾਇਲ ਬਣਾਉਣ ਲਈ ਅਸਫ਼ਲ ਹੈ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "File ‘{FilePath}’ already exists.\n\nDo you want to overwrite it?",
+ "message": "File ‘{FilePath}’ already exists.\n\nDo you want to overwrite it?",
+ "translation": "‘{FilePath}’ ਫ਼ਾਇਲ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹੈ।\n\nਕੀ ਤੁਸੀਂ ਇਸ ਉੱਤੇ ਲਿਖਣਾ ਚਾਹੁੰਦੇ ਹੋ?",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "FilePath",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "filePath"
+ }
+ ]
+ },
+ {
+ "id": "Active",
+ "message": "Active",
+ "translation": "ਸਰਗਰਮ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Activating",
+ "message": "Activating",
+ "translation": "ਸਰਗਰਮ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Inactive",
+ "message": "Inactive",
+ "translation": "ਨਾ-ਸਰਗਰਮ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Deactivating",
+ "message": "Deactivating",
+ "translation": "ਨਾ-ਸਰਗਰਮ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Unknown state",
+ "message": "Unknown state",
+ "translation": "ਅਣਪਛਾਤੀ ਸਥਿਤੀ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Log",
+ "message": "Log",
+ "translation": "ਲਾਗੂ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Copy",
+ "message": "&Copy",
+ "translation": "ਕਾਪੀ ਕਰੋ(&C)",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Select &all",
+ "message": "Select &all",
+ "translation": "ਸਾਰੇ ਚੁਣੋ(&a)",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Save to file…",
+ "message": "&Save to file…",
+ "translation": "ਫ਼ਾਇਲ ਵਿੱਚ ਸੰਭਾਲੋ(&S)…",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Time",
+ "message": "Time",
+ "translation": "ਸਮਾਂ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Log message",
+ "message": "Log message",
+ "translation": "ਲਾਗ ਸੁਨੇਹਾ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Text Files (*.txt)|*.txt|All Files (*.*)|*.*",
+ "message": "Text Files (*.txt)|*.txt|All Files (*.*)|*.*",
+ "translation": "ਲਿਖਤ ਫ਼ਾਇਲਾਂ (*.txt)|*.txt|ਸਾਰੀਆਂ ਫ਼ਾਇਲਾਂ (*.*)|*.*",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Export log to file",
+ "message": "Export log to file",
+ "translation": "ਲਾਗ ਫ਼ਾਇਲ ਵਿੱਚ ਬਰਾਮਦ ਕਰੋ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&About WireGuard…",
+ "message": "&About WireGuard…",
+ "translation": "ਵਾਇਰਗਾਰਡ ਬਾਰੇ(&A)…",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Tunnel Error",
+ "message": "Tunnel Error",
+ "translation": "ਟਨਲ ਗਲਤੀ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "{ErrMsg}\n\nPlease consult the log for more information.",
+ "message": "{ErrMsg}\n\nPlease consult the log for more information.",
+ "translation": "{ErrMsg}\n\nPlease consult the log for more information.",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "ErrMsg",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "errMsg"
+ }
+ ]
+ },
+ {
+ "id": "{Title} (out of date)",
+ "message": "{Title} (out of date)",
+ "translation": "{Title} (out of date)",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Title",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "mtw.Title()"
+ }
+ ]
+ },
+ {
+ "id": "WireGuard Detection Error",
+ "message": "WireGuard Detection Error",
+ "translation": "WireGuard ਖੋਜ ਗ਼ਲਤੀ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "WireGuard: Deactivated",
+ "message": "WireGuard: Deactivated",
+ "translation": "ਵਾਇਰਗਾਰਡ: ਨਾ-ਸਰਗਰਮ ਕੀਤਾ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Status: Unknown",
+ "message": "Status: Unknown",
+ "translation": "ਸਥਿਤੀ: ਅਣਪਛਾਤੀ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Addresses: None",
+ "message": "Addresses: None",
+ "translation": "ਸਿਰਨਾਵੇਂ: ਕੋਈ ਨਹੀਂ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Manage tunnels…",
+ "message": "&Manage tunnels…",
+ "translation": "ਟਨਲਾਂ ਦਾ ਇੰਤਜ਼ਾਮ ਕਰੋ(&M)…",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Import tunnel(s) from file…",
+ "message": "&Import tunnel(s) from file…",
+ "translation": "ਫ਼ਾਇਲ ਤੋਂ ਟਨਲਾਂ ਦਰਾਮਦ ਕਰੋ(&I)…",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "E&xit",
+ "message": "E&xit",
+ "translation": "ਬਾਹਰ(&x)",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Tunnels",
+ "message": "&Tunnels",
+ "translation": "ਟਨਲ(&T)",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "WireGuard Activated",
+ "message": "WireGuard Activated",
+ "translation": "ਵਾਇਰਗਾਰਡ ਸਰਗਰਮ ਕੀਤਾ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "The {Name} tunnel has been activated.",
+ "message": "The {Name} tunnel has been activated.",
+ "translation": "{Name} ਟਨਲ ਸਰਗਰਮ ਕੀਤੀ ਗਈ ਹੈ।",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Name",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "tunnel.Name"
+ }
+ ]
+ },
+ {
+ "id": "WireGuard Deactivated",
+ "message": "WireGuard Deactivated",
+ "translation": "ਵਾਇਰਗਾਰਡ ਨਾ-ਸਰਗਰਮ ਕੀਤਾ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "The {Name} tunnel has been deactivated.",
+ "message": "The {Name} tunnel has been deactivated.",
+ "translation": "{Name} ਟਨਲ ਨਾ-ਸਰਗਰਮ ਕੀਤੀ ਗਈ ਹੈ।",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Name",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "tunnel.Name"
+ }
+ ]
+ },
+ {
+ "id": "WireGuard Tunnel Error",
+ "message": "WireGuard Tunnel Error",
+ "translation": "ਵਾਇਰਗਾਰਡ ਟਨਲ ਗਲਤੀ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "WireGuard: {TextForStateglobalState_true}",
+ "message": "WireGuard: {TextForStateglobalState_true}",
+ "translation": "ਵਾਇਰਗਾਰਡ: {TextForStateglobalState_true}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "TextForStateglobalState_true",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "textForState(globalState, true)"
+ }
+ ]
+ },
+ {
+ "id": "Status: {StateText}",
+ "message": "Status: {StateText}",
+ "translation": "ਸਥਿਤੀ: {StateText}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "StateText",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "stateText"
+ }
+ ]
+ },
+ {
+ "id": "Addresses: {EnumerationSeparator}",
+ "message": "Addresses: {EnumerationSeparator}",
+ "translation": "ਸਿਰਨਾਵੇਂ: {EnumerationSeparator}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "EnumerationSeparator",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "strings.Join(addrs, l18n.EnumerationSeparator())"
+ }
+ ]
+ },
+ {
+ "id": "An Update is Available!",
+ "message": "An Update is Available!",
+ "translation": "ਅੱਪਡੇਟ ਮੌਜੂਦ ਹੈ!",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "WireGuard Update Available",
+ "message": "WireGuard 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.",
+ "translation": "ਵਾਇਰਗਾਰਡ ਲਈ ਅੱਪਡੇਟ ਹੁਣ ਮੌਜੂਦ ਹੈ। ਜਿੰਨਾ ਛੇਤੀ ਹੋ ਸਕੇ ਤੁਹਾਨੂੰ ਅੱਪਡੇਟ ਕਰਨ ਦੀ ਸਲਾਹ ਦਿੱਤੀ ਜਾਂਦੀ ਹੈ।",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Tunnels",
+ "message": "Tunnels",
+ "translation": "ਟਨਲਾਂ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Edit",
+ "message": "&Edit",
+ "translation": "ਸੋਧੋ(&E)",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Add &empty tunnel…",
+ "message": "Add &empty tunnel…",
+ "translation": "…ਖਾਲੀ ਟਨਲ ਜੋੜੋ(&e)",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Add Tunnel",
+ "message": "Add Tunnel",
+ "translation": "ਟਨਲ ਜੋੜੋ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Remove selected tunnel(s)",
+ "message": "Remove selected tunnel(s)",
+ "translation": "ਚੁਣੀਆਂ ਟਨਲਾਂ ਨੂੰ ਹਟਾਓ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Export all tunnels to zip",
+ "message": "Export all tunnels to zip",
+ "translation": "ਸਾਰੀਆਂ ਟਨਲਾਂ ਨੂੰ ਜ਼ਿੱਪ ਵਜੋਂ ਬਰਾਮਦ ਕਰੋ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Toggle",
+ "message": "&Toggle",
+ "translation": "ਪਲਟੋ(&T)",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Export all tunnels to &zip…",
+ "message": "Export all tunnels to &zip…",
+ "translation": "ਸਾਰੀਆਂ ਟਨਲਾਂ ਨੂੰ ਜ਼ਿੱਪ ਵਜੋਂ ਬਰਾਮਦ ਕਰੋ…",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Edit &selected tunnel…",
+ "message": "Edit &selected tunnel…",
+ "translation": "ਚੁਣੀ ਟਨਲ ਸੋਧੋ(&s)…",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Remove selected tunnel(s)",
+ "message": "&Remove selected tunnel(s)",
+ "translation": "ਚੁਣੀਆਂ ਟਨਲਾਂ ਨੂੰ ਹਟਾਓ(&R)",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "no configuration files were found",
+ "message": "no configuration files were found",
+ "translation": "ਕੋਈ ਸੰਰਚਨਾ ਫ਼ਾਇਲਾਂ ਨਹੀਂ ਲੱਭੀਆਂ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Imported tunnels",
+ "message": "Imported tunnels",
+ "translation": "ਇੰਪੋਰਟ ਕੀਤੀਆਂ ਟਨਲਾਂ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Unable to create tunnel",
+ "message": "Unable to create tunnel",
+ "translation": "ਟਨਲ ਬਣਾਉਣ ਲਈ ਅਸਮਰੱਥ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Are you sure you would like to delete {TunnelCount} tunnels?",
+ "message": "Are you sure you would like to delete {TunnelCount} tunnels?",
+ "translation": {
+ "select": {
+ "feature": "plural",
+ "arg": "TunnelCount",
+ "cases": {
+ "one": {
+ "msg": "ਕੀ ਤੁਸੀਂ {TunnelCount} ਟਨਲ ਨੂੰ ਹਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ?"
+ },
+ "other": {
+ "msg": "ਕੀ ਤੁਸੀਂ {TunnelCount} ਟਨਲਾਂ ਨੂੰ ਹਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ?"
+ }
+ }
+ }
+ },
+ "placeholders": [
+ {
+ "id": "TunnelCount",
+ "string": "%[1]d",
+ "type": "int",
+ "underlyingType": "int",
+ "argNum": 1,
+ "expr": "tunnelCount"
+ }
+ ]
+ },
+ {
+ "id": "Delete tunnel ‘{TunnelName}’",
+ "message": "Delete tunnel ‘{TunnelName}’",
+ "translation": "‘{TunnelName}’ ਟਨਲ ਨੂੰ ਹਟਾਓ",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "TunnelName",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "tunnelName"
+ }
+ ]
+ },
+ {
+ "id": "Are you sure you would like to delete tunnel ‘{TunnelName}’?",
+ "message": "Are you sure you would like to delete tunnel ‘{TunnelName}’?",
+ "translation": "ਕੀ ਤੁਸੀਂ ‘{TunnelName}‘ ਟਨਲ ਨੂੰ ਹਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ?",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "TunnelName",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "tunnelName"
+ }
+ ]
+ },
+ {
+ "id": "Unable to delete tunnel",
+ "message": "Unable to delete tunnel",
+ "translation": "ਟਨਲ ਹਟਾਉਣ ਲਈ ਅਸਮਰੱਥ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Configuration Files (*.zip, *.conf)|*.zip;*.conf|All Files (*.*)|*.*",
+ "message": "Configuration Files (*.zip, *.conf)|*.zip;*.conf|All Files (*.*)|*.*",
+ "translation": "ਸੰਰਚਨਾ ਫ਼ਾਇਲਾਂ (*.zip, *.conf)|*.zip;*.conf|ਸਾਰੀਆਂ ਫ਼ਾਇਲਾਂ (*.*)|*.*",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Import tunnel(s) from file",
+ "message": "Import tunnel(s) from file",
+ "translation": "ਫ਼ਾਇਲ ਤੋਂ ਟਨਲਾਂ ਦਰਾਮਦ ਕਰੋ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Configuration ZIP Files (*.zip)|*.zip",
+ "message": "Configuration ZIP Files (*.zip)|*.zip",
+ "translation": "ਸੰਰਚਨਾ ਜ਼ਿੱਪ ਫਾਇਲਾਂ (*.zip)|*.zip",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Update Now",
+ "message": "Update Now",
+ "translation": "ਹੁਣੇ ਅੱਪਡੇਟ ਕਰੋ",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Status: Complete!",
+ "message": "Status: Complete!",
+ "translation": "ਸਥਿਤੀ: ਪੂਰਾ!",
+ "translatorComment": "Copied from source."
+ }
+ ]
+} \ No newline at end of file
diff --git a/locales/pl/messages.gotext.json b/locales/pl/messages.gotext.json
index da52f616..072fa8e4 100644
--- a/locales/pl/messages.gotext.json
+++ b/locales/pl/messages.gotext.json
@@ -8,16 +8,1900 @@
"translatorComment": "Copied from source."
},
{
+ "id": "(no argument): elevate and install manager service",
+ "message": "(no argument): elevate and install manager service",
+ "translation": "(brak argumentu): Podnieś uprawnienia i zainstaluj usługę menedżera",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Usage: {Args0} [\n{String}]",
+ "message": "Usage: {Args0} [\n{String}]",
+ "translation": "Użycie: {Args0} [{String}]",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Args0",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "os.Args[0]"
+ },
+ {
+ "id": "String",
+ "string": "%[2]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 2,
+ "expr": "builder.String()"
+ }
+ ]
+ },
+ {
"id": "Command Line Options",
"message": "Command Line Options",
"translation": "Opcje wiersza poleceń",
"translatorComment": "Copied from source."
},
{
- "id": "You must use the 64-bit version of WireGuard on this computer.",
- "message": "You must use the 64-bit version of WireGuard on this computer.",
- "translation": "Musisz użyć 64-bitowej wersji WireGuard na tym komputerze.",
+ "id": "Unable to determine whether the process is running under WOW64: {Err}",
+ "message": "Unable to determine whether the process is running under WOW64: {Err}",
+ "translation": "Nie można określić, czy proces jest uruchomiony w środowisku WOW64: {Err}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Err",
+ "string": "%[1]v",
+ "type": "error",
+ "underlyingType": "interface{Error() string}",
+ "argNum": 1,
+ "expr": "err"
+ }
+ ]
+ },
+ {
+ "id": "You must use the native version of WireGuard on this computer.",
+ "message": "You must use the native version of WireGuard on this computer.",
+ "translation": "Musisz użyć natywnej wersji WireGuard na tym komputerze.",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Unable to open current process token: {Err}",
+ "message": "Unable to open current process token: {Err}",
+ "translation": "Nie można otworzyć bieżącego tokenu procesu: {Err}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Err",
+ "string": "%[1]v",
+ "type": "error",
+ "underlyingType": "interface{Error() string}",
+ "argNum": 1,
+ "expr": "err"
+ }
+ ]
+ },
+ {
+ "id": "WireGuard may only be used by users who are a member of the Builtin {AdminGroupName} group.",
+ "message": "WireGuard may only be used by users who are a member of the Builtin {AdminGroupName} group.",
+ "translation": "WireGuard może być używany tylko przez użytkowników, którzy są członkami wbudowanej grupy {AdminGroupName}.",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "AdminGroupName",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "elevate.AdminGroupName()"
+ }
+ ]
+ },
+ {
+ "id": "WireGuard is running, but the UI is only accessible from desktops of the Builtin {AdminGroupName} group.",
+ "message": "WireGuard is running, but the UI is only accessible from desktops of the Builtin {AdminGroupName} group.",
+ "translation": "WireGuard jest uruchomiony, ale interfejs jest dostępny tylko z poziomu użytkowników należących do wbudowanej grupy {AdminGroupName}.",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "AdminGroupName",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "elevate.AdminGroupName()"
+ }
+ ]
+ },
+ {
+ "id": "WireGuard system tray icon did not appear after 30 seconds.",
+ "message": "WireGuard system tray icon did not appear after 30 seconds.",
+ "translation": "Ikona WireGuard nie pojawiła się po 30 sekundach w zasobniku systemowym.",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Now",
+ "message": "Now",
+ "translation": "Teraz",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "System clock wound backward!",
+ "message": "System clock wound backward!",
+ "translation": "Zegar systemowy został cofnięty!",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "{Years} year(s)",
+ "message": "{Years} year(s)",
+ "translation": {
+ "select": {
+ "feature": "plural",
+ "arg": "Years",
+ "cases": {
+ "one": {
+ "msg": "{Years} rok"
+ },
+ "few": {
+ "msg": "{Years} lata"
+ },
+ "many": {
+ "msg": "{Years} lat"
+ },
+ "other": {
+ "msg": "{Years} lata"
+ }
+ }
+ }
+ },
+ "placeholders": [
+ {
+ "id": "Years",
+ "string": "%[1]d",
+ "type": "int64",
+ "underlyingType": "int64",
+ "argNum": 1,
+ "expr": "years"
+ }
+ ]
+ },
+ {
+ "id": "{Days} day(s)",
+ "message": "{Days} day(s)",
+ "translation": {
+ "select": {
+ "feature": "plural",
+ "arg": "Days",
+ "cases": {
+ "one": {
+ "msg": "{Days} dzień"
+ },
+ "few": {
+ "msg": "{Days} dni"
+ },
+ "many": {
+ "msg": "{Days} dni"
+ },
+ "other": {
+ "msg": "{Days} dni"
+ }
+ }
+ }
+ },
+ "placeholders": [
+ {
+ "id": "Days",
+ "string": "%[1]d",
+ "type": "int64",
+ "underlyingType": "int64",
+ "argNum": 1,
+ "expr": "days"
+ }
+ ]
+ },
+ {
+ "id": "{Hours} hour(s)",
+ "message": "{Hours} hour(s)",
+ "translation": {
+ "select": {
+ "feature": "plural",
+ "arg": "Hours",
+ "cases": {
+ "one": {
+ "msg": "{Hours} godzina"
+ },
+ "few": {
+ "msg": "{Hours} godziny"
+ },
+ "many": {
+ "msg": "{Hours} godzin"
+ },
+ "other": {
+ "msg": "{Hours} godziny"
+ }
+ }
+ }
+ },
+ "placeholders": [
+ {
+ "id": "Hours",
+ "string": "%[1]d",
+ "type": "int64",
+ "underlyingType": "int64",
+ "argNum": 1,
+ "expr": "hours"
+ }
+ ]
+ },
+ {
+ "id": "{Minutes} minute(s)",
+ "message": "{Minutes} minute(s)",
+ "translation": {
+ "select": {
+ "feature": "plural",
+ "arg": "Minutes",
+ "cases": {
+ "one": {
+ "msg": "{Minutes} minuta"
+ },
+ "few": {
+ "msg": "{Minutes} minuty"
+ },
+ "many": {
+ "msg": "{Minutes} minut"
+ },
+ "other": {
+ "msg": "{Minutes} minut"
+ }
+ }
+ }
+ },
+ "placeholders": [
+ {
+ "id": "Minutes",
+ "string": "%[1]d",
+ "type": "int64",
+ "underlyingType": "int64",
+ "argNum": 1,
+ "expr": "minutes"
+ }
+ ]
+ },
+ {
+ "id": "{Seconds} second(s)",
+ "message": "{Seconds} second(s)",
+ "translation": {
+ "select": {
+ "feature": "plural",
+ "arg": "Seconds",
+ "cases": {
+ "one": {
+ "msg": "{Seconds} sekunda"
+ },
+ "few": {
+ "msg": "{Seconds} sekundy"
+ },
+ "many": {
+ "msg": "{Seconds} sekund"
+ },
+ "other": {
+ "msg": "{Seconds} sekund"
+ }
+ }
+ }
+ },
+ "placeholders": [
+ {
+ "id": "Seconds",
+ "string": "%[1]d",
+ "type": "int64",
+ "underlyingType": "int64",
+ "argNum": 1,
+ "expr": "seconds"
+ }
+ ]
+ },
+ {
+ "id": "{Timestamp} ago",
+ "message": "{Timestamp} ago",
+ "translation": "{Timestamp} temu",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Timestamp",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "timestamp"
+ }
+ ]
+ },
+ {
+ "id": "{Bytes} B",
+ "message": "{Bytes} B",
+ "translation": "{Bytes} B",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Bytes",
+ "string": "%[1]d",
+ "type": "golang.zx2c4.com/wireguard/windows/conf.Bytes",
+ "underlyingType": "uint64",
+ "argNum": 1,
+ "expr": "b"
+ }
+ ]
+ },
+ {
+ "id": "{Float64b__1024} KiB",
+ "message": "{Float64b__1024} KiB",
+ "translation": "{Float64b__1024} KiB",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Float64b__1024",
+ "string": "%.2[1]f",
+ "type": "float64",
+ "underlyingType": "float64",
+ "argNum": 1,
+ "expr": "float64(b) / 1024"
+ }
+ ]
+ },
+ {
+ "id": "{Float64b__1024__1024} MiB",
+ "message": "{Float64b__1024__1024} MiB",
+ "translation": "{Float64b__1024__1024} MiB",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Float64b__1024__1024",
+ "string": "%.2[1]f",
+ "type": "float64",
+ "underlyingType": "float64",
+ "argNum": 1,
+ "expr": "float64(b) / (1024 * 1024)"
+ }
+ ]
+ },
+ {
+ "id": "{Float64b__1024__1024__1024} GiB",
+ "message": "{Float64b__1024__1024__1024} GiB",
+ "translation": "{Float64b__1024__1024__1024} GiB",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Float64b__1024__1024__1024",
+ "string": "%.2[1]f",
+ "type": "float64",
+ "underlyingType": "float64",
+ "argNum": 1,
+ "expr": "float64(b) / (1024 * 1024 * 1024)"
+ }
+ ]
+ },
+ {
+ "id": "{Float64b__1024__1024__1024__1024} TiB",
+ "message": "{Float64b__1024__1024__1024__1024} TiB",
+ "translation": "{Float64b__1024__1024__1024__1024} TiB",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Float64b__1024__1024__1024__1024",
+ "string": "%.2[1]f",
+ "type": "float64",
+ "underlyingType": "float64",
+ "argNum": 1,
+ "expr": "float64(b) / (1024 * 1024 * 1024) / 1024"
+ }
+ ]
+ },
+ {
+ "id": "{Why}: {Offender}",
+ "message": "{Why}: {Offender}",
+ "translation": "{Why}: {Offender}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Why",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "e.why"
+ },
+ {
+ "id": "Offender",
+ "string": "%[2]q",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 2,
+ "expr": "e.offender"
+ }
+ ]
+ },
+ {
+ "id": "Invalid IP address",
+ "message": "Invalid IP address",
+ "translation": "Nieprawidłowy adres IP",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Invalid network prefix length",
+ "message": "Invalid network prefix length",
+ "translation": "Nieprawidłowa długość prefiksu sieci",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Missing port from endpoint",
+ "message": "Missing port from endpoint",
+ "translation": "Brak portu urządzenia końcowego",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Invalid endpoint host",
+ "message": "Invalid endpoint host",
+ "translation": "Nieprawidłowy host (urządzenie końcowe)",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Brackets must contain an IPv6 address",
+ "message": "Brackets must contain an IPv6 address",
+ "translation": "Nawiasy muszą zawierać adres IPv6",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Invalid MTU",
+ "message": "Invalid MTU",
+ "translation": "Nieprawidłowe MTU",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Invalid port",
+ "message": "Invalid port",
+ "translation": "Nieprawidłowy port",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Invalid persistent keepalive",
+ "message": "Invalid persistent keepalive",
+ "translation": "Nieprawidłowy parametr utrzymania połączenia",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Invalid key: {Err}",
+ "message": "Invalid key: {Err}",
+ "translation": "Nieprawidłowy klucz: {Err}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Err",
+ "string": "%[1]v",
+ "type": "error",
+ "underlyingType": "interface{Error() string}",
+ "argNum": 1,
+ "expr": "err"
+ }
+ ]
+ },
+ {
+ "id": "Keys must decode to exactly 32 bytes",
+ "message": "Keys must decode to exactly 32 bytes",
+ "translation": "Klucze muszą zostać zdekodowane do dokładnie 32 bajtów",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Number must be a number between 0 and 2^64-1: {Err}",
+ "message": "Number must be a number between 0 and 2^64-1: {Err}",
+ "translation": "Liczba musi zawierać się w przedziale 0 - 2^64-1: {Err}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Err",
+ "string": "%[1]v",
+ "type": "error",
+ "underlyingType": "interface{Error() string}",
+ "argNum": 1,
+ "expr": "err"
+ }
+ ]
+ },
+ {
+ "id": "Two commas in a row",
+ "message": "Two commas in a row",
+ "translation": "Dwa przecinki z rzędu",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Tunnel name is not valid",
+ "message": "Tunnel name is not valid",
+ "translation": "Nazwa tunelu jest nieprawidłowa",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Line must occur in a section",
+ "message": "Line must occur in a section",
+ "translation": "Linia musi występować w sekcji",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Config key is missing an equals separator",
+ "message": "Config key is missing an equals separator",
+ "translation": "Klucz konfiguracyjny nie zawiera separatora równorzędnego",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Key must have a value",
+ "message": "Key must have a value",
+ "translation": "Klucz musi mieć wartość",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Invalid key for [Interface] section",
+ "message": "Invalid key for [Interface] section",
+ "translation": "Nieprawidłowy klucz dla sekcji [Interface]",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Invalid key for [Peer] section",
+ "message": "Invalid key for [Peer] section",
+ "translation": "Nieprawidłowy klucz dla sekcji [Peer]",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "An interface must have a private key",
+ "message": "An interface must have a private key",
+ "translation": "Interfejs musi mieć klucz prywatny",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "[none specified]",
+ "message": "[none specified]",
+ "translation": "[nie określono]",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "All peers must have public keys",
+ "message": "All peers must have public keys",
+ "translation": "Wszyscy uczestnicy muszą mieć klucze publiczne",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Error in getting configuration",
+ "message": "Error in getting configuration",
+ "translation": "Błąd podczas pobierania konfiguracji",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Invalid key for interface section",
+ "message": "Invalid key for interface section",
+ "translation": "Nieprawidłowy klucz dla sekcji interface",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Protocol version must be 1",
+ "message": "Protocol version must be 1",
+ "translation": "Wersja protokołu musi być 1",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Invalid key for peer section",
+ "message": "Invalid key for peer section",
+ "translation": "Nieprawidłowy klucz dla sekcji peer",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "[EnumerationSeparator]",
+ "message": "[EnumerationSeparator]",
+ "translation": ", ",
+ "comment": "Text to insert between items when listing - most western languages will translate ‘[EnumerationSeparator]’ into ‘, ’ to produce lists like ‘apple, orange, strawberry’. Eastern languages might translate into ‘、’ to produce lists like ‘リンゴ、オレンジ、イチゴ’."
+ },
+ {
+ "id": "[UnitSeparator]",
+ "message": "[UnitSeparator]",
+ "translation": ", ",
+ "comment": "Text to insert when combining units of a measure - most languages will translate ‘[UnitSeparator]’ into ‘ ’ (space) to produce lists like ‘2 minuti 30 sekund’, or empty string ‘’ to produce ‘2分30秒’."
+ },
+ {
+ "id": "About WireGuard",
+ "message": "About WireGuard",
+ "translation": "Informacje o WireGuard",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "WireGuard logo image",
+ "message": "WireGuard logo image",
+ "translation": "Logo WireGuard",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}-{GOARCH}\nOperating system: {OsName}\nArchitecture: {NativeArch}",
+ "message": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}-{GOARCH}\nOperating system: {OsName}\nArchitecture: {NativeArch}",
+ "translation": "Wersja aplikacji: {Number}\nWersja implementacji w jęz. Go: {WireGuardGoVersion}\nWersja jęz. Go: {Version_go}-{GOARCH}\nSystem operacyjny: {OsName}\nArchitektura: {NativeArch}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Number",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "version.Number"
+ },
+ {
+ "id": "WireGuardGoVersion",
+ "string": "%[2]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 2,
+ "expr": "device.WireGuardGoVersion"
+ },
+ {
+ "id": "Version_go",
+ "string": "%[3]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 3,
+ "expr": "strings.TrimPrefix(runtime.Version(), \"go\")"
+ },
+ {
+ "id": "GOARCH",
+ "string": "%[4]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 4,
+ "expr": "runtime.GOARCH"
+ },
+ {
+ "id": "OsName",
+ "string": "%[5]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 5,
+ "expr": "version.OsName()"
+ },
+ {
+ "id": "NativeArch",
+ "string": "%[6]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 6,
+ "expr": "version.NativeArch()"
+ }
+ ]
+ },
+ {
+ "id": "Close",
+ "message": "Close",
+ "translation": "Zamknij",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "♥ &Donate!",
+ "message": "♥ &Donate!",
+ "translation": "♥ &Wpłać!",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Status:",
+ "message": "Status:",
+ "translation": "Status:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Deactivate",
+ "message": "&Deactivate",
+ "translation": "&Dezaktywuj",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Activate",
+ "message": "&Activate",
+ "translation": "&Aktywuj",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Public key:",
+ "message": "Public key:",
+ "translation": "Klucz publiczny:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Listen port:",
+ "message": "Listen port:",
+ "translation": "Port nasłuchu:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "MTU:",
+ "message": "MTU:",
+ "translation": "MTU:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Addresses:",
+ "message": "Addresses:",
+ "translation": "Adresy:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "DNS servers:",
+ "message": "DNS servers:",
+ "translation": "Serwery DNS:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Scripts:",
+ "message": "Scripts:",
+ "translation": "Skrypty:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Preshared key:",
+ "message": "Preshared key:",
+ "translation": "PSK:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Allowed IPs:",
+ "message": "Allowed IPs:",
+ "translation": "Dozwolone adresy IP:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Endpoint:",
+ "message": "Endpoint:",
+ "translation": "Urządzenie końcowe:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Persistent keepalive:",
+ "message": "Persistent keepalive:",
+ "translation": "Utrzymanie połączenia:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Latest handshake:",
+ "message": "Latest handshake:",
+ "translation": "Ostatni uścisk dłoni (handshake):",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Transfer:",
+ "message": "Transfer:",
+ "translation": "Transfer:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "pre-up",
+ "message": "pre-up",
+ "translation": "przed włączeniem",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "post-up",
+ "message": "post-up",
+ "translation": "po włączeniu",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "pre-down",
+ "message": "pre-down",
+ "translation": "przed wyłączeniem",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "post-down",
+ "message": "post-down",
+ "translation": "po wyłączeniu",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "disabled, per policy",
+ "message": "disabled, per policy",
+ "translation": "wyłączone, według zasad grupy",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "enabled",
+ "message": "enabled",
+ "translation": "włączyć",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "{String} received, {String_1} sent",
+ "message": "{String} received, {String_1} sent",
+ "translation": "{String} odebrano, {String_1} wysłano",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "String",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "c.RxBytes.String()"
+ },
+ {
+ "id": "String_1",
+ "string": "%[2]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 2,
+ "expr": "c.TxBytes.String()"
+ }
+ ]
+ },
+ {
+ "id": "Failed to determine tunnel state",
+ "message": "Failed to determine tunnel state",
+ "translation": "Nie udało się określić stanu tunelu",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Failed to activate tunnel",
+ "message": "Failed to activate tunnel",
+ "translation": "Nie udało się aktywować tunelu",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Failed to deactivate tunnel",
+ "message": "Failed to deactivate tunnel",
+ "translation": "Nie można dezaktywować tunelu",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Interface: {Name}",
+ "message": "Interface: {Name}",
+ "translation": "Interfejs: {Name}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Name",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "config.Name"
+ }
+ ]
+ },
+ {
+ "id": "Peer",
+ "message": "Peer",
+ "translation": "Peer",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Create new tunnel",
+ "message": "Create new tunnel",
+ "translation": "Utwórz nowy tunel",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Edit tunnel",
+ "message": "Edit tunnel",
+ "translation": "Edytuj tunel",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Name:",
+ "message": "&Name:",
+ "translation": "&Nazwa:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Public key:",
+ "message": "&Public key:",
+ "translation": "&Klucz publiczny:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "(unknown)",
+ "message": "(unknown)",
+ "translation": "(nieznany)",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Block untunneled traffic (kill-switch)",
+ "message": "&Block untunneled traffic (kill-switch)",
+ "translation": "Zablokuj niezabezpieczony ruch (wyłącznik awaryjny)",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface or is to the wrong DNS server, with special exceptions for DHCP and NDP.",
+ "message": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface or is to the wrong DNS server, with special exceptions for DHCP and NDP.",
+ "translation": "Jeżeli konfiguracja ma dokładnie jednego peer'a i ma w liście dozwolonych IP przynajmniej jeden adres 0.0.0.0/0 lub ::/0, wtedy usługa tunelowania dołącza do zapory sieciowej zestaw zasad, żeby zablokować ruch, który nie jest z interfejsu sieciowego lub jest skierowany do złego serwera DNS, z wyjątkiem dla serwera DHCP i NDP.",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Save",
+ "message": "&Save",
+ "translation": "&Zapisz",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Cancel",
+ "message": "Cancel",
+ "translation": "Anuluj",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Configuration:",
+ "message": "&Configuration:",
+ "translation": "&Konfiguracja:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Invalid name",
+ "message": "Invalid name",
+ "translation": "Nieprawidłowa nazwa",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "A name is required.",
+ "message": "A name is required.",
+ "translation": "Nazwa jest wymagana.",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Tunnel name ‘{NewName}’ is invalid.",
+ "message": "Tunnel name ‘{NewName}’ is invalid.",
+ "translation": "Nazwa tunelu ‘{NewName}’ jest niepoprawna.",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "NewName",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "newName"
+ }
+ ]
+ },
+ {
+ "id": "Unable to list existing tunnels",
+ "message": "Unable to list existing tunnels",
+ "translation": "Nie można wylistować istniejących tuneli",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Tunnel already exists",
+ "message": "Tunnel already exists",
+ "translation": "Tunel już istnieje",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Another tunnel already exists with the name ‘{NewName}’.",
+ "message": "Another tunnel already exists with the name ‘{NewName}’.",
+ "translation": "Inny tunel już istnieje z tą samą nazwą ‘{NewName}’.",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "NewName",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "newName"
+ }
+ ]
+ },
+ {
+ "id": "Unable to create new configuration",
+ "message": "Unable to create new configuration",
+ "translation": "Nie można utworzyć nowej konfiguracji",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Writing file failed",
+ "message": "Writing file failed",
+ "translation": "Zapis pliku się nie powiódł",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "File ‘{FilePath}’ already exists.\n\nDo you want to overwrite it?",
+ "message": "File ‘{FilePath}’ already exists.\n\nDo you want to overwrite it?",
+ "translation": "Plik ‘{FilePath}’ już istnieje. Czy chcesz go nadpisać?",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "FilePath",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "filePath"
+ }
+ ]
+ },
+ {
+ "id": "Active",
+ "message": "Active",
+ "translation": "Aktywny",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Activating",
+ "message": "Activating",
+ "translation": "Aktywowanie",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Inactive",
+ "message": "Inactive",
+ "translation": "Nieaktywny",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Deactivating",
+ "message": "Deactivating",
+ "translation": "Dezaktywowanie",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Unknown state",
+ "message": "Unknown state",
+ "translation": "Stan nieznany",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Log",
+ "message": "Log",
+ "translation": "Dziennik",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Copy",
+ "message": "&Copy",
+ "translation": "&Kopiuj",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Select &all",
+ "message": "Select &all",
+ "translation": "Wybierz &wszystko",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Save to file…",
+ "message": "&Save to file…",
+ "translation": "&Zapisz do pliku…",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Time",
+ "message": "Time",
+ "translation": "Czas",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Log message",
+ "message": "Log message",
+ "translation": "Wiadomości dziennika",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Text Files (*.txt)|*.txt|All Files (*.*)|*.*",
+ "message": "Text Files (*.txt)|*.txt|All Files (*.*)|*.*",
+ "translation": "Pliki tekstowe (*.txt)|*.txt|Wszystkie pliki (*.*)|*.*",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Export log to file",
+ "message": "Export log to file",
+ "translation": "Eksportuj dziennik do pliku",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&About WireGuard…",
+ "message": "&About WireGuard…",
+ "translation": "&Informacje o WireGuard…",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Tunnel Error",
+ "message": "Tunnel Error",
+ "translation": "Błąd tunelu",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "{ErrMsg}\n\nPlease consult the log for more information.",
+ "message": "{ErrMsg}\n\nPlease consult the log for more information.",
+ "translation": "{ErrMsg}\n\nAby uzyskać więcej informacji, zapoznaj się z logiem.",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "ErrMsg",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "errMsg"
+ }
+ ]
+ },
+ {
+ "id": "{Title} (out of date)",
+ "message": "{Title} (out of date)",
+ "translation": "{Title} (nieaktualny)",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Title",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "mtw.Title()"
+ }
+ ]
+ },
+ {
+ "id": "WireGuard Detection Error",
+ "message": "WireGuard Detection Error",
+ "translation": "Błąd detekcji WireGuard",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Unable to wait for WireGuard window to appear: {Err}",
+ "message": "Unable to wait for WireGuard window to appear: {Err}",
+ "translation": "Nie można poczekać na pojawienie się okna WireGuard: {Err}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Err",
+ "string": "%[1]v",
+ "type": "error",
+ "underlyingType": "interface{Error() string}",
+ "argNum": 1,
+ "expr": "err"
+ }
+ ]
+ },
+ {
+ "id": "WireGuard: Deactivated",
+ "message": "WireGuard: Deactivated",
+ "translation": "WireGuard: Dezaktywowany",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Status: Unknown",
+ "message": "Status: Unknown",
+ "translation": "Status: Nieznany",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Addresses: None",
+ "message": "Addresses: None",
+ "translation": "Adresy: Brak",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Manage tunnels…",
+ "message": "&Manage tunnels…",
+ "translation": "&Zarządzaj tunelami…",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Import tunnel(s) from file…",
+ "message": "&Import tunnel(s) from file…",
+ "translation": "&Importuj tunel (tunele) z pliku…",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "E&xit",
+ "message": "E&xit",
+ "translation": "W&yjście",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Tunnels",
+ "message": "&Tunnels",
+ "translation": "&Tunele",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "WireGuard Activated",
+ "message": "WireGuard Activated",
+ "translation": "WireGuard Aktywny",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "The {Name} tunnel has been activated.",
+ "message": "The {Name} tunnel has been activated.",
+ "translation": "Tunel {Name} został aktywowany.",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Name",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "tunnel.Name"
+ }
+ ]
+ },
+ {
+ "id": "WireGuard Deactivated",
+ "message": "WireGuard Deactivated",
+ "translation": "WireGuard Dezaktywowany",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "The {Name} tunnel has been deactivated.",
+ "message": "The {Name} tunnel has been deactivated.",
+ "translation": "Tunel {Name} został dezaktywowany.",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Name",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "tunnel.Name"
+ }
+ ]
+ },
+ {
+ "id": "WireGuard Tunnel Error",
+ "message": "WireGuard Tunnel Error",
+ "translation": "Błąd tunelu WireGuard",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "WireGuard: {TextForStateglobalState_true}",
+ "message": "WireGuard: {TextForStateglobalState_true}",
+ "translation": "WireGuard: {TextForStateglobalState_true}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "TextForStateglobalState_true",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "textForState(globalState, true)"
+ }
+ ]
+ },
+ {
+ "id": "Status: {StateText}",
+ "message": "Status: {StateText}",
+ "translation": "Status: {StateText}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "StateText",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "stateText"
+ }
+ ]
+ },
+ {
+ "id": "Addresses: {EnumerationSeparator}",
+ "message": "Addresses: {EnumerationSeparator}",
+ "translation": "Adresy: {EnumerationSeparator}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "EnumerationSeparator",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "strings.Join(addrs, l18n.EnumerationSeparator())"
+ }
+ ]
+ },
+ {
+ "id": "An Update is Available!",
+ "message": "An Update is Available!",
+ "translation": "Dostępna nowa aktualizacja!",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "WireGuard Update Available",
+ "message": "WireGuard Update Available",
+ "translation": "Aktualizacja WireGuard 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ę.",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Tunnels",
+ "message": "Tunnels",
+ "translation": "Tunele",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Edit",
+ "message": "&Edit",
+ "translation": "&Edytuj",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Add &empty tunnel…",
+ "message": "Add &empty tunnel…",
+ "translation": "Dodaj &pusty tunel…",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Add Tunnel",
+ "message": "Add Tunnel",
+ "translation": "Dodaj Tunel",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Remove selected tunnel(s)",
+ "message": "Remove selected tunnel(s)",
+ "translation": "Usuń wybrany tunel (tunele)",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Export all tunnels to zip",
+ "message": "Export all tunnels to zip",
+ "translation": "Eksportuj wszystkie tunele do archiwum ZIP",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Toggle",
+ "message": "&Toggle",
+ "translation": "&Przełącz",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Export all tunnels to &zip…",
+ "message": "Export all tunnels to &zip…",
+ "translation": "Eksportuj wszystkie tunele do archiwum &zip…",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Edit &selected tunnel…",
+ "message": "Edit &selected tunnel…",
+ "translation": "Edytuj &wybrany tunel…",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Remove selected tunnel(s)",
+ "message": "&Remove selected tunnel(s)",
+ "translation": "&Usuń wybrany tunel (tunele)",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "no configuration files were found",
+ "message": "no configuration files were found",
+ "translation": "brak plików konfiguracyjnych",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Could not import selected configuration: {LastErr}",
+ "message": "Could not import selected configuration: {LastErr}",
+ "translation": "Nie można zaimportować wybranej konfiguracji: {LastErr}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "LastErr",
+ "string": "%[1]v",
+ "type": "error",
+ "underlyingType": "interface{Error() string}",
+ "argNum": 1,
+ "expr": "lastErr"
+ }
+ ]
+ },
+ {
+ "id": "Could not enumerate existing tunnels: {LastErr}",
+ "message": "Could not enumerate existing tunnels: {LastErr}",
+ "translation": "Nie można wskazać istniejących tuneli: {LastErr}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "LastErr",
+ "string": "%[1]v",
+ "type": "error",
+ "underlyingType": "interface{Error() string}",
+ "argNum": 1,
+ "expr": "lastErr"
+ }
+ ]
+ },
+ {
+ "id": "Another tunnel already exists with the name ‘{Name}’",
+ "message": "Another tunnel already exists with the name ‘{Name}’",
+ "translation": "Inny tunel już istnieje z tą samą nazwą ‘{Name}’",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Name",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "unparsedConfig.Name"
+ }
+ ]
+ },
+ {
+ "id": "Unable to import configuration: {LastErr}",
+ "message": "Unable to import configuration: {LastErr}",
+ "translation": "Nie można zaimportować konfiguracji: {LastErr}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "LastErr",
+ "string": "%[1]v",
+ "type": "error",
+ "underlyingType": "interface{Error() string}",
+ "argNum": 1,
+ "expr": "lastErr"
+ }
+ ]
+ },
+ {
+ "id": "Imported tunnels",
+ "message": "Imported tunnels",
+ "translation": "Zaimportowane tunele",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Imported {M} tunnels",
+ "message": "Imported {M} tunnels",
+ "translation": {
+ "select": {
+ "feature": "plural",
+ "arg": "M",
+ "cases": {
+ "one": {
+ "msg": "Zaimportowano {M} tunel"
+ },
+ "few": {
+ "msg": "Zaimportowano {M} tunele"
+ },
+ "many": {
+ "msg": "Zaimportowano {M} tuneli"
+ },
+ "other": {
+ "msg": "Zaimportowano {M} tuneli"
+ }
+ }
+ }
+ },
+ "placeholders": [
+ {
+ "id": "M",
+ "string": "%[1]d",
+ "type": "int",
+ "underlyingType": "int",
+ "argNum": 1,
+ "expr": "m"
+ }
+ ]
+ },
+ {
+ "id": "Imported {M} of {N} tunnels",
+ "message": "Imported {M} of {N} tunnels",
+ "translation": {
+ "select": {
+ "feature": "plural",
+ "arg": "N",
+ "cases": {
+ "one": {
+ "msg": "Zaimportowano {M} z {N} tunel"
+ },
+ "few": {
+ "msg": "Zaimportowano {M} z {N} tunele"
+ },
+ "many": {
+ "msg": "Zaimportowano {M} z {N} tuneli"
+ },
+ "other": {
+ "msg": "Zaimportowano {M} z {N} tuneli"
+ }
+ }
+ }
+ },
+ "placeholders": [
+ {
+ "id": "M",
+ "string": "%[1]d",
+ "type": "int",
+ "underlyingType": "int",
+ "argNum": 1,
+ "expr": "m"
+ },
+ {
+ "id": "N",
+ "string": "%[2]d",
+ "type": "int",
+ "underlyingType": "int",
+ "argNum": 2,
+ "expr": "n"
+ }
+ ]
+ },
+ {
+ "id": "Unable to create tunnel",
+ "message": "Unable to create tunnel",
+ "translation": "Nie można utworzyć tunelu",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Delete {TunnelCount} tunnels",
+ "message": "Delete {TunnelCount} tunnels",
+ "translation": {
+ "select": {
+ "feature": "plural",
+ "arg": "TunnelCount",
+ "cases": {
+ "one": {
+ "msg": "Usuń {TunnelCount} tunel"
+ },
+ "few": {
+ "msg": "Usuń {TunnelCount} tunele"
+ },
+ "many": {
+ "msg": "Usuń {TunnelCount} tuneli"
+ },
+ "other": {
+ "msg": "Usuń {TunnelCount} tuneli"
+ }
+ }
+ }
+ },
+ "placeholders": [
+ {
+ "id": "TunnelCount",
+ "string": "%[1]d",
+ "type": "int",
+ "underlyingType": "int",
+ "argNum": 1,
+ "expr": "tunnelCount"
+ }
+ ]
+ },
+ {
+ "id": "Are you sure you would like to delete {TunnelCount} tunnels?",
+ "message": "Are you sure you would like to delete {TunnelCount} tunnels?",
+ "translation": {
+ "select": {
+ "feature": "plural",
+ "arg": "TunnelCount",
+ "cases": {
+ "one": {
+ "msg": "Czy na pewno chcesz usunąć {TunnelCount} tunel?"
+ },
+ "few": {
+ "msg": "Czy na pewno chcesz usunąć {TunnelCount} tunele?"
+ },
+ "many": {
+ "msg": "Czy na pewno chcesz usunąć {TunnelCount} tuneli?"
+ },
+ "other": {
+ "msg": "Czy na pewno chcesz usunąć {TunnelCount} tuneli?"
+ }
+ }
+ }
+ },
+ "placeholders": [
+ {
+ "id": "TunnelCount",
+ "string": "%[1]d",
+ "type": "int",
+ "underlyingType": "int",
+ "argNum": 1,
+ "expr": "tunnelCount"
+ }
+ ]
+ },
+ {
+ "id": "Delete tunnel ‘{TunnelName}’",
+ "message": "Delete tunnel ‘{TunnelName}’",
+ "translation": "Usuń tunel ‘{TunnelName}’",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "TunnelName",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "tunnelName"
+ }
+ ]
+ },
+ {
+ "id": "Are you sure you would like to delete tunnel ‘{TunnelName}’?",
+ "message": "Are you sure you would like to delete tunnel ‘{TunnelName}’?",
+ "translation": "Czy na pewno chcesz usunąć tunel ‘{TunnelName}’?",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "TunnelName",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "tunnelName"
+ }
+ ]
+ },
+ {
+ "id": "{Question} You cannot undo this action.",
+ "message": "{Question} You cannot undo this action.",
+ "translation": "{Question} Tej akcji nie można cofnąć.",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Question",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "question"
+ }
+ ]
+ },
+ {
+ "id": "Unable to delete tunnel",
+ "message": "Unable to delete tunnel",
+ "translation": "Nie można usunąć tunelu",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "A tunnel was unable to be removed: {Error}",
+ "message": "A tunnel was unable to be removed: {Error}",
+ "translation": "Tunel nie mógł zostać usunięty: {Error}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Error",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "errors[0].Error()"
+ }
+ ]
+ },
+ {
+ "id": "Unable to delete tunnels",
+ "message": "Unable to delete tunnels",
+ "translation": "Nie można usunąć tuneli",
"translatorComment": "Copied from source."
+ },
+ {
+ "id": "{Lenerrors} tunnels were unable to be removed.",
+ "message": "{Lenerrors} tunnels were unable to be removed.",
+ "translation": {
+ "select": {
+ "feature": "plural",
+ "arg": "Lenerrors",
+ "cases": {
+ "one": {
+ "msg": "{Lenerrors} tunel nie może zostać usunięty."
+ },
+ "few": {
+ "msg": "{Lenerrors} tunele nie mogą być usunięte."
+ },
+ "many": {
+ "msg": "{Lenerrors} tunele nie mogą być usunięte."
+ },
+ "other": {
+ "msg": "{Lenerrors} tunele nie mogą być usunięte."
+ }
+ }
+ }
+ },
+ "placeholders": [
+ {
+ "id": "Lenerrors",
+ "string": "%[1]d",
+ "type": "int",
+ "underlyingType": "int",
+ "argNum": 1,
+ "expr": "len(errors)"
+ }
+ ]
+ },
+ {
+ "id": "Configuration Files (*.zip, *.conf)|*.zip;*.conf|All Files (*.*)|*.*",
+ "message": "Configuration Files (*.zip, *.conf)|*.zip;*.conf|All Files (*.*)|*.*",
+ "translation": "Pliki konfiguracji (*.zip, *.conf)|*.zip;*.conf|Wszystkie pliki (*.*)|*.*",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Import tunnel(s) from file",
+ "message": "Import tunnel(s) from file",
+ "translation": "Importuj tunel (tunele) z pliku",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Configuration ZIP Files (*.zip)|*.zip",
+ "message": "Configuration ZIP Files (*.zip)|*.zip",
+ "translation": "Pliki ZIP konfiguracji (*.zip)|*.zip",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Export tunnels to zip",
+ "message": "Export tunnels to zip",
+ "translation": "Eksportuj tunele do archiwum ZIP",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "{Title} (unsigned build, no updates)",
+ "message": "{Title} (unsigned build, no updates)",
+ "translation": "{Title} (wersja niepodpisana, brak aktualizacji)",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Title",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "mtw.Title()"
+ }
+ ]
+ },
+ {
+ "id": "Error Exiting WireGuard",
+ "message": "Error Exiting WireGuard",
+ "translation": "Błąd podczas zamykania WireGuard",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Unable to exit service due to: {Err}. You may want to stop WireGuard from the service manager.",
+ "message": "Unable to exit service due to: {Err}. You may want to stop WireGuard from the service manager.",
+ "translation": "Nie można wyłączyć usługi ze względu na: {Err}. Jeśli chcesz wyłączyć WireGuard możesz to zrobić z poziomu menedżera usług.",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Err",
+ "string": "%[1]v",
+ "type": "error",
+ "underlyingType": "interface{Error() string}",
+ "argNum": 1,
+ "expr": "err"
+ }
+ ]
+ },
+ {
+ "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ę.",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Status: Waiting for user",
+ "message": "Status: Waiting for user",
+ "translation": "Status: Czekam na użytkownika",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Update Now",
+ "message": "Update Now",
+ "translation": "Uaktualnij teraz",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Status: Waiting for updater service",
+ "message": "Status: Waiting for updater service",
+ "translation": "Status: Czekam na usługę aktualizacji",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Error: {Err}. Please try again.",
+ "message": "Error: {Err}. Please try again.",
+ "translation": "Błąd: {Err}. Spróbuj ponownie.",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Err",
+ "string": "%[1]v",
+ "type": "error",
+ "underlyingType": "interface{Error() string}",
+ "argNum": 1,
+ "expr": "err"
+ }
+ ]
+ },
+ {
+ "id": "Status: Complete!",
+ "message": "Status: Complete!",
+ "translation": "Status: Ukończone!",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "http2: Framer {F}: failed to decode just-written frame",
+ "message": "http2: Framer {F}: failed to decode just-written frame",
+ "translation": "http2: Framer {F}: nie mógł zdekodować właśnie zapisanej ramki",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "F",
+ "string": "%[1]p",
+ "type": "*net/http.http2Framer",
+ "underlyingType": "*net/http.http2Framer",
+ "argNum": 1,
+ "expr": "f"
+ }
+ ]
+ },
+ {
+ "id": "http2: Framer {F}: wrote {Http2summarizeFramefr}",
+ "message": "http2: Framer {F}: wrote {Http2summarizeFramefr}",
+ "translation": "http2: Framer {F}: zapis {Http2summarizeFramefr}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "F",
+ "string": "%[1]p",
+ "type": "*net/http.http2Framer",
+ "underlyingType": "*net/http.http2Framer",
+ "argNum": 1,
+ "expr": "f"
+ },
+ {
+ "id": "Http2summarizeFramefr",
+ "string": "%[2]v",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 2,
+ "expr": "http2summarizeFrame(fr)"
+ }
+ ]
+ },
+ {
+ "id": "http2: Framer {Fr}: read {Http2summarizeFramef}",
+ "message": "http2: Framer {Fr}: read {Http2summarizeFramef}",
+ "translation": "http2: Framer {Fr}: odczyt {Http2summarizeFramef}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "Fr",
+ "string": "%[1]p",
+ "type": "*net/http.http2Framer",
+ "underlyingType": "*net/http.http2Framer",
+ "argNum": 1,
+ "expr": "fr"
+ },
+ {
+ "id": "Http2summarizeFramef",
+ "string": "%[2]v",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 2,
+ "expr": "http2summarizeFrame(f)"
+ }
+ ]
+ },
+ {
+ "id": "http2: decoded hpack field {HeaderField}",
+ "message": "http2: decoded hpack field {HeaderField}",
+ "translation": "http2: zdekodwanie hpack nie powiodło się {HeaderField}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "HeaderField",
+ "string": "%+[1]v",
+ "type": "vendor/golang.org/x/net/http2/hpack.HeaderField",
+ "underlyingType": "struct{Name string; Value string; Sensitive bool}",
+ "argNum": 1,
+ "expr": "hf"
+ }
+ ]
}
]
} \ No newline at end of file
diff --git a/locales/ro/messages.gotext.json b/locales/ro/messages.gotext.json
index 9c75901f..56a96a63 100644
--- a/locales/ro/messages.gotext.json
+++ b/locales/ro/messages.gotext.json
@@ -60,9 +60,9 @@
]
},
{
- "id": "You must use the 64-bit version of WireGuard on this computer.",
- "message": "You must use the 64-bit version of WireGuard on this computer.",
- "translation": "Trebuie să utilizezi versiunea de 64 biți a WireGuard pe acest calculator.",
+ "id": "You must use the native version of WireGuard on this computer.",
+ "message": "You must use the native version of WireGuard on this computer.",
+ "translation": "Trebuie să utilizezi versiunea nativă a WireGuard pe acest calculator.",
"translatorComment": "Copied from source."
},
{
@@ -601,9 +601,9 @@
"translatorComment": "Copied from source."
},
{
- "id": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}\nOperating system: {OsName}\nArchitecture: {GOARCH}",
- "message": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}\nOperating system: {OsName}\nArchitecture: {GOARCH}",
- "translation": "Versiunea aplicației: {Number}\nVersiunea bibliotecii subiacente Go: {WireGuardGoVersion}\nVersiunea Go: {Version_go}\nSistem de operare: {OsName}\nArhitectură: {GOARCH}",
+ "id": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}-{GOARCH}\nOperating system: {OsName}\nArchitecture: {NativeArch}",
+ "message": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}-{GOARCH}\nOperating system: {OsName}\nArchitecture: {NativeArch}",
+ "translation": "Versiunea aplicației: {Number}\nVersiunea bibliotecii Go: {WireGuardGoVersion}\nVersiunea Go: {Version_go}-{GOARCH}\nSistem de operare: {OsName}\nArhitectură: {NativeArch}",
"translatorComment": "Copied from source.",
"placeholders": [
{
@@ -631,20 +631,28 @@
"expr": "strings.TrimPrefix(runtime.Version(), \"go\")"
},
{
- "id": "OsName",
+ "id": "GOARCH",
"string": "%[4]s",
"type": "string",
"underlyingType": "string",
"argNum": 4,
- "expr": "version.OsName()"
+ "expr": "runtime.GOARCH"
},
{
- "id": "GOARCH",
+ "id": "OsName",
"string": "%[5]s",
"type": "string",
"underlyingType": "string",
"argNum": 5,
- "expr": "runtime.GOARCH"
+ "expr": "version.OsName()"
+ },
+ {
+ "id": "NativeArch",
+ "string": "%[6]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 6,
+ "expr": "version.NativeArch()"
}
]
},
@@ -709,6 +717,12 @@
"translatorComment": "Copied from source."
},
{
+ "id": "Scripts:",
+ "message": "Scripts:",
+ "translation": "Scripturi:",
+ "translatorComment": "Copied from source."
+ },
+ {
"id": "Preshared key:",
"message": "Preshared key:",
"translation": "Cheie predistribuită:",
@@ -745,6 +759,36 @@
"translatorComment": "Copied from source."
},
{
+ "id": "pre-up",
+ "message": "pre-up",
+ "translation": "pre-pornire",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "post-up",
+ "message": "post-up",
+ "translation": "post-pornire",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "pre-down",
+ "message": "pre-down",
+ "translation": "pre-oprire",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "post-down",
+ "message": "post-down",
+ "translation": "post-oprire",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "disabled, per policy",
+ "message": "disabled, per policy",
+ "translation": "dezactivat, conform politicii",
+ "translatorComment": "Copied from source."
+ },
+ {
"id": "enabled",
"message": "enabled",
"translation": "activată",
@@ -851,9 +895,9 @@
"translatorComment": "Copied from source."
},
{
- "id": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface, with special exceptions for DHCP and NDP.",
- "message": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface, with special exceptions for DHCP and NDP.",
- "translation": "Când o configurație conține exact o pereche, iar acea pereche are IP-uri permise care conțin cel puțin o variantă dintre 0.0.0.0/0 sau ::/0, atunci serviciul tunelului activează un set de reguli al paravanului de protecție pentru a bloca întregul trafic care nu provine de la interfața tunelului, precum și întregul trafic direcționat spre aceasta, existând excepții speciale pentru DHCP și NDP.",
+ "id": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface or is to the wrong DNS server, with special exceptions for DHCP and NDP.",
+ "message": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface or is to the wrong DNS server, with special exceptions for DHCP and NDP.",
+ "translation": "Când o configurație conține exact o pereche, iar acea pereche are IP-uri permise care conțin cel puțin o variantă dintre 0.0.0.0/0 sau ::/0, atunci serviciul tunelului activează un set de reguli al paravanului de protecție pentru a bloca întregul trafic care nu provine de la interfața tunelului sau care este direcționat către serverul DNS greșit, existând excepții speciale pentru DHCP și NDP.",
"translatorComment": "Copied from source."
},
{
@@ -1139,100 +1183,106 @@
"translatorComment": "Copied from source."
},
{
- "id": "WireGuard Tunnel Error",
- "message": "WireGuard Tunnel Error",
- "translation": "Eroare de tunel WireGuard",
+ "id": "&Tunnels",
+ "message": "&Tunnels",
+ "translation": "&Tuneluri",
"translatorComment": "Copied from source."
},
{
- "id": "WireGuard: {TextForStateglobalState_true}",
- "message": "WireGuard: {TextForStateglobalState_true}",
- "translation": "WireGuard: {TextForStateglobalState_true}",
+ "id": "WireGuard Activated",
+ "message": "WireGuard Activated",
+ "translation": "WireGuard activat",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "The {Name} tunnel has been activated.",
+ "message": "The {Name} tunnel has been activated.",
+ "translation": "Tunelul {Name} a fost activat.",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "TextForStateglobalState_true",
+ "id": "Name",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "textForState(globalState, true)"
+ "expr": "tunnel.Name"
}
]
},
{
- "id": "Status: {StateText}",
- "message": "Status: {StateText}",
- "translation": "Stare: {StateText}",
+ "id": "WireGuard Deactivated",
+ "message": "WireGuard Deactivated",
+ "translation": "WireGuard dezactivat",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "The {Name} tunnel has been deactivated.",
+ "message": "The {Name} tunnel has been deactivated.",
+ "translation": "Tunelul {Name} a fost dezactivat.",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "StateText",
+ "id": "Name",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "stateText"
+ "expr": "tunnel.Name"
}
]
},
{
- "id": "Addresses: {String}",
- "message": "Addresses: {String}",
- "translation": "Adrese: {String}",
+ "id": "WireGuard Tunnel Error",
+ "message": "WireGuard Tunnel Error",
+ "translation": "Eroare de tunel WireGuard",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "WireGuard: {TextForStateglobalState_true}",
+ "message": "WireGuard: {TextForStateglobalState_true}",
+ "translation": "WireGuard: {TextForStateglobalState_true}",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "String",
+ "id": "TextForStateglobalState_true",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "sb.String()"
+ "expr": "textForState(globalState, true)"
}
]
},
{
- "id": "WireGuard Activated",
- "message": "WireGuard Activated",
- "translation": "WireGuard activat",
- "translatorComment": "Copied from source."
- },
- {
- "id": "The {Name} tunnel has been activated.",
- "message": "The {Name} tunnel has been activated.",
- "translation": "Tunelul {Name} a fost activat.",
+ "id": "Status: {StateText}",
+ "message": "Status: {StateText}",
+ "translation": "Stare: {StateText}",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "Name",
+ "id": "StateText",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "tunnel.Name"
+ "expr": "stateText"
}
]
},
{
- "id": "WireGuard Deactivated",
- "message": "WireGuard Deactivated",
- "translation": "WireGuard dezactivat",
- "translatorComment": "Copied from source."
- },
- {
- "id": "The {Name} tunnel has been deactivated.",
- "message": "The {Name} tunnel has been deactivated.",
- "translation": "Tunelul {Name} a fost dezactivat.",
+ "id": "Addresses: {EnumerationSeparator}",
+ "message": "Addresses: {EnumerationSeparator}",
+ "translation": "Adrese: {EnumerationSeparator}",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "Name",
+ "id": "EnumerationSeparator",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "tunnel.Name"
+ "expr": "strings.Join(addrs, l18n.EnumerationSeparator())"
}
]
},
@@ -1263,7 +1313,7 @@
{
"id": "&Edit",
"message": "&Edit",
- "translation": "%Editare",
+ "translation": "&Editare",
"translatorComment": "Copied from source."
},
{
@@ -1315,6 +1365,12 @@
"translatorComment": "Copied from source."
},
{
+ "id": "no configuration files were found",
+ "message": "no configuration files were found",
+ "translation": "nu au fost găsite fișiere de configurare",
+ "translatorComment": "Copied from source."
+ },
+ {
"id": "Could not import selected configuration: {LastErr}",
"message": "Could not import selected configuration: {LastErr}",
"translation": "Configurația selectată nu a putut fi importată: {LastErr}",
@@ -1706,7 +1762,7 @@
{
"id": "Update Now",
"message": "Update Now",
- "translation": "Actualizare acum",
+ "translation": "Actualizează acum",
"translatorComment": "Copied from source."
},
{
diff --git a/locales/ru/messages.gotext.json b/locales/ru/messages.gotext.json
index c4438813..9cc9b0f1 100644
--- a/locales/ru/messages.gotext.json
+++ b/locales/ru/messages.gotext.json
@@ -60,9 +60,9 @@
]
},
{
- "id": "You must use the 64-bit version of WireGuard on this computer.",
- "message": "You must use the 64-bit version of WireGuard on this computer.",
- "translation": "Используйте 64-битную версию WireGuard на этом компьютере.",
+ "id": "You must use the native version of WireGuard on this computer.",
+ "message": "You must use the native version of WireGuard on this computer.",
+ "translation": "Используйте нативную версию WireGuard на этом компьютере.",
"translatorComment": "Copied from source."
},
{
@@ -516,7 +516,7 @@
{
"id": "Tunnel name is not valid",
"message": "Tunnel name is not valid",
- "translation": "Неправильное имя туннеля",
+ "translation": "Название туннеля недействительно",
"translatorComment": "Copied from source."
},
{
@@ -526,6 +526,12 @@
"translatorComment": "Copied from source."
},
{
+ "id": "Config key is missing an equals separator",
+ "message": "Config key is missing an equals separator",
+ "translation": "В ключе конфигурации отсутствует разделитель",
+ "translatorComment": "Copied from source."
+ },
+ {
"id": "Key must have a value",
"message": "Key must have a value",
"translation": "Ключ должен иметь значение",
@@ -558,7 +564,7 @@
{
"id": "All peers must have public keys",
"message": "All peers must have public keys",
- "translation": "Все пиры должны иметь открытые ключи",
+ "translation": "Все пиры должны иметь публичные ключи",
"translatorComment": "Copied from source."
},
{
@@ -582,7 +588,7 @@
{
"id": "Invalid key for peer section",
"message": "Invalid key for peer section",
- "translation": "Неверный ключ для секции Пира",
+ "translation": "Недействительный ключ для секции пира",
"translatorComment": "Copied from source."
},
{
@@ -610,9 +616,9 @@
"translatorComment": "Copied from source."
},
{
- "id": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}\nOperating system: {OsName}\nArchitecture: {GOARCH}",
- "message": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}\nOperating system: {OsName}\nArchitecture: {GOARCH}",
- "translation": "Версия приложения: {Number}\nВерсия бэкэнда: {WireGuardGoVersion}\nВерсия перехода: {Version_go}\nОперационная система: {OsName}\nАрхитектура: {GOARCH}",
+ "id": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}-{GOARCH}\nOperating system: {OsName}\nArchitecture: {NativeArch}",
+ "message": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}-{GOARCH}\nOperating system: {OsName}\nArchitecture: {NativeArch}",
+ "translation": "Версия приложения: {Number}\nВерсия Go-бэкенда: {WireGuardGoVersion}\nВерсия Go: {Version_go}-{GOARCH}\nОперационная система: {OsName}\nАрхитектура: {NativeArch}",
"translatorComment": "Copied from source.",
"placeholders": [
{
@@ -640,20 +646,28 @@
"expr": "strings.TrimPrefix(runtime.Version(), \"go\")"
},
{
- "id": "OsName",
+ "id": "GOARCH",
"string": "%[4]s",
"type": "string",
"underlyingType": "string",
"argNum": 4,
- "expr": "version.OsName()"
+ "expr": "runtime.GOARCH"
},
{
- "id": "GOARCH",
+ "id": "OsName",
"string": "%[5]s",
"type": "string",
"underlyingType": "string",
"argNum": 5,
- "expr": "runtime.GOARCH"
+ "expr": "version.OsName()"
+ },
+ {
+ "id": "NativeArch",
+ "string": "%[6]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 6,
+ "expr": "version.NativeArch()"
}
]
},
@@ -718,6 +732,12 @@
"translatorComment": "Copied from source."
},
{
+ "id": "Scripts:",
+ "message": "Scripts:",
+ "translation": "Скрипты:",
+ "translatorComment": "Copied from source."
+ },
+ {
"id": "Preshared key:",
"message": "Preshared key:",
"translation": "Общий ключ:",
@@ -754,6 +774,36 @@
"translatorComment": "Copied from source."
},
{
+ "id": "pre-up",
+ "message": "pre-up",
+ "translation": "перед подключением",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "post-up",
+ "message": "post-up",
+ "translation": "после подключения",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "pre-down",
+ "message": "pre-down",
+ "translation": "перед отключением",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "post-down",
+ "message": "post-down",
+ "translation": "после отключения",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "disabled, per policy",
+ "message": "disabled, per policy",
+ "translation": "отключено, по политике",
+ "translatorComment": "Copied from source."
+ },
+ {
"id": "enabled",
"message": "enabled",
"translation": "включено",
@@ -838,7 +888,7 @@
{
"id": "&Name:",
"message": "&Name:",
- "translation": "&Имя:",
+ "translation": "&Название:",
"translatorComment": "Copied from source."
},
{
@@ -856,13 +906,13 @@
{
"id": "&Block untunneled traffic (kill-switch)",
"message": "&Block untunneled traffic (kill-switch)",
- "translation": "&Блокировать нетуннельный трафик",
+ "translation": "&Блокировать нетуннелированный трафик",
"translatorComment": "Copied from source."
},
{
- "id": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface, with special exceptions for DHCP and NDP.",
- "message": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface, with special exceptions for DHCP and NDP.",
- "translation": "Когда конфигурация имеет ровно одного пира, и у этого пира есть разрешенные IP, содержащие хотя бы один из 0.0.0.0/0 или ::/0, то служба туннеля обязывает брандмауэр блокировать весь входящий и исходящий трафик, который не проходит через туннель, за исключением DHCP и NDP.",
+ "id": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface or is to the wrong DNS server, with special exceptions for DHCP and NDP.",
+ "message": "When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface or is to the wrong DNS server, with special exceptions for DHCP and NDP.",
+ "translation": "Если конфигурация имеет ровно один пир, а этот пир имеет разрешенные IP, содержащие хотя бы один из 0.0.0.0/0 или ::/0, то туннельный сервис использует набор правил брандмауэра для блокирования всего трафика, который не проходит через туннель или к неверным DNS-серверам, за исключением DHCP и NDP.",
"translatorComment": "Copied from source."
},
{
@@ -886,19 +936,19 @@
{
"id": "Invalid name",
"message": "Invalid name",
- "translation": "Недопустимое имя",
+ "translation": "Некорректное название",
"translatorComment": "Copied from source."
},
{
"id": "A name is required.",
"message": "A name is required.",
- "translation": "Требуется имя.",
+ "translation": "Необходимо название.",
"translatorComment": "Copied from source."
},
{
"id": "Tunnel name ‘{NewName}’ is invalid.",
"message": "Tunnel name ‘{NewName}’ is invalid.",
- "translation": "Имя туннеля ‘{NewName}’ недопустимо.",
+ "translation": "Название туннеля ‘{NewName}’ недопустимо.",
"translatorComment": "Copied from source.",
"placeholders": [
{
@@ -1006,13 +1056,13 @@
{
"id": "&Copy",
"message": "&Copy",
- "translation": "&Копировать",
+ "translation": "&Скопировать",
"translatorComment": "Copied from source."
},
{
"id": "Select &all",
"message": "Select &all",
- "translation": "Выбрать &всё",
+ "translation": "Выбрать &все",
"translatorComment": "Copied from source."
},
{
@@ -1030,7 +1080,7 @@
{
"id": "Log message",
"message": "Log message",
- "translation": "Сообщение Журнала",
+ "translation": "Сообщение журнала",
"translatorComment": "Copied from source."
},
{
@@ -1042,7 +1092,7 @@
{
"id": "Export log to file",
"message": "Export log to file",
- "translation": "Экспорт Журнала в файл",
+ "translation": "Экспорт журнала в файл",
"translatorComment": "Copied from source."
},
{
@@ -1148,100 +1198,106 @@
"translatorComment": "Copied from source."
},
{
- "id": "WireGuard Tunnel Error",
- "message": "WireGuard Tunnel Error",
- "translation": "Ошибка туннеля WireGuard",
+ "id": "&Tunnels",
+ "message": "&Tunnels",
+ "translation": "&Туннели",
"translatorComment": "Copied from source."
},
{
- "id": "WireGuard: {TextForStateglobalState_true}",
- "message": "WireGuard: {TextForStateglobalState_true}",
- "translation": "WireGuard: {TextForStateglobalState_true}",
+ "id": "WireGuard Activated",
+ "message": "WireGuard Activated",
+ "translation": "WireGuard Включен",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "The {Name} tunnel has been activated.",
+ "message": "The {Name} tunnel has been activated.",
+ "translation": "Туннель {Name} подключен.",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "TextForStateglobalState_true",
+ "id": "Name",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "textForState(globalState, true)"
+ "expr": "tunnel.Name"
}
]
},
{
- "id": "Status: {StateText}",
- "message": "Status: {StateText}",
- "translation": "Статус: {StateText}",
+ "id": "WireGuard Deactivated",
+ "message": "WireGuard Deactivated",
+ "translation": "WireGuard Выключен",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "The {Name} tunnel has been deactivated.",
+ "message": "The {Name} tunnel has been deactivated.",
+ "translation": "Туннель {Name} отключен.",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "StateText",
+ "id": "Name",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "stateText"
+ "expr": "tunnel.Name"
}
]
},
{
- "id": "Addresses: {String}",
- "message": "Addresses: {String}",
- "translation": "Адреса: {String}",
+ "id": "WireGuard Tunnel Error",
+ "message": "WireGuard Tunnel Error",
+ "translation": "Ошибка туннеля WireGuard",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "WireGuard: {TextForStateglobalState_true}",
+ "message": "WireGuard: {TextForStateglobalState_true}",
+ "translation": "WireGuard: {TextForStateglobalState_true}",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "String",
+ "id": "TextForStateglobalState_true",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "sb.String()"
+ "expr": "textForState(globalState, true)"
}
]
},
{
- "id": "WireGuard Activated",
- "message": "WireGuard Activated",
- "translation": "WireGuard Включен",
- "translatorComment": "Copied from source."
- },
- {
- "id": "The {Name} tunnel has been activated.",
- "message": "The {Name} tunnel has been activated.",
- "translation": "Туннель {Name} подключен.",
+ "id": "Status: {StateText}",
+ "message": "Status: {StateText}",
+ "translation": "Статус: {StateText}",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "Name",
+ "id": "StateText",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "tunnel.Name"
+ "expr": "stateText"
}
]
},
{
- "id": "WireGuard Deactivated",
- "message": "WireGuard Deactivated",
- "translation": "WireGuard Выключен",
- "translatorComment": "Copied from source."
- },
- {
- "id": "The {Name} tunnel has been deactivated.",
- "message": "The {Name} tunnel has been deactivated.",
- "translation": "Туннель {Name} отключен.",
+ "id": "Addresses: {EnumerationSeparator}",
+ "message": "Addresses: {EnumerationSeparator}",
+ "translation": "Адреса: {EnumerationSeparator}",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "Name",
+ "id": "EnumerationSeparator",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "tunnel.Name"
+ "expr": "strings.Join(addrs, l18n.EnumerationSeparator())"
}
]
},
@@ -1324,6 +1380,12 @@
"translatorComment": "Copied from source."
},
{
+ "id": "no configuration files were found",
+ "message": "no configuration files were found",
+ "translation": "файлы конфигурации не были найдены",
+ "translatorComment": "Copied from source."
+ },
+ {
"id": "Could not import selected configuration: {LastErr}",
"message": "Could not import selected configuration: {LastErr}",
"translation": "Невозможно импортировать конфигурацию: {LastErr}",
@@ -1736,7 +1798,7 @@
{
"id": "Status: Waiting for updater service",
"message": "Status: Waiting for updater service",
- "translation": "Статус: Обновление",
+ "translation": "Статус: Ожидание обновления",
"translatorComment": "Copied from source."
},
{
diff --git a/locales/sk/messages.gotext.json b/locales/sk/messages.gotext.json
index cbe7412a..c8d05aa2 100644
--- a/locales/sk/messages.gotext.json
+++ b/locales/sk/messages.gotext.json
@@ -60,12 +60,6 @@
]
},
{
- "id": "You must use the 64-bit version of WireGuard on this computer.",
- "message": "You must use the 64-bit version of WireGuard on this computer.",
- "translation": "Na tomto počítači je potrebné použiť 64 bitovú verziu aplikácie WireGuard.",
- "translatorComment": "Copied from source."
- },
- {
"id": "Unable to open current process token: {Err}",
"message": "Unable to open current process token: {Err}",
"translation": "Nepodarilo sa otvoriť token aktuálneho procesu: {Err}",
@@ -582,88 +576,378 @@
"translatorComment": "Copied from source."
},
{
- "id": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}\nOperating system: {OsName}\nArchitecture: {GOARCH}",
- "message": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}\nOperating system: {OsName}\nArchitecture: {GOARCH}",
- "translation": "Verzia aplikácie: {Number}\nVerzia Go backendu: {WireGuardGoVersion}\nVerzia Go: {Version_go}\nOperačný systém: {OsName}\nArchitektúra: {GOARCH}",
+ "id": "Close",
+ "message": "Close",
+ "translation": "Zatvoriť",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "♥ &Donate!",
+ "message": "♥ &Donate!",
+ "translation": "♥ a Darovat!",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Status:",
+ "message": "Status:",
+ "translation": "Stav:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Deactivate",
+ "message": "&Deactivate",
+ "translation": "a Deaktivovať",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Activate",
+ "message": "&Activate",
+ "translation": "a Aktivovať",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Public key:",
+ "message": "Public key:",
+ "translation": "Verejný kľúč:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Listen port:",
+ "message": "Listen port:",
+ "translation": "Otvorený port:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "MTU:",
+ "message": "MTU:",
+ "translation": "MTU:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Addresses:",
+ "message": "Addresses:",
+ "translation": "Adresy:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "DNS servers:",
+ "message": "DNS servers:",
+ "translation": "Servery DNS:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Preshared key:",
+ "message": "Preshared key:",
+ "translation": "Vopred zdieľaný kľúč:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Allowed IPs:",
+ "message": "Allowed IPs:",
+ "translation": "Povolené IP adresy:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Endpoint:",
+ "message": "Endpoint:",
+ "translation": "Koncový bod:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Persistent keepalive:",
+ "message": "Persistent keepalive:",
+ "translation": "Perzistentný keepalive:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Latest handshake:",
+ "message": "Latest handshake:",
+ "translation": "Posledné spojenie (handshake):",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Transfer:",
+ "message": "Transfer:",
+ "translation": "Prenos:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "enabled",
+ "message": "enabled",
+ "translation": "povolené",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "{String} received, {String_1} sent",
+ "message": "{String} received, {String_1} sent",
+ "translation": "{String} prijatých, {String_1} odoslaných",
"translatorComment": "Copied from source.",
"placeholders": [
{
- "id": "Number",
+ "id": "String",
"string": "%[1]s",
"type": "string",
"underlyingType": "string",
"argNum": 1,
- "expr": "version.Number"
+ "expr": "c.RxBytes.String()"
},
{
- "id": "WireGuardGoVersion",
+ "id": "String_1",
"string": "%[2]s",
"type": "string",
"underlyingType": "string",
"argNum": 2,
- "expr": "device.WireGuardGoVersion"
- },
+ "expr": "c.TxBytes.String()"
+ }
+ ]
+ },
+ {
+ "id": "Failed to determine tunnel state",
+ "message": "Failed to determine tunnel state",
+ "translation": "Nepodarilo sa zistiť stav tunela",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Failed to activate tunnel",
+ "message": "Failed to activate tunnel",
+ "translation": "Nepodarilo sa aktivovať tunel",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Failed to deactivate tunnel",
+ "message": "Failed to deactivate tunnel",
+ "translation": "Nepodarilo sa deaktivovať tunel",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Interface: {Name}",
+ "message": "Interface: {Name}",
+ "translation": "Rozhranie: {Name}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
{
- "id": "Version_go",
- "string": "%[3]s",
+ "id": "Name",
+ "string": "%[1]s",
"type": "string",
"underlyingType": "string",
- "argNum": 3,
- "expr": "strings.TrimPrefix(runtime.Version(), \"go\")"
- },
+ "argNum": 1,
+ "expr": "config.Name"
+ }
+ ]
+ },
+ {
+ "id": "Peer",
+ "message": "Peer",
+ "translation": "Peer",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Create new tunnel",
+ "message": "Create new tunnel",
+ "translation": "Vytvoriť nový tunel",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Edit tunnel",
+ "message": "Edit tunnel",
+ "translation": "Upraviť tunel",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Name:",
+ "message": "&Name:",
+ "translation": "&Názov:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Public key:",
+ "message": "&Public key:",
+ "translation": "&Verejný kľúč:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "(unknown)",
+ "message": "(unknown)",
+ "translation": "(neznámy)",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Block untunneled traffic (kill-switch)",
+ "message": "&Block untunneled traffic (kill-switch)",
+ "translation": "&Blokovať netunelovaný prenos (kill-switch)",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Save",
+ "message": "&Save",
+ "translation": "&Uložiť",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Cancel",
+ "message": "Cancel",
+ "translation": "Zrušiť",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Configuration:",
+ "message": "&Configuration:",
+ "translation": "&Konfigurácia:",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Invalid name",
+ "message": "Invalid name",
+ "translation": "Neplatný názov",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "A name is required.",
+ "message": "A name is required.",
+ "translation": "Názov je povinný.",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Tunnel name ‘{NewName}’ is invalid.",
+ "message": "Tunnel name ‘{NewName}’ is invalid.",
+ "translation": "Názov tunela ‘{NewName}’ je neplatný.",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
{
- "id": "OsName",
- "string": "%[4]s",
+ "id": "NewName",
+ "string": "%[1]s",
"type": "string",
"underlyingType": "string",
- "argNum": 4,
- "expr": "version.OsName()"
- },
+ "argNum": 1,
+ "expr": "newName"
+ }
+ ]
+ },
+ {
+ "id": "Unable to list existing tunnels",
+ "message": "Unable to list existing tunnels",
+ "translation": "Nepodarilo sa pripraviť zoznam existujúcich tunelov",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Tunnel already exists",
+ "message": "Tunnel already exists",
+ "translation": "Tunel už existuje",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "Another tunnel already exists with the name ‘{NewName}’.",
+ "message": "Another tunnel already exists with the name ‘{NewName}’.",
+ "translation": "Tunel s názvom ‘{NewName}’ už existuje.",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
{
- "id": "GOARCH",
- "string": "%[5]s",
+ "id": "NewName",
+ "string": "%[1]s",
"type": "string",
"underlyingType": "string",
- "argNum": 5,
- "expr": "runtime.GOARCH"
+ "argNum": 1,
+ "expr": "newName"
}
]
},
{
- "id": "Close",
- "message": "Close",
- "translation": "Zatvoriť",
+ "id": "Unable to create new configuration",
+ "message": "Unable to create new configuration",
+ "translation": "Nie je možné vytvoriť novú konfiguráciu",
"translatorComment": "Copied from source."
},
{
- "id": "♥ &Donate!",
- "message": "♥ &Donate!",
- "translation": "♥ a Darovat!",
+ "id": "Writing file failed",
+ "message": "Writing file failed",
+ "translation": "Nepodarilo sa zapísať do súboru",
"translatorComment": "Copied from source."
},
{
- "id": "Status:",
- "message": "Status:",
- "translation": "Stav:",
+ "id": "File ‘{FilePath}’ already exists.\n\nDo you want to overwrite it?",
+ "message": "File ‘{FilePath}’ already exists.\n\nDo you want to overwrite it?",
+ "translation": "Súbor ‘{FilePath}’ už existuje.\n\nŽeláte si ho prepísať?",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "FilePath",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "filePath"
+ }
+ ]
+ },
+ {
+ "id": "Active",
+ "message": "Active",
+ "translation": "Aktívny",
"translatorComment": "Copied from source."
},
{
- "id": "&Deactivate",
- "message": "&Deactivate",
- "translation": "a Deaktivovať",
+ "id": "Activating",
+ "message": "Activating",
+ "translation": "Aktivuje sa",
"translatorComment": "Copied from source."
},
{
- "id": "&Activate",
- "message": "&Activate",
- "translation": "a Aktivovať",
+ "id": "Inactive",
+ "message": "Inactive",
+ "translation": "Neaktívny",
"translatorComment": "Copied from source."
},
{
- "id": "Public key:",
- "message": "Public key:",
- "translation": "Verejný kľúč:",
+ "id": "Deactivating",
+ "message": "Deactivating",
+ "translation": "Deaktivuje sa",
"translatorComment": "Copied from source."
+ },
+ {
+ "id": "Unknown state",
+ "message": "Unknown state",
+ "translation": "Neznámy stav",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&About WireGuard…",
+ "message": "&About WireGuard…",
+ "translation": "&O WireGuard…",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Manage tunnels…",
+ "message": "&Manage tunnels…",
+ "translation": "&Spravovať tunely…",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "&Import tunnel(s) from file…",
+ "message": "&Import tunnel(s) from file…",
+ "translation": "&Importovať tunel(y) zo súboru…",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "E&xit",
+ "message": "E&xit",
+ "translation": "U&končiť",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "WireGuard: {TextForStateglobalState_true}",
+ "message": "WireGuard: {TextForStateglobalState_true}",
+ "translation": "WireGuard: {TextForStateglobalState_true}",
+ "translatorComment": "Copied from source.",
+ "placeholders": [
+ {
+ "id": "TextForStateglobalState_true",
+ "string": "%[1]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 1,
+ "expr": "textForState(globalState, true)"
+ }
+ ]
}
]
} \ No newline at end of file
diff --git a/locales/sl/messages.gotext.json b/locales/sl/messages.gotext.json
index 0b1b4cbc..c3418f02 100644
--- a/locales/sl/messages.gotext.json
+++ b/locales/sl/messages.gotext.json
@@ -60,9 +60,9 @@
]
},
{
- "id": "You must use the 64-bit version of WireGuard on this computer.",
- "message": "You must use the 64-bit version of WireGuard on this computer.",
- "translation": "Na temu računalniku morate uporabiti 64-bitno različico WireGuarda.",
+ "id": "You must use the native version of WireGuard on this computer.",
+ "message": "You must use the native version of WireGuard on this computer.",
+ "translation": "Na temu računalniku morate uporabiti enako-arhitekturno različico WireGuarda.",
"translatorComment": "Copied from source."
},
{
@@ -526,6 +526,12 @@
"translatorComment": "Copied from source."
},
{
+ "id": "Config key is missing an equals separator",
+ "message": "Config key is missing an equals separator",
+ "translation": "Ključu v konfiguraciji manjka ločilo enačaj",
+ "translatorComment": "Copied from source."
+ },
+ {
"id": "Key must have a value",
"message": "Key must have a value",
"translation": "Ključ mora imeti vrednost",
@@ -610,9 +616,9 @@
"translatorComment": "Copied from source."
},
{
- "id": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}\nOperating system: {OsName}\nArchitecture: {GOARCH}",
- "message": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}\nOperating system: {OsName}\nArchitecture: {GOARCH}",
- "translation": "Verzija aplikacije: {Number}\nVerzija wireguard-go: {WireGuardGoVersion}\nVerzija Go: {Version_go}\nOperacijski sistem: {OsName}\nArhitektura: {GOARCH}",
+ "id": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}-{GOARCH}\nOperating system: {OsName}\nArchitecture: {NativeArch}",
+ "message": "App version: {Number}\nGo backend version: {WireGuardGoVersion}\nGo version: {Version_go}-{GOARCH}\nOperating system: {OsName}\nArchitecture: {NativeArch}",
+ "translation": "Verzija aplikacije: {Number}\nVerzija wireguard-go: {WireGuardGoVersion}\nVerzija Go: {Version_go}-{GOARCH}\nOperacijski sistem: {OsName}\nArhitektura: {NativeArch}",
"translatorComment": "Copied from source.",
"placeholders": [
{
@@ -640,20 +646,28 @@
"expr": "strings.TrimPrefix(runtime.Version(), \"go\")"
},
{
- "id": "OsName",
+ "id": "GOARCH",
"string": "%[4]s",
"type": "string",
"underlyingType": "string",
"argNum": 4,
- "expr": "version.OsName()"
+ "expr": "runtime.GOARCH"
},
{
- "id": "GOARCH",
+ "id": "OsName",
"string": "%[5]s",
"type": "string",
"underlyingType": "string",
"argNum": 5,
- "expr": "runtime.GOARCH"
+ "expr": "version.OsName()"
+ },
+ {
+ "id": "NativeArch",
+ "string": "%[6]s",
+ "type": "string",
+ "underlyingType": "string",
+ "argNum": 6,
+ "expr": "version.NativeArch()"
}
]
},
@@ -718,6 +732,12 @@
"translatorComment": "Copied from source."
},
{
+ "id": "Scripts:",
+ "message": "Scripts:",
+ "translation": "Skripta:",
+ "translatorComment": "Copied from source."
+ },
+ {
"id": "Preshared key:",
"message": "Preshared key:",
"translation": "Ključ v skupni rabi:",
@@ -754,6 +774,36 @@
"translatorComment": "Copied from source."
},
{
+ "id": "pre-up",
+ "message": "pre-up",
+ "translation": "pred-aktivacijo",
+ "translatorComment": "Copied from source."
+ },
+ {
+ "id": "post-up",
+ "message": "post-up",
+ "translation": "po-aktivaciji",
+ "translatorComment": "Copied from source."