diff --git a/.gitattributes b/.gitattributes index 907d7857..cfe74ca3 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,19 +1,5 @@ -*.exe filter=lfs diff=lfs merge=lfs -text -*.dll filter=lfs diff=lfs merge=lfs -text -*.sys filter=lfs diff=lfs merge=lfs -text -*.cat filter=lfs diff=lfs merge=lfs -text -pia-openvpn filter=lfs diff=lfs merge=lfs -text -*.ttf filter=lfs diff=lfs merge=lfs -text -*.ico filter=lfs diff=lfs merge=lfs -text -*.bmp filter=lfs diff=lfs merge=lfs -text -*.png filter=lfs diff=lfs merge=lfs -text -# Causes issues with .so symlinks when checked out on Windows -#*.so filter=lfs diff=lfs merge=lfs -text -*.bin filter=lfs diff=lfs merge=lfs -text -*.msi filter=lfs diff=lfs merge=lfs -text *.ts text eol=lf /CHANGELOG.md merge=union eol=lf text - *.sh text eol=lf # Diff UTF-16LE *.rc Windows resource scripts @@ -21,3 +7,28 @@ pia-openvpn filter=lfs diff=lfs merge=lfs -text # Add to your ~/.gitconfig: #[diff "rcdiff"] # textconv = "iconv -f utf-16le -t utf-8" + +# Use LFS for everything under deps/built/ +# While file extension globs are pretty good for most LFS files, +# these directories contain macOS/Linux executables with no +# extension, which can't be easily globbed. +deps/built/*/** filter=lfs diff=lfs merge=lfs -text + +# dump_syms binaries - similar to deps/built, contains +# extensionless macOS binary +deps/dump_syms/** filter=lfs diff=lfs merge=lfs -text + +# File types to store in LFS +*.sys filter=lfs diff=lfs merge=lfs -text +*.cat filter=lfs diff=lfs merge=lfs -text +*.ttf filter=lfs diff=lfs merge=lfs -text +*.ico filter=lfs diff=lfs merge=lfs -text +*.bmp filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.bin filter=lfs diff=lfs merge=lfs -text +*.msi filter=lfs diff=lfs merge=lfs -text +*.icns filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.otf filter=lfs diff=lfs merge=lfs -text +*.sketch filter=lfs diff=lfs merge=lfs -text +*.sym filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore index 263e253e..f95f1ccb 100644 --- a/.gitignore +++ b/.gitignore @@ -3,13 +3,22 @@ /build-*/ # Default build directory when opening CMake glue project in most IDEs /build/ -# Qt Creator project preferences -*.user .DS_Store *.qm node_modules releases *.qmlc +# Various IDEs +# Qt Creator project preferences +*.user +# KDevelop / Kate *.kdev4 *.swp +# IntelliJ IDEA .idea/* +# Visual Studio +/.vs/ +*.sln +*.vcxproj +*.vcxproj.filters +/Debug/ diff --git a/CHANGELOG.md b/CHANGELOG.md index de534715..b48e4fb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +### v2.10.0 +* Reduced memory and CPU usage of the graphical client +* The split tunnel UI on Windows now displays executable paths instead of link paths for Start Menu apps +* WireGuard now works correctly on macOS and Linux when jumbo frames are enabled on the network interface +* The PIA daemon on Linux no longer writes to stderr when run as a service to avoid flooding system logs +* In-app updates on Linux now detect xfce4-terminal on systems without an x-terminal-emulator symlink +* Fixed a crash on Windows caused by some older OpenGL drivers +* Fixed an install issue on Linux that prevented creation of the piavpn group in some cases +* Fixed an issue causing the support tool to appear more than once on Linux in some cases +* Fixed libxcb dependencies in Linux arm64 build + ### v2.9.0 * Updated to OpenVPN 2.5.1 and OpenSSL 1.1.1k * Improved accessibility of the "Add Automation Rule" dialog diff --git a/brands/pia/icons/app.icns b/brands/pia/icons/app.icns index e052f551..584b3ce4 100644 Binary files a/brands/pia/icons/app.icns and b/brands/pia/icons/app.icns differ diff --git a/client/res/components/changelog/ChangelogWindow.qml b/client/res/components/changelog/ChangelogWindow.qml index 3f8b5d9d..9166c866 100644 --- a/client/res/components/changelog/ChangelogWindow.qml +++ b/client/res/components/changelog/ChangelogWindow.qml @@ -36,177 +36,199 @@ DecoratedWindow { color: Theme.dashboard.backgroundColor readonly property real contentMargin: 20 - windowLogicalWidth: newsContent.width - windowLogicalHeight: newsContent.height + tabBar.height + readonly property real contentWidth: 680 + windowLogicalWidth: contentWidth + // Just like SettingsWindow, the content of this window is only loaded when + // needed, so the window size is set when the content becomes active, or when + // the content's size changes (possible when changing language). (Unlike + // SettingsWindow, this window's width is fixed, only the height changes.) + windowLogicalHeight: 200 // Default to the actual changelog since we haven't updated What's New in // some time. property int activePage: 1 readonly property int pageCount: 2 - // Have to wrap everything in an Item so we can attach a Keys.onPressed - // handler that covers both the tabs and the content - Item { + function updateWindowSize() { + if(contentLoader.item) + changelog.windowLogicalHeight = contentLoader.item.contentHeight + } + + Loader { + id: contentLoader anchors.fill: parent + active: changelog.visible || changelog.positioningForShow + sourceComponent: contentComponent + onItemChanged: changelog.updateWindowSize() + } - Rectangle { - id: tabBar - color: Theme.changelog.tabBackgroundColor - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - height: 40 + // Have to wrap everything in an Item so we can attach a Keys.onPressed + // handler that covers both the tabs and the content + Component { + id: contentComponent + Item { + readonly property int contentHeight: newsContent.height + tabBar.height + onContentHeightChanged: changelog.updateWindowSize() - // Highlight indicator Rectangle { - readonly property var currentItem: { - // Depend on tabsRepeater's children (both these are needed to ensure - // the bindings are reevaluated) - var dummy = tabsRepeater.children - var dummy2 = tabsRepeater.count - return tabsRepeater.itemAt(changelog.activePage) - } - x: tabsRow.x + (currentItem ? currentItem.x : 0) - y: tabBar.height - height - width: currentItem ? currentItem.width : 10 - height: 2 - color: Theme.changelog.tabHighlightColor - - Behavior on x { - SmoothedAnimation { - duration: 200 + id: tabBar + color: Theme.changelog.tabBackgroundColor + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + height: 40 + + // Highlight indicator + Rectangle { + readonly property var currentItem: { + // Depend on tabsRepeater's children (both these are needed to ensure + // the bindings are reevaluated) + var dummy = tabsRepeater.children + var dummy2 = tabsRepeater.count + return tabsRepeater.itemAt(changelog.activePage) } - enabled: changelog.visible - } - Behavior on width { - SmoothedAnimation { - duration: 200 + x: tabsRow.x + (currentItem ? currentItem.x : 0) + y: tabBar.height - height + width: currentItem ? currentItem.width : 10 + height: 2 + color: Theme.changelog.tabHighlightColor + + Behavior on x { + SmoothedAnimation { + duration: 200 + } + enabled: changelog.visible + } + Behavior on width { + SmoothedAnimation { + duration: 200 + } + enabled: changelog.visible } - enabled: changelog.visible } - } - - Row { - id: tabsRow - x: 23 - y: 12 - height: parent.height - y - - NativeAcc.TabList.name: changelog.title - - spacing: 16 - activeFocusOnTab: true - Repeater { - id: tabsRepeater - model: [ - // Don't actually translate the content of the model; this avoids - // rebuilding the repeater content when the language changes - {label: QT_TR_NOOP("What's new")}, - {label: QT_TR_NOOP("Changelog")} - ] - - Item { - id: tabBound - - width: tabText.width + tabText.x*2 - height: parent.height - readonly property int tabIndex: index - - NativeAcc.Tab.name: tabText.text - NativeAcc.Tab.checked: changelog.activePage === tabIndex - NativeAcc.Tab.onActivated: mouseClick() - - function mouseClick() { - tabsRow.forceActiveFocus(Qt.MouseFocusReason) - changelog.activePage = tabIndex + Row { + id: tabsRow + x: 23 + y: 12 + height: parent.height - y + + NativeAcc.TabList.name: changelog.title + + spacing: 16 + activeFocusOnTab: true + + Repeater { + id: tabsRepeater + model: [ + // Don't actually translate the content of the model; this avoids + // rebuilding the repeater content when the language changes + {label: QT_TR_NOOP("What's new")}, + {label: QT_TR_NOOP("Changelog")} + ] + + Item { + id: tabBound + + width: tabText.width + tabText.x*2 + height: parent.height + readonly property int tabIndex: index + + NativeAcc.Tab.name: tabText.text + NativeAcc.Tab.checked: changelog.activePage === tabIndex + NativeAcc.Tab.onActivated: mouseClick() + + function mouseClick() { + tabsRow.forceActiveFocus(Qt.MouseFocusReason) + changelog.activePage = tabIndex + } + + Text { + id: tabText + x: 3 // sets both horizontal margins due to tabBound.width calculation + y: (tabBound.height - height)/2 + color: Theme.changelog.tabTextColor + text: uiTr(modelData.label) + font.pixelSize: 12 + } + MouseArea { + anchors.fill: parent + onClicked: mouseClick() + } } + } - Text { - id: tabText - x: 3 // sets both horizontal margins due to tabBound.width calculation - y: (tabBound.height - height)/2 - color: Theme.changelog.tabTextColor - text: uiTr(modelData.label) - font.pixelSize: 12 - } - MouseArea { - anchors.fill: parent - onClicked: mouseClick() + Keys.onPressed: { + var pagesTranslated = tabsRepeater.model.map(function(page) { + return uiTr(page.label) + }) + var nextIndex = KeyUtil.handleHorzKeyEvent(event, pagesTranslated, + undefined, + changelog.activePage) + if(nextIndex !== -1) { + changelog.activePage = nextIndex + tabsFocusCue.reveal() } } } - Keys.onPressed: { - var pagesTranslated = tabsRepeater.model.map(function(page) { - return uiTr(page.label) - }) - var nextIndex = KeyUtil.handleHorzKeyEvent(event, pagesTranslated, - undefined, - changelog.activePage) - if(nextIndex !== -1) { - changelog.activePage = nextIndex - tabsFocusCue.reveal() - } + OutlineFocusCue { + id: tabsFocusCue + anchors.fill: tabsRow + control: tabsRow } } - OutlineFocusCue { - id: tabsFocusCue - anchors.fill: tabsRow - control: tabsRow - } - } - - // Handle Ctrl+[Shift]+Tab anywhere in the tabs or the page content - Keys.onPressed: { - var nextIndex = KeyUtil.handleSettingsTabKeyEvent(event, changelog.activePage, - changelog.pageCount) - if(nextIndex >= 0) { - changelog.activePage = nextIndex - // If this _was_ from the list view, show its focus; this has no effect if - // it isn't focused. - tabsFocusCue.reveal() + // Handle Ctrl+[Shift]+Tab anywhere in the tabs or the page content + Keys.onPressed: { + var nextIndex = KeyUtil.handleSettingsTabKeyEvent(event, changelog.activePage, + changelog.pageCount) + if(nextIndex >= 0) { + changelog.activePage = nextIndex + // If this _was_ from the list view, show its focus; this has no effect if + // it isn't focused. + tabsFocusCue.reveal() + } } - } - FocusScope { - anchors.left: parent.left - anchors.right: parent.right - anchors.top: tabBar.bottom - anchors.bottom: parent.bottom - - StackLayout { - id: pagesStack - anchors.fill: parent - currentIndex: changelog.activePage - clip: true - - WindowScrollView { - id: newsScrollView - contentWidth: newsContent.width - contentHeight: newsContent.height - label: uiTr("What's new") - - WhatsNewContent { - id: newsContent - width: 680 + FocusScope { + anchors.left: parent.left + anchors.right: parent.right + anchors.top: tabBar.bottom + anchors.bottom: parent.bottom + + StackLayout { + id: pagesStack + anchors.fill: parent + currentIndex: changelog.activePage + clip: true + + WindowScrollView { + id: newsScrollView + contentWidth: newsContent.width + contentHeight: newsContent.height + label: uiTr("What's new") + + WhatsNewContent { + id: newsContent + width: 680 + } } - } - WindowScrollView { - id: changelogScrollView - contentWidth: changelogText.width - contentHeight: changelogText.height - label: uiTr("Changelog") - - MarkdownPage { - id: changelogText - width: pagesStack.width - margins: contentMargin - fontPixelSize: 14 - text: uiBrand(NativeHelpers.readResourceText("qrc:/CHANGELOG.md")) - color: Theme.dashboard.textColor + WindowScrollView { + id: changelogScrollView + contentWidth: changelogText.width + contentHeight: changelogText.height + label: uiTr("Changelog") + + MarkdownPage { + id: changelogText + width: pagesStack.width + margins: contentMargin + fontPixelSize: 14 + text: uiBrand(NativeHelpers.readResourceText("qrc:/CHANGELOG.md")) + color: Theme.dashboard.textColor + } } } } diff --git a/client/res/components/client/ClientUIState.qml b/client/res/components/client/ClientUIState.qml index 9fe7f220..293c5e4a 100644 --- a/client/res/components/client/ClientUIState.qml +++ b/client/res/components/client/ClientUIState.qml @@ -39,5 +39,6 @@ QtObject { readonly property var settings: QtObject { property bool gatheringDiagnostics: false + property int currentPage: 0 } } diff --git a/client/res/components/common/DecoratedWindow.qml b/client/res/components/common/DecoratedWindow.qml index c3608239..a3fec466 100644 --- a/client/res/components/common/DecoratedWindow.qml +++ b/client/res/components/common/DecoratedWindow.qml @@ -46,6 +46,16 @@ PiaWindow { readonly property real actualLogicalWidth: maxSize.effectiveSize.width readonly property real actualLogicalHeight: maxSize.effectiveSize.height + // Indicator that we're sizing and centering the window just before it's + // shown. Most PIA windows try to unload some or all of their content when + // they're not being shown. Just binding Loader.active to Window.visible + // nearly works, _except_ when we need the content to determine the window's + // size just before it's centered and shown. + // + // If the content is needed to determine the window size, load the content + // when either Window.active or DecoratedWindow.positioningForShow is true. + property bool positioningForShow: false + // Properties provided by all top-level windows - see DashboardPopup // Content scale - needed for some overlay-layer components. @@ -130,6 +140,13 @@ PiaWindow { } } + property var centerWindowTimer: Timer { + interval: 0 + repeat: false + running: false + onTriggered: decoratedWindow.centerOnActiveScreen() + } + // Replacement for show() that additionally positions the window and raises it, // which is normally what you want for a secondary modeless dialog window. function open() { @@ -138,7 +155,10 @@ PiaWindow { // Going to position the window based on its size, the scale and size must // be initialized before we do this (needed for Linux) NativeHelpers.initScaling() - centerOnActiveScreen(); + // If the content is loaded dynamically, tell the window that we're + // centering so the content is loaded + positioningForShow = true + centerOnActiveScreen() // If the window isn't visible already and doesn't have a focused control, // focus the first focusable control. (Keep the existing focus if there @@ -155,6 +175,8 @@ PiaWindow { // whereas requestActivate() does the trick on Windows. raise(); requestActivate(); + // The window is now visible, we're done positioning for the initial show + positioningForShow = false } function getScreenContainingMouseCursor() { diff --git a/client/res/components/common/WindowLoader.qml b/client/res/components/common/WindowLoader.qml index 118f49e1..487ab7f6 100644 --- a/client/res/components/common/WindowLoader.qml +++ b/client/res/components/common/WindowLoader.qml @@ -47,6 +47,9 @@ QtObject { } } + // The loaded window, once it has been loaded by open() + property alias window: loader.item + function open() { switch(loader.status) { case Loader.Null: diff --git a/client/res/components/common/regions/RegionListView.qml b/client/res/components/common/regions/RegionListView.qml index 61785982..9863c44f 100644 --- a/client/res/components/common/regions/RegionListView.qml +++ b/client/res/components/common/regions/RegionListView.qml @@ -493,7 +493,7 @@ Rectangle { // The country has more than one region, and the country name doesn't // match. Filter the individual regions. else { - var filteredRegions = countries[i].locations.filter(function(loc) { + var filteredRegions = filteredLocations.filter(function(loc) { return matchesSearchTerm(Daemon.getLocationName(loc)) }) // If at least one region matched, include the country with the filtered diff --git a/client/res/components/core/Loader.qml b/client/res/components/core/Loader.qml new file mode 100644 index 00000000..040b319b --- /dev/null +++ b/client/res/components/core/Loader.qml @@ -0,0 +1,30 @@ +// Copyright (c) 2021 Private Internet Access, Inc. +// +// This file is part of the Private Internet Access Desktop Client. +// +// The Private Internet Access Desktop Client is free software: you can +// redistribute it and/or modify it under the terms of the GNU General Public +// License as published by the Free Software Foundation, either version 3 of +// the License, or (at your option) any later version. +// +// The Private Internet Access Desktop Client is distributed in the hope that +// it will be useful, but WITHOUT ANY WARRANTY; without even the implied +// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with the Private Internet Access Desktop Client. If not, see +// . + +import QtQuick 2.11 as QtQuick +// Loader tweaks: +// - Spy on deactivation of loaders to trigger cache trimming and GC after +// components are unloaded. +QtQuick.Loader { + id: coreLoader + + onActiveChanged: { + if(!coreLoader.active) + LoaderCleaner.loaderDeactivated() + } +} diff --git a/client/res/components/core/LoaderCleaner.qml b/client/res/components/core/LoaderCleaner.qml new file mode 100644 index 00000000..9cc04d15 --- /dev/null +++ b/client/res/components/core/LoaderCleaner.qml @@ -0,0 +1,67 @@ +// Copyright (c) 2021 Private Internet Access, Inc. +// +// This file is part of the Private Internet Access Desktop Client. +// +// The Private Internet Access Desktop Client is free software: you can +// redistribute it and/or modify it under the terms of the GNU General Public +// License as published by the Free Software Foundation, either version 3 of +// the License, or (at your option) any later version. +// +// The Private Internet Access Desktop Client is distributed in the hope that +// it will be useful, but WITHOUT ANY WARRANTY; without even the implied +// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with the Private Internet Access Desktop Client. If not, see +// . + +pragma Singleton +import QtQml 2.15 as QtQml + +QtQml.QtObject { + id: loaderCleaner + + // Signal emitted to actually trigger a cleanup - this is handled in + // main.qml, which is aware of all the windows in the program (some cleanup + // is applied to each window individually). + signal cleanupTriggered() + + // Whenever a cleanup occurs, we clean up twice, in case the first cleanup + // causes more references to be unneeded but not cleaned up immediately + // (which is particularly relevant since much of Qt's cleanup uses + // deleteLater().) + readonly property int initialCleanupTime: 3000 // 3 seconds + readonly property int finalCleanupTime: 6000 // 6 seconds (after initial cleanup) + property var cleanupTimer: QtQml.Timer { + interval: initialCleanupTime + repeat: false + running: false + onTriggered: { + // We shouldn't deactivate any loaders when doing cleanup, but if it + // does occur, we'll still properly handle first/second cleanups by + // checking the initial state. (Don't reset back to the first cleanup if + // an unload occurs here, or we might get stuck in a cleanup loop if it + // happened.) + let wasFirstCleanup = cleanupTimer.interval === loaderCleaner.initialCleanupTime + loaderCleaner.cleanupTriggered() + if(wasFirstCleanup) { + console.info("Clean up again in " + loaderCleaner.finalCleanupTime + " ms") + cleanupTimer.interval = loaderCleaner.finalCleanupTime + cleanupTimer.restart() + } + else { + console.info("Second clean up completed") + } + } + } + + function loaderDeactivated() { + // Do the first cleanup after no loaders have deactivated for 30 seconds. + // (If we had done the first cleanup and were waiting to do the second, + // reset back to the short interval.) + console.info("Loader deactivated, clean up in " + loaderCleaner.initialCleanupTime + " ms") + cleanupTimer.interval = loaderCleaner.initialCleanupTime + cleanupTimer.restart() + } +} diff --git a/client/res/components/core/qmldir b/client/res/components/core/qmldir index 9021dbe9..49968fb2 100644 --- a/client/res/components/core/qmldir +++ b/client/res/components/core/qmldir @@ -8,6 +8,12 @@ # cases - if a particular tweak only applies "most of the time" or "some of the # time", it probably belongs in "common" instead as a specifically named type. # +# == Conventions == +# +# Since many of these components override QtQuick types (and hence use the same +# names), Qt imports are always qualified with the module name - for example, +# `import QtQuick 2.11 as QtQuick` and later `QtQuick.Text`. +# # == Arabic / RTL == # # These tweaks mainly pertain to layout fixups for Arabic (text alignment and @@ -24,5 +30,16 @@ # the text content. (See Text.qml.) The default behavior is unusable even # without layout mirroring, because Text objects become a hodgepodged mess by # default, since the effect of the automatic alignment depends on how the Text -# bounds are specified. Layout mirroring mirrors this for RTL (if/when RTL is -# actually supported). +# bounds are specified. Layout mirroring mirrors this for RTL. +# +# == Loader cleanup == +# +# PIA tries to unload as much UI as it can when not being used to avoid +# hogging resources (though there is still room for more improvement). QtQuick +# by default caches aggressively though and does not let go of most resources +# until explicitly told to. In particular, the QML cache is never cleaned by +# default after a Loader deactivates. The Loader override spies on Loader +# deactivations to trigger cleanups after UI is unloaded. LoaderCleaner is the +# singleton containing the timers and signal used for this purpose. +module PIA.Core +singleton LoaderCleaner LoaderCleaner.qml diff --git a/client/res/components/dashboard/DashboardFrameLoader.qml b/client/res/components/dashboard/DashboardFrameLoader.qml index 5c98cefe..2468843c 100644 --- a/client/res/components/dashboard/DashboardFrameLoader.qml +++ b/client/res/components/dashboard/DashboardFrameLoader.qml @@ -19,6 +19,7 @@ import QtQuick 2.9 import QtQuick.Window 2.10 import PIA.NativeHelpers 1.0 +import "../core" QtObject { id: dashFrameLoader diff --git a/client/res/components/dashboard/HeaderBar.qml b/client/res/components/dashboard/HeaderBar.qml index 6d95dbc8..a6838073 100644 --- a/client/res/components/dashboard/HeaderBar.qml +++ b/client/res/components/dashboard/HeaderBar.qml @@ -401,9 +401,9 @@ Item { target: Daemon.account function onLoggedInChanged() { if (!Daemon.account.loggedIn) { - pm.setPage('login') + pm.setPage(pm.pageIndices.login) } else { - pm.setPage('connect') + pm.setPage(pm.pageIndices.connect) } } } diff --git a/client/res/components/dashboard/PageManager.qml b/client/res/components/dashboard/PageManager.qml index 0af6b754..fc569c94 100644 --- a/client/res/components/dashboard/PageManager.qml +++ b/client/res/components/dashboard/PageManager.qml @@ -25,6 +25,7 @@ import "./login" import "./connect" import "./region" import "../daemon" +import "../core" Item { id: manager @@ -35,120 +36,79 @@ Item { // the height of the active page. Each page provides a 'pageHeight' property // that indicates the height that it currently wants. (Some pages // always return a constant value, but others update it on the fly.) - property int pageHeight: getPage(state).pageHeight - // This is the maximum possible height of any page. DashboardWindowPlacement - // uses this to choose where to place the dashboard, which ensures that it - // will still fit on the screen even if it grows to this height after it's - // shown. - property int maxPageHeight: { - var max = 0 - max = Math.max(max, loginPage.maxPageHeight) - max = Math.max(max, connectPage.maxPageHeight) - max = Math.max(max, regionPage.maxPageHeight) - return max - } - readonly property string backButtonDescription: getPage(state).backButtonDescription || "" - readonly property var backButtonFunction: getPage(state).backButtonFunction + property int pageHeight: getCurrentPage().pageHeight + // This is the likely maximum possible height of any page. (Note the + // absolute paranoid maximum; see ConnectPage.) DashboardWindowPlacement + // uses this to choose where to place the dashboard, so it will still fit on + // the screen even if it grows to this height after it's shown. It's OK if + // the dashboard grows past this height in some circumstances; the dashboard + // will scroll if needed. + // + // The likely max. height is just determined by the ConnectPage. Although + // the other pages in principle could contribute to this, they are unloaded + // when not needed, and in practice the connect page always determines the + // max. height. + property int maxPageHeight: connectPage.maxPageHeight + readonly property string backButtonDescription: getCurrentPage().backButtonDescription || "" + readonly property var backButtonFunction: getCurrentPage().backButtonFunction // Provide ConnectPage.clipRightExtend when it's the active page - readonly property real clipRightExtend: getPage(state).clipRightExtend || 0 + readonly property real clipRightExtend: getCurrentPage().clipRightExtend || 0 readonly property int offScreenOffset: parent.width + 10 - transitions: [ - Transition { - from: "*" - to: "*" - - SmoothedAnimation { - targets: [loginPage, connectPage, regionPage] - properties: "x" - duration: 400 - } - enabled: manager.Window.window.visible - } - ] - - state: 'login' - - states: [ - State { - name: "login" - PropertyChanges { - target: loginPage - x: 0 - } - PropertyChanges { - target: connectPage - x: offScreenOffset - } - PropertyChanges { - target: regionPage - x: offScreenOffset - } - }, - State { - name: "connect" - PropertyChanges { - target: loginPage - x: -1 * offScreenOffset - } - PropertyChanges { - target: connectPage - x: 0 - } - PropertyChanges { - target: regionPage - x: offScreenOffset - } - }, - State { - name: "region" - PropertyChanges { - target: loginPage - x: -1 * offScreenOffset - } - PropertyChanges { - target: connectPage - x: -1 * offScreenOffset - } - PropertyChanges { - target: regionPage - x: 0 - } - } - ] - - - signal pageChange() + readonly property var pageIndices: ({ + login: 0, + connect: 1, + region: 2 + }) + + property int currentPageIdx: pageIndices.login + + function pageXOffset(pageIndex) { + // Page is to the left of the current page + if(pageIndex < currentPageIdx) + return -offScreenOffset + // Page is to the right of the current page + if(pageIndex > currentPageIdx) + return offScreenOffset + // Page is the current page + return 0 + } - function setPage (pageId) { - var currentPage = getPage(state) - var nextPage = getPage(pageId) + function setPage(pageIndex) { + var currentPage = getCurrentPage() if(currentPage.beforeExit) { currentPage.beforeExit() } - state = pageId + console.info("Change page from " + currentPageIdx + " to " + pageIndex) + currentPageIdx = pageIndex // Focus the new page - although the old page will be hidden, apparently Qt // would still allow it to have focus. // Pages are FocusScopes, so this preserves the specific focused control // within each page, etc. + var nextPage = getCurrentPage() nextPage.focus = true - pageChange() if(nextPage.onEnter) { nextPage.onEnter() } } - function getPage(pageId) { - switch(pageId) { - case 'login': - return loginPage; - case 'connect': - return connectPage; - case 'region': - return regionPage + // Get the current page - used to hook up some properties that always refer + // to the "current page" (back button, page sizing, etc.). + // + // Since some pages are only loaded when active, we can't provide a general + // accessor to get the page for any index. We can always get the current + // page, because dynamic pages are always loaded when they're current. + function getCurrentPage() { + switch(currentPageIdx) { + case pageIndices.login: + return loginPageLoader.item || connectPage + case pageIndices.connect: + return connectPage + case pageIndices.region: + return regionPageLoader.item || connectPage } } @@ -159,36 +119,82 @@ Item { return pageX > -offScreenOffset && pageX < offScreenOffset } - LoginPage { - id: loginPage + // The login page is loaded dynamically, like the region page. Although this + // page doesn't consume significant resources like the region page, it's + // only used when logged out. + Loader { + id: loginPageLoader width: parent.width anchors.top: parent.top anchors.bottom: parent.bottom + + x: pageXOffset(pageIndices.login) + Behavior on x { + SmoothedAnimation { + duration: 400 + } + enabled: manager.Window.window && manager.Window.window.visible + } + visible: isPageVisible(x) + active: visible || manager.currentPageIdx === manager.pageIndices.login + + sourceComponent: Component { LoginPage {} } } + // The connect page is always loaded. The sizing characteristics of the + // connect page are always needed in order for the dashboard to position + // itself (see maxPageHeight above), and the connect page is active most of + // the time. ConnectPage { id: connectPage width: parent.width anchors.top: parent.top anchors.bottom: parent.bottom layoutHeight: manager.layoutHeight + + x: pageXOffset(pageIndices.connect) + Behavior on x { + SmoothedAnimation { + duration: 400 + } + enabled: manager.Window.window && manager.Window.window.visible + } + visible: isPageVisible(x) } - RegionPage { - id: regionPage + // The region page is loaded only when active. This page consumes a lot of + // memory and also can cause a lot of heap thrashing as updates to regions + // come in from the daemon (which can happen a lot as latencies are updated). + // + // This page also isn't active very often, usually it's opened and then + // quickly used/closed. + // + // Loaders always ensure that the loader and its loaded item are the same + // size (if the loader is positioned, it sizes the item to the loader; + // otherwise the loader takes the size of the loaded item). Because of this, + // we can do all layout with the Loader itself treating it as a "proxy" for + // for the real region page whether or not it is actually loaded. + Loader { + id: regionPageLoader width: parent.width anchors.top: parent.top anchors.bottom: parent.bottom + + x: pageXOffset(pageIndices.region) + Behavior on x { + SmoothedAnimation { + duration: 400 + } + enabled: manager.Window.window && manager.Window.window.visible + } + visible: isPageVisible(x) - } + active: visible || manager.currentPageIdx === manager.pageIndices.region - Timer { - running: true - interval: 1000 - onTriggered: { -// pageManager.state = "connect" + sourceComponent: Component { + RegionPage {} } } @@ -199,11 +205,11 @@ Item { console.log('login onEnter - logged in') // Go directly to this page (don't do the transitions in setPage()) since // we haven't done the initial page's onEnter yet - state = 'connect' + currentPageIdx = pageIndices.connect } // Call the initial page's onEnter - var currentPage = getPage(state) + var currentPage = getCurrentPage() if(currentPage.onEnter) { currentPage.onEnter() } diff --git a/client/res/components/dashboard/connect/modules/RegionModule.qml b/client/res/components/dashboard/connect/modules/RegionModule.qml index 4689e83a..73f5ee90 100644 --- a/client/res/components/dashboard/connect/modules/RegionModule.qml +++ b/client/res/components/dashboard/connect/modules/RegionModule.qml @@ -258,7 +258,7 @@ MovableModule { // Show this focus cue on the inside, since it fills the full module focusCueInside: true onClicked: { - pageManager.setPage("region") + pageManager.setPage(pageManager.pageIndices.region) } } } diff --git a/client/res/components/dashboard/region/RegionPage.qml b/client/res/components/dashboard/region/RegionPage.qml index e3f98476..76ae498e 100644 --- a/client/res/components/dashboard/region/RegionPage.qml +++ b/client/res/components/dashboard/region/RegionPage.qml @@ -50,14 +50,14 @@ FocusScope { // Choose this location, and reconnect if we were connected to a // different one VpnConnection.selectLocation(locationId) - pageManager.setPage('connect') + pageManager.setPage(pageManager.pageIndices.connect) } } function beforeExit() { } function backButton() { - pageManager.setPage('connect') + pageManager.setPage(pageManager.pageIndices.connect) } function onEnter() { headerBar.logoCentered = true @@ -69,7 +69,7 @@ FocusScope { Connections { target: ClientNotifications function onShowRegions() { - pageManager.setPage('region') + pageManager.setPage(pageManager.pageIndices.region) } } } diff --git a/client/res/components/main.qml b/client/res/components/main.qml index 039d1fb1..6556d8e4 100644 --- a/client/res/components/main.qml +++ b/client/res/components/main.qml @@ -20,6 +20,7 @@ import QtQuick 2.10 import "./settings" import "./dashboard" import "./common" +import "./core" import "./devtools" import "../javascript/app.js" as App import PIA.NativeHelpers 1.0 @@ -100,10 +101,7 @@ QtObject { interval: 1 repeat: false running: false - onTriggered: { - wChangeLog.show(); - wChangeLog.centerOnActiveScreen(); - } + onTriggered: wChangeLog.open() } // Qt has issues when the theme is changed while the dashboard is hidden @@ -141,4 +139,45 @@ QtObject { context: Qt.ApplicationShortcut onActivated: wDevToolsLoader.open() } + + function cleanUpResources() { + console.info("trimming component cache") + NativeHelpers.trimComponentCache() + console.info("release window resources: settings") + NativeHelpers.releaseWindowResources(wSettings) + console.info("release window resources: changelog") + NativeHelpers.releaseWindowResources(wChangeLog) + console.info("release window resources: onboarding") + NativeHelpers.releaseWindowResources(wOnboarding) + if(wDevToolsLoader.window) { + console.info("release window resources: dev tools") + NativeHelpers.releaseWindowResources(wDevToolsLoader.window) + } + if(dashboard.window) { + console.info("release window resources: dashboard") + NativeHelpers.releaseWindowResources(dashboard.window) + } + console.info("running GC") + gc() + console.info("Trim and GC complete") + } + + // Ctrl+Shift+G runs all the cleanup and garbage collection. This is mainly + // for dev but there's no harm having the shortcut around all the time. We + // don't really want to require opening dev tools to do this, since we + // normally use it to see if something has left around a bunch of GC-able + // memory, and opening dev tools itself consumes a lot of memory. + property var gcShortcut: Shortcut { + sequence: "Ctrl+Shift+G" + context: Qt.ApplicationShortcut + onActivated: cleanUpResources() + } + + // Clean up when LoaderCleaner triggers it due to Loaders having deactivated + property var loaderCleanerConnections: Connections { + target: LoaderCleaner + function onCleanupTriggered() { + cleanUpResources() + } + } } diff --git a/client/res/components/onboarding/OnboardingWindow.qml b/client/res/components/onboarding/OnboardingWindow.qml index 3a778c26..b2c04bb2 100644 --- a/client/res/components/onboarding/OnboardingWindow.qml +++ b/client/res/components/onboarding/OnboardingWindow.qml @@ -23,6 +23,7 @@ import QtQuick.Window 2.10 import PIA.NativeHelpers 1.0 import '../theme' import '../common' +import '../core' SecondaryWindow { id: onboardingWindow @@ -41,9 +42,13 @@ SecondaryWindow { visible: false - PageController { - id: pageController - width: contentLogicalWidth - height: contentLogicalHeight + Loader { + id: pageControllerLoader + width: contentLogicalWidth + height: contentLogicalHeight + // We don't need to apply onboardingWindow.positioningForShow here, + // because this window's size doesn't depend on the content. + active: onboardingWindow.visible + sourceComponent: Component { PageController {} } } } diff --git a/client/res/components/settings/SettingsWindow.qml b/client/res/components/settings/SettingsWindow.qml index 6ba2e147..2c7fa5df 100644 --- a/client/res/components/settings/SettingsWindow.qml +++ b/client/res/components/settings/SettingsWindow.qml @@ -37,8 +37,20 @@ import "./pages" SecondaryWindow { id: settings - contentLogicalWidth: tabLayout.item.implicitWidth - contentLogicalHeight: tabLayout.item.implicitHeight + // The Settings window's size is determined by its content (it can depend on + // the size of translations), but the content is unloaded when the window + // isn't shown. + // + // When the content is loaded, use the content's size. This happens in the + // right order when the window is shown, the initial "dummy" size isn't + // displayed. + // + // When the content is unloaded, remember the last size it reported. This + // _is_ important to preserve the window size as it's being hidden; if we + // reverted to a dummy size when the content was unloaded, the dummy size + // would be visible during the "close" animation from the window manager. + contentLogicalWidth: 200 + contentLogicalHeight: 200 // The "dedicated IP" page currently is shown based on a feature toggle, or // if any dedicated IPs have already been added. @@ -148,7 +160,7 @@ SecondaryWindow { function selectPage(id) { var index = pages.findIndex(page => page.name === id) if(index >= 0) - tabLayout.item.selectPage(index) + Client.uiState.settings.currentPage = index } property var currentAlert @@ -211,12 +223,21 @@ SecondaryWindow { function showSettings() { open() } + + function updateWindowSize() { + if(tabLayout.item) { + settings.contentLogicalWidth = tabLayout.item.implicitWidth + settings.contentLogicalHeight = tabLayout.item.implicitHeight + } + } Component { id: hbarComponent HorizontalTabLayout { pages: settings.pages pageTitleFunc: getPageTitle + onImplicitWidthChanged: settings.updateWindowSize() + onImplicitHeightChanged: settings.updateWindowSize() } } Component { @@ -225,6 +246,8 @@ SecondaryWindow { pages: settings.pages pageTitleFunc: getPageTitle pageHeadingFunc: getHeadingTitle + onImplicitWidthChanged: settings.updateWindowSize() + onImplicitHeightChanged: settings.updateWindowSize() } } @@ -233,6 +256,8 @@ SecondaryWindow { sourceComponent: Theme.settings.horizontal ? hbarComponent : vbarComponent width: parent.width height: parent.height + active: settings.visible || settings.positioningForShow + onItemChanged: settings.updateWindowSize() } Component { diff --git a/client/res/components/settings/pages/SplitTunnelAppRow.qml b/client/res/components/settings/pages/SplitTunnelAppRow.qml index 48539b01..9bb678b0 100644 --- a/client/res/components/settings/pages/SplitTunnelAppRow.qml +++ b/client/res/components/settings/pages/SplitTunnelAppRow.qml @@ -37,6 +37,7 @@ SplitTunnelRowBase { property bool showAppIcons: true property string appPath + property string linkTarget // Mode [exclude/include] property string appMode @@ -172,6 +173,9 @@ SplitTunnelRowBase { // There isn't anything meaningful we can display for a UWP app if(appPath.startsWith("uwp:")) return uiTr("Microsoft Store app") + if (linkTarget) { + return linkTarget.replace(/\//g, '\\') + } // Normalize to backslashes on Windows. The backend tolerates // either, but slashes look out of place on Windows. diff --git a/client/res/components/settings/pages/SplitTunnelSettings.qml b/client/res/components/settings/pages/SplitTunnelSettings.qml index dd036298..77ab2dce 100644 --- a/client/res/components/settings/pages/SplitTunnelSettings.qml +++ b/client/res/components/settings/pages/SplitTunnelSettings.qml @@ -98,6 +98,7 @@ TableBase { Layout.fillWidth: true showAppIcons: Qt.platform.os !== 'linux' appPath: modelData.path + linkTarget: modelData.linkTarget appMode: modelData.mode appName: SplitTunnelManager.getNameFromPath(appPath) parentTable: splitTunnelSettings diff --git a/client/res/components/settings/tabs/HorizontalTabLayout.qml b/client/res/components/settings/tabs/HorizontalTabLayout.qml index f2e574c0..8091ee18 100644 --- a/client/res/components/settings/tabs/HorizontalTabLayout.qml +++ b/client/res/components/settings/tabs/HorizontalTabLayout.qml @@ -24,6 +24,7 @@ import "../pages" import "../../theme" import "../../common" import "../../core" +import "../../client" import "qrc:/javascript/keyutil.js" as KeyUtil import PIA.NativeAcc 1.0 as NativeAcc @@ -50,10 +51,6 @@ FocusScope { implicitWidth: Math.max(minimumWidth, listView.width + 2*minimumTabEndMargins) implicitHeight: Theme.settings.hbarHeight + Theme.settings.hbarContentTopMargin + Theme.settings.contentHeight + Theme.settings.hbarContentBottomMargin - function selectPage(index) { - listView.currentIndex = index - } - TabLayoutCommon { id: common } @@ -66,7 +63,11 @@ FocusScope { // Highlight indicator Rectangle { x: { - var currentItem = pagesRepeater.itemAt(listView.currentIndex) + // These dummy dependencies are needed to recalculate x when + // pagesRepeater.itemAt() changes while the tabs are loaded + var tabsDep = pagesRepeater.children + var tabsDep2 = pagesRepeater.count + var currentItem = pagesRepeater.itemAt(Client.uiState.settings.currentPage) return listView.x + (currentItem ? currentItem.x : 0) } y: listView.height - height @@ -137,7 +138,7 @@ FocusScope { } activeFocusOnTab: true - property int currentIndex: 0 + readonly property int currentIndex: Client.uiState.settings.currentPage Repeater { id: pagesRepeater @@ -153,7 +154,7 @@ FocusScope { function mouseClick() { listView.forceActiveFocus(Qt.MouseFocusReason) - listView.currentIndex = index + Client.uiState.settings.currentPage = index } TabIcon { @@ -185,7 +186,7 @@ FocusScope { undefined, listView.currentIndex) if(nextIndex !== -1) { - listView.currentIndex = nextIndex + Client.uiState.settings.currentPage = nextIndex focusCue.reveal() } } @@ -225,6 +226,7 @@ FocusScope { source: "../pages/" + modelData.component width: parent.width height: parent.height + active: visible } } Layout.fillWidth: true @@ -250,7 +252,7 @@ FocusScope { var nextIndex = KeyUtil.handleSettingsTabKeyEvent(event, listView.currentIndex, pagesRepeater.count) if(nextIndex >= 0) { - listView.currentIndex = nextIndex + Client.uiState.settings.currentPage = nextIndex // If this _was_ from the list view, show its focus; this has no effect if // it isn't focused. focusCue.reveal() diff --git a/client/res/components/settings/tabs/VerticalTabLayout.qml b/client/res/components/settings/tabs/VerticalTabLayout.qml index 48c5deb8..6495629f 100644 --- a/client/res/components/settings/tabs/VerticalTabLayout.qml +++ b/client/res/components/settings/tabs/VerticalTabLayout.qml @@ -24,6 +24,7 @@ import "../pages" import "../../theme" import "../../common" import "../../core" +import "../../client" import "qrc:/javascript/keyutil.js" as KeyUtil import PIA.NativeAcc 1.0 as NativeAcc import PIA.NativeHelpers 1.0 @@ -40,10 +41,6 @@ FocusScope { implicitWidth: Theme.settings.vbarWidth + Theme.settings.vbarContentLeftMargin + Theme.settings.contentWidth + Theme.settings.vbarContentRightMargin implicitHeight: Theme.settings.headingHeight + Theme.settings.vbarContentTopMargin + Theme.settings.contentHeight + Theme.settings.vbarContentBottomMargin - function selectPage(index) { - listView.currentIndex = index - } - TabLayoutCommon { id: common } @@ -66,7 +63,11 @@ FocusScope { Rectangle { x: 0 y: { - var currentItem = pagesRepeater.itemAt(listView.currentIndex) + // These dummy dependencies are needed to recalculate y when + // pagesRepeater.itemAt() changes while the tabs are loaded + var tabsDep = pagesRepeater.children + var tabsDep2 = pagesRepeater.count + var currentItem = pagesRepeater.itemAt(Client.uiState.settings.currentPage) return listView.y + (currentItem ? currentItem.y : 0) } width: Theme.settings.vbarWidth @@ -97,7 +98,7 @@ FocusScope { activeFocusOnTab: true - property int currentIndex: 0 + readonly property int currentIndex: Client.uiState.settings.currentPage Repeater { id: pagesRepeater @@ -113,14 +114,14 @@ FocusScope { function mouseClick() { listView.forceActiveFocus(Qt.MouseFocusReason) - listView.currentIndex = index + Client.uiState.settings.currentPage = index } TabIcon { id: tabIcon x: 20 anchors.verticalCenter: parent.verticalCenter - active: listView.currentIndex == index + active: listView.currentIndex === index } Text { id: tabText @@ -145,7 +146,7 @@ FocusScope { undefined, listView.currentIndex) if(nextIndex !== -1) { - listView.currentIndex = nextIndex + Client.uiState.settings.currentPage = nextIndex focusCue.reveal() } } @@ -191,6 +192,7 @@ FocusScope { source: "../pages/" + modelData.component width: parent.width height: parent.height + active: visible } } Layout.fillWidth: true @@ -216,7 +218,7 @@ FocusScope { var nextIndex = KeyUtil.handleSettingsTabKeyEvent(event, listView.currentIndex, pagesRepeater.count) if(nextIndex >= 0) { - listView.currentIndex = nextIndex + Client.uiState.settings.currentPage = nextIndex // If this _was_ from the list view, show its focus; this has no effect if // it isn't focused. focusCue.reveal() diff --git a/client/res/extra/fonts/Roboto-Regular.otf b/client/res/extra/fonts/Roboto-Regular.otf index ebc8a949..db448e31 100644 Binary files a/client/res/extra/fonts/Roboto-Regular.otf and b/client/res/extra/fonts/Roboto-Regular.otf differ diff --git a/client/src/client.h b/client/src/client.h index 8861d628..b5b44579 100644 --- a/client/src/client.h +++ b/client/src/client.h @@ -255,6 +255,9 @@ class Client : public QObject, public Singleton void handleURL (const QString &url); void checkForURL(); + // Trim the QML component cache. + void trimComponentCache() {_engine.trimComponentCache();} + IMPLEMENT_NOTIFICATIONS(Client) private: diff --git a/client/src/main.cpp b/client/src/main.cpp index d8b1c2d9..5cbc52d9 100644 --- a/client/src/main.cpp +++ b/client/src/main.cpp @@ -334,6 +334,11 @@ int clientMain(int argc, char *argv[]) qunsetenv("QT_D3D_ADAPTER_INDEX"); } } + // In Qt 5.15.2, the 'threaded' render loop is still the default for RHI on + // D3D11, but it has threading issues with resource cleanup and can + // deadlock. This was changed in 6.0, the 'basic' render loop is now + // always used for RHI/D3D11. + qputenv("QSG_RENDER_LOOP", "basic"); // The environment variables above must be set before setting the scene // graph backend, this causes Qt Quick to create the RHI interface, which // checks the environment then. @@ -359,6 +364,20 @@ int clientMain(int argc, char *argv[]) linuxLanguagePreAppInit(); #endif +#ifdef Q_OS_WIN + // D3D is forced on Windows, but Qt may still load OpenGL just to check for + // some driver workarounds, which causes a crash on certain buggy drivers. + // + // This env var avoids the test and crash, which don't matter for us since + // we use D3D. This is the only value that always avoids it; "software" + // or "desktop" both can still end up doing the test. + // + // This means that Qt will fail to load OpenGL since we do not actually + // ship ANGLE, which is fine - it handles the static GL context gracefully, + // and we don't use OpenGL otherwise. + ::qputenv("QT_OPENGL", QByteArrayLiteral("angle")); +#endif + PiaClientApplication app{argc, argv}; Path::initializePostApp(); @@ -389,7 +408,7 @@ int clientMain(int argc, char *argv[]) QGuiApplication::setApplicationDisplayName(QStringLiteral(PIA_PRODUCT_NAME)); QGuiApplication::setQuitOnLastWindowClosed(false); - AppSingleton runGuard(Path::ClientExecutable); + AppSingleton runGuard; qint64 runningInstancePid = runGuard.isAnotherInstanceRunning(); if(runningInstancePid > 0) { qWarning () << "Exiting because another instance appears to be running"; diff --git a/client/src/nativehelpers.cpp b/client/src/nativehelpers.cpp index fb1d10ab..6f926bb7 100644 --- a/client/src/nativehelpers.cpp +++ b/client/src/nativehelpers.cpp @@ -22,6 +22,7 @@ #include "nativehelpers.h" #include "balancetext.h" #include "clientsettings.h" +#include "client.h" #include "path.h" #include "version.h" #include "brand.h" @@ -160,7 +161,7 @@ NativeHelpers::NativeHelpers() &NativeHelpers::logToFileChanged); } -void NativeHelpers::initDashboardPopup(QWindow *pDashboard) +void NativeHelpers::initDashboardPopup(QQuickWindow *pDashboard) { if(!pDashboard) { @@ -180,7 +181,7 @@ void NativeHelpers::initDashboardPopup(QWindow *pDashboard) #endif } -void NativeHelpers::initDecoratedWindow(QWindow *pWindow) +void NativeHelpers::initDecoratedWindow(QQuickWindow *pWindow) { if(!pWindow) { @@ -188,6 +189,12 @@ void NativeHelpers::initDecoratedWindow(QWindow *pWindow) return; } + // Tell Qt to release any resources that it can when they're not needed. + // It's normal to run PIA in the background for a long time with no + // windows open, try not to hog resources. + pWindow->setPersistentSceneGraph(false); + pWindow->setPersistentOpenGLContext(false); + #if defined(Q_OS_WIN) winSetIcon(*pWindow); #elif defined(Q_OS_LINUX) @@ -206,6 +213,14 @@ void NativeHelpers::itemStackAfter(QQuickItem *pFirst, QQuickItem *pSecond) pSecond->stackAfter(pFirst); } +void NativeHelpers::releaseWindowResources(QQuickWindow *pWindow) +{ + // Tell Qt to release graphical resources for this window that are no + // longer needed. + if(pWindow) + pWindow->releaseResources(); +} + QString NativeHelpers::getClientVersion() { return QStringLiteral(PIA_VERSION); @@ -325,7 +340,11 @@ void NativeHelpers::crashClient() testCrash(); } - +void NativeHelpers::trimComponentCache() +{ + if(g_client) + g_client->trimComponentCache(); +} void NativeHelpers::writeDummyLogs() { diff --git a/client/src/nativehelpers.h b/client/src/nativehelpers.h index 86dca83a..e3197683 100644 --- a/client/src/nativehelpers.h +++ b/client/src/nativehelpers.h @@ -27,6 +27,7 @@ #include #include #include +#include #ifdef Q_OS_WIN #include @@ -79,16 +80,20 @@ class NativeHelpers : public QObject // Do native initialization on the popup-style dashboard window. // On OS X, sets the dashboard window to appear on all workspaces. - Q_INVOKABLE void initDashboardPopup(QWindow *pDashboard); + Q_INVOKABLE void initDashboardPopup(QQuickWindow *pDashboard); // Do native initialization on decorated windows - settings and changelog // windows (pretty much anything other than the popup dashboard). // On Windows, sets the window icons - Q_INVOKABLE void initDecoratedWindow(QWindow *pWindow); + Q_INVOKABLE void initDecoratedWindow(QQuickWindow *pWindow); // Stack the second item after the first. See QQuickItem::stackAfter() Q_INVOKABLE void itemStackAfter(QQuickItem *pFirst, QQuickItem *pSecond); + // Release cached graphics resources owned by a window. See + // QQuickWindow::releaseResources() + Q_INVOKABLE void releaseWindowResources(QQuickWindow *pWindow); + // Get the client's build version. Q_INVOKABLE QString getClientVersion(); @@ -110,11 +115,11 @@ class NativeHelpers : public QObject Q_INVOKABLE void setDockVisibility (bool enabled); -// TODO: only ensure this runs on _DEBUG -// #ifdef _DEBUG // Trigger a crash on the client. Used to test the crash handler Q_INVOKABLE void crashClient(); + Q_INVOKABLE void trimComponentCache(); + // Write around 1 mb of dummy logs to test log overflow Q_INVOKABLE void writeDummyLogs(); diff --git a/common/src/appsingleton.cpp b/common/src/appsingleton.cpp index c398ed6b..56a48b81 100644 --- a/common/src/appsingleton.cpp +++ b/common/src/appsingleton.cpp @@ -19,16 +19,19 @@ #include "appsingleton.h" #include "brand.h" -#ifdef Q_OS_WIN +#if defined(Q_OS_WIN) #pragma comment(lib, "Kernel32.lib") #include #include #include +#elif defined(Q_OS_LINUX) +#include // readlink() #endif #include #include #include +#include const int RESOURCE_LENGTH_LIMIT = 4000; @@ -46,12 +49,11 @@ bool AppSingleton::unlockResourceShare() return _resourceShare.unlock(); } -AppSingleton::AppSingleton(const QString &executableName, QObject *parent) : - QObject(parent), - _pidShare(executableName), - _resourceShare(executableName + QStringLiteral("_RESOURCE")), - _executableName(executableName) { - +AppSingleton::AppSingleton() + : _executablePath{QCoreApplication::applicationFilePath()}, + _pidShare{_executablePath}, + _resourceShare{_executablePath + QStringLiteral("_RESOURCE")} +{ _resourceShare.create(RESOURCE_LENGTH_LIMIT); if(_resourceShare.error() == QSharedMemory::NoError || _resourceShare.error() == QSharedMemory::AlreadyExists) { @@ -71,8 +73,9 @@ AppSingleton::~AppSingleton() // Returns true if finds a process with the pid // Returns false on any error or if unable -bool findRunningProcess (qint64 pid, const QString &processName) { -#ifdef Q_OS_WIN +bool findRunningProcess (qint64 pid, const QString &processPath) { + qInfo() << "This process:" << processPath; +#if defined(Q_OS_WIN) HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, (DWORD)pid); @@ -81,25 +84,25 @@ bool findRunningProcess (qint64 pid, const QString &processName) { GetModuleFileNameExW(hProcess, NULL, szProcessName.data(), szProcessName.size()); // GetModuleFileName returns a file path with back slashes. We need to convert that - // to forward slashes which is the parameter of processName + // to forward slashes which is the parameter of processPath QString winProcName = QString::fromWCharArray(szProcessName.data()) .replace(QStringLiteral("\\"), QStringLiteral("/")) .trimmed(); CloseHandle(hProcess); - if(processName == winProcName) + qInfo() << "PID" << pid << "-" << winProcName; + + if(processPath == winProcName) return true; else - qDebug () << "Found a process " << processName << " running with PID " << pid << " instead."; + qDebug () << "Found a process " << processPath << " running with PID " << pid << " instead."; } else qDebug () << "Unable to open process " << pid; return false; -#endif - - // For mac and Linux -#ifdef Q_OS_UNIX +#elif defined(Q_OS_MACOS) + // For mac, use ps to get the executable for this PID QProcess ps; ps.setProgram(QStringLiteral("ps")); QStringList args; @@ -112,9 +115,9 @@ bool findRunningProcess (qint64 pid, const QString &processName) { // ps -p -o comm= // Prints a full path to the running process. This should ideally be equal. QString stdout = QString::fromUtf8(ps.readAllStandardOutput()).trimmed(); - qDebug () << "ps -p returns " << stdout; + qInfo() << "PID" << pid << "-" << stdout; - // processName contains an absolute path to the process according to the `Path` class + // processPath contains an absolute path to the process according to the `Path` class // // ps -p can return /opt/piavpn/bin/vpn-client or ./vpn-client // or even simply 'vpn-client' if app was restarted from support tool @@ -123,10 +126,23 @@ bool findRunningProcess (qint64 pid, const QString &processName) { // solid guarantee that this will always return the full path - QFileInfo procInfo(processName), psInfo(stdout); + QFileInfo procInfo(processPath), psInfo(stdout); // Ensure that the filenames are same for both the process and the output from `ps` // Also if the path is invalid, `fileName` will return an empty string. Check that this isn't empty. return (procInfo.fileName() == psInfo.fileName()) && !procInfo.fileName().isEmpty(); +#elif defined(Q_OS_LINUX) + // Linux process names are limited to 15 chars (16 including a terminating + // null char). Read /proc/####/exe to get the process executable. + QString procExePath{QStringLiteral("/proc/%1/exe").arg(pid)}; + std::string procExe{}; + procExe.resize(2048); + ssize_t exeLen = ::readlink(qPrintable(procExePath), &procExe[0], procExe.size()); + procExe.resize(std::max(ssize_t{0}, exeLen)); + QString procExeU16{QString::fromUtf8(procExe.c_str())}; + + qInfo() << "PID" << pid << "-" << procExeU16; + + return processPath == procExeU16; #endif return false; @@ -161,7 +177,7 @@ qint64 AppSingleton::isAnotherInstanceRunning() bool processFound = false; qDebug () << "Value of shared memory data is " << *data; if(*data > 0) - processFound = findRunningProcess(*data, _executableName); + processFound = findRunningProcess(*data, _executablePath); // If no other instance is found, write the current PID as the primary running application. if(!processFound) diff --git a/common/src/appsingleton.h b/common/src/appsingleton.h index ecb138b7..5f0ed7b6 100644 --- a/common/src/appsingleton.h +++ b/common/src/appsingleton.h @@ -26,24 +26,48 @@ #include #include - +// AppSingleton is used by applications that are limited to one instance per +// session. It uses shared memory to: +// - detect if a prior instance of this application is already running +// - optionally pass a requested resource back to the first instance +// +// Applications limited to one instance this way should create an AppSingleton +// and then check if a prior instance already existed using +// isAnotherInstanceRunning(). +// +// If there is, the app should exit. A launch resource can be passed to the +// existing instance using setLaunchResource() (the first instance then must be +// signaled to check it, which is not handled by AppSingleton.) +// +// AppSingleton's shared resources are specific to the exact executable path +// passed to the constructor. If two copies of the app are executed from +// different paths, they act independently (both can run, and they do not send +// launch resources to each other). (This is intentional, primarily to permit +// running dev and release builds at the same time.) class COMMON_EXPORT AppSingleton : public QObject, public Singleton { Q_OBJECT private: + QString _executablePath; + // Shared memory used to indicate the running instance of this application. + // AppSingleton writes our PID here if no prior instance is found. + // We don't clear this PID if our process exits, since we have to be able to + // handle a crashed process anyway that failed to clear its PID. (If a + // prior PID is found, we check that it's still running and is actually this + // application before accepting it as a prior instance.) QSharedMemory _pidShare; + // Shared memory used to pass a launch resource to the existing instance of + // this app. QSharedMemory _resourceShare; - QString _executableName; - bool lockResourceShare (); + bool lockResourceShare(); bool unlockResourceShare(); public: - AppSingleton(const QString &executableName, QObject *parent = nullptr); + AppSingleton(); ~AppSingleton(); - // returns the pid of the original instance, if available. // -1 otherwise qint64 isAnotherInstanceRunning (); diff --git a/common/src/builtin/logging.cpp b/common/src/builtin/logging.cpp index 95fb1cf7..fe7311e9 100644 --- a/common/src/builtin/logging.cpp +++ b/common/src/builtin/logging.cpp @@ -596,6 +596,18 @@ static QString buildLogFilePrefix(QDateTime now, QtMsgType type, void Logger::loggingHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { +#if defined(Q_OS_WIN) + // We force ANGLE in client/src/main.cpp to avoid Qt loading OpenGL, which + // crashes on certain buggy drivers. However, this means that it logs + // errors relating to the failure to load OpenGL. These can safely be + // ignored since we don't use OpenGL. + if(msg.startsWith("Failed to load libEGL") || + msg.startsWith("QWindowsEGLStaticContext::create: Failed to load and resolve libEGL functions")) + { + return; // Silently ignore this trace + } +#endif + // Override with simpler endl; gets converted by text file handling anyway const char endl = '\n'; diff --git a/common/src/builtin/util.cpp b/common/src/builtin/util.cpp index 22fbb335..7d58e3d0 100644 --- a/common/src/builtin/util.cpp +++ b/common/src/builtin/util.cpp @@ -22,6 +22,7 @@ #include "util.h" #include "error.h" #include "path.h" +#include "version.h" #ifdef PIA_CRASH_REPORTING #if defined(Q_OS_MACOS) @@ -153,6 +154,19 @@ void setUtf8LocaleCodec() QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); } +QString getVersionInfo() +{ + QString version_info; + QString timestamp = QDateTime::currentDateTime().toUTC().toString("yyyyMMddhhmmss"); +#if defined(PIA_CLIENT) + version_info = "client"; +#elif defined(PIA_DAEMON) + version_info = "daemon"; +#endif + version_info += QString("-") + PIA_VERSION + "-" + timestamp; + return version_info; +} + #ifdef PIA_CRASH_REPORTING @@ -170,17 +184,22 @@ bool DumpCallback(const char *dump_dir, void *context, bool succeeded) { Q_UNUSED(context); - QString path = QString::fromUtf8(dump_dir) + QLatin1String("/") + QString::fromUtf8(minidump_id) + ".dmp"; - qDebug("%s, dump path: %s\n", succeeded ? "Succeed to write minidump" : "Failed to write minidump", qPrintable(path)); + QString old_path = QString::fromUtf8(dump_dir) + QLatin1String("/") + + QString::fromUtf8(minidump_id) + ".dmp"; + QString new_path = QString::fromUtf8(dump_dir) + QLatin1String("/") + + getVersionInfo() + "-" + + QString::fromUtf8(minidump_id) + ".dmp"; + qDebug("%s, dump path: %s\n", succeeded ? "Succeed to write minidump" : "Failed to write minidump", qPrintable(new_path)); if(succeeded) { + QFile::rename(old_path, new_path); #ifdef PIA_CLIENT startSupportTool(QStringLiteral("crash"), {}); #endif #if defined(PIA_DAEMON) - QFile::setPermissions(path, QFileDevice::ReadUser|QFileDevice::ReadGroup); + QFile::setPermissions(new_path, QFileDevice::ReadUser|QFileDevice::ReadGroup); #endif } return succeeded; @@ -196,10 +215,15 @@ bool DumpCallback(const wchar_t* dump_dir, Q_UNUSED(assertion); Q_UNUSED(exinfo); - QString path = QString::fromWCharArray(dump_dir) + QLatin1String("/") + QString::fromWCharArray(minidump_id) + ".dmp"; - qDebug("%s, dump path: %s\n", succeeded ? "Succeed to write minidump" : "Failed to write minidump", qPrintable(path)); + QString old_path = QString::fromWCharArray(dump_dir) + QLatin1String("/") + + QString::fromWCharArray(minidump_id) + ".dmp"; + QString new_path = QString::fromWCharArray(dump_dir) + QLatin1String("/") + + getVersionInfo() + "-" + + QString::fromWCharArray(minidump_id) + ".dmp"; + qDebug("%s, dump path: %s\n", succeeded ? "Succeed to write minidump" : "Failed to write minidump", qPrintable(new_path)); if(succeeded) { + QFile::rename(old_path, new_path); startSupportTool("crash", {}); } @@ -209,12 +233,15 @@ bool DumpCallback(const wchar_t* dump_dir, bool DumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded) { - qDebug("%s, dump path: %s\n", succeeded ? "Succeed to write minidump" : "Failed to write minidump", descriptor.path()); + QString new_path = QString::fromUtf8(descriptor.directory().c_str()) + QLatin1String("/") + + getVersionInfo() + ".dmp"; + qDebug("%s, dump path: %s\n", succeeded ? "Succeed to write minidump" : "Failed to write minidump", qPrintable(new_path)); if(succeeded) { #if defined(PIA_DAEMON) QFile::setPermissions(descriptor.path(), QFileDevice::ReadUser|QFileDevice::ReadGroup); #endif + QFile::rename(descriptor.path(), new_path); startSupportTool("crash", {}); } @@ -288,7 +315,7 @@ void monitorDaemonDumps() // to write diagnostics, but this would be relatively complex to handle // all the possibilities of it failing and ensure that we still report // the problem. - startSupportTool(QStringLiteral("crash"), {}); + startSupportTool(QStringLiteral("logs"), {}); break; } } diff --git a/daemon/src/daemon.cpp b/daemon/src/daemon.cpp index bc42bf07..557f57d1 100644 --- a/daemon/src/daemon.cpp +++ b/daemon/src/daemon.cpp @@ -50,6 +50,8 @@ #include #include #include +#include +#include #if defined(Q_OS_WIN) #include @@ -57,6 +59,8 @@ #include "win/win_util.h" #include #include +#include +#include #pragma comment(lib, "advapi32.lib") #endif @@ -444,6 +448,9 @@ Daemon::Daemon(QObject* parent) _checkForAppMessagesTimer.setInterval(msec(appMessagesCheckInterval)); connect(&_checkForAppMessagesTimer, &QTimer::timeout, this, &Daemon::checkForAppMessages); + _memTraceTimer.setInterval(msec(std::chrono::minutes(5))); + connect(&_memTraceTimer, &QTimer::timeout, this, &Daemon::traceMemory); + auto connectPropertyChanges = [this](NativeJsonObject &object, QSet Daemon::* pSet) { connect(&object, &NativeJsonObject::propertyChanged, this, @@ -658,6 +665,9 @@ Daemon::Daemon(QObject* parent) _data.modernRegionMeta()); _updateDownloader.run(true, _environment.getUpdateApi()); + _memTraceTimer.start(); + traceMemory(); + // Refresh metadata right away too RPC_refreshMetadata(); @@ -1360,6 +1370,7 @@ void Daemon::RPC_refreshMetadata() { refreshDedicatedIps(); checkForAppMessages(); + refreshAccountInfo(); _updateDownloader.refreshUpdate(); } @@ -1645,6 +1656,7 @@ void Daemon::start() _accountRefreshTimer.start(); refreshAccountInfo(); } + traceMemory(); qInfo() << "Daemon started and waiting for connections..."; @@ -3552,6 +3564,139 @@ void Daemon::disconnectVPN() _state.automationLastTrigger({}); } +#ifdef Q_OS_UNIX +void logMetricsForProcessUnix (const QString &identifier, uint pid) { + QProcess psProcess; + psProcess.start(QStringLiteral("/bin/ps"), QStringList () + << QStringLiteral("-p") << QString::number(pid) + << QStringLiteral("-o") << QStringLiteral("rss=,pcpu=")); + psProcess.waitForFinished(); + + if(psProcess.exitCode() != 0) + return; + + auto psParts = QString::fromUtf8(psProcess.readAllStandardOutput()).trimmed().split(QRegularExpression("\\s+")); + if(psParts.count() == 2) { + // Write metrics to the log with the format: + // "Metrics: client_mem=13251,client_cpu=0.2" + + qInfo() << QStringLiteral("Metrics: %1_mem=%2,%1_cpu=%3").arg(identifier).arg(psParts[0]).arg(psParts[1]); + } else { + qWarning () << "Unexpected output from ps - " << psParts; + return; + } + +} +void logProcessMemoryUnix (const QString &identifier, const QString &name) { + // Determine the PID of the process using pgrep + QProcess pgrepProcess; + pgrepProcess.start(QStringLiteral("pgrep"), QStringList () << name); + pgrepProcess.waitForFinished(); + // Found at-least one process + if(pgrepProcess.exitCode() == 0) { + auto lines = pgrepProcess.readAllStandardOutput().split('\n'); + + for(int i = 0; i < lines.size(); ++i) { + bool ok = true; + auto pid = lines.at(i).trimmed().toUInt(&ok); + // We are logging metrics for all processes which match this name. + // ideally, there should be just one process. However, if there are more, + // a suffix is added to the identifier + if(ok) { + logMetricsForProcessUnix( + i == 0 ? identifier : QStringLiteral("%1_%2").arg(identifier).arg(i), + pid); + } + } + } +} +#endif + + +#ifdef Q_OS_WIN +void logProcessMemoryWindows(const QString &id, DWORD pid) { + WinHandle clientProcess{::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, + false, pid)}; + DWORD dwError{ERROR_SUCCESS}; + if(!clientProcess) + dwError = ::GetLastError(); + PROCESS_MEMORY_COUNTERS_EX procMem{}; + procMem.cb = sizeof(procMem); + BOOL gotMemory = FALSE; + if(clientProcess) + { + gotMemory = ::GetProcessMemoryInfo(clientProcess.get(), + reinterpret_cast(&procMem), + procMem.cb); + if(!gotMemory) + dwError = ::GetLastError(); + } + + if(gotMemory) + { + qInfo () << QStringLiteral("Metrics: %1_privatemem=%2,%1_nonpaged_pool=%3,%1_paged_pool=%4,%1_workingset=%5") + .arg(id) + .arg(procMem.PrivateUsage) + .arg(procMem.QuotaNonPagedPoolUsage) + .arg(procMem.QuotaPagedPoolUsage) + .arg(procMem.WorkingSetSize); + } + else + { + qWarning() << "Could not access memory for " << id; + } +} +#endif + +void Daemon::traceMemory() { + qDebug () << "Tracing memory"; +#ifdef Q_OS_MACOS + logProcessMemoryUnix(QStringLiteral("client"), QStringLiteral(BRAND_NAME)); + logProcessMemoryUnix(QStringLiteral("daemon"), QStringLiteral(BRAND_CODE "-daemon")); + logProcessMemoryUnix(QStringLiteral("openvpn"), QStringLiteral(BRAND_CODE "-openvpn")); + logProcessMemoryUnix(QStringLiteral("wireguard"), QStringLiteral(BRAND_CODE "-wireguard-go")); +#endif + +#ifdef Q_OS_LINUX + logProcessMemoryUnix(QStringLiteral("client"), QStringLiteral(BRAND_CODE "-client")); + logProcessMemoryUnix(QStringLiteral("daemon"), QStringLiteral(BRAND_CODE "-daemon")); + logProcessMemoryUnix(QStringLiteral("openvpn"), QStringLiteral(BRAND_CODE "-openvpn")); + logProcessMemoryUnix(QStringLiteral("wireguard"), QStringLiteral(BRAND_CODE "-wireguard-go")); +#endif + +#ifdef Q_OS_WIN + std::unordered_map targets; + + targets[QStringView{BRAND_CODE L"-client.exe"}] = QStringLiteral("client"); + targets[QStringView{BRAND_CODE L"-service.exe"}] = QStringLiteral("daemon"); + targets[QStringView{BRAND_CODE L"-openvpn.exe"}] = QStringLiteral("openvpn"); + targets[QStringView{BRAND_CODE L"-wgservice.exe"}] = QStringLiteral("wireguard"); + + WinHandle procSnapshot{::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)}; + + PROCESSENTRY32W proc{}; + proc.dwSize = sizeof(proc); + + BOOL nextProc = ::Process32FirstW(procSnapshot.get(), &proc); + while(nextProc) + { + // Avoid wchar_t[] constructor for QStringView which assumes the string + // fills the entire array + QStringView processName{&proc.szExeFile[0]}; + auto itTarget = targets.find(processName); + if(itTarget != targets.end()) { + logProcessMemoryWindows(itTarget->second, proc.th32ProcessID); + } + nextProc = ::Process32NextW(procSnapshot.get(), &proc); + } + DWORD dwError = ::GetLastError(); + if(dwError != ERROR_SUCCESS && dwError != ERROR_NO_MORE_FILES) + { + qWarning() << "Unable to enumerate processes:" << WinErrTracer{dwError}; + } +#endif +} + ClientConnection::ClientConnection(IPCConnection *connection, LocalMethodRegistry* registry, QObject *parent) : QObject(parent) , _connection(connection) diff --git a/daemon/src/daemon.h b/daemon/src/daemon.h index d782adec..985d4bc5 100644 --- a/daemon/src/daemon.h +++ b/daemon/src/daemon.h @@ -539,6 +539,8 @@ public slots: // or the setting value that we connected with void updatePortForwarder(); + void traceMemory(); + // Set _state.overridesActive() or _state.overridesFailed() and log // appropriately void setOverrideActive(const QString &resourceName); @@ -619,6 +621,7 @@ public slots: QTimer _dedicatedIpRefreshTimer; QTimer _checkForAppMessagesTimer; + QTimer _memTraceTimer; // Ongoing login attempt. If we try to log in again or log out, we need to // abort the prior attempt. This is an AbortableTask so it'll still diff --git a/daemon/src/posix/posix_main.cpp b/daemon/src/posix/posix_main.cpp index 7ce118ba..a698e675 100644 --- a/daemon/src/posix/posix_main.cpp +++ b/daemon/src/posix/posix_main.cpp @@ -76,7 +76,7 @@ static void terminateHandler() int main(int argc, char** argv) { setUtf8LocaleCodec(); - Logger::initialize(true); + Logger::initialize(isatty(2)); FUNCTION_LOGGING_CATEGORY("posix.main"); diff --git a/daemon/src/win/win_daemon.cpp b/daemon/src/win/win_daemon.cpp index 0c900505..11c79619 100644 --- a/daemon/src/win/win_daemon.cpp +++ b/daemon/src/win/win_daemon.cpp @@ -398,19 +398,6 @@ WinDaemon::WinDaemon(QObject* parent) _wfpCalloutMonitor.doManualCheck(); }); - _clientMemTraceTimer.setInterval(msec(std::chrono::minutes(5))); - connect(&_clientMemTraceTimer, &QTimer::timeout, this, &WinDaemon::traceClientMemory); - - connect(this, &WinDaemon::daemonActivated, this, [this]() - { - _clientMemTraceTimer.start(); - traceClientMemory(); - }); - connect(this, &WinDaemon::daemonDeactivated, this, [this]() - { - _clientMemTraceTimer.stop(); - }); - // Split tunnel support errors are platform-dependent, nothing else adds // them (otherwise we'd have to do a proper get-append-set below) Q_ASSERT(_state.splitTunnelSupportErrors().empty()); @@ -1600,73 +1587,6 @@ class TraceMemSize : public DebugTraceable std::size_t _mem; }; -void WinDaemon::traceClientMemory() -{ - WinHandle procSnapshot{::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)}; - - PROCESSENTRY32W proc{}; - proc.dwSize = sizeof(proc); - - QStringView clientModule{BRAND_CODE L"-client.exe"}; - QStringView cliModule{BRAND_CODE L"ctl.exe"}; - - int clientProcesses{0}; - - BOOL nextProc = ::Process32FirstW(procSnapshot.get(), &proc); - while(nextProc) - { - // Avoid wchar_t[] constructor for QStringView which assumes the string - // fills the entire array - QStringView processName{&proc.szExeFile[0]}; - if(clientModule == processName || cliModule == processName) - { - ++clientProcesses; - WinHandle clientProcess{::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, - false, proc.th32ProcessID)}; - DWORD dwError{ERROR_SUCCESS}; - if(!clientProcess) - dwError = ::GetLastError(); - PROCESS_MEMORY_COUNTERS_EX procMem{}; - procMem.cb = sizeof(procMem); - BOOL gotMemory = FALSE; - if(clientProcess) - { - gotMemory = ::GetProcessMemoryInfo(clientProcess.get(), - reinterpret_cast(&procMem), - procMem.cb); - if(!gotMemory) - dwError = ::GetLastError(); - } - - if(gotMemory) - { - qInfo() << "PID" << proc.th32ProcessID - << processName - << "- Private mem:" << TraceMemSize{procMem.PrivateUsage} - << "- Nonpaged pool:" << TraceMemSize{procMem.QuotaNonPagedPoolUsage} - << "- Paged pool:" << TraceMemSize{procMem.QuotaPagedPoolUsage} - << "- Working set:" << TraceMemSize{procMem.WorkingSetSize}; - } - else - { - qInfo() << "PID" << proc.th32ProcessID - << QStringView{proc.szExeFile} - << "- can't access memory usage -" - << WinErrTracer{dwError}; - } - } - nextProc = ::Process32NextW(procSnapshot.get(), &proc); - } - - DWORD dwError = ::GetLastError(); - if(dwError != ERROR_SUCCESS && dwError != ERROR_NO_MORE_FILES) - { - qWarning() << "Unable to enumerate processes:" << WinErrTracer{dwError}; - } - - qInfo() << "Found" << clientProcesses << "running client processes"; -} - void WinDaemon::wireguardServiceFailed() { // If the connection failed after the WG service was started, check whether diff --git a/daemon/src/win/win_daemon.h b/daemon/src/win/win_daemon.h index f0583825..e027608f 100644 --- a/daemon/src/win/win_daemon.h +++ b/daemon/src/win/win_daemon.h @@ -231,9 +231,6 @@ class WinDaemon : public Daemon, private MessageWnd // some circumstances. void checkWintunInstallation(); - // Trace memory usage of client processes. - void traceClientMemory(); - public: // WireguardServiceBackend calls these methods to hint to us to consider // re-checking the WinTUN installation state. @@ -330,9 +327,6 @@ class WinDaemon : public Daemon, private MessageWnd WinAppMonitor _appMonitor; SubnetBypass _subnetBypass; - - // Trace memory usage of client processes periodically - QTimer _clientMemTraceTimer; }; #undef g_daemon diff --git a/daemon/src/wireguardmethod.cpp b/daemon/src/wireguardmethod.cpp index 5c374c5e..c4aef5af 100644 --- a/daemon/src/wireguardmethod.cpp +++ b/daemon/src/wireguardmethod.cpp @@ -662,7 +662,7 @@ unsigned WireguardMethod::determinePosixMtu(const QHostAddress &host) // Find the MTU for the interface used to reach the remote host. mtu = findHostMtu(host.toString()); // Default to 1500 if no MTU was found. - if(!mtu) + if(!mtu || mtu > 1500) mtu = 1500; // Subtract 80 bytes for encapsulation. mtu -= 80; diff --git a/deps/breakpad/client/linux/data/linux-gate-amd.sym b/deps/breakpad/client/linux/data/linux-gate-amd.sym index e042a5ec..d1daa3ac 100644 --- a/deps/breakpad/client/linux/data/linux-gate-amd.sym +++ b/deps/breakpad/client/linux/data/linux-gate-amd.sym @@ -1,3 +1,3 @@ -MODULE Linux x86 B8CFDE93002D54DA1900A40AA1BD67690 linux-gate.so -PUBLIC 400 0 __kernel_vsyscall -STACK WIN 4 400 100 1 1 0 0 0 0 0 1 +version https://git-lfs.github.com/spec/v1 +oid sha256:240e0f7005f84106d7ae89d6fd10e4b9b819e1c240328a9cae4a9ee657bac619 +size 132 diff --git a/deps/breakpad/client/linux/data/linux-gate-intel.sym b/deps/breakpad/client/linux/data/linux-gate-intel.sym index c209c237..38386e37 100644 --- a/deps/breakpad/client/linux/data/linux-gate-intel.sym +++ b/deps/breakpad/client/linux/data/linux-gate-intel.sym @@ -1,3 +1,3 @@ -MODULE Linux x86 4FBDA58B5A1DF5A379E3CF19A235EA090 linux-gate.so -PUBLIC 400 0 __kernel_vsyscall -STACK WIN 4 400 200 3 3 0 0 0 0 0 1 \ No newline at end of file +version https://git-lfs.github.com/spec/v1 +oid sha256:bbdd1692c130636975cff06924da20346d41252f540804d95197f89121f9c42b +size 131 diff --git a/deps/built/linux/arm64/artifacts/libcrypto.so b/deps/built/linux/arm64/artifacts/libcrypto.so deleted file mode 120000 index 88520eab..00000000 --- a/deps/built/linux/arm64/artifacts/libcrypto.so +++ /dev/null @@ -1 +0,0 @@ -libcrypto.so.1.1 \ No newline at end of file diff --git a/deps/built/linux/arm64/artifacts/libcrypto.so.1.1 b/deps/built/linux/arm64/artifacts/libcrypto.so.1.1 deleted file mode 100755 index ff350c9f..00000000 Binary files a/deps/built/linux/arm64/artifacts/libcrypto.so.1.1 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libssl.so b/deps/built/linux/arm64/artifacts/libssl.so deleted file mode 120000 index 21a9bcd9..00000000 --- a/deps/built/linux/arm64/artifacts/libssl.so +++ /dev/null @@ -1 +0,0 @@ -libssl.so.1.1 \ No newline at end of file diff --git a/deps/built/linux/arm64/artifacts/libssl.so.1.1 b/deps/built/linux/arm64/artifacts/libssl.so.1.1 deleted file mode 100755 index fc425ece..00000000 Binary files a/deps/built/linux/arm64/artifacts/libssl.so.1.1 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-composite.so.0.0.0 b/deps/built/linux/arm64/artifacts/libxcb-composite.so.0.0.0 deleted file mode 100755 index e87c4bc7..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-composite.so.0.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-damage.so.0.0.0 b/deps/built/linux/arm64/artifacts/libxcb-damage.so.0.0.0 deleted file mode 100755 index 59211050..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-damage.so.0.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-dpms.so.0.0.0 b/deps/built/linux/arm64/artifacts/libxcb-dpms.so.0.0.0 deleted file mode 100755 index 68875751..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-dpms.so.0.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-dri2.so.0.0.0 b/deps/built/linux/arm64/artifacts/libxcb-dri2.so.0.0.0 deleted file mode 100755 index 6ffa2971..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-dri2.so.0.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-dri3.so.0.0.0 b/deps/built/linux/arm64/artifacts/libxcb-dri3.so.0.0.0 deleted file mode 100755 index 5e36f025..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-dri3.so.0.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-ewmh.so.2.0.0 b/deps/built/linux/arm64/artifacts/libxcb-ewmh.so.2.0.0 deleted file mode 100755 index 8b2c013a..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-ewmh.so.2.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-glx.so.0.0.0 b/deps/built/linux/arm64/artifacts/libxcb-glx.so.0.0.0 deleted file mode 100755 index 2c8919f8..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-glx.so.0.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-icccm.so.4.0.0 b/deps/built/linux/arm64/artifacts/libxcb-icccm.so.4.0.0 deleted file mode 100755 index 30afbc0a..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-icccm.so.4.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-image.so.0.0.0 b/deps/built/linux/arm64/artifacts/libxcb-image.so.0.0.0 deleted file mode 100755 index 4fffe8e0..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-image.so.0.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-keysyms.so.1.0.0 b/deps/built/linux/arm64/artifacts/libxcb-keysyms.so.1.0.0 deleted file mode 100755 index bb25c796..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-keysyms.so.1.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-present.so.0.0.0 b/deps/built/linux/arm64/artifacts/libxcb-present.so.0.0.0 deleted file mode 100755 index 05f6c551..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-present.so.0.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-randr.so.0.1.0 b/deps/built/linux/arm64/artifacts/libxcb-randr.so.0.1.0 deleted file mode 100755 index d7022995..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-randr.so.0.1.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-record.so.0.0.0 b/deps/built/linux/arm64/artifacts/libxcb-record.so.0.0.0 deleted file mode 100755 index 0c92bd3d..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-record.so.0.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-render-util.so.0.0.0 b/deps/built/linux/arm64/artifacts/libxcb-render-util.so.0.0.0 deleted file mode 100755 index 1c07639a..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-render-util.so.0.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-render.so.0.0.0 b/deps/built/linux/arm64/artifacts/libxcb-render.so.0.0.0 deleted file mode 100755 index b59f230f..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-render.so.0.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-res.so.0.0.0 b/deps/built/linux/arm64/artifacts/libxcb-res.so.0.0.0 deleted file mode 100755 index 0d5248ff..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-res.so.0.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-screensaver.so.0.0.0 b/deps/built/linux/arm64/artifacts/libxcb-screensaver.so.0.0.0 deleted file mode 100755 index 3101835d..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-screensaver.so.0.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-shape.so.0.0.0 b/deps/built/linux/arm64/artifacts/libxcb-shape.so.0.0.0 deleted file mode 100755 index fd2b8194..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-shape.so.0.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-shm.so.0.0.0 b/deps/built/linux/arm64/artifacts/libxcb-shm.so.0.0.0 deleted file mode 100755 index c2d8e0e4..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-shm.so.0.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-sync.so.1.0.0 b/deps/built/linux/arm64/artifacts/libxcb-sync.so.1.0.0 deleted file mode 100755 index 0a9c5eea..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-sync.so.1.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-util.so.1.0.0 b/deps/built/linux/arm64/artifacts/libxcb-util.so.1.0.0 deleted file mode 100755 index d2ea32e2..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-util.so.1.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-xf86dri.so.0.0.0 b/deps/built/linux/arm64/artifacts/libxcb-xf86dri.so.0.0.0 deleted file mode 100755 index 3872a1b9..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-xf86dri.so.0.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-xfixes.so.0.0.0 b/deps/built/linux/arm64/artifacts/libxcb-xfixes.so.0.0.0 deleted file mode 100755 index e79f8086..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-xfixes.so.0.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-xinerama.so.0.0.0 b/deps/built/linux/arm64/artifacts/libxcb-xinerama.so.0.0.0 deleted file mode 100755 index 1f32c68f..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-xinerama.so.0.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-xinput.so.0.1.0 b/deps/built/linux/arm64/artifacts/libxcb-xinput.so.0.1.0 deleted file mode 100755 index 0970adfd..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-xinput.so.0.1.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-xkb.so.1.0.0 b/deps/built/linux/arm64/artifacts/libxcb-xkb.so.1.0.0 deleted file mode 100755 index 27ac213c..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-xkb.so.1.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-xtest.so.0.0.0 b/deps/built/linux/arm64/artifacts/libxcb-xtest.so.0.0.0 deleted file mode 100755 index 2d891717..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-xtest.so.0.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-xv.so.0.0.0 b/deps/built/linux/arm64/artifacts/libxcb-xv.so.0.0.0 deleted file mode 100755 index 7d9d3bf7..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-xv.so.0.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-xvmc.so.0.0.0 b/deps/built/linux/arm64/artifacts/libxcb-xvmc.so.0.0.0 deleted file mode 100755 index 5e3b56a8..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb-xvmc.so.0.0.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/libxcb.so.1.1.0 b/deps/built/linux/arm64/artifacts/libxcb.so.1.1.0 deleted file mode 100755 index b1f80a2c..00000000 Binary files a/deps/built/linux/arm64/artifacts/libxcb.so.1.1.0 and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/pia-hnsd b/deps/built/linux/arm64/artifacts/pia-hnsd deleted file mode 100755 index 93d58ee4..00000000 Binary files a/deps/built/linux/arm64/artifacts/pia-hnsd and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/pia-openvpn b/deps/built/linux/arm64/artifacts/pia-openvpn deleted file mode 100755 index d9c74ce2..00000000 --- a/deps/built/linux/arm64/artifacts/pia-openvpn +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9d4ae0cd12608d618d825a5e89fc085411cb9e99c7242e3f6cfcd1f97ce3c5a9 -size 858560 diff --git a/deps/built/linux/arm64/artifacts/pia-ss-local b/deps/built/linux/arm64/artifacts/pia-ss-local deleted file mode 100755 index b8b9c706..00000000 Binary files a/deps/built/linux/arm64/artifacts/pia-ss-local and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/pia-unbound b/deps/built/linux/arm64/artifacts/pia-unbound deleted file mode 100755 index f33131f9..00000000 Binary files a/deps/built/linux/arm64/artifacts/pia-unbound and /dev/null differ diff --git a/deps/built/linux/arm64/artifacts/pia-wireguard-go b/deps/built/linux/arm64/artifacts/pia-wireguard-go deleted file mode 100755 index cfda572e..00000000 Binary files a/deps/built/linux/arm64/artifacts/pia-wireguard-go and /dev/null differ diff --git a/deps/built/linux/arm64/libcrypto.so.1.1 b/deps/built/linux/arm64/libcrypto.so.1.1 index fe9b0e6b..2ba9af47 100755 Binary files a/deps/built/linux/arm64/libcrypto.so.1.1 and b/deps/built/linux/arm64/libcrypto.so.1.1 differ diff --git a/deps/built/linux/arm64/libssl.so.1.1 b/deps/built/linux/arm64/libssl.so.1.1 index 997bd295..369555a4 100755 Binary files a/deps/built/linux/arm64/libssl.so.1.1 and b/deps/built/linux/arm64/libssl.so.1.1 differ diff --git a/deps/built/linux/arm64/artifacts/libxcb-composite.so b/deps/built/linux/arm64/libxcb-composite.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-composite.so rename to deps/built/linux/arm64/libxcb-composite.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-composite.so.0 b/deps/built/linux/arm64/libxcb-composite.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-composite.so.0 rename to deps/built/linux/arm64/libxcb-composite.so.0 diff --git a/deps/built/linux/arm64/libxcb-composite.so.0.0.0 b/deps/built/linux/arm64/libxcb-composite.so.0.0.0 new file mode 100755 index 00000000..2b37ced0 --- /dev/null +++ b/deps/built/linux/arm64/libxcb-composite.so.0.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:523c871724909e9fe283f1b7a0dbae77b3bc2a65e1b3645fb9375a0c1997e540 +size 29168 diff --git a/deps/built/linux/arm64/artifacts/libxcb-damage.so b/deps/built/linux/arm64/libxcb-damage.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-damage.so rename to deps/built/linux/arm64/libxcb-damage.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-damage.so.0 b/deps/built/linux/arm64/libxcb-damage.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-damage.so.0 rename to deps/built/linux/arm64/libxcb-damage.so.0 diff --git a/deps/built/linux/arm64/libxcb-damage.so.0.0.0 b/deps/built/linux/arm64/libxcb-damage.so.0.0.0 new file mode 100755 index 00000000..82bedc8b --- /dev/null +++ b/deps/built/linux/arm64/libxcb-damage.so.0.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4478a8c1649f85d324dbf36577466e9e1224f0b8e53a3cb864b26af947fc6230 +size 23968 diff --git a/deps/built/linux/arm64/artifacts/libxcb-dpms.so b/deps/built/linux/arm64/libxcb-dpms.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-dpms.so rename to deps/built/linux/arm64/libxcb-dpms.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-dpms.so.0 b/deps/built/linux/arm64/libxcb-dpms.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-dpms.so.0 rename to deps/built/linux/arm64/libxcb-dpms.so.0 diff --git a/deps/built/linux/arm64/libxcb-dpms.so.0.0.0 b/deps/built/linux/arm64/libxcb-dpms.so.0.0.0 new file mode 100755 index 00000000..778f999b --- /dev/null +++ b/deps/built/linux/arm64/libxcb-dpms.so.0.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4bef9d48880128744537c5fb0344e9a6619895133e2c718e2120b23b53470b60 +size 26408 diff --git a/deps/built/linux/arm64/artifacts/libxcb-dri2.so b/deps/built/linux/arm64/libxcb-dri2.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-dri2.so rename to deps/built/linux/arm64/libxcb-dri2.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-dri2.so.0 b/deps/built/linux/arm64/libxcb-dri2.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-dri2.so.0 rename to deps/built/linux/arm64/libxcb-dri2.so.0 diff --git a/deps/built/linux/arm64/libxcb-dri2.so.0.0.0 b/deps/built/linux/arm64/libxcb-dri2.so.0.0.0 new file mode 100755 index 00000000..a2f79e54 --- /dev/null +++ b/deps/built/linux/arm64/libxcb-dri2.so.0.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e3755ee1b85e8c05930bb4206b44d2f3505e35074f60cb891e6f520ff98d1f65 +size 54384 diff --git a/deps/built/linux/arm64/artifacts/libxcb-dri3.so b/deps/built/linux/arm64/libxcb-dri3.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-dri3.so rename to deps/built/linux/arm64/libxcb-dri3.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-dri3.so.0 b/deps/built/linux/arm64/libxcb-dri3.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-dri3.so.0 rename to deps/built/linux/arm64/libxcb-dri3.so.0 diff --git a/deps/built/linux/arm64/libxcb-dri3.so.0.0.0 b/deps/built/linux/arm64/libxcb-dri3.so.0.0.0 new file mode 100755 index 00000000..fb6408bc --- /dev/null +++ b/deps/built/linux/arm64/libxcb-dri3.so.0.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78b00db89f49803602c9b34636e05fc70b17dbe1d060e580a3298040dcf4b56b +size 47848 diff --git a/deps/built/linux/arm64/artifacts/libxcb-ewmh.so b/deps/built/linux/arm64/libxcb-ewmh.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-ewmh.so rename to deps/built/linux/arm64/libxcb-ewmh.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-ewmh.so.2 b/deps/built/linux/arm64/libxcb-ewmh.so.2 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-ewmh.so.2 rename to deps/built/linux/arm64/libxcb-ewmh.so.2 diff --git a/deps/built/linux/arm64/libxcb-ewmh.so.2.0.0 b/deps/built/linux/arm64/libxcb-ewmh.so.2.0.0 new file mode 100755 index 00000000..a1b23257 --- /dev/null +++ b/deps/built/linux/arm64/libxcb-ewmh.so.2.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7327d6528b3a51aa6c83fc793866ff4a58e8cbec3fe789f6351b762855308aec +size 173456 diff --git a/deps/built/linux/arm64/artifacts/libxcb-glx.so b/deps/built/linux/arm64/libxcb-glx.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-glx.so rename to deps/built/linux/arm64/libxcb-glx.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-glx.so.0 b/deps/built/linux/arm64/libxcb-glx.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-glx.so.0 rename to deps/built/linux/arm64/libxcb-glx.so.0 diff --git a/deps/built/linux/arm64/libxcb-glx.so.0.0.0 b/deps/built/linux/arm64/libxcb-glx.so.0.0.0 new file mode 100755 index 00000000..bb326b25 --- /dev/null +++ b/deps/built/linux/arm64/libxcb-glx.so.0.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a0d6bd7ac3affe767c538c7670637e805248c42d4ac0c7497d36acf9ee4da558 +size 369304 diff --git a/deps/built/linux/arm64/artifacts/libxcb-icccm.so b/deps/built/linux/arm64/libxcb-icccm.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-icccm.so rename to deps/built/linux/arm64/libxcb-icccm.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-icccm.so.4 b/deps/built/linux/arm64/libxcb-icccm.so.4 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-icccm.so.4 rename to deps/built/linux/arm64/libxcb-icccm.so.4 diff --git a/deps/built/linux/arm64/libxcb-icccm.so.4.0.0 b/deps/built/linux/arm64/libxcb-icccm.so.4.0.0 new file mode 100755 index 00000000..bb84c584 --- /dev/null +++ b/deps/built/linux/arm64/libxcb-icccm.so.4.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ba7678ef321688533fe6708b10d3459aaa9d3015229c82fb71f0c5efe7ec8d8 +size 67232 diff --git a/deps/built/linux/arm64/artifacts/libxcb-image.so b/deps/built/linux/arm64/libxcb-image.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-image.so rename to deps/built/linux/arm64/libxcb-image.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-image.so.0 b/deps/built/linux/arm64/libxcb-image.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-image.so.0 rename to deps/built/linux/arm64/libxcb-image.so.0 diff --git a/deps/built/linux/arm64/libxcb-image.so.0.0.0 b/deps/built/linux/arm64/libxcb-image.so.0.0.0 new file mode 100755 index 00000000..2f3f858b --- /dev/null +++ b/deps/built/linux/arm64/libxcb-image.so.0.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f3f31d39a83dd23b624111b98d8d825aab3267e31d11f18a4f7490cb9403e852 +size 59880 diff --git a/deps/built/linux/arm64/artifacts/libxcb-keysyms.so b/deps/built/linux/arm64/libxcb-keysyms.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-keysyms.so rename to deps/built/linux/arm64/libxcb-keysyms.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-keysyms.so.1 b/deps/built/linux/arm64/libxcb-keysyms.so.1 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-keysyms.so.1 rename to deps/built/linux/arm64/libxcb-keysyms.so.1 diff --git a/deps/built/linux/arm64/libxcb-keysyms.so.1.0.0 b/deps/built/linux/arm64/libxcb-keysyms.so.1.0.0 new file mode 100755 index 00000000..ad947a79 --- /dev/null +++ b/deps/built/linux/arm64/libxcb-keysyms.so.1.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c68760b73f2e9d284c30fe1607dc0c4a53a0e2c6a47158d98e1081a8b6a5ed9 +size 26832 diff --git a/deps/built/linux/arm64/artifacts/libxcb-present.so b/deps/built/linux/arm64/libxcb-present.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-present.so rename to deps/built/linux/arm64/libxcb-present.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-present.so.0 b/deps/built/linux/arm64/libxcb-present.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-present.so.0 rename to deps/built/linux/arm64/libxcb-present.so.0 diff --git a/deps/built/linux/arm64/libxcb-present.so.0.0.0 b/deps/built/linux/arm64/libxcb-present.so.0.0.0 new file mode 100755 index 00000000..c0d5b108 --- /dev/null +++ b/deps/built/linux/arm64/libxcb-present.so.0.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b65b4b38157340bc17c45773f2a263322b765e5b3e1c234dc92fcdae11e6cfb +size 30864 diff --git a/deps/built/linux/arm64/artifacts/libxcb-randr.so b/deps/built/linux/arm64/libxcb-randr.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-randr.so rename to deps/built/linux/arm64/libxcb-randr.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-randr.so.0 b/deps/built/linux/arm64/libxcb-randr.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-randr.so.0 rename to deps/built/linux/arm64/libxcb-randr.so.0 diff --git a/deps/built/linux/arm64/libxcb-randr.so.0.1.0 b/deps/built/linux/arm64/libxcb-randr.so.0.1.0 new file mode 100755 index 00000000..22bf3e38 --- /dev/null +++ b/deps/built/linux/arm64/libxcb-randr.so.0.1.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d3ea3320fdcd42b012546d6f7a12f8f5cf56d7726d9ba9a78c605f641003a2a3 +size 208560 diff --git a/deps/built/linux/arm64/artifacts/libxcb-record.so b/deps/built/linux/arm64/libxcb-record.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-record.so rename to deps/built/linux/arm64/libxcb-record.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-record.so.0 b/deps/built/linux/arm64/libxcb-record.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-record.so.0 rename to deps/built/linux/arm64/libxcb-record.so.0 diff --git a/deps/built/linux/arm64/libxcb-record.so.0.0.0 b/deps/built/linux/arm64/libxcb-record.so.0.0.0 new file mode 100755 index 00000000..1fded1df --- /dev/null +++ b/deps/built/linux/arm64/libxcb-record.so.0.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b36e65a1fe3c9d30d9ea3510e187ae6503003ef597eba4efe5632f0e8e1aacbe +size 49592 diff --git a/deps/built/linux/arm64/artifacts/libxcb-render-util.so b/deps/built/linux/arm64/libxcb-render-util.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-render-util.so rename to deps/built/linux/arm64/libxcb-render-util.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-render-util.so.0 b/deps/built/linux/arm64/libxcb-render-util.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-render-util.so.0 rename to deps/built/linux/arm64/libxcb-render-util.so.0 diff --git a/deps/built/linux/arm64/libxcb-render-util.so.0.0.0 b/deps/built/linux/arm64/libxcb-render-util.so.0.0.0 new file mode 100755 index 00000000..1cae68ea --- /dev/null +++ b/deps/built/linux/arm64/libxcb-render-util.so.0.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7e77ccd3f8c060d58d368d1941c0b5d8d2ec6bcbea144ca7eb96d7d96b076da7 +size 43592 diff --git a/deps/built/linux/arm64/artifacts/libxcb-render.so b/deps/built/linux/arm64/libxcb-render.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-render.so rename to deps/built/linux/arm64/libxcb-render.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-render.so.0 b/deps/built/linux/arm64/libxcb-render.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-render.so.0 rename to deps/built/linux/arm64/libxcb-render.so.0 diff --git a/deps/built/linux/arm64/libxcb-render.so.0.0.0 b/deps/built/linux/arm64/libxcb-render.so.0.0.0 new file mode 100755 index 00000000..d96a562d --- /dev/null +++ b/deps/built/linux/arm64/libxcb-render.so.0.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:72e88e8668807b29c6c0d78271987e6a1eec0d52b6a67437f3a35d40e6d6778f +size 170648 diff --git a/deps/built/linux/arm64/artifacts/libxcb-res.so b/deps/built/linux/arm64/libxcb-res.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-res.so rename to deps/built/linux/arm64/libxcb-res.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-res.so.0 b/deps/built/linux/arm64/libxcb-res.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-res.so.0 rename to deps/built/linux/arm64/libxcb-res.so.0 diff --git a/deps/built/linux/arm64/libxcb-res.so.0.0.0 b/deps/built/linux/arm64/libxcb-res.so.0.0.0 new file mode 100755 index 00000000..5a24c1db --- /dev/null +++ b/deps/built/linux/arm64/libxcb-res.so.0.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:929682d4a6a78f64aa3807fac22d64ce54ae968e0c1a657b080845c4605b5999 +size 44248 diff --git a/deps/built/linux/arm64/artifacts/libxcb-screensaver.so b/deps/built/linux/arm64/libxcb-screensaver.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-screensaver.so rename to deps/built/linux/arm64/libxcb-screensaver.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-screensaver.so.0 b/deps/built/linux/arm64/libxcb-screensaver.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-screensaver.so.0 rename to deps/built/linux/arm64/libxcb-screensaver.so.0 diff --git a/deps/built/linux/arm64/libxcb-screensaver.so.0.0.0 b/deps/built/linux/arm64/libxcb-screensaver.so.0.0.0 new file mode 100755 index 00000000..07d1bdcc --- /dev/null +++ b/deps/built/linux/arm64/libxcb-screensaver.so.0.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b762307d3c04c12c9f7cdb1dbac6ec3e2d7e0260fb74f605f6d780ab468c21f1 +size 36448 diff --git a/deps/built/linux/arm64/artifacts/libxcb-shape.so b/deps/built/linux/arm64/libxcb-shape.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-shape.so rename to deps/built/linux/arm64/libxcb-shape.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-shape.so.0 b/deps/built/linux/arm64/libxcb-shape.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-shape.so.0 rename to deps/built/linux/arm64/libxcb-shape.so.0 diff --git a/deps/built/linux/arm64/libxcb-shape.so.0.0.0 b/deps/built/linux/arm64/libxcb-shape.so.0.0.0 new file mode 100755 index 00000000..01a9c9db --- /dev/null +++ b/deps/built/linux/arm64/libxcb-shape.so.0.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d836d81dcd49038abffa82748c6fc520d7075a5c3d6ebf9acef5081efb7960df +size 36888 diff --git a/deps/built/linux/arm64/artifacts/libxcb-shm.so b/deps/built/linux/arm64/libxcb-shm.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-shm.so rename to deps/built/linux/arm64/libxcb-shm.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-shm.so.0 b/deps/built/linux/arm64/libxcb-shm.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-shm.so.0 rename to deps/built/linux/arm64/libxcb-shm.so.0 diff --git a/deps/built/linux/arm64/libxcb-shm.so.0.0.0 b/deps/built/linux/arm64/libxcb-shm.so.0.0.0 new file mode 100755 index 00000000..801a3e48 --- /dev/null +++ b/deps/built/linux/arm64/libxcb-shm.so.0.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:231f0fe626b443781b1f7e7af226b76edede6fdf086df1c608fdc44c0686c37b +size 33040 diff --git a/deps/built/linux/arm64/artifacts/libxcb-sync.so b/deps/built/linux/arm64/libxcb-sync.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-sync.so rename to deps/built/linux/arm64/libxcb-sync.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-sync.so.1 b/deps/built/linux/arm64/libxcb-sync.so.1 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-sync.so.1 rename to deps/built/linux/arm64/libxcb-sync.so.1 diff --git a/deps/built/linux/arm64/libxcb-sync.so.1.0.0 b/deps/built/linux/arm64/libxcb-sync.so.1.0.0 new file mode 100755 index 00000000..e15acf46 --- /dev/null +++ b/deps/built/linux/arm64/libxcb-sync.so.1.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:202bdea08e7a9852c41723ac2ad3992668f694c0ac648da1d99790565f104054 +size 79544 diff --git a/deps/built/linux/arm64/artifacts/libxcb-util.so b/deps/built/linux/arm64/libxcb-util.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-util.so rename to deps/built/linux/arm64/libxcb-util.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-util.so.1 b/deps/built/linux/arm64/libxcb-util.so.1 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-util.so.1 rename to deps/built/linux/arm64/libxcb-util.so.1 diff --git a/deps/built/linux/arm64/libxcb-util.so.1.0.0 b/deps/built/linux/arm64/libxcb-util.so.1.0.0 new file mode 100755 index 00000000..28439f28 --- /dev/null +++ b/deps/built/linux/arm64/libxcb-util.so.1.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f03cf947f0fe7bb6db1bc062cb6fe406ad40625bce54be35e054c9bcd6d452f1 +size 55888 diff --git a/deps/built/linux/arm64/artifacts/libxcb-xf86dri.so b/deps/built/linux/arm64/libxcb-xf86dri.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-xf86dri.so rename to deps/built/linux/arm64/libxcb-xf86dri.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-xf86dri.so.0 b/deps/built/linux/arm64/libxcb-xf86dri.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-xf86dri.so.0 rename to deps/built/linux/arm64/libxcb-xf86dri.so.0 diff --git a/deps/built/linux/arm64/libxcb-xf86dri.so.0.0.0 b/deps/built/linux/arm64/libxcb-xf86dri.so.0.0.0 new file mode 100755 index 00000000..a39469e0 --- /dev/null +++ b/deps/built/linux/arm64/libxcb-xf86dri.so.0.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6fc21c49142ce6e43124390d137decedf04ef158c688db582340f3cf85a7ee3e +size 48584 diff --git a/deps/built/linux/arm64/artifacts/libxcb-xfixes.so b/deps/built/linux/arm64/libxcb-xfixes.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-xfixes.so rename to deps/built/linux/arm64/libxcb-xfixes.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-xfixes.so.0 b/deps/built/linux/arm64/libxcb-xfixes.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-xfixes.so.0 rename to deps/built/linux/arm64/libxcb-xfixes.so.0 diff --git a/deps/built/linux/arm64/libxcb-xfixes.so.0.0.0 b/deps/built/linux/arm64/libxcb-xfixes.so.0.0.0 new file mode 100755 index 00000000..0f25e838 --- /dev/null +++ b/deps/built/linux/arm64/libxcb-xfixes.so.0.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a961e19dbc34e62646c4c1f7e1983873ef491a1602d8686440ab0cf793d8fc38 +size 93640 diff --git a/deps/built/linux/arm64/artifacts/libxcb-xinerama.so b/deps/built/linux/arm64/libxcb-xinerama.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-xinerama.so rename to deps/built/linux/arm64/libxcb-xinerama.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-xinerama.so.0 b/deps/built/linux/arm64/libxcb-xinerama.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-xinerama.so.0 rename to deps/built/linux/arm64/libxcb-xinerama.so.0 diff --git a/deps/built/linux/arm64/libxcb-xinerama.so.0.0.0 b/deps/built/linux/arm64/libxcb-xinerama.so.0.0.0 new file mode 100755 index 00000000..156bd5c4 --- /dev/null +++ b/deps/built/linux/arm64/libxcb-xinerama.so.0.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:40543245632296bd6cd4ca6a7ab74aceb9ae10169c7b7e9b3dcee26deee8f980 +size 28480 diff --git a/deps/built/linux/arm64/artifacts/libxcb-xinput.so b/deps/built/linux/arm64/libxcb-xinput.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-xinput.so rename to deps/built/linux/arm64/libxcb-xinput.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-xinput.so.0 b/deps/built/linux/arm64/libxcb-xinput.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-xinput.so.0 rename to deps/built/linux/arm64/libxcb-xinput.so.0 diff --git a/deps/built/linux/arm64/libxcb-xinput.so.0.1.0 b/deps/built/linux/arm64/libxcb-xinput.so.0.1.0 new file mode 100755 index 00000000..ccfed2e3 --- /dev/null +++ b/deps/built/linux/arm64/libxcb-xinput.so.0.1.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f928fc494ca3ed65ec3228403760678fa6f5068e64993fd5679b6a8b03e4289 +size 500328 diff --git a/deps/built/linux/arm64/artifacts/libxcb-xkb.so b/deps/built/linux/arm64/libxcb-xkb.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-xkb.so rename to deps/built/linux/arm64/libxcb-xkb.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-xkb.so.1 b/deps/built/linux/arm64/libxcb-xkb.so.1 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-xkb.so.1 rename to deps/built/linux/arm64/libxcb-xkb.so.1 diff --git a/deps/built/linux/arm64/libxcb-xkb.so.1.0.0 b/deps/built/linux/arm64/libxcb-xkb.so.1.0.0 new file mode 100755 index 00000000..d68c6b7b --- /dev/null +++ b/deps/built/linux/arm64/libxcb-xkb.so.1.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27a29bc95afb25b75daac598856a94bbc86d26a47331c22a450058e3e0b75908 +size 406368 diff --git a/deps/built/linux/arm64/artifacts/libxcb-xtest.so b/deps/built/linux/arm64/libxcb-xtest.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-xtest.so rename to deps/built/linux/arm64/libxcb-xtest.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-xtest.so.0 b/deps/built/linux/arm64/libxcb-xtest.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-xtest.so.0 rename to deps/built/linux/arm64/libxcb-xtest.so.0 diff --git a/deps/built/linux/arm64/libxcb-xtest.so.0.0.0 b/deps/built/linux/arm64/libxcb-xtest.so.0.0.0 new file mode 100755 index 00000000..fa1bb520 --- /dev/null +++ b/deps/built/linux/arm64/libxcb-xtest.so.0.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:85b56e121fd4032788bfa5ddca7b03ca680a52c4275d5a95bcd7681469621b02 +size 23160 diff --git a/deps/built/linux/arm64/artifacts/libxcb-xv.so b/deps/built/linux/arm64/libxcb-xv.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-xv.so rename to deps/built/linux/arm64/libxcb-xv.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-xv.so.0 b/deps/built/linux/arm64/libxcb-xv.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-xv.so.0 rename to deps/built/linux/arm64/libxcb-xv.so.0 diff --git a/deps/built/linux/arm64/libxcb-xv.so.0.0.0 b/deps/built/linux/arm64/libxcb-xv.so.0.0.0 new file mode 100755 index 00000000..6f774346 --- /dev/null +++ b/deps/built/linux/arm64/libxcb-xv.so.0.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e30a56f595563fbb2b3581e0a579543eca0d878180297f10b94ae2bc62a7ab62 +size 91912 diff --git a/deps/built/linux/arm64/artifacts/libxcb-xvmc.so b/deps/built/linux/arm64/libxcb-xvmc.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-xvmc.so rename to deps/built/linux/arm64/libxcb-xvmc.so diff --git a/deps/built/linux/arm64/artifacts/libxcb-xvmc.so.0 b/deps/built/linux/arm64/libxcb-xvmc.so.0 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb-xvmc.so.0 rename to deps/built/linux/arm64/libxcb-xvmc.so.0 diff --git a/deps/built/linux/arm64/libxcb-xvmc.so.0.0.0 b/deps/built/linux/arm64/libxcb-xvmc.so.0.0.0 new file mode 100755 index 00000000..4e1fd0e1 --- /dev/null +++ b/deps/built/linux/arm64/libxcb-xvmc.so.0.0.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15244a86cd6f74b482a38f60a1756075b7773fd0f76e992b451738242541362e +size 45984 diff --git a/deps/built/linux/arm64/artifacts/libxcb.so b/deps/built/linux/arm64/libxcb.so similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb.so rename to deps/built/linux/arm64/libxcb.so diff --git a/deps/built/linux/arm64/artifacts/libxcb.so.1 b/deps/built/linux/arm64/libxcb.so.1 similarity index 100% rename from deps/built/linux/arm64/artifacts/libxcb.so.1 rename to deps/built/linux/arm64/libxcb.so.1 diff --git a/deps/built/linux/arm64/libxcb.so.1.1.0 b/deps/built/linux/arm64/libxcb.so.1.1.0 new file mode 100755 index 00000000..5d737b43 --- /dev/null +++ b/deps/built/linux/arm64/libxcb.so.1.1.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7e0d43029c2c1806f8c3d3bbfdb40129db3837412e6f2bc813e1f960ea5a172a +size 620528 diff --git a/deps/built/linux/arm64/pia-hnsd b/deps/built/linux/arm64/pia-hnsd index 116f033d..ae715061 100755 Binary files a/deps/built/linux/arm64/pia-hnsd and b/deps/built/linux/arm64/pia-hnsd differ diff --git a/deps/built/linux/arm64/pia-ss-local b/deps/built/linux/arm64/pia-ss-local index b8b9c706..7f1842ec 100755 Binary files a/deps/built/linux/arm64/pia-ss-local and b/deps/built/linux/arm64/pia-ss-local differ diff --git a/deps/built/linux/arm64/pia-unbound b/deps/built/linux/arm64/pia-unbound index dd8545e2..ee38631f 100755 Binary files a/deps/built/linux/arm64/pia-unbound and b/deps/built/linux/arm64/pia-unbound differ diff --git a/deps/built/linux/arm64/pia-wireguard-go b/deps/built/linux/arm64/pia-wireguard-go index 1a814c46..18f12228 100755 Binary files a/deps/built/linux/arm64/pia-wireguard-go and b/deps/built/linux/arm64/pia-wireguard-go differ diff --git a/deps/built/linux/armhf/libcrypto.so.1.1 b/deps/built/linux/armhf/libcrypto.so.1.1 index 4dc8c4e6..5a032267 100755 Binary files a/deps/built/linux/armhf/libcrypto.so.1.1 and b/deps/built/linux/armhf/libcrypto.so.1.1 differ diff --git a/deps/built/linux/armhf/libssl.so.1.1 b/deps/built/linux/armhf/libssl.so.1.1 index 6ac6ac57..c7aa16a0 100755 Binary files a/deps/built/linux/armhf/libssl.so.1.1 and b/deps/built/linux/armhf/libssl.so.1.1 differ diff --git a/deps/built/linux/armhf/libxcb-composite.so.0.0.0 b/deps/built/linux/armhf/libxcb-composite.so.0.0.0 index 5aef1abc..4dbc7e6f 100755 Binary files a/deps/built/linux/armhf/libxcb-composite.so.0.0.0 and b/deps/built/linux/armhf/libxcb-composite.so.0.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-damage.so.0.0.0 b/deps/built/linux/armhf/libxcb-damage.so.0.0.0 index b2ca60c9..18de2343 100755 Binary files a/deps/built/linux/armhf/libxcb-damage.so.0.0.0 and b/deps/built/linux/armhf/libxcb-damage.so.0.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-dpms.so.0.0.0 b/deps/built/linux/armhf/libxcb-dpms.so.0.0.0 index 13f5769a..46f45055 100755 Binary files a/deps/built/linux/armhf/libxcb-dpms.so.0.0.0 and b/deps/built/linux/armhf/libxcb-dpms.so.0.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-dri2.so.0.0.0 b/deps/built/linux/armhf/libxcb-dri2.so.0.0.0 index d323cb79..b96acba6 100755 Binary files a/deps/built/linux/armhf/libxcb-dri2.so.0.0.0 and b/deps/built/linux/armhf/libxcb-dri2.so.0.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-dri3.so.0.0.0 b/deps/built/linux/armhf/libxcb-dri3.so.0.0.0 index 4081555e..6ef7737f 100755 Binary files a/deps/built/linux/armhf/libxcb-dri3.so.0.0.0 and b/deps/built/linux/armhf/libxcb-dri3.so.0.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-ewmh.so.2.0.0 b/deps/built/linux/armhf/libxcb-ewmh.so.2.0.0 index 82f6a3d6..336bd1a1 100755 Binary files a/deps/built/linux/armhf/libxcb-ewmh.so.2.0.0 and b/deps/built/linux/armhf/libxcb-ewmh.so.2.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-glx.so.0.0.0 b/deps/built/linux/armhf/libxcb-glx.so.0.0.0 index 60d1b5e9..7f40b608 100755 Binary files a/deps/built/linux/armhf/libxcb-glx.so.0.0.0 and b/deps/built/linux/armhf/libxcb-glx.so.0.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-icccm.so.4.0.0 b/deps/built/linux/armhf/libxcb-icccm.so.4.0.0 index 00170727..9c10db1d 100755 Binary files a/deps/built/linux/armhf/libxcb-icccm.so.4.0.0 and b/deps/built/linux/armhf/libxcb-icccm.so.4.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-image.so.0.0.0 b/deps/built/linux/armhf/libxcb-image.so.0.0.0 index 482bfefe..0cfeebbb 100755 Binary files a/deps/built/linux/armhf/libxcb-image.so.0.0.0 and b/deps/built/linux/armhf/libxcb-image.so.0.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-keysyms.so.1.0.0 b/deps/built/linux/armhf/libxcb-keysyms.so.1.0.0 index 50951709..01a6bd38 100755 Binary files a/deps/built/linux/armhf/libxcb-keysyms.so.1.0.0 and b/deps/built/linux/armhf/libxcb-keysyms.so.1.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-present.so.0.0.0 b/deps/built/linux/armhf/libxcb-present.so.0.0.0 index c8abaaae..8ffd64d7 100755 Binary files a/deps/built/linux/armhf/libxcb-present.so.0.0.0 and b/deps/built/linux/armhf/libxcb-present.so.0.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-randr.so.0.1.0 b/deps/built/linux/armhf/libxcb-randr.so.0.1.0 index 71328184..89cde356 100755 Binary files a/deps/built/linux/armhf/libxcb-randr.so.0.1.0 and b/deps/built/linux/armhf/libxcb-randr.so.0.1.0 differ diff --git a/deps/built/linux/armhf/libxcb-record.so.0.0.0 b/deps/built/linux/armhf/libxcb-record.so.0.0.0 index 63691754..a1f65bd0 100755 Binary files a/deps/built/linux/armhf/libxcb-record.so.0.0.0 and b/deps/built/linux/armhf/libxcb-record.so.0.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-render-util.so.0.0.0 b/deps/built/linux/armhf/libxcb-render-util.so.0.0.0 index 2369c6e9..de368a90 100755 Binary files a/deps/built/linux/armhf/libxcb-render-util.so.0.0.0 and b/deps/built/linux/armhf/libxcb-render-util.so.0.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-render.so.0.0.0 b/deps/built/linux/armhf/libxcb-render.so.0.0.0 index 4a5698c3..09f41e4d 100755 Binary files a/deps/built/linux/armhf/libxcb-render.so.0.0.0 and b/deps/built/linux/armhf/libxcb-render.so.0.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-res.so.0.0.0 b/deps/built/linux/armhf/libxcb-res.so.0.0.0 index cc521342..c969a4aa 100755 Binary files a/deps/built/linux/armhf/libxcb-res.so.0.0.0 and b/deps/built/linux/armhf/libxcb-res.so.0.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-screensaver.so.0.0.0 b/deps/built/linux/armhf/libxcb-screensaver.so.0.0.0 index 06ddd34f..d65a6fa0 100755 Binary files a/deps/built/linux/armhf/libxcb-screensaver.so.0.0.0 and b/deps/built/linux/armhf/libxcb-screensaver.so.0.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-shape.so.0.0.0 b/deps/built/linux/armhf/libxcb-shape.so.0.0.0 index e3d183c0..5e7c7f23 100755 Binary files a/deps/built/linux/armhf/libxcb-shape.so.0.0.0 and b/deps/built/linux/armhf/libxcb-shape.so.0.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-shm.so.0.0.0 b/deps/built/linux/armhf/libxcb-shm.so.0.0.0 index 6f4e4872..73dc62ac 100755 Binary files a/deps/built/linux/armhf/libxcb-shm.so.0.0.0 and b/deps/built/linux/armhf/libxcb-shm.so.0.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-sync.so.1.0.0 b/deps/built/linux/armhf/libxcb-sync.so.1.0.0 index d88178e6..d22ab7f4 100755 Binary files a/deps/built/linux/armhf/libxcb-sync.so.1.0.0 and b/deps/built/linux/armhf/libxcb-sync.so.1.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-util.so.1.0.0 b/deps/built/linux/armhf/libxcb-util.so.1.0.0 index d39a494b..6d0474da 100755 Binary files a/deps/built/linux/armhf/libxcb-util.so.1.0.0 and b/deps/built/linux/armhf/libxcb-util.so.1.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-xf86dri.so.0.0.0 b/deps/built/linux/armhf/libxcb-xf86dri.so.0.0.0 index 767720cf..9065e26c 100755 Binary files a/deps/built/linux/armhf/libxcb-xf86dri.so.0.0.0 and b/deps/built/linux/armhf/libxcb-xf86dri.so.0.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-xfixes.so.0.0.0 b/deps/built/linux/armhf/libxcb-xfixes.so.0.0.0 index 77cc5ffc..72712753 100755 Binary files a/deps/built/linux/armhf/libxcb-xfixes.so.0.0.0 and b/deps/built/linux/armhf/libxcb-xfixes.so.0.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-xinerama.so.0.0.0 b/deps/built/linux/armhf/libxcb-xinerama.so.0.0.0 index 36c5f7db..03c27f83 100755 Binary files a/deps/built/linux/armhf/libxcb-xinerama.so.0.0.0 and b/deps/built/linux/armhf/libxcb-xinerama.so.0.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-xinput.so.0.1.0 b/deps/built/linux/armhf/libxcb-xinput.so.0.1.0 index ec2dff1b..1480a0d1 100755 Binary files a/deps/built/linux/armhf/libxcb-xinput.so.0.1.0 and b/deps/built/linux/armhf/libxcb-xinput.so.0.1.0 differ diff --git a/deps/built/linux/armhf/libxcb-xkb.so.1.0.0 b/deps/built/linux/armhf/libxcb-xkb.so.1.0.0 index abc7b697..ed4f8728 100755 Binary files a/deps/built/linux/armhf/libxcb-xkb.so.1.0.0 and b/deps/built/linux/armhf/libxcb-xkb.so.1.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-xtest.so.0.0.0 b/deps/built/linux/armhf/libxcb-xtest.so.0.0.0 index be0e18e2..9fb91c1e 100755 Binary files a/deps/built/linux/armhf/libxcb-xtest.so.0.0.0 and b/deps/built/linux/armhf/libxcb-xtest.so.0.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-xv.so.0.0.0 b/deps/built/linux/armhf/libxcb-xv.so.0.0.0 index 2029d3c0..c8cf5aba 100755 Binary files a/deps/built/linux/armhf/libxcb-xv.so.0.0.0 and b/deps/built/linux/armhf/libxcb-xv.so.0.0.0 differ diff --git a/deps/built/linux/armhf/libxcb-xvmc.so.0.0.0 b/deps/built/linux/armhf/libxcb-xvmc.so.0.0.0 index b03e2311..e220241c 100755 Binary files a/deps/built/linux/armhf/libxcb-xvmc.so.0.0.0 and b/deps/built/linux/armhf/libxcb-xvmc.so.0.0.0 differ diff --git a/deps/built/linux/armhf/libxcb.so.1.1.0 b/deps/built/linux/armhf/libxcb.so.1.1.0 index 2baacf4b..9a8c96e8 100755 Binary files a/deps/built/linux/armhf/libxcb.so.1.1.0 and b/deps/built/linux/armhf/libxcb.so.1.1.0 differ diff --git a/deps/built/linux/armhf/pia-hnsd b/deps/built/linux/armhf/pia-hnsd index afa4dccf..c8f7acc0 100755 Binary files a/deps/built/linux/armhf/pia-hnsd and b/deps/built/linux/armhf/pia-hnsd differ diff --git a/deps/built/linux/armhf/pia-ss-local b/deps/built/linux/armhf/pia-ss-local index 0906d9c9..fb8b6119 100755 Binary files a/deps/built/linux/armhf/pia-ss-local and b/deps/built/linux/armhf/pia-ss-local differ diff --git a/deps/built/linux/armhf/pia-unbound b/deps/built/linux/armhf/pia-unbound index 580cd225..b8e5d7ec 100755 Binary files a/deps/built/linux/armhf/pia-unbound and b/deps/built/linux/armhf/pia-unbound differ diff --git a/deps/built/linux/armhf/pia-wireguard-go b/deps/built/linux/armhf/pia-wireguard-go index 22c44585..d4757820 100755 Binary files a/deps/built/linux/armhf/pia-wireguard-go and b/deps/built/linux/armhf/pia-wireguard-go differ diff --git a/deps/built/linux/x86_64/libcrypto.so.1.1 b/deps/built/linux/x86_64/libcrypto.so.1.1 index 13773adc..1332a5d7 100755 Binary files a/deps/built/linux/x86_64/libcrypto.so.1.1 and b/deps/built/linux/x86_64/libcrypto.so.1.1 differ diff --git a/deps/built/linux/x86_64/libssl.so.1.1 b/deps/built/linux/x86_64/libssl.so.1.1 index 51c0a379..f9ae4d72 100755 Binary files a/deps/built/linux/x86_64/libssl.so.1.1 and b/deps/built/linux/x86_64/libssl.so.1.1 differ diff --git a/deps/built/linux/x86_64/libxcb-composite.so.0.0.0 b/deps/built/linux/x86_64/libxcb-composite.so.0.0.0 index 80bcf384..f079b77b 100755 Binary files a/deps/built/linux/x86_64/libxcb-composite.so.0.0.0 and b/deps/built/linux/x86_64/libxcb-composite.so.0.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-damage.so.0.0.0 b/deps/built/linux/x86_64/libxcb-damage.so.0.0.0 index b929fc5c..d1b50ac4 100755 Binary files a/deps/built/linux/x86_64/libxcb-damage.so.0.0.0 and b/deps/built/linux/x86_64/libxcb-damage.so.0.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-dpms.so.0.0.0 b/deps/built/linux/x86_64/libxcb-dpms.so.0.0.0 index 213f8d33..583a8152 100755 Binary files a/deps/built/linux/x86_64/libxcb-dpms.so.0.0.0 and b/deps/built/linux/x86_64/libxcb-dpms.so.0.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-dri2.so.0.0.0 b/deps/built/linux/x86_64/libxcb-dri2.so.0.0.0 index 06441a25..781b72b0 100755 Binary files a/deps/built/linux/x86_64/libxcb-dri2.so.0.0.0 and b/deps/built/linux/x86_64/libxcb-dri2.so.0.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-dri3.so.0.0.0 b/deps/built/linux/x86_64/libxcb-dri3.so.0.0.0 index 5d228310..362ceed5 100755 Binary files a/deps/built/linux/x86_64/libxcb-dri3.so.0.0.0 and b/deps/built/linux/x86_64/libxcb-dri3.so.0.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-ewmh.so.2.0.0 b/deps/built/linux/x86_64/libxcb-ewmh.so.2.0.0 index 891fe761..718ecbd9 100755 Binary files a/deps/built/linux/x86_64/libxcb-ewmh.so.2.0.0 and b/deps/built/linux/x86_64/libxcb-ewmh.so.2.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-glx.so.0.0.0 b/deps/built/linux/x86_64/libxcb-glx.so.0.0.0 index aa0629d7..4491ff77 100755 Binary files a/deps/built/linux/x86_64/libxcb-glx.so.0.0.0 and b/deps/built/linux/x86_64/libxcb-glx.so.0.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-icccm.so.4.0.0 b/deps/built/linux/x86_64/libxcb-icccm.so.4.0.0 index 6dcd14a6..71890c8b 100755 Binary files a/deps/built/linux/x86_64/libxcb-icccm.so.4.0.0 and b/deps/built/linux/x86_64/libxcb-icccm.so.4.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-image.so.0.0.0 b/deps/built/linux/x86_64/libxcb-image.so.0.0.0 index 67ad2c09..0346a596 100755 Binary files a/deps/built/linux/x86_64/libxcb-image.so.0.0.0 and b/deps/built/linux/x86_64/libxcb-image.so.0.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-keysyms.so.1.0.0 b/deps/built/linux/x86_64/libxcb-keysyms.so.1.0.0 index 74caaa65..ef3b43f7 100755 Binary files a/deps/built/linux/x86_64/libxcb-keysyms.so.1.0.0 and b/deps/built/linux/x86_64/libxcb-keysyms.so.1.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-present.so.0.0.0 b/deps/built/linux/x86_64/libxcb-present.so.0.0.0 index 92587dab..8e241f81 100755 Binary files a/deps/built/linux/x86_64/libxcb-present.so.0.0.0 and b/deps/built/linux/x86_64/libxcb-present.so.0.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-randr.so.0.1.0 b/deps/built/linux/x86_64/libxcb-randr.so.0.1.0 index 04dad38f..4e58da12 100755 Binary files a/deps/built/linux/x86_64/libxcb-randr.so.0.1.0 and b/deps/built/linux/x86_64/libxcb-randr.so.0.1.0 differ diff --git a/deps/built/linux/x86_64/libxcb-record.so.0.0.0 b/deps/built/linux/x86_64/libxcb-record.so.0.0.0 index 490acb4d..7be3bd37 100755 Binary files a/deps/built/linux/x86_64/libxcb-record.so.0.0.0 and b/deps/built/linux/x86_64/libxcb-record.so.0.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-render-util.so.0.0.0 b/deps/built/linux/x86_64/libxcb-render-util.so.0.0.0 index 954f69eb..03815ed5 100755 Binary files a/deps/built/linux/x86_64/libxcb-render-util.so.0.0.0 and b/deps/built/linux/x86_64/libxcb-render-util.so.0.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-render.so.0.0.0 b/deps/built/linux/x86_64/libxcb-render.so.0.0.0 index 8dcf9f09..d3ab92e0 100755 Binary files a/deps/built/linux/x86_64/libxcb-render.so.0.0.0 and b/deps/built/linux/x86_64/libxcb-render.so.0.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-res.so.0.0.0 b/deps/built/linux/x86_64/libxcb-res.so.0.0.0 index 647f7434..23c6c877 100755 Binary files a/deps/built/linux/x86_64/libxcb-res.so.0.0.0 and b/deps/built/linux/x86_64/libxcb-res.so.0.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-screensaver.so.0.0.0 b/deps/built/linux/x86_64/libxcb-screensaver.so.0.0.0 index 4696bad1..3854ea97 100755 Binary files a/deps/built/linux/x86_64/libxcb-screensaver.so.0.0.0 and b/deps/built/linux/x86_64/libxcb-screensaver.so.0.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-shape.so.0.0.0 b/deps/built/linux/x86_64/libxcb-shape.so.0.0.0 index ead6d3c5..3424f3a0 100755 Binary files a/deps/built/linux/x86_64/libxcb-shape.so.0.0.0 and b/deps/built/linux/x86_64/libxcb-shape.so.0.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-shm.so.0.0.0 b/deps/built/linux/x86_64/libxcb-shm.so.0.0.0 index 7e54431b..463dce05 100755 Binary files a/deps/built/linux/x86_64/libxcb-shm.so.0.0.0 and b/deps/built/linux/x86_64/libxcb-shm.so.0.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-sync.so.1.0.0 b/deps/built/linux/x86_64/libxcb-sync.so.1.0.0 index 22737d31..591aaf9d 100755 Binary files a/deps/built/linux/x86_64/libxcb-sync.so.1.0.0 and b/deps/built/linux/x86_64/libxcb-sync.so.1.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-util.so.1.0.0 b/deps/built/linux/x86_64/libxcb-util.so.1.0.0 index e2de8aac..53a7f8a9 100755 Binary files a/deps/built/linux/x86_64/libxcb-util.so.1.0.0 and b/deps/built/linux/x86_64/libxcb-util.so.1.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-xf86dri.so.0.0.0 b/deps/built/linux/x86_64/libxcb-xf86dri.so.0.0.0 index 67f7dd1a..be2f560a 100755 Binary files a/deps/built/linux/x86_64/libxcb-xf86dri.so.0.0.0 and b/deps/built/linux/x86_64/libxcb-xf86dri.so.0.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-xfixes.so.0.0.0 b/deps/built/linux/x86_64/libxcb-xfixes.so.0.0.0 index 74fe5953..39a70816 100755 Binary files a/deps/built/linux/x86_64/libxcb-xfixes.so.0.0.0 and b/deps/built/linux/x86_64/libxcb-xfixes.so.0.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-xinerama.so.0.0.0 b/deps/built/linux/x86_64/libxcb-xinerama.so.0.0.0 index bc570bc9..1fee4598 100755 Binary files a/deps/built/linux/x86_64/libxcb-xinerama.so.0.0.0 and b/deps/built/linux/x86_64/libxcb-xinerama.so.0.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-xinput.so.0.1.0 b/deps/built/linux/x86_64/libxcb-xinput.so.0.1.0 index d0fd1914..a8a48928 100755 Binary files a/deps/built/linux/x86_64/libxcb-xinput.so.0.1.0 and b/deps/built/linux/x86_64/libxcb-xinput.so.0.1.0 differ diff --git a/deps/built/linux/x86_64/libxcb-xkb.so.1.0.0 b/deps/built/linux/x86_64/libxcb-xkb.so.1.0.0 index ad07f33c..41def31e 100755 Binary files a/deps/built/linux/x86_64/libxcb-xkb.so.1.0.0 and b/deps/built/linux/x86_64/libxcb-xkb.so.1.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-xtest.so.0.0.0 b/deps/built/linux/x86_64/libxcb-xtest.so.0.0.0 index db8307fa..b3e13f57 100755 Binary files a/deps/built/linux/x86_64/libxcb-xtest.so.0.0.0 and b/deps/built/linux/x86_64/libxcb-xtest.so.0.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-xv.so.0.0.0 b/deps/built/linux/x86_64/libxcb-xv.so.0.0.0 index b75f7a9f..e7350d7d 100755 Binary files a/deps/built/linux/x86_64/libxcb-xv.so.0.0.0 and b/deps/built/linux/x86_64/libxcb-xv.so.0.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb-xvmc.so.0.0.0 b/deps/built/linux/x86_64/libxcb-xvmc.so.0.0.0 index fb7ec72e..2c793fec 100755 Binary files a/deps/built/linux/x86_64/libxcb-xvmc.so.0.0.0 and b/deps/built/linux/x86_64/libxcb-xvmc.so.0.0.0 differ diff --git a/deps/built/linux/x86_64/libxcb.so.1.1.0 b/deps/built/linux/x86_64/libxcb.so.1.1.0 index a2d93011..3085995d 100755 Binary files a/deps/built/linux/x86_64/libxcb.so.1.1.0 and b/deps/built/linux/x86_64/libxcb.so.1.1.0 differ diff --git a/deps/built/linux/x86_64/pia-hnsd b/deps/built/linux/x86_64/pia-hnsd index bb31a9e0..b9e402f8 100755 Binary files a/deps/built/linux/x86_64/pia-hnsd and b/deps/built/linux/x86_64/pia-hnsd differ diff --git a/deps/built/linux/x86_64/pia-ss-local b/deps/built/linux/x86_64/pia-ss-local index 6fa2190c..8dd74f71 100755 Binary files a/deps/built/linux/x86_64/pia-ss-local and b/deps/built/linux/x86_64/pia-ss-local differ diff --git a/deps/built/linux/x86_64/pia-unbound b/deps/built/linux/x86_64/pia-unbound index 89865f33..db979fb1 100755 Binary files a/deps/built/linux/x86_64/pia-unbound and b/deps/built/linux/x86_64/pia-unbound differ diff --git a/deps/built/linux/x86_64/pia-wireguard-go b/deps/built/linux/x86_64/pia-wireguard-go index db5e77b9..d14d01d5 100755 Binary files a/deps/built/linux/x86_64/pia-wireguard-go and b/deps/built/linux/x86_64/pia-wireguard-go differ diff --git a/deps/built/mac/x86_64/libcrypto.1.1.dylib b/deps/built/mac/x86_64/libcrypto.1.1.dylib index 23d34fae..d98299b5 100755 Binary files a/deps/built/mac/x86_64/libcrypto.1.1.dylib and b/deps/built/mac/x86_64/libcrypto.1.1.dylib differ diff --git a/deps/built/mac/x86_64/libssl.1.1.dylib b/deps/built/mac/x86_64/libssl.1.1.dylib index e312b44c..27aa6ac4 100755 Binary files a/deps/built/mac/x86_64/libssl.1.1.dylib and b/deps/built/mac/x86_64/libssl.1.1.dylib differ diff --git a/deps/built/mac/x86_64/pia-hnsd b/deps/built/mac/x86_64/pia-hnsd index 0a7c486c..b04dfcca 100755 Binary files a/deps/built/mac/x86_64/pia-hnsd and b/deps/built/mac/x86_64/pia-hnsd differ diff --git a/deps/built/mac/x86_64/pia-ss-local b/deps/built/mac/x86_64/pia-ss-local index fb097b29..e7719f2f 100755 Binary files a/deps/built/mac/x86_64/pia-ss-local and b/deps/built/mac/x86_64/pia-ss-local differ diff --git a/deps/built/mac/x86_64/pia-unbound b/deps/built/mac/x86_64/pia-unbound index c5ab6aa1..6960e2c7 100755 Binary files a/deps/built/mac/x86_64/pia-unbound and b/deps/built/mac/x86_64/pia-unbound differ diff --git a/deps/built/mac/x86_64/pia-wireguard-go b/deps/built/mac/x86_64/pia-wireguard-go index ebe2cf20..be154ba8 100755 Binary files a/deps/built/mac/x86_64/pia-wireguard-go and b/deps/built/mac/x86_64/pia-wireguard-go differ diff --git a/deps/dump_syms/dump_syms_mac b/deps/dump_syms/dump_syms_mac index b77a849b..6956265f 100755 Binary files a/deps/dump_syms/dump_syms_mac and b/deps/dump_syms/dump_syms_mac differ diff --git a/extras/icons/pia.icns b/extras/icons/pia.icns index e052f551..584b3ce4 100644 Binary files a/extras/icons/pia.icns and b/extras/icons/pia.icns differ diff --git a/extras/installer/linux/installfiles/run-in-terminal.sh b/extras/installer/linux/installfiles/run-in-terminal.sh index 0bf3c591..6cadcd57 100644 --- a/extras/installer/linux/installfiles/run-in-terminal.sh +++ b/extras/installer/linux/installfiles/run-in-terminal.sh @@ -28,6 +28,8 @@ elif [ -x /usr/bin/konsole ]; then TERMCMD=/usr/bin/konsole elif [ -x /usr/bin/xterm ]; then TERMCMD=/usr/bin/xterm +elif [ -x /usr/bin/xfce4-terminal ]; then + TERMCMD=/usr/bin/xfce4-terminal else exit 3 fi diff --git a/extras/installer/linux/linux_installer.sh b/extras/installer/linux/linux_installer.sh index de92a9a8..e8b12c1c 100755 --- a/extras/installer/linux/linux_installer.sh +++ b/extras/installer/linux/linux_installer.sh @@ -225,7 +225,7 @@ function installDependencies() { function addGroups() { for group in "$@"; do - if ! grep -q $group /etc/group; then + if ! getent group $group >/dev/null 2>&1; then sudo groupadd $group || true echoPass "Added group $group" fi @@ -294,7 +294,9 @@ function installPia() { fi sudo cp "$root/installfiles/piavpn.desktop" "/usr/share/applications/${brandCode}vpn.desktop" if hash update-desktop-database 2>/dev/null; then - sudo update-desktop-database + # Silence output from update-desktop-database; it dumps errors for _any_ + # quirks in installed .desktop files, not just ours. + sudo update-desktop-database 2>/dev/null fi echoPass "Created desktop entry" diff --git a/extras/support-tool/components/FileList.qml b/extras/support-tool/components/FileList.qml index 51ba5320..bec80df3 100644 --- a/extras/support-tool/components/FileList.qml +++ b/extras/support-tool/components/FileList.qml @@ -24,8 +24,9 @@ import PIA.ReportHelper 1.0 import QtQuick.Dialogs 1.3 Item { - height: 320 - ColumnLayout { + height: fileListLayout.height + Column { + id: fileListLayout width: parent.width spacing: 0 @@ -55,7 +56,7 @@ Item { Rectangle { border.color: "#889099" height: 30 - Layout.fillWidth: true + width: parent.width Text { anchors.left: parent.left anchors.top: parent.top @@ -86,7 +87,7 @@ Item { Rectangle { id: fileListContainer opacity: 0.8 - Layout.fillWidth: true + width: parent.width height: fileList.height border.color: "#d7d8d9" ListView { diff --git a/extras/support-tool/components/PayloadInfo.qml b/extras/support-tool/components/PayloadInfo.qml index 9f8a868d..fc8d892a 100644 --- a/extras/support-tool/components/PayloadInfo.qml +++ b/extras/support-tool/components/PayloadInfo.qml @@ -28,30 +28,34 @@ Item { anchors.margins: 2 anchors.rightMargin: 10 contentWidth: width + contentHeight: infoContents.height clip: true + Flickable { + anchors.fill: parent + // Disable bouncy overscroll, which is a bizarre default for desktop + // platforms + boundsBehavior: Flickable.StopAtBounds - ColumnLayout { - id: infoContents - width: parent.width - Text { - visible: params.mode == "logs" - text: "

" + ReportHelper.getBrandParam("brandName") + " will upload debug logs and some information about your network setup. This information doesn't contain any information about your internet usage.

" - wrapMode: Text.Wrap - Layout.fillWidth: true - Layout.topMargin: 10 - } - Text { - visible: params.mode == "crash" - text: "

Please re-start the application. If problem persists, please contact support.


If you have a killswitch enabled your internet might not be accessible. Restarting the application should correct this.

" - wrapMode: Text.Wrap - Layout.fillWidth: true - Layout.topMargin: 10 - } - FileList { - Layout.rightMargin: 20 - Layout.topMargin: 10 - Layout.fillWidth: true + Column { + id: infoContents + width: parent.width - 20 + spacing: 10 + Text { + visible: params.mode == "logs" + text: "

" + ReportHelper.getBrandParam("brandName") + " will upload debug logs and some information about your network setup. This information doesn't contain any information about your internet usage.

" + wrapMode: Text.Wrap + width: parent.width + } + Text { + visible: params.mode == "crash" + text: "

Please re-start the application. If problem persists, please contact support.


If you have a killswitch enabled your internet might not be accessible. Restarting the application should correct this.

" + wrapMode: Text.Wrap + width: parent.width + } + FileList { + width: parent.width + } } } } diff --git a/extras/support-tool/payloadbuilder.cpp b/extras/support-tool/payloadbuilder.cpp index 86460959..8a14aabd 100644 --- a/extras/support-tool/payloadbuilder.cpp +++ b/extras/support-tool/payloadbuilder.cpp @@ -140,27 +140,15 @@ void PayloadBuilder::addFile(const QString &fullPath) void PayloadBuilder::addClientDumpFile(const QString &fullPath) { QFileInfo fi(fullPath); - // For the dump file, there's no way to tell exactly when the crash happened - // from the dmp file itself. - // Instead of including a separate manifest containing this meta data, for now - // we can re-name the file to include the timestamp - - // - // Also, some platforms (like Linux) doesn't always support timestamps on the - // filesystems. So we include a random string in the filename so - // there's always a valid filename. The first 5 characters of the filename - // should be good enough - addFileToPayload(fullPath, QStringLiteral("client-crash/%1-%2.dmp") - .arg(fi.fileName().left(5)) - .arg(fi.birthTime().toString(QStringLiteral("yyyy-MM-dd_HH-mm-ss")))); + addFileToPayload(fullPath, QStringLiteral("client-crash/%1.dmp") + .arg(fi.fileName())); } void PayloadBuilder::addDaemonDumpFile(const QString &fullPath) { QFileInfo fi(fullPath); - addFileToPayload(fullPath, QStringLiteral("daemon-crash/%1-%2.dmp") - .arg(fi.fileName().left(5)) - .arg(fi.birthTime().toString(QStringLiteral("yyyy-MM-dd_HH-mm-ss")))); + addFileToPayload(fullPath, QStringLiteral("daemon-crash/%1.dmp") + .arg(fi.fileName())); } void PayloadBuilder::addFileToPayload(const QString &sourcePath, const QString &targetName) diff --git a/extras/support-tool/support_tool_main.cpp b/extras/support-tool/support_tool_main.cpp index 0957b22e..e848f32f 100644 --- a/extras/support-tool/support_tool_main.cpp +++ b/extras/support-tool/support_tool_main.cpp @@ -97,7 +97,7 @@ int main(int argc, char *argv[]) Path::initializePostApp(); parser.process(app); - AppSingleton runGuard(Path::SupportToolExecutable); + AppSingleton runGuard; if(runGuard.isAnotherInstanceRunning() > 0) { qWarning () << "Exiting because another instance appears to be running"; app.quit(); diff --git a/graphics/alert-icons.zip b/graphics/alert-icons.zip index 16af91be..6e6dde05 100644 Binary files a/graphics/alert-icons.zip and b/graphics/alert-icons.zip differ diff --git a/graphics/geo_icons/icons(1).zip b/graphics/geo_icons/icons(1).zip index 0e7d6cae..78e0fcf5 100644 Binary files a/graphics/geo_icons/icons(1).zip and b/graphics/geo_icons/icons(1).zip differ diff --git a/graphics/shadows.sketch b/graphics/shadows.sketch index 27c14562..3653bb9a 100644 Binary files a/graphics/shadows.sketch and b/graphics/shadows.sketch differ diff --git a/rake/model/build.rb b/rake/model/build.rb index 731f2365..401ce42f 100644 --- a/rake/model/build.rb +++ b/rake/model/build.rb @@ -8,12 +8,12 @@ # and creates a per-component build directory for this component. class Build # The major-minor-patch parts of this version - VersionMMP = [2, 9, 0] + VersionMMP = [2, 10, 0] # The base major-minor-patch version, as a string VersionBase = "#{VersionMMP[0]}.#{VersionMMP[1]}.#{VersionMMP[2]}" # The prerelease tags for this build (dot-separated, excluding leading # dash), or empty string if none - VersionPrerelease = '' + VersionPrerelease = 'beta.1' # Select a build configuration based on environment variables, or use # defaults for the host platform if unspecified diff --git a/rake/product/windows.rb b/rake/product/windows.rb index 687ff14b..0eb16119 100644 --- a/rake/product/windows.rb +++ b/rake/product/windows.rb @@ -322,6 +322,9 @@ def self.defineTools(toolsStage) Executable.new("#{Build::Brand}-closegui") .source('tools/closegui') .install(toolsStage, :bin) + Executable.new("win-httpstunnel") + .source('tools/win-httpstunnel') + .install(toolsStage, :bin) end def self.collectSymbols(version, stage, debugSymbols) diff --git a/tools/win-httpstunnel/win-httpstunnel.cpp b/tools/win-httpstunnel/win-httpstunnel.cpp new file mode 100644 index 00000000..6db70034 --- /dev/null +++ b/tools/win-httpstunnel/win-httpstunnel.cpp @@ -0,0 +1,949 @@ +// Copyright (c) 2021 Private Internet Access, Inc. +// +// This file is part of the Private Internet Access Desktop Client. +// +// The Private Internet Access Desktop Client is free software: you can +// redistribute it and/or modify it under the terms of the GNU General Public +// License as published by the Free Software Foundation, either version 3 of +// the License, or (at your option) any later version. +// +// The Private Internet Access Desktop Client is distributed in the hope that +// it will be useful, but WITHOUT ANY WARRANTY; without even the implied +// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with the Private Internet Access Desktop Client. If not, see +// . + +#define WIN32_LEAN_AND_MEAN +#define SECURITY_WIN32 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "User32.lib") +#pragma comment(lib, "Ws2_32.lib") +#pragma comment(lib, "Crypt32.lib") +#pragma comment(lib, "Secur32.lib") + +class Winsock +{ +public: + Winsock(int reqMajor, int reqMinor) + { + // Major number is in low byte, minor number is in high byte + int err = ::WSAStartup(MAKEWORD(reqMinor, reqMajor), &_wsaData); + // If it failed, abort, do not call WSACleanup() + if(err) + throw std::runtime_error{"Winsock 2.2 is required"}; + // Note that if it returned a lower version than we requested (2.1, etc.), + // then we do need to call WSACleanup() even if we reject that version. + } + + ~Winsock() + { + ::WSACleanup(); + } + +private: + Winsock(const Winsock &) = delete; + Winsock &operator=(const Winsock &) = delete; + +public: + bool supportsVersion(int supportMajor, int supportMinor) + { + if(LOBYTE(_wsaData.wVersion) < supportMajor) + return false; + if(LOBYTE(_wsaData.wVersion) == supportMajor) + return HIBYTE(_wsaData.wVersion) >= supportMinor; + return true; + } + +private: + WSADATA _wsaData; +}; + +class SecurityInterface +{ +public: + SecurityInterface() + { + _pSecFuncTable = ::InitSecurityInterfaceW(); + if(!_pSecFuncTable) + throw std::runtime_error{"Unable to load SSPI security interface"}; + // TODO - also check the needed entry points + } + + const SecurityFunctionTableW &get() const {return *_pSecFuncTable;} +private: + SecurityFunctionTableW *_pSecFuncTable; +}; + +// SSPI handle deleters +class SspiCredDeleter +{ +public: + void operator()(CredHandle &h){::FreeCredentialsHandle(&h);} +}; + +class SspiContextDeleter +{ +public: + SspiContextDeleter(const SecurityInterface &secItf) : _secItf{secItf} {} + +public: + void operator()(CtxtHandle &h){_secItf.get().DeleteSecurityContext(&h);} +private: + const SecurityInterface &_secItf; +}; + +// SSPI object owner. Note that SSPI handles are 128 bits on 64-bit platforms, +// not just a plain pointer (two ULONG_PTRs in general). Most API functions take a +// pointer-to-handle as a result. +// +// The various SSPI handle types are actually all typedefs of the same underlying +// opaque handle, but they do require different deleters, so the deleter must be +// specfieid. (The HandleT actually doesn't matter as a result, but it's helpful +// for exposition at least.) +template +class SspiHandle +{ +public: + SspiHandle(DeleterT del = {}) : _handle{}, _deleter{std::move(del)} {} + ~SspiHandle() {clear();} + +public: + void clear() {_deleter(_handle);} + // Not const-correct - most API functions require a non-const pointer + // Don't mess with the handle through this :-/ Don't use this to + // populate SspiHandle with a new handle; use receive() for that + HandleT *get() {return &_handle;} + // Receive a handle into the SspiHandle(), used to pass a pointer to an + // empty handle to an API that creates and stores it + HandleT *receive() {clear(); return get();} + +private: + HandleT _handle; + DeleterT _deleter; +}; + +using SspiCredHandle = SspiHandle; +// SspiCtxtHandle requires a SecurityInterface since the deleter API is +// part of the package interface +using SspiCtxtHandle = SspiHandle; + +class WinSocket +{ +public: + WinSocket(int af, int type, int protocol) + : _socket{INVALID_SOCKET} + { + _socket = ::socket(af, type, protocol); + if(_socket == INVALID_SOCKET) + throw std::runtime_error{"Unable to allocate socket"}; + } + + ~WinSocket() + { + ::closesocket(_socket); + } + +private: + WinSocket(const WinSocket &) = delete; + WinSocket &operator=(const WinSocket &) = delete; + +public: + int connect(const sockaddr *name, int namelen) + { + return ::connect(_socket, name, namelen); + } + int send(const char *buf, int len, int flags) + { + return ::send(_socket, buf, len, flags); + } + int recv(char *buf, int len, int flags) + { + return ::recv(_socket, buf, len, flags); + } + int eventSelect(WSAEVENT eventHandle, long events) + { + return ::WSAEventSelect(_socket, eventHandle, events); + } + int enumNetworkEvents(WSAEVENT eventHandle, WSANETWORKEVENTS *pEvents) + { + return ::WSAEnumNetworkEvents(_socket, eventHandle, pEvents); + } + +private: + SOCKET _socket; +}; + +class WinSocketEvent +{ +public: + WinSocketEvent() + : _event{::WSACreateEvent()} + { + if(_event == WSA_INVALID_EVENT) + throw std::runtime_error{"Failed to create Winsock event"}; + } + ~WinSocketEvent() + { + // Somehow WSACloseEvent() can fail (returns zero), but what could we + // even do if it did? + ::WSACloseEvent(_event); + } + +private: + WinSocketEvent(const WinSocketEvent &) = delete; + WinSocketEvent &operator=(const WinSocketEvent &) = delete; + +public: + WSAEVENT get() {return _event;} + operator WSAEVENT() {return get();} + +private: + WSAEVENT _event; +}; + +class WinError : public std::runtime_error +{ +public: + // GetLastError() and security APIs return DWORD, WSAGetLastError() + // returns int. Both work with FormatMessage() + WinError(const char *pMsg, int err) : WinError{pMsg, static_cast(err)} {} + WinError(const char *pMsg, DWORD err) + : std::runtime_error{pMsg}, _err{err} + { + } + +public: + DWORD getWinErr() const {return _err;} + std::string getWinMsg() const; + +private: + DWORD _err; +}; + +std::string WinError::getWinMsg() const +{ + LPSTR errMsg{nullptr}; + + auto len = ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + nullptr, _err, 0, + reinterpret_cast(&errMsg), 0, nullptr); + std::string msg{errMsg, len}; + ::LocalFree(errMsg); + + return msg; +} + +void createClientCredential(const SecurityInterface &secItf, SspiCredHandle &clientCred) +{ + SCHANNEL_CRED clientCredSpec{}; + clientCredSpec.dwVersion = SCHANNEL_CRED_VERSION; + // Use the defaults for everything in SCHANNEL_CRED; all fields + // support 0 indicating the system defaults. No client cert is + // specified. + TimeStamp expire{}; + SECURITY_STATUS err = secItf.get().AcquireCredentialsHandleW(nullptr, UNISP_NAME_W, + SECPKG_CRED_OUTBOUND, nullptr, &clientCredSpec, nullptr, nullptr, + clientCred.receive(), &expire); + if(err != SEC_E_OK) + throw WinError{"Unable to obtain credentials handle", err}; +} + +void secureHandshake(const SecurityInterface &secItf, SspiCredHandle &clientCred, WinSocket &socket, + const char *pProxyHostname, SspiCtxtHandle &secureCtxt, + std::vector &receivedData) +{ + SecBufferDesc sendBufferDesc{}; + SecBuffer sendBuffer{}; + sendBufferDesc.ulVersion = SECBUFFER_VERSION; + sendBufferDesc.cBuffers = 1; + sendBufferDesc.pBuffers = &sendBuffer; + sendBuffer.cbBuffer = 0; + sendBuffer.BufferType = SECBUFFER_TOKEN; + sendBuffer.pvBuffer = nullptr; + DWORD sspiContextFlags{}; + DWORD sspiRequestFlags{ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_CONFIDENTIALITY | ISC_REQ_EXTENDED_ERROR | + ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_STREAM}; + DWORD sspiRequiredRetFlags{ISC_RET_CONFIDENTIALITY | ISC_RET_REPLAY_DETECT | ISC_RET_SEQUENCE_DETECT | + ISC_RET_STREAM}; + + // Convert the host name to UTF-16 - since we've already assumed it's + // ASCII by using getaddrinfo(), just widen each char to 16 bits. + std::wstring hostnameW; + std::size_t hostnameLen{std::strlen(pProxyHostname)}; + hostnameW.resize(hostnameLen); + std::copy(pProxyHostname, pProxyHostname + hostnameLen, + hostnameW.data()); + SECURITY_STATUS err = secItf.get().InitializeSecurityContextW(clientCred.get(), nullptr, + hostnameW.data(), sspiRequestFlags, 0, 0, nullptr, 0, secureCtxt.receive(), + &sendBufferDesc, &sspiContextFlags, nullptr); + + if(err != SEC_I_CONTINUE_NEEDED) + throw WinError{"Unable to initialize security context", err}; + + // Send the response to the server if present + if(sendBuffer.cbBuffer > 0 && sendBuffer.pvBuffer) + { + int sent = socket.send(reinterpret_cast(sendBuffer.pvBuffer), + sendBuffer.cbBuffer, 0); + secItf.get().FreeContextBuffer(sendBuffer.pvBuffer); + if(sent != sendBuffer.cbBuffer) + throw WinError{"Failed to send context response", ::WSAGetLastError()}; + } + + // Keep receiving until the handshake is complete + receivedData.resize(65536); + std::size_t queuedSize{0}; + + while(err == SEC_I_CONTINUE_NEEDED || err == SEC_E_INCOMPLETE_MESSAGE) + { + // Read if we don't have a complete message yet (there's nothing queued, + // or SSPI said we don't have a complete message). + // + // We can skip this if we just completed a step and we already have more + // data ready. + if(queuedSize == 0 || err == SEC_E_INCOMPLETE_MESSAGE) + { + int received = socket.recv(receivedData.data() + queuedSize, + receivedData.size() - queuedSize, 0); + if(received == SOCKET_ERROR || received == 0) + throw WinError{"Server disconnected during handshake", ::WSAGetLastError()}; + queuedSize += received; + } + + // Pass the input data to SSPI. Provide it an extra input buffer in case it + // does not consume all the data; it will copy the remainder to the extra + // buffer. + SecBufferDesc inputBufferDesc{}; + SecBuffer inputBuffers[2]; + SecBufferDesc outputBufferDesc{}; + SecBuffer outputBuffer; + inputBufferDesc.ulVersion = SECBUFFER_VERSION; + inputBufferDesc.cBuffers = 2; + inputBufferDesc.pBuffers = inputBuffers; + inputBuffers[0].cbBuffer = queuedSize; + inputBuffers[0].BufferType = SECBUFFER_TOKEN; + inputBuffers[0].pvBuffer = reinterpret_cast(receivedData.data()); + inputBuffers[1].cbBuffer = 0; + inputBuffers[1].BufferType = SECBUFFER_EMPTY; + inputBuffers[1].pvBuffer = nullptr; + outputBufferDesc.ulVersion = SECBUFFER_VERSION; + outputBufferDesc.cBuffers = 1; + outputBufferDesc.pBuffers = &outputBuffer; + outputBuffer.cbBuffer = 0; + outputBuffer.BufferType = SECBUFFER_TOKEN; + outputBuffer.pvBuffer = nullptr; + + err = secItf.get().InitializeSecurityContextW(clientCred.get(), + secureCtxt.get(), nullptr, sspiRequestFlags, 0, 0, &inputBufferDesc, + 0, nullptr, &outputBufferDesc, &sspiContextFlags, nullptr); + + // If any response content was given, send it. This can happen even if the + // negotiation fails, such as for an error indication. + if(outputBuffer.cbBuffer > 0 && outputBuffer.pvBuffer) + { + auto sent = socket.send(reinterpret_cast(outputBuffer.pvBuffer), outputBuffer.cbBuffer, 0); + secItf.get().FreeContextBuffer(outputBuffer.pvBuffer); + if(sent != outputBuffer.cbBuffer) + throw WinError{"Failed to send handshake response data to server", ::WSAGetLastError()}; + } + + // If the message was incomplete, keep the data in receivedData and keep receiving. + if(err == SEC_E_INCOMPLETE_MESSAGE) + continue; + + // If we processed a message and are continuing or finished, we'll check for extra data. + // + // Any other codes are not expected; fail the handshake. + // Schannel does not use the 'complete' statuses (SEC_I_COMPLETE_NEEDED, + // SEC_I_COMPLETE_AND_CONTINUE). We don't support any client credentials at + // this layer (SEC_I_INCOMPLETE_CREDENTIALS). + if(err != SEC_E_OK && err != SEC_I_CONTINUE_NEEDED) + throw WinError{"Server handshake failed", err}; + + // Put extra data back in receivedData, or reset it if there is no extra data. + if(inputBuffers[1].BufferType == SECBUFFER_EXTRA) + { + std::copy(receivedData.begin() + queuedSize - inputBuffers[1].cbBuffer, + receivedData.begin() + queuedSize, receivedData.begin()); + queuedSize = inputBuffers[1].cbBuffer; + } + else + queuedSize = 0; + } + + // We're done, if the handshake didn't complete successfully, bail. + if(err != SEC_E_OK) + throw WinError{"Server handshake failed", err}; + + // Make sure we actually got the security guarantees we asked for + if((sspiContextFlags & sspiRequiredRetFlags) != sspiRequiredRetFlags) + throw WinError{"Handshake failed security requirements", err}; + + // Resize the vector to indicate how much leftover data there actually were + receivedData.resize(queuedSize); +} + +class MsgBuf +{ +public: + MsgBuf(const SecPkgContext_StreamSizes &sizes) + : _headerSize{sizes.cbHeader}, _trailerSize{sizes.cbTrailer} + { + setMsgSize(sizes.cbMaximumMessage); + } + +public: + std::size_t headerSize() const {return _headerSize;} + std::size_t msgSize() const {return _data.size() - _headerSize - _trailerSize;} + void setMsgSize(std::size_t msgSize) {_data.resize(_headerSize + msgSize + _trailerSize);} + std::size_t trailerSize() const {return _trailerSize;} + + char *headerBuf() {return _data.data();} + char *msgBuf() {return _data.data() + _headerSize;} + char *trailerBuf() {return _data.data() + (_data.size() - _trailerSize);} + +private: + std::size_t _headerSize; + std::size_t _trailerSize; + std::vector _data; +}; + +template +void appendMsg(MsgBuf &msg, std::size_t &writePos, const char (&data)[N]) +{ + std::copy(&data[0], &data[N], msg.msgBuf() + writePos); + writePos += N; +} +void appendMsg(MsgBuf &msg, std::size_t &writePos, const char *data) +{ + auto len = data ? std::strlen(data) : 0; + std::copy(data, data + len, msg.msgBuf() + writePos); + writePos += len; +} +void appendMsg(MsgBuf &msg, std::size_t &writePos, const std::string &data) +{ + std::copy(data.begin(), data.end(), msg.msgBuf() + writePos); + writePos += data.size(); +} + +void syncEncryptSend(const SecurityInterface &secItf, WinSocket &socket, + SspiCtxtHandle &secureCtxt, MsgBuf &msg) +{ + SecBufferDesc sendBufferDesc; + SecBuffer sendBuffers[4]{}; + + sendBufferDesc.ulVersion = SECBUFFER_VERSION; + sendBufferDesc.cBuffers = 4; + sendBufferDesc.pBuffers = sendBuffers; + + sendBuffers[0].cbBuffer = msg.headerSize(); + sendBuffers[0].BufferType = SECBUFFER_STREAM_HEADER; + sendBuffers[0].pvBuffer = reinterpret_cast(msg.headerBuf()); + sendBuffers[1].cbBuffer = msg.msgSize(); + sendBuffers[1].BufferType = SECBUFFER_DATA; + sendBuffers[1].pvBuffer = reinterpret_cast(msg.msgBuf()); + sendBuffers[2].cbBuffer = msg.trailerSize(); + sendBuffers[2].BufferType = SECBUFFER_STREAM_TRAILER; + sendBuffers[2].pvBuffer = reinterpret_cast(msg.trailerBuf()); + sendBuffers[3].BufferType = SECBUFFER_EMPTY; + + SECURITY_STATUS err = secItf.get().EncryptMessage(secureCtxt.get(), 0, &sendBufferDesc, 0); + + auto totalSize = sendBuffers[0].cbBuffer + sendBuffers[1].cbBuffer + sendBuffers[2].cbBuffer; + auto sent = socket.send(msg.headerBuf(), totalSize, 0); + if(sent != totalSize) + throw WinError{"Failed to send data to server", ::WSAGetLastError()}; +} + +void syncReceiveDecrypt(const SecurityInterface &secItf, WinSocket &socket, + SspiCtxtHandle &secureCtxt, std::vector &buffer, + char *(&pMsg), std::size_t &msgLen, char *(&pExtra), std::size_t &extraLen) +{ + std::size_t queuedData = buffer.size(); + buffer.resize(65536); + SECURITY_STATUS err{SEC_E_OK}; + + pMsg = nullptr; + msgLen = 0; + pExtra = nullptr; + extraLen = 0; + + do + { + // Skip the recv() the first time around if there's already queued data, + // we might already have a full message + if(err == SEC_E_INCOMPLETE_MESSAGE || queuedData == 0) + { + auto received = socket.recv(buffer.data() + queuedData, buffer.size() - queuedData, 0); + if(received == SOCKET_ERROR && ::WSAGetLastError() == WSAEWOULDBLOCK) + { + // That's fine, just go on as if we received no data + received = 0; + } + else if(received == 0 || received == SOCKET_ERROR) + { + std::cerr << "receiver error: " << received << " - " << std::hex << WSAGetLastError() << std::endl; + throw WinError{"Server disconnected", ::WSAGetLastError()}; + } + queuedData += received; + } + + // Try to decrypt the data + SecBufferDesc receiveBufferDesc{}; + SecBuffer receiveBuffers[4]{}; + receiveBufferDesc.ulVersion = SECBUFFER_VERSION; + receiveBufferDesc.cBuffers = 4; + receiveBufferDesc.pBuffers = receiveBuffers; + receiveBuffers[0].cbBuffer = queuedData; + receiveBuffers[0].BufferType = SECBUFFER_DATA; + receiveBuffers[0].pvBuffer = reinterpret_cast(buffer.data()); + receiveBuffers[1].BufferType = SECBUFFER_EMPTY; + receiveBuffers[2].BufferType = SECBUFFER_EMPTY; + receiveBuffers[3].BufferType = SECBUFFER_EMPTY; + + err = secItf.get().DecryptMessage(secureCtxt.get(), &receiveBufferDesc, 0, nullptr); + + if(err == SEC_I_CONTEXT_EXPIRED) + throw WinError{"Server signaled end of connection", err}; + // TODO - implement renegotiate + if(err == SEC_I_RENEGOTIATE) + throw WinError{"Server requested renegotiate; not implemented", err}; + if(err == SEC_E_OK) + { + // Find the data and extra data + for(std::size_t i=0; i<4 && (!pMsg || !pExtra); ++i) + { + if(!pMsg && receiveBuffers[i].BufferType == SECBUFFER_DATA) + { + pMsg = reinterpret_cast(receiveBuffers[i].pvBuffer); + msgLen = receiveBuffers[i].cbBuffer; + } + if(!pExtra && receiveBuffers[i].BufferType == SECBUFFER_EXTRA) + { + pExtra = reinterpret_cast(receiveBuffers[i].pvBuffer); + extraLen = receiveBuffers[i].cbBuffer; + } + } + } + else if(err != SEC_E_INCOMPLETE_MESSAGE) + throw WinError{"Error decrypting incoming data", err}; + } + while(err == SEC_E_INCOMPLETE_MESSAGE); +} + +void readHttpsConnectResponse(const char *pMsgBegin, const char *pMsgEnd) +{ + // Read the status line + std::string crLf{"\r\n"}; + auto pStatusLineEnd = std::search(pMsgBegin, pMsgEnd, crLf.begin(), crLf.end()); + // If there were no headers, no CRLF is found (the message does not + // include the terminating CRLFCRLF), and the entire message is the status + // line (pStatusLineEnd == pMsgEnd). + + // Check for the 200 status code. We don't really care about the HTTP + // version or status message, so just look for " 200 " instead of + // actually splitting on spaces. + std::string okStatus{" 200 "}; + if(std::search(pMsgBegin, pStatusLineEnd, okStatus.begin(), okStatus.end()) + == pStatusLineEnd) + { + // Didn't find " 200 ". It's some other result code, or an entirely + // malformed response. + std::cerr << "Server responded: "; + std::cerr.write(pMsgBegin, (pMsgEnd-pMsgBegin)); + std::cerr << std::endl; + std::cerr << std::endl; + throw std::runtime_error{"Server rejected HTTP CONNECT"}; + } + + // Success - go ahead with connection. + // Don't even read the remaining headers if there are any, we don't care + // about them for HTTP CONNECT. +} + +std::string base64Encode(const char *pData, std::size_t len) +{ + const char b64chars[65]{"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"}; + + std::string result; + std::size_t base64Len{len / 3 * 4}; + if(len % 3) + base64Len += 4; + result.resize(base64Len); + + char *pOut = result.data(); + while(len >= 3) + { + auto word = static_cast((pData[0] << 16) | (pData[1] << 8) | pData[2]); + + pOut[0] = b64chars[word >> 18]; + word &= 0x03FFFF; + pOut[1] = b64chars[word >> 12]; + word &= 0x000FFF; + pOut[2] = b64chars[word >> 6]; + word &= 0x00003F; + pOut[3] = b64chars[word]; + + pData += 3; + len -= 3; + pOut += 4; + } + + // Handle tail bytes and '=' padding. There are only two possibilites - + // len=2 (AAA=) and len=1 (AA==) + if(len) + { + auto tail = static_cast(pData[0] << 16); + if(len == 2) + tail |= (pData[1] << 8); + + pOut[0] = b64chars[tail >> 18]; + tail &= 0x03FFFF; + pOut[1] = b64chars[tail >> 12]; + tail &= 0x000FFF; + if(len == 2) + pOut[2] = b64chars[tail >> 6]; + else + pOut[2] = '='; + pOut[3] = '='; + } + + return result; +} + +class CliParams +{ +public: + CliParams(int argc, char **argv) + { + if(argc != 5) + throw std::runtime_error{"Incorrect number of arguments"}; + + _pBasicAuth = argv[1]; + _pProxyHost = argv[2]; + const char *proxyPortStr{argv[3]}; + _pTunnelTarget = argv[4]; + if(!_pBasicAuth || !_pProxyHost || !proxyPortStr || !_pTunnelTarget) + throw std::runtime_error{"Incorrect number of arguments"}; + + // Parse proxyPortStr; it must only contain digits and result in a value + // in range [1, 65536]. std::atoi() also allows whitespace, +-, etc., so + // check digits manually too. + for(const char *pPortChar = proxyPortStr; *pPortChar; ++pPortChar) + { + if(*pPortChar < '0' || *pPortChar > '9') + throw std::runtime_error{"Invalid proxy port"}; + } + int parsedPort = std::atoi(proxyPortStr); + if(parsedPort < 1 || parsedPort > 65535) + throw std::runtime_error{"Invalid proxy port"}; + _proxyPort = static_cast(parsedPort); + } + +public: + const char *basicAuth() const {return _pBasicAuth;} + const char *proxyHost() const {return _pProxyHost;} + std::uint16_t proxyPort() const {return _proxyPort;} + const char *tunnelTarget() const {return _pTunnelTarget;} + +private: + const char *_pBasicAuth; + const char *_pProxyHost; + std::uint16_t _proxyPort; + const char *_pTunnelTarget; +}; + +void printUsage(const char *name) +{ + std::cerr << "usage:" << std::endl; + std::cerr << " " << name << " " << std::endl; + std::cerr << std::endl; + std::cerr << "example:" << std::endl; + std::cerr << " " << name << " me:mypassword our.proxy.example.com 8443 internal.service.example.com:22" << std::endl; + std::cerr << std::endl; + std::cerr << "Creates a tunneled connection through an HTTPS proxy using HTTPS CONNECT." << std::endl; + std::cerr << "stdin is sent to the tunneled connection, output is sent to stdout." << std::endl; + std::cerr << "Intended for use as an SSH ProxyCommand." << std::endl; +} + +int main(int argc, char **argv) +{ + _setmode(_fileno(stdout), _O_BINARY); + _setmode(_fileno(stdin), _O_BINARY); + + std::optional params; + + try + { + params.emplace(argc, argv); + } + catch(const std::exception &ex) + { + std::cerr << ex.what() << std::endl; + std::cerr << std::endl; + const char *name = (argc > 0 && argv[0]) ? argv[0] : "win-httpstunnel.exe"; + printUsage(name); + return -1; + } + + try + { + SECURITY_STATUS err; + + SecurityInterface secItf{}; + + Winsock ws{2, 2}; + if(!ws.supportsVersion(2, 2)) + throw std::runtime_error{"Winsock 2.2 is required"}; + + SspiCredHandle clientCred; + createClientCredential(secItf, clientCred); + + // Resolve the proxy hostname + ADDRINFOA addrInfoHints{}; + addrInfoHints.ai_family = AF_INET; + addrInfoHints.ai_socktype = SOCK_STREAM; + addrInfoHints.ai_protocol = IPPROTO_TCP; + ADDRINFOA *pProxyAddrInfoRaw{}; + err = ::getaddrinfo(params->proxyHost(), "1", &addrInfoHints, + &pProxyAddrInfoRaw); + // Own the returned pointer + std::unique_ptr pProxyAddrInfo{pProxyAddrInfoRaw, &::freeaddrinfo}; + if(err || !pProxyAddrInfo) + throw WinError{"Unable to resolve proxy hostname", err}; + // Although getaddrinfo() returns a linked list of addresses, we asked + // for IPv4 only, so we just inspect the first one. + if(pProxyAddrInfo->ai_family != AF_INET || + pProxyAddrInfo->ai_addrlen != sizeof(SOCKADDR_IN) || !pProxyAddrInfo->ai_addr) + throw std::runtime_error{"Proxy host was resolved but did not return an IPv4 address"}; + const SOCKADDR_IN *pProxyAddr = reinterpret_cast(pProxyAddrInfo->ai_addr); + + WinSocket socket{AF_INET, SOCK_STREAM, IPPROTO_TCP}; + SOCKADDR_IN sin{}; + sin.sin_family = AF_INET; + sin.sin_port = htons(params->proxyPort()); + sin.sin_addr.S_un.S_addr = pProxyAddr->sin_addr.S_un.S_addr; + + if(socket.connect(reinterpret_cast(&sin), sizeof(sin))) + { + throw WinError{"Unable to connect to remote host", ::WSAGetLastError()}; + } + + SspiCtxtHandle secureCtxt{secItf}; + std::vector receivedData; + secureHandshake(secItf, clientCred, socket, params->proxyHost(), secureCtxt, receivedData); + + SecPkgContext_StreamSizes sizes{}; + err = secItf.get().QueryContextAttributes(secureCtxt.get(), SECPKG_ATTR_STREAM_SIZES, &sizes); + if(err != SEC_E_OK) + throw WinError{"Failed to query message size limits", err}; + + MsgBuf sendBuf{sizes}; + std::size_t httpsConnectMsgSize{0}; + appendMsg(sendBuf, httpsConnectMsgSize, "CONNECT "); + appendMsg(sendBuf, httpsConnectMsgSize, params->tunnelTarget()); + appendMsg(sendBuf, httpsConnectMsgSize, " HTTP/1.1"); + appendMsg(sendBuf, httpsConnectMsgSize, "\r\nUser-Agent: win-httptunnel\r\nProxy-Authorization: Basic "); + appendMsg(sendBuf, httpsConnectMsgSize, + base64Encode(params->basicAuth(), std::strlen(params->basicAuth()))); + appendMsg(sendBuf, httpsConnectMsgSize, "\r\n\r\n"); + sendBuf.setMsgSize(httpsConnectMsgSize); + + syncEncryptSend(secItf, socket, secureCtxt, sendBuf); + sendBuf.setMsgSize(sizes.cbMaximumMessage); + + // At this point, since the TLS handshake is complete, receivedData is + // now the queue for encrypted data, which might already contain data. + // + // decryptedData will be the queue for decrypted received data, which + // may contain partial HTTP lines until we complete the CONNECT. + std::vector decryptedData; + std::string responseEnd{"\r\n\r\n"}; + std::vector::iterator responseEndPos; + + // Receive the HTTP CONNECT response. + while((responseEndPos = std::search(decryptedData.begin(), decryptedData.end(), + responseEnd.begin(), responseEnd.end())) == decryptedData.end()) + { + char *pMsg{}, *pExtra{}; + std::size_t msgLen{}, extraLen{}; + syncReceiveDecrypt(secItf, socket, secureCtxt, receivedData, pMsg, msgLen, pExtra, extraLen); + + // Add the decrypted "message" (which is a TLS message record, not + // necessarily a complete HTTP response) to the decrypted data + // queue + if(pMsg && msgLen > 0) + { + auto existingSize = decryptedData.size(); + decryptedData.resize(existingSize + msgLen); + std::copy(pMsg, pMsg+msgLen, decryptedData.begin() + existingSize); + } + + // Retain any extra data in the received data queue + if(pExtra && extraLen > 0) + { + receivedData.erase(receivedData.begin(), receivedData.begin() + (pExtra - receivedData.data())); + receivedData.resize(extraLen); + } + else + { + receivedData.resize(0); + } + } + + // Make sure we got 200 OK + const char *pResponseEnd = decryptedData.data(); + pResponseEnd += (responseEndPos - decryptedData.begin()); + readHttpsConnectResponse(decryptedData.data(), pResponseEnd); + + // If we got any extra decrypted data, write it out now. + responseEndPos += responseEnd.size(); + pResponseEnd += responseEnd.size(); // Valid because we matched this string + std::cout.write(pResponseEnd, decryptedData.end() - responseEndPos); + std::cout.flush(); + + // Poll both stdin and the socket's read/close events. Set up an event + // to use WaitForMultipleObjects(). + WinSocketEvent socketEvent; + if(socket.eventSelect(socketEvent, FD_READ|FD_CLOSE) == SOCKET_ERROR) + throw WinError{"Unable to select read/close events on socket", ::WSAGetLastError()}; + + HANDLE waitHandles[2]{socketEvent.get(), ::GetStdHandle(STD_INPUT_HANDLE)}; + + // Windows pipes are not waitable objects (:facepalm:) If stdin is a + // pipe, we have to fall back to short-polling. + DWORD waitHandleCount = 2; + DWORD waitTimeout = INFINITE; + if(GetFileType(waitHandles[1]) == FILE_TYPE_PIPE) + { + waitHandleCount = 1; // Don't wait on the pipe + waitTimeout = 100; // Wake periodically to try reading from the pipe + } + + bool continueReading{true}; + do + { + switch(::WaitForMultipleObjects(waitHandleCount, waitHandles, FALSE, waitTimeout)) + { + case WAIT_OBJECT_0: // Socket + { + WSANETWORKEVENTS netEvents{}; + if(socket.enumNetworkEvents(socketEvent, &netEvents) == SOCKET_ERROR) + throw WinError{"Failed to get network events from socket", ::WSAGetLastError()}; + + if(netEvents.lNetworkEvents & FD_READ) + { + // Read and forward to stdout + char *pMsg{}, *pExtra{}; + std::size_t msgLen{}, extraLen{}; + do + { + syncReceiveDecrypt(secItf, socket, secureCtxt, receivedData, pMsg, msgLen, pExtra, extraLen); + if(pMsg && msgLen > 0) + { + std::cout.write(pMsg, msgLen); + std::cout.flush(); + } + + if(pExtra && extraLen > 0) + { + receivedData.erase(receivedData.begin(), receivedData.begin() + (pExtra - receivedData.data())); + receivedData.resize(extraLen); + } + else + { + receivedData.resize(0); + } + } + // If we read a message and still have extra data, try + // to decrypt again, we might already have another + // message. + while(pMsg && msgLen > 0 && !receivedData.empty()); + } + if(netEvents.lNetworkEvents & FD_CLOSE) + { + continueReading = false; + } + break; + } + case WAIT_OBJECT_0 + 1: // stdin + { + DWORD readSize{}; + if(!::ReadFile(waitHandles[1], sendBuf.msgBuf(), + sendBuf.msgSize(), &readSize, nullptr)) + { + throw WinError{"Failed to read from stdin", ::GetLastError()}; + } + sendBuf.setMsgSize(readSize); + syncEncryptSend(secItf, socket, secureCtxt, sendBuf); + sendBuf.setMsgSize(sizes.cbMaximumMessage); + break; + } + case WAIT_TIMEOUT: // peek stdin pipe + { + DWORD readSize{}; + do + { + if(!PeekNamedPipe(waitHandles[1], nullptr, 0, nullptr, + &readSize, nullptr)) + { + throw WinError{"Failed to check for input on stdin", ::GetLastError()}; + } + // If there is something to read, read up to a message + // length (then check again). + if(readSize) + { + if(!ReadFile(waitHandles[1], sendBuf.msgBuf(), + sendBuf.msgSize(), &readSize, nullptr)) + { + throw WinError{"Failed to read from stdin", ::GetLastError()}; + } + sendBuf.setMsgSize(readSize); + syncEncryptSend(secItf, socket, secureCtxt, sendBuf); + sendBuf.setMsgSize(sizes.cbMaximumMessage); + } + } + while(readSize > 0); + break; + } + default: + // Any other result is unexpected. (WAIT_FAILED is + // obviously an error, we don't expect any abandon results + // since we are not waiting on mutexes.) + throw std::runtime_error{"Failed waiting on input events"}; + } + } + while(continueReading); + } + catch(const WinError &ex) + { + std::cerr << ex.what() << std::endl; + std::cerr << "(" << ex.getWinErr() << ") " << ex.getWinMsg() << std::endl; + return -1; + } + catch(const std::exception &ex) + { + std::cerr << ex.what() << std::endl; + return -1; + } +}