From cff4e77bfe62606c19c205e02fd63b3777855392 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Wed, 1 May 2019 11:14:12 +0200 Subject: version: dynamically get file version --- version/debugging_linux.go | 32 +++++++++++++++++ version/mksyscall.go | 2 +- version/official_linux.go | 12 ------- version/os_linux.go | 30 ---------------- version/useragent.go | 16 +++++++++ version/version.go | 20 ----------- version/version_windows.go | 83 +++++++++++++++++++++++++++++++++++++++++++++ version/zsyscall_windows.go | 45 ++++++++++++++++++++++-- 8 files changed, 175 insertions(+), 65 deletions(-) create mode 100644 version/debugging_linux.go delete mode 100644 version/official_linux.go delete mode 100644 version/os_linux.go create mode 100644 version/useragent.go delete mode 100644 version/version.go create mode 100644 version/version_windows.go (limited to 'version') diff --git a/version/debugging_linux.go b/version/debugging_linux.go new file mode 100644 index 00000000..df5dbd2f --- /dev/null +++ b/version/debugging_linux.go @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package version + +// For testing the updater package from linux. Debug stuff only. + +func IsOfficialPath(path string) bool { + return true +} + +func utsToStr(u [65]byte) string { + i := bytes.IndexByte(u[:], 0) + if i < 0 { + return string(u[:]) + } + return string(u[:i]) +} + +func OsName() string { + var utsname unix.Utsname + if unix.Uname(&utsname) != nil { + return "Unix Unknown" + } + return fmt.Sprintf("%s %s %s", utsToStr(utsname.Sysname), utsToStr(utsname.Release), utsToStr(utsname.Version)) +} + +func RunningVersion() string { + return "0.0.0.0" +} diff --git a/version/mksyscall.go b/version/mksyscall.go index abd538e5..3c993928 100644 --- a/version/mksyscall.go +++ b/version/mksyscall.go @@ -5,4 +5,4 @@ package version -//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go os_windows.go +//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go os_windows.go version_windows.go diff --git a/version/official_linux.go b/version/official_linux.go deleted file mode 100644 index d3ca3349..00000000 --- a/version/official_linux.go +++ /dev/null @@ -1,12 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package version - -// For testing the updater package from linux. Debug stuff only. - -func IsOfficialPath(path string) bool { - return true -} diff --git a/version/os_linux.go b/version/os_linux.go deleted file mode 100644 index 7e5c4f1c..00000000 --- a/version/os_linux.go +++ /dev/null @@ -1,30 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package version - -import ( - "bytes" - "fmt" - "golang.org/x/sys/unix" -) - -// This isn't a Linux program, yes, but having the updater package work across platforms is quite helpful for testing. - -func utsToStr(u [65]byte) string { - i := bytes.IndexByte(u[:], 0) - if i < 0 { - return string(u[:]) - } - return string(u[:i]) -} - -func OsName() string { - var utsname unix.Utsname - if unix.Uname(&utsname) != nil { - return "Unix Unknown" - } - return fmt.Sprintf("%s %s %s", utsToStr(utsname.Sysname), utsToStr(utsname.Release), utsToStr(utsname.Version)) -} diff --git a/version/useragent.go b/version/useragent.go new file mode 100644 index 00000000..321bc0a6 --- /dev/null +++ b/version/useragent.go @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package version + +import ( + "fmt" + "golang.zx2c4.com/wireguard/device" + "runtime" +) + +func UserAgent() string { + return fmt.Sprintf("WireGuard/%s (wireguard-go %s; %s; %s; %s)", RunningVersion(), device.WireGuardGoVersion, OsName(), runtime.Version(), runtime.GOARCH) +} diff --git a/version/version.go b/version/version.go deleted file mode 100644 index 951d2731..00000000 --- a/version/version.go +++ /dev/null @@ -1,20 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package version - -// #include "../version.h" -import "C" -import ( - "fmt" - "golang.zx2c4.com/wireguard/device" - "runtime" -) - -const WireGuardWindowsVersion = C.WIREGUARD_WINDOWS_VERSION - -func UserAgent() string { - return fmt.Sprintf("WireGuard/%s (wireguard-go %s; %s; %s; %s)", WireGuardWindowsVersion, device.WireGuardGoVersion, OsName(), runtime.Version(), runtime.GOARCH) -} diff --git a/version/version_windows.go b/version/version_windows.go new file mode 100644 index 00000000..e567e7a0 --- /dev/null +++ b/version/version_windows.go @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package version + +import ( + "errors" + "fmt" + "golang.org/x/sys/windows" + "os" + "runtime" + "strings" + "unsafe" +) + +//sys GetFileVersionInfoSize(filename *uint16, zero *uint32) (size uint32, err error) = version.GetFileVersionInfoSizeW +//sys GetFileVersionInfo(filename *uint16, zero uint32, size uint32, block *byte) (err error) = version.GetFileVersionInfoW +//sys VerQueryValue(block *byte, section *uint16, value **byte, size *uint32) (err error) = version.VerQueryValueW + +type vsFixedFileInfo struct { + Signature uint32 + StrucVersion uint32 + FileVersionMS uint32 + FileVersionLS uint32 + ProductVersionMS uint32 + ProductVersionLS uint32 + FileFlagsMask uint32 + FileFlags uint32 + FileOS uint32 + FileType uint32 + FileSubtype uint32 + FileDateMS uint32 + FileDateLS uint32 +} + +const vsFixedFileInfoSignature = 0xFEEF04BD + +var cachedVersion string + +func RunningVersion() string { + if len(cachedVersion) != 0 { + return cachedVersion + } + key16 := []uint16{'\\', 0x00} + self, err := os.Executable() + if err != nil { + panic(err) + } + self16, err := windows.UTF16PtrFromString(self) + if err != nil { + panic(err) + } + var zero uint32 + size, err := GetFileVersionInfoSize(self16, &zero) + if err != nil { + panic(err) + } + buffer := make([]byte, size) + err = GetFileVersionInfo(self16, zero, size, &buffer[0]) + if err != nil { + panic(err) + } + var fixedFileInfo *vsFixedFileInfo + err = VerQueryValue(&buffer[0], &key16[0], (**byte)(unsafe.Pointer(&fixedFileInfo)), &size) + if err != nil { + panic(err) + } + if uintptr(size) < unsafe.Sizeof(vsFixedFileInfo{}) || fixedFileInfo == nil || fixedFileInfo.Signature != vsFixedFileInfoSignature { + panic(errors.New("Incorrect return of VS_FIXEDFILEINFO")) + } + version := fmt.Sprintf("%d.%d.%d.%d", (fixedFileInfo.FileVersionMS>>16)&0xffff, (fixedFileInfo.FileVersionMS>>0)&0xffff, (fixedFileInfo.FileVersionLS>>16)&0xffff, (fixedFileInfo.FileVersionLS>>0)&0xffff) + runtime.KeepAlive(buffer) // The win32 API aliases it in fixedFileInfo, but Go doesn't know that. + if strings.HasSuffix(version, ".0") { + version = version[:len(version)-2] + } + if strings.HasSuffix(version, ".0") { + version = version[:len(version)-2] + } + cachedVersion = version + return version +} diff --git a/version/zsyscall_windows.go b/version/zsyscall_windows.go index 9f11ce70..ef4e11d7 100644 --- a/version/zsyscall_windows.go +++ b/version/zsyscall_windows.go @@ -37,9 +37,13 @@ func errnoErr(e syscall.Errno) error { } var ( - modntdll = windows.NewLazySystemDLL("ntdll.dll") + modntdll = windows.NewLazySystemDLL("ntdll.dll") + modversion = windows.NewLazySystemDLL("version.dll") - procRtlGetVersion = modntdll.NewProc("RtlGetVersion") + procRtlGetVersion = modntdll.NewProc("RtlGetVersion") + procGetFileVersionInfoSizeW = modversion.NewProc("GetFileVersionInfoSizeW") + procGetFileVersionInfoW = modversion.NewProc("GetFileVersionInfoW") + procVerQueryValueW = modversion.NewProc("VerQueryValueW") ) func rtlGetVersion(versionInfo *osVersionInfo) (nterr uint32) { @@ -47,3 +51,40 @@ func rtlGetVersion(versionInfo *osVersionInfo) (nterr uint32) { nterr = uint32(r0) return } + +func GetFileVersionInfoSize(filename *uint16, zero *uint32) (size uint32, err error) { + r0, _, e1 := syscall.Syscall(procGetFileVersionInfoSizeW.Addr(), 2, uintptr(unsafe.Pointer(filename)), uintptr(unsafe.Pointer(zero)), 0) + size = uint32(r0) + if size == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func GetFileVersionInfo(filename *uint16, zero uint32, size uint32, block *byte) (err error) { + r1, _, e1 := syscall.Syscall6(procGetFileVersionInfoW.Addr(), 4, uintptr(unsafe.Pointer(filename)), uintptr(zero), uintptr(size), uintptr(unsafe.Pointer(block)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func VerQueryValue(block *byte, section *uint16, value **byte, size *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procVerQueryValueW.Addr(), 4, uintptr(unsafe.Pointer(block)), uintptr(unsafe.Pointer(section)), uintptr(unsafe.Pointer(value)), uintptr(unsafe.Pointer(size)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} -- cgit v1.2.3-59-g8ed1b