diff --git a/vpn/wireguard/wireguard.ui b/vpn/wireguard/wireguard.ui index e0c378ff..4206405d 100644 --- a/vpn/wireguard/wireguard.ui +++ b/vpn/wireguard/wireguard.ui @@ -1,184 +1,202 @@ WireGuardProp 0 0 495 454 WireGuard Settings Interface Address (IPv4): IPv4 Internet address with CIDR (example: 10.22.13.123/32) assigned to the local interface. IPv4 or IPv6 address (or both) required Address (IPv6): IPv6 Internet address with CIDR assigned to the local interface. (example: fc00:aaaa:aaaa:aa03::1bc9/128) IPv4 or IPv6 address (or both) required Private key: Required. A base64 private key generated by wg genkey. Peer Public key: Required. A base64 public key calculated by wg pubkey from a private key, and usually transmitted out of band to the author of the configuration file. Allowed IPs: Required. A comma-separated list of IP (v4 or v6) addresses with CIDR masks from which incoming traffic for this peer is allowed and to which outgoing traffic for this peer is directed. The catch-all 0.0.0.0/0 may be specified for matching all IPv4 addresses, and ::/0 may be specified for matching all IPv6 addresses. - + - Endpoint: + Endpoint Address: - + Optional. -An endpoint IP followed by a colon, and then a port number. +An endpoint IP address or name. + + + + Endpoint Port: + + + + + + + Optional. +An endpoint port number. + + + + + + Qt::Horizontal 40 20 Advanced... Qt::Vertical 20 0 PasswordField QLineEdit
passwordfield.h
diff --git a/vpn/wireguard/wireguardwidget.cpp b/vpn/wireguard/wireguardwidget.cpp index 073add06..f19f54ed 100644 --- a/vpn/wireguard/wireguardwidget.cpp +++ b/vpn/wireguard/wireguardwidget.cpp @@ -1,286 +1,330 @@ /* Copyright 2018 Bruce Anderson This program 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 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program 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 this program. If not, see . */ #include "debug.h" #include "wireguardwidget.h" #include "wireguardadvancedwidget.h" #include "simpleipv4addressvalidator.h" #include "simpleiplistvalidator.h" #include "wireguardkeyvalidator.h" #include #include +#include #include #include "nm-wireguard-service.h" class WireGuardSettingWidget::Private { public: Private(); Ui_WireGuardProp ui; NetworkManager::VpnSetting::Ptr setting; KSharedConfigPtr config; QPalette warningPalette; QPalette normalPalette; WireGuardKeyValidator *keyValidator; bool addressValid; bool privateKeyValid; bool publicKeyValid; bool allowedIpsValid; bool endpointValid; }; WireGuardSettingWidget::Private::Private(void) : addressValid(false) , privateKeyValid(false) , publicKeyValid(false) , allowedIpsValid(false) , endpointValid(true) // optional so blank is valid { } WireGuardSettingWidget::WireGuardSettingWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent) : SettingWidget(setting, parent) , d(new Private) { qDBusRegisterMetaType(); d->ui.setupUi(this); d->setting = setting; d->config = KSharedConfig::openConfig(); d->warningPalette = KColorScheme::createApplicationPalette(d->config); d->normalPalette = KColorScheme::createApplicationPalette(d->config); KColorScheme::adjustBackground(d->warningPalette, KColorScheme::NegativeBackground, QPalette::Base, KColorScheme::ColorSet::View, d->config); KColorScheme::adjustBackground(d->normalPalette, KColorScheme::NormalBackground, QPalette::Base, KColorScheme::ColorSet::View, d->config); connect(d->ui.addressIPv4LineEdit, &QLineEdit::textChanged, this, &WireGuardSettingWidget::checkAddressValid); connect(d->ui.addressIPv6LineEdit, &QLineEdit::textChanged, this, &WireGuardSettingWidget::checkAddressValid); connect(d->ui.privateKeyLineEdit, &PasswordField::textChanged, this, &WireGuardSettingWidget::checkPrivateKeyValid); connect(d->ui.publicKeyLineEdit, &QLineEdit::textChanged, this, &WireGuardSettingWidget::checkPublicKeyValid); connect(d->ui.allowedIPsLineEdit, &QLineEdit::textChanged, this, &WireGuardSettingWidget::checkAllowedIpsValid); - connect(d->ui.endpointLineEdit, &QLineEdit::textChanged, this, &WireGuardSettingWidget::checkEndpointValid); + connect(d->ui.endpointAddressLineEdit, &QLineEdit::textChanged, this, &WireGuardSettingWidget::checkEndpointValid); + connect(d->ui.endpointPortLineEdit, &QLineEdit::textChanged, this, &WireGuardSettingWidget::checkEndpointValid); d->ui.privateKeyLineEdit->setPasswordModeEnabled(true); connect(d->ui.btnAdvanced, &QPushButton::clicked, this, &WireGuardSettingWidget::showAdvanced); SimpleIpV4AddressValidator *ip4WithCidrValidator = new SimpleIpV4AddressValidator(this, SimpleIpV4AddressValidator::AddressStyle::WithCidr); d->ui.addressIPv4LineEdit->setValidator(ip4WithCidrValidator); // Create a validator for the IPv6 address line edit // Address must be a valid IP address with a CIDR suffix SimpleIpV6AddressValidator *ip6WithCidrValidator = new SimpleIpV6AddressValidator(this, SimpleIpV6AddressValidator::AddressStyle::WithCidr); d->ui.addressIPv6LineEdit->setValidator(ip6WithCidrValidator); // This is done as a private variable rather than a local variable so it can be // used both here and to validate the private key later d->keyValidator = new WireGuardKeyValidator(this); d->ui.publicKeyLineEdit->setValidator(d->keyValidator); - // Create validator for Endpoint - SimpleIpV4AddressValidator *endpointValidator = - new SimpleIpV4AddressValidator(this, SimpleIpV4AddressValidator::AddressStyle::WithPort); - d->ui.endpointLineEdit->setValidator(endpointValidator); - // Create validator for AllowedIPs SimpleIpListValidator *allowedIPsValidator = new SimpleIpListValidator(this, SimpleIpListValidator::WithCidr, SimpleIpListValidator::Both); d->ui.allowedIPsLineEdit->setValidator(allowedIPsValidator); + // Create validator for endpoint port + QIntValidator *portValidator = new QIntValidator(this); + portValidator->setBottom(0); + portValidator->setTop(65535); + d->ui.endpointPortLineEdit->setValidator(portValidator); + // Connect for setting check watchChangedSetting(); KAcceleratorManager::manage(this); if (setting && !setting->isNull()) { loadConfig(d->setting); } // Set the initial backgrounds on all the widgets checkAddressValid(); checkPrivateKeyValid(); checkPublicKeyValid(); checkAllowedIpsValid(); checkEndpointValid(); } WireGuardSettingWidget::~WireGuardSettingWidget() { delete d; } void WireGuardSettingWidget::loadConfig(const NetworkManager::Setting::Ptr &setting) { Q_UNUSED(setting) // General settings const NMStringMap dataMap = d->setting->data(); d->ui.addressIPv4LineEdit->setText(dataMap[NM_WG_KEY_ADDR_IP4]); d->ui.addressIPv6LineEdit->setText(dataMap[NM_WG_KEY_ADDR_IP6]); d->ui.privateKeyLineEdit->setText(dataMap[NM_WG_KEY_PRIVATE_KEY]); d->ui.publicKeyLineEdit->setText(dataMap[NM_WG_KEY_PUBLIC_KEY]); d->ui.allowedIPsLineEdit->setText(dataMap[NM_WG_KEY_ALLOWED_IPS]); - d->ui.endpointLineEdit->setText(dataMap[NM_WG_KEY_ENDPOINT]); + + // An endpoint is stored as : + QString storedEndpoint = dataMap[NM_WG_KEY_ENDPOINT]; + QStringList endpointList = storedEndpoint.contains("]:") ? + dataMap[NM_WG_KEY_ENDPOINT].split("]:") : + dataMap[NM_WG_KEY_ENDPOINT].split(":"); + + d->ui.endpointAddressLineEdit->setText(endpointList[0].remove("[")); + d->ui.endpointPortLineEdit->setText(endpointList[1]); } void WireGuardSettingWidget::loadSecrets(const NetworkManager::Setting::Ptr &setting) { // Currently WireGuard does not have any secrets Q_UNUSED(setting) } QVariantMap WireGuardSettingWidget::setting() const { NMStringMap data = d->setting->data(); NetworkManager::VpnSetting setting; setting.setServiceType(QLatin1String(NM_DBUS_SERVICE_WIREGUARD)); // required settings setProperty(data, QLatin1String(NM_WG_KEY_ADDR_IP4), d->ui.addressIPv4LineEdit->displayText()); setProperty(data, QLatin1String(NM_WG_KEY_ADDR_IP6), d->ui.addressIPv6LineEdit->displayText()); setProperty(data, QLatin1String(NM_WG_KEY_PRIVATE_KEY), d->ui.privateKeyLineEdit->text()); setProperty(data, QLatin1String(NM_WG_KEY_PUBLIC_KEY), d->ui.publicKeyLineEdit->displayText()); setProperty(data, QLatin1String(NM_WG_KEY_ALLOWED_IPS), d->ui.allowedIPsLineEdit->displayText()); - setProperty(data, QLatin1String(NM_WG_KEY_ENDPOINT), d->ui.endpointLineEdit->displayText()); + + // Endpoint isn't required and is created from
: + QString addressString = d->ui.endpointAddressLineEdit->displayText(); + if (!addressString.isEmpty()) { + // If there is a ':' in the address string then it is an IPv6 address and + // the output needs to be formatted as '[1:2:3:4:5:6:7:8]:123' otherwhise + // it is formatted as '1.2.3.4:123' or 'ab.com:123' + if (addressString.contains(":")) + setProperty(data, QLatin1String(NM_WG_KEY_ENDPOINT), + "[" + + d->ui.endpointAddressLineEdit->displayText().trimmed() + + "]:" + + d->ui.endpointPortLineEdit->displayText().trimmed()); + else + setProperty(data, QLatin1String(NM_WG_KEY_ENDPOINT), + d->ui.endpointAddressLineEdit->displayText().trimmed() + + ":" + + d->ui.endpointPortLineEdit->displayText().trimmed()); + } setting.setData(data); return setting.toMap(); } void WireGuardSettingWidget::setProperty(NMStringMap &data, const QLatin1String &key, const QString &value) const { if (!value.isEmpty()) data.insert(key, value); else data.remove(key); } void WireGuardSettingWidget::showAdvanced() { QPointer adv = new WireGuardAdvancedWidget(d->setting, this); connect(adv.data(), &WireGuardAdvancedWidget::accepted, [adv, this] () { NetworkManager::VpnSetting::Ptr advData = adv->setting(); if (!advData.isNull()) { d->setting->setData(advData->data()); } }); connect(adv.data(), &WireGuardAdvancedWidget::finished, [adv] () { if (adv) { adv->deleteLater(); } }); adv->setModal(true); adv->show(); } bool WireGuardSettingWidget::isValid() const { return d->addressValid && d->privateKeyValid && d->publicKeyValid && d->allowedIpsValid && d->endpointValid; } void WireGuardSettingWidget::checkAddressValid() { int pos = 0; QLineEdit *widget = d->ui.addressIPv4LineEdit; QString value(widget->displayText()); bool ip4valid = (widget->validator()->validate(value, pos) == QValidator::Acceptable); bool ip4present = !widget->displayText().isEmpty(); widget = d->ui.addressIPv6LineEdit; value = widget->displayText(); bool ip6valid = QValidator::Acceptable == widget->validator()->validate(value, pos); bool ip6present = !widget->displayText().isEmpty(); d->addressValid = (ip4valid && ip6valid) || (ip4valid && !ip6present) || (!ip4present && ip6valid); setBackground(d->ui.addressIPv4LineEdit, d->addressValid); setBackground(d->ui.addressIPv6LineEdit, d->addressValid); slotWidgetChanged(); } void WireGuardSettingWidget::checkPrivateKeyValid() { int pos = 0; PasswordField *widget = d->ui.privateKeyLineEdit; QString value = widget->text(); d->privateKeyValid = QValidator::Acceptable == d->keyValidator->validate(value, pos); setBackground(widget, d->privateKeyValid); slotWidgetChanged(); } void WireGuardSettingWidget::checkPublicKeyValid() { int pos = 0; QLineEdit *widget = d->ui.publicKeyLineEdit; QString value = widget->displayText(); d->publicKeyValid = QValidator::Acceptable == widget->validator()->validate(value, pos); setBackground(widget, d->publicKeyValid); slotWidgetChanged(); } void WireGuardSettingWidget::checkAllowedIpsValid() { int pos = 0; QLineEdit *widget = d->ui.allowedIPsLineEdit; QString value = widget->displayText(); d->allowedIpsValid = QValidator::Acceptable == widget->validator()->validate(value, pos); setBackground(widget, d->allowedIpsValid); slotWidgetChanged(); } void WireGuardSettingWidget::checkEndpointValid() { int pos = 0; - QLineEdit *widget = d->ui.endpointLineEdit; - QString value = widget->displayText(); - d->endpointValid = QValidator::Acceptable == widget->validator()->validate(value, pos) - || value.isEmpty(); - setBackground(widget, d->endpointValid); + QLineEdit *addressWidget = d->ui.endpointAddressLineEdit; + QLineEdit *portWidget = d->ui.endpointPortLineEdit; + QString addressValue = addressWidget->displayText(); + QString portString = portWidget->displayText(); + + QUrl temp; + static QRegExpValidator fqdnValidator(QRegExp(QLatin1String("(?=.{5,254}$)([a-zA-Z0-9][a-zA-Z0-9-]{1,62}\\.){1,63}[a-zA-Z]{2,63}")), 0); + static SimpleIpV4AddressValidator ipv4Validator(0); + static SimpleIpV6AddressValidator ipv6Validator(0); + + bool addressValid = QValidator::Acceptable == fqdnValidator.validate(addressValue, pos) + || QValidator::Acceptable == ipv4Validator.validate(addressValue, pos) + || QValidator::Acceptable == ipv6Validator.validate(addressValue, pos); + bool bothEmpty = addressValue.isEmpty() && portString.isEmpty(); + // Because of the validator, if the port is non-empty, it is valid + bool portValid = !portString.isEmpty(); + d->endpointValid = bothEmpty || (addressValid && portValid); + setBackground(addressWidget, bothEmpty || addressValid); + setBackground(portWidget, bothEmpty || portValid); + slotWidgetChanged(); } void WireGuardSettingWidget::setBackground(QWidget *w, bool result) const { if (result) w->setPalette(d->normalPalette); else w->setPalette(d->warningPalette); }