mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-07-03 14:07:39 +02:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1d846d144f | |||
| 98a7ea0fcf | |||
| ca8eb7c724 | |||
| 1192f3d8a7 | |||
| 5961d4cefc | |||
| f72684e4d4 | |||
| 898f497f8e | |||
| 8c94f70edf | |||
| a144d495ee | |||
| f0e66e4ecf | |||
| 6f0e9a136b |
@@ -20,6 +20,7 @@ set(LIBS ${LIBS}
|
||||
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
MACOSX_BUNDLE TRUE
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/macos/app/AmneziaVPN.entitlements"
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
)
|
||||
@@ -29,10 +30,12 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
||||
|
||||
set(HEADERS ${HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/protocols/ikev2_vpn_protocol_mac.h
|
||||
)
|
||||
|
||||
set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/protocols/ikev2_vpn_protocol_mac.mm
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -291,7 +291,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
||||
#elif defined(Q_OS_MAC)
|
||||
switch (c) {
|
||||
case DockerContainer::WireGuard: return true;
|
||||
case DockerContainer::Ipsec: return false;
|
||||
case DockerContainer::Ipsec: return true;
|
||||
default: return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,13 @@
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/route.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <libproc.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <semaphore.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <QHostAddress>
|
||||
@@ -475,3 +482,112 @@ QString NetworkUtilities::getGatewayAndIface()
|
||||
return gateway;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
QString NetworkUtilities::ipAddressByInterfaceName(const QString &interfaceName)
|
||||
{
|
||||
struct ifaddrs *ifaddr, *ifa;
|
||||
char host[NI_MAXHOST];
|
||||
|
||||
if (getifaddrs(&ifaddr) == -1)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
|
||||
{
|
||||
if (ifa->ifa_addr == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int family = ifa->ifa_addr->sa_family;
|
||||
QString iname = QString::fromStdString(ifa->ifa_name);
|
||||
|
||||
if (family == AF_INET && iname == interfaceName)
|
||||
{
|
||||
int s = getnameinfo(ifa->ifa_addr,
|
||||
(family == AF_INET) ? sizeof(struct sockaddr_in) :
|
||||
sizeof(struct sockaddr_in6),
|
||||
host, NI_MAXHOST,
|
||||
NULL, 0, NI_NUMERICHOST);
|
||||
if (s != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return QString::fromStdString(host);
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(ifaddr);
|
||||
return "";
|
||||
}
|
||||
|
||||
QString NetworkUtilities::lastConnectedNetworkInterfaceName()
|
||||
{
|
||||
QString ifname("");
|
||||
|
||||
struct ifaddrs * interfaces = NULL;
|
||||
struct ifaddrs * temp_addr = NULL;
|
||||
|
||||
if( getifaddrs(&interfaces) == 0 )
|
||||
{
|
||||
//Loop through linked list of interfaces
|
||||
temp_addr = interfaces;
|
||||
while( temp_addr != NULL )
|
||||
{
|
||||
if( temp_addr->ifa_addr->sa_family == AF_INET )
|
||||
{
|
||||
QString tname = temp_addr->ifa_name;
|
||||
if( tname.startsWith("utun") )
|
||||
ifname = tname;
|
||||
else if( tname.startsWith("ipsec") )
|
||||
ifname = tname;
|
||||
else if( tname.startsWith("ppp") )
|
||||
ifname = tname;
|
||||
}
|
||||
|
||||
temp_addr = temp_addr->ifa_next;
|
||||
}
|
||||
|
||||
freeifaddrs(interfaces);
|
||||
}
|
||||
return ifname;
|
||||
}
|
||||
|
||||
QString execCmd(const QString &cmd)
|
||||
{
|
||||
char buffer[1024];
|
||||
QString result = "";
|
||||
FILE* pipe = popen(cmd.toStdString().c_str(), "r");
|
||||
if (!pipe) return "";
|
||||
while (!feof(pipe))
|
||||
{
|
||||
if (fgets(buffer, 1024, pipe) != NULL)
|
||||
{
|
||||
result += buffer;
|
||||
}
|
||||
}
|
||||
pclose(pipe);
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList NetworkUtilities::getListOfDnsNetworkServiceEntries()
|
||||
{
|
||||
QStringList result;
|
||||
QString command = "echo 'list' | scutil | grep /Network/Service | grep DNS";
|
||||
QString cmdOutput = execCmd(command).trimmed();
|
||||
// qDebug() << "Raw result: " << cmdOutput;
|
||||
|
||||
QStringList lines = cmdOutput.split('\n');
|
||||
for (QString line : lines)
|
||||
{
|
||||
if (line.contains("="))
|
||||
{
|
||||
QString entry = line.mid(line.indexOf("=")+1).trimmed();
|
||||
result.append(entry);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -20,16 +20,22 @@ public:
|
||||
static QString getGatewayAndIface();
|
||||
// Returns the Interface Index that could Route to dst
|
||||
static int AdapterIndexTo(const QHostAddress& dst);
|
||||
|
||||
|
||||
static QRegularExpression ipAddressRegExp();
|
||||
static QRegularExpression ipAddressPortRegExp();
|
||||
static QRegExp ipAddressWithSubnetRegExp();
|
||||
static QRegExp ipNetwork24RegExp();
|
||||
static QRegExp ipPortRegExp();
|
||||
static QRegExp domainRegExp();
|
||||
|
||||
|
||||
static QString netMaskFromIpWithSubnet(const QString ip);
|
||||
static QString ipAddressFromIpWithSubnet(const QString ip);
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
static QString ipAddressByInterfaceName(const QString &interfaceName);
|
||||
static QString lastConnectedNetworkInterfaceName();
|
||||
static QStringList getListOfDnsNetworkServiceEntries();
|
||||
#endif
|
||||
static QStringList summarizeRoutes(const QStringList &ips, const QString cidr);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.networking.networkextension</key>
|
||||
<array>
|
||||
<string>packet-tunnel-provider</string>
|
||||
</array>
|
||||
<key>com.apple.developer.networking.vpn.api</key>
|
||||
<array>
|
||||
<string>allow-vpn</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
|
||||
|
||||
#include "openvpnprotocol.h"
|
||||
|
||||
|
||||
class Ikev2Protocol : public VpnProtocol
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Ikev2Protocol(const QJsonObject& configuration, QObject* parent = nullptr);
|
||||
virtual ~Ikev2Protocol() override;
|
||||
|
||||
void readIkev2Configuration(const QJsonObject &configuration);
|
||||
bool create_new_vpn(const QString &vpn_name, const QString &serv_addr);
|
||||
bool delete_vpn_connection(const QString &vpn_name);
|
||||
bool connect_to_vpn(const QString & vpn_name);
|
||||
bool disconnect_vpn();
|
||||
void closeWindscribeActiveConnection();
|
||||
ErrorCode start() override;
|
||||
void stop() override;
|
||||
|
||||
static QString tunnelName() { return "AmneziaVPN IKEv2"; }
|
||||
|
||||
private slots:
|
||||
void handleNotificationImpl(int status);
|
||||
|
||||
private:
|
||||
mutable QRecursiveMutex mutex_;
|
||||
void *notificationId_;
|
||||
QJsonObject m_config;
|
||||
QJsonObject m_ikev2_config;
|
||||
|
||||
QString ipsecAdapterName_;
|
||||
|
||||
bool isConnectingStateReachedAfterStartingConnection_;
|
||||
|
||||
void handleNotification(void *notification);
|
||||
bool isFailedAuthError(QMap<time_t, QString> &logs);
|
||||
bool isSocketError(QMap<time_t, QString> &logs);
|
||||
bool setCustomDns(const QString &overrideDnsIpAddress);
|
||||
};
|
||||
@@ -0,0 +1,397 @@
|
||||
#include "ikev2_vpn_protocol_mac.h"
|
||||
|
||||
#include <core/networkUtilities.h>
|
||||
#include <SystemConfiguration/SCSchemaDefinitions.h>
|
||||
#include <SystemConfiguration/SCNetwork.h>
|
||||
#include <SystemConfiguration/SCNetworkConnection.h>
|
||||
#include <SystemConfiguration/SCNetworkConfiguration.h>
|
||||
#import <NetworkExtension/NetworkExtension.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#include <QWaitCondition>
|
||||
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/pkcs12.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include <sys/sysctl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <net/if.h>
|
||||
#include <net/route.h>
|
||||
|
||||
static NSString * const IKEv2ServiceName = @"AmneziaVPN IKEv2";
|
||||
|
||||
static Ikev2Protocol* self = nullptr;
|
||||
|
||||
Ikev2Protocol::Ikev2Protocol(const QJsonObject &configuration, QObject* parent) :
|
||||
VpnProtocol(configuration, parent)
|
||||
{
|
||||
qDebug() << "IpsecProtocol::IpsecProtocol()";
|
||||
m_routeGateway = NetworkUtilities::getGatewayAndIface();
|
||||
self = this;
|
||||
readIkev2Configuration(configuration);
|
||||
}
|
||||
|
||||
Ikev2Protocol::~Ikev2Protocol()
|
||||
{
|
||||
qDebug() << "IpsecProtocol::~IpsecProtocol()";
|
||||
disconnect_vpn();
|
||||
QThread::msleep(1000);
|
||||
Ikev2Protocol::stop();
|
||||
}
|
||||
|
||||
void Ikev2Protocol::stop()
|
||||
{
|
||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||
qDebug() << "IpsecProtocol::stop()";
|
||||
}
|
||||
|
||||
void Ikev2Protocol::readIkev2Configuration(const QJsonObject &configuration)
|
||||
{
|
||||
qDebug() << "IpsecProtocol::readIkev2Configuration";
|
||||
m_config = configuration;
|
||||
auto ikev2_data = m_config.value(ProtocolProps::key_proto_config_data(Proto::Ikev2)).toObject();
|
||||
m_ikev2_config = QJsonDocument::fromJson(ikev2_data.value(config_key::config).toString().toUtf8()).object();
|
||||
|
||||
}
|
||||
|
||||
CFDataRef CreatePersistentRefForIdentity(SecIdentityRef identity)
|
||||
{
|
||||
CFTypeRef persistent_ref = NULL;
|
||||
const void *keys[] = { kSecReturnPersistentRef, kSecValueRef };
|
||||
const void *values[] = { kCFBooleanTrue, identity };
|
||||
CFDictionaryRef dict = CFDictionaryCreate(NULL, keys, values,
|
||||
sizeof(keys) / sizeof(*keys), NULL, NULL);
|
||||
|
||||
if (SecItemCopyMatching(dict, &persistent_ref) != 0) {
|
||||
SecItemAdd(dict, &persistent_ref);
|
||||
}
|
||||
|
||||
if (dict)
|
||||
CFRelease(dict);
|
||||
|
||||
return (CFDataRef)persistent_ref;
|
||||
}
|
||||
|
||||
NSData *searchKeychainCopyMatching(const char *certName)
|
||||
{
|
||||
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
||||
[dict setObject:(__bridge id)kSecClassCertificate forKey:(__bridge id)kSecClass];
|
||||
[dict setObject:[NSString stringWithUTF8String:certName] forKey:(__bridge id)kSecAttrLabel];
|
||||
[dict setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
|
||||
[dict setObject:@YES forKey:(__bridge id)kSecReturnPersistentRef];
|
||||
|
||||
CFTypeRef result = NULL;
|
||||
SecItemCopyMatching((__bridge CFDictionaryRef)dict, &result);
|
||||
|
||||
return (NSData *)result;
|
||||
}
|
||||
|
||||
ErrorCode Ikev2Protocol::start()
|
||||
{
|
||||
|
||||
qDebug() << "IpsecProtocol::start";
|
||||
|
||||
static QMutex mutexLocal;
|
||||
mutexLocal.lock();
|
||||
|
||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||
NEVPNManager *manager = [NEVPNManager sharedManager];
|
||||
|
||||
[manager loadFromPreferencesWithCompletionHandler:^(NSError *err)
|
||||
{
|
||||
mutexLocal.lock();
|
||||
|
||||
if (err)
|
||||
{
|
||||
qDebug() << "First load vpn preferences failed:" << QString::fromNSString(err.localizedDescription);
|
||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||
mutexLocal.unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
NSData *output = NULL;
|
||||
|
||||
BIO *ibio, *obio = NULL;
|
||||
BUF_MEM *bptr;
|
||||
|
||||
STACK_OF(X509) *certstack = sk_X509_new_null();
|
||||
BIO *p12 = BIO_new(BIO_s_mem());
|
||||
|
||||
EVP_PKEY *pkey;
|
||||
X509 *cert;
|
||||
|
||||
BIO_write(p12, QByteArray::fromBase64(m_ikev2_config[config_key::cert].toString().toUtf8()),
|
||||
QByteArray::fromBase64(m_ikev2_config[config_key::cert].toString().toUtf8()).size());
|
||||
|
||||
PKCS12 *pkcs12 = d2i_PKCS12_bio(p12, NULL);
|
||||
PKCS12_parse(pkcs12, m_ikev2_config[config_key::password].toString().toStdString().c_str(), &pkey, &cert, &certstack);
|
||||
|
||||
// We output everything in PEM
|
||||
obio = BIO_new(BIO_s_mem());
|
||||
|
||||
// TODO: support protecting the private key with a PEM passphrase
|
||||
if (pkey)
|
||||
{
|
||||
PEM_write_bio_PrivateKey(obio, pkey, NULL, NULL, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
if (cert)
|
||||
{
|
||||
PEM_write_bio_X509(obio, cert);
|
||||
}
|
||||
|
||||
if (certstack && sk_X509_num(certstack))
|
||||
{
|
||||
for (int i = 0; i < sk_X509_num(certstack); i++)
|
||||
PEM_write_bio_X509_AUX(obio, sk_X509_value(certstack, i));
|
||||
}
|
||||
|
||||
BIO_get_mem_ptr(obio, &bptr);
|
||||
|
||||
output = [NSData dataWithBytes: bptr->data length: bptr->length];
|
||||
|
||||
NSData *PKCS12Data = [[NSData alloc] initWithBase64EncodedString:m_ikev2_config[config_key::cert].toString().toNSString() options:0];
|
||||
|
||||
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
|
||||
OSStatus ret = SecPKCS12Import(
|
||||
(__bridge CFDataRef)output,
|
||||
(__bridge CFDictionaryRef)@{(id)kSecImportExportPassphrase:@""},
|
||||
&items);
|
||||
|
||||
if (ret != errSecSuccess) {
|
||||
qDebug() << "import err ret " << ret;
|
||||
}
|
||||
|
||||
NSDictionary *firstItem = [(__bridge_transfer NSArray *)items firstObject];
|
||||
SecIdentityRef identity = (__bridge SecIdentityRef)(firstItem[(__bridge id)kSecImportItemIdentity]);
|
||||
|
||||
NEVPNProtocolIKEv2 *protocol = [[NEVPNProtocolIKEv2 alloc] init];
|
||||
protocol.serverAddress = m_ikev2_config.value(amnezia::config_key::hostName).toString().toNSString();
|
||||
protocol.certificateType = NEVPNIKEv2CertificateTypeRSA;
|
||||
|
||||
protocol.remoteIdentifier = m_ikev2_config.value(amnezia::config_key::hostName).toString().toNSString();
|
||||
protocol.authenticationMethod = NEVPNIKEAuthenticationMethodCertificate;
|
||||
protocol.identityReference = searchKeychainCopyMatching(m_ikev2_config.value(amnezia::config_key::userName).toString().toLocal8Bit().data());
|
||||
|
||||
protocol.useExtendedAuthentication = NO;
|
||||
protocol.enablePFS = YES;
|
||||
|
||||
protocol.IKESecurityAssociationParameters.encryptionAlgorithm = NEVPNIKEv2EncryptionAlgorithmAES256;
|
||||
protocol.IKESecurityAssociationParameters.diffieHellmanGroup = NEVPNIKEv2DiffieHellmanGroup19;
|
||||
protocol.IKESecurityAssociationParameters.integrityAlgorithm = NEVPNIKEv2IntegrityAlgorithmSHA256;
|
||||
protocol.IKESecurityAssociationParameters.lifetimeMinutes = 1440;
|
||||
|
||||
protocol.childSecurityAssociationParameters.encryptionAlgorithm = NEVPNIKEv2EncryptionAlgorithmAES256;
|
||||
protocol.childSecurityAssociationParameters.diffieHellmanGroup = NEVPNIKEv2DiffieHellmanGroup19;
|
||||
protocol.childSecurityAssociationParameters.integrityAlgorithm = NEVPNIKEv2IntegrityAlgorithmSHA256;
|
||||
protocol.childSecurityAssociationParameters.lifetimeMinutes = 1440;
|
||||
|
||||
[manager setEnabled:YES];
|
||||
[manager setProtocolConfiguration:(protocol)];
|
||||
[manager setOnDemandEnabled:NO];
|
||||
[manager setLocalizedDescription:@"Amnezia VPN"];
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
NSString *strProtocol = [NSString stringWithFormat:@"{Protocol: %@", protocol];
|
||||
qDebug() << QString::fromNSString(strProtocol);
|
||||
#endif
|
||||
|
||||
// do config stuff
|
||||
[manager saveToPreferencesWithCompletionHandler:^(NSError *err)
|
||||
{
|
||||
if (err)
|
||||
{
|
||||
qDebug() << "First save vpn preferences failed:" << QString::fromNSString(err.localizedDescription);
|
||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||
mutexLocal.unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
// load and save preferences again, otherwise Mac bug (https://forums.developer.apple.com/thread/25928)
|
||||
[manager loadFromPreferencesWithCompletionHandler:^(NSError *err)
|
||||
{
|
||||
if (err)
|
||||
{
|
||||
qDebug() << "Second load vpn preferences failed:" << QString::fromNSString(err.localizedDescription);
|
||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||
mutexLocal.unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
[manager saveToPreferencesWithCompletionHandler:^(NSError *err)
|
||||
{
|
||||
if (err)
|
||||
{
|
||||
qDebug() << "Second Save vpn preferences failed:" << QString::fromNSString(err.localizedDescription);
|
||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||
mutexLocal.unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
notificationId_ = [[NSNotificationCenter defaultCenter] addObserverForName: (NSString *)NEVPNStatusDidChangeNotification object: manager.connection queue: nil usingBlock: ^ (NSNotification *notification)
|
||||
{
|
||||
this->handleNotification(notification);
|
||||
}];
|
||||
|
||||
qDebug() << "NEVPNConnection current status:" << (int)manager.connection.status;
|
||||
|
||||
NSError *startError;
|
||||
[manager.connection startVPNTunnelAndReturnError:&startError];
|
||||
if (startError)
|
||||
{
|
||||
qDebug() << "Error starting ikev2 connection:" << QString::fromNSString(startError.localizedDescription);
|
||||
[[NSNotificationCenter defaultCenter] removeObserver: (id)notificationId_ name: (NSString *)NEVPNStatusDidChangeNotification object: manager.connection];
|
||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||
}
|
||||
mutexLocal.unlock();
|
||||
}
|
||||
}];
|
||||
}
|
||||
}];
|
||||
}
|
||||
}];
|
||||
}
|
||||
}];
|
||||
|
||||
mutexLocal.unlock();
|
||||
|
||||
setConnectionState(Vpn::ConnectionState::Connected);
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
bool Ikev2Protocol::create_new_vpn(const QString & vpn_name,
|
||||
const QString & serv_addr) {
|
||||
qDebug() << "Ikev2Protocol::create_new_vpn()";
|
||||
return true;
|
||||
}
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
bool Ikev2Protocol::delete_vpn_connection(const QString &vpn_name) {
|
||||
|
||||
return false;
|
||||
}
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
bool Ikev2Protocol::connect_to_vpn(const QString & vpn_name) {
|
||||
return false;
|
||||
}
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
bool Ikev2Protocol::disconnect_vpn() {
|
||||
|
||||
NEVPNManager *manager = [NEVPNManager sharedManager];
|
||||
|
||||
// #713: If user had started connecting to IKev2 on Mac and quickly started after this connecting to Wireguard
|
||||
|
||||
// then manager.connection.status doesn't have time to change to NEVPNStatusConnecting
|
||||
// and remains NEVPNStatusDisconnected as it was before connection tries.
|
||||
// Then we should check below isConnectingStateReachedAfterStartingConnection_ flag to be sure that connecting started.
|
||||
// Without this check we will start connecting to the Wireguard when IKEv2 connecting process hasn't finished yet.
|
||||
if (manager.connection.status == NEVPNStatusDisconnected && isConnectingStateReachedAfterStartingConnection_)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver: (id)notificationId_ name: (NSString *)NEVPNStatusDidChangeNotification object: manager.connection];
|
||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||
}
|
||||
else
|
||||
{
|
||||
[manager.connection stopVPNTunnel];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Ikev2Protocol::closeWindscribeActiveConnection()
|
||||
{
|
||||
|
||||
NEVPNManager *manager = [NEVPNManager sharedManager];
|
||||
if (manager)
|
||||
{
|
||||
[manager loadFromPreferencesWithCompletionHandler:^(NSError *err)
|
||||
{
|
||||
if (!err)
|
||||
{
|
||||
NEVPNConnection * connection = [manager connection];
|
||||
if (connection.status == NEVPNStatusConnected || connection.status == NEVPNStatusConnecting)
|
||||
{
|
||||
if ([manager.localizedDescription isEqualToString:@"Amnezia VPN"] == YES)
|
||||
{
|
||||
qDebug() << "Previous IKEv2 connection is active. Stop it.";
|
||||
[connection stopVPNTunnel];
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Ikev2Protocol::handleNotificationImpl(int status)
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
|
||||
NEVPNManager *manager = [NEVPNManager sharedManager];
|
||||
|
||||
if (status == NEVPNStatusInvalid)
|
||||
{
|
||||
qDebug() << "Connection status changed: NEVPNStatusInvalid";
|
||||
[[NSNotificationCenter defaultCenter] removeObserver: (id)notificationId_ name: (NSString *)NEVPNStatusDidChangeNotification object: manager.connection];
|
||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||
}
|
||||
else if (status == NEVPNStatusDisconnected)
|
||||
{
|
||||
qDebug() << "Connection status changed: NEVPNStatusDisconnected";
|
||||
IpcClient::Interface()->disableKillSwitch();
|
||||
|
||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||
[[NSNotificationCenter defaultCenter] removeObserver: (id)notificationId_ name: (NSString *)NEVPNStatusDidChangeNotification object: manager.connection];
|
||||
|
||||
}
|
||||
else if (status == NEVPNStatusConnecting)
|
||||
{
|
||||
isConnectingStateReachedAfterStartingConnection_ = true;
|
||||
setConnectionState(Vpn::ConnectionState::Connecting);
|
||||
qDebug() << "Connection status changed: NEVPNStatusConnecting";
|
||||
}
|
||||
else if (status == NEVPNStatusConnected)
|
||||
{
|
||||
qDebug() << "Connection status changed: NEVPNStatusConnected";
|
||||
|
||||
QString ipsecAdapterName_ = NetworkUtilities::lastConnectedNetworkInterfaceName();
|
||||
m_vpnLocalAddress = NetworkUtilities::ipAddressByInterfaceName(ipsecAdapterName_);
|
||||
m_vpnGateway = m_vpnLocalAddress;
|
||||
|
||||
QList<QHostAddress> dnsAddr;
|
||||
dnsAddr.push_back(QHostAddress(m_config.value(config_key::dns1).toString()));
|
||||
dnsAddr.push_back(QHostAddress(m_config.value(config_key::dns2).toString()));
|
||||
|
||||
IpcClient::Interface()->updateResolvers(ipsecAdapterName_, dnsAddr);
|
||||
|
||||
if (QVariant(m_config.value(config_key::killSwitchOption).toString()).toBool()) {
|
||||
qDebug() << "enable killswitch";
|
||||
IpcClient::Interface()->enableKillSwitch(m_config, 0);
|
||||
}
|
||||
|
||||
setConnectionState(Vpn::ConnectionState::Connected);
|
||||
}
|
||||
else if (status == NEVPNStatusReasserting)
|
||||
{
|
||||
qDebug() << "Connection status changed: NEVPNStatusReasserting";
|
||||
setConnectionState(Vpn::ConnectionState::Connecting);
|
||||
}
|
||||
else if (status == NEVPNStatusDisconnecting)
|
||||
{
|
||||
qDebug() << "Connection status changed: NEVPNStatusDisconnecting";
|
||||
setConnectionState(Vpn::ConnectionState::Disconnecting);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Ikev2Protocol::handleNotification(void *notification)
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
NSNotification *nsNotification = (NSNotification *)notification;
|
||||
NEVPNConnection *connection = nsNotification.object;
|
||||
QMetaObject::invokeMethod(this, "handleNotificationImpl", Q_ARG(int, (int)connection.status));
|
||||
}
|
||||
|
||||
@@ -17,7 +17,11 @@
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
#include "ikev2_vpn_protocol_linux.h"
|
||||
#include "ikev2_vpn_protocol_linux.h"
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_MACX
|
||||
#include "ikev2_vpn_protocol_mac.h"
|
||||
#endif
|
||||
|
||||
VpnProtocol::VpnProtocol(const QJsonObject &configuration, QObject *parent)
|
||||
@@ -110,10 +114,7 @@ QString VpnProtocol::vpnGateway() const
|
||||
VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject &configuration)
|
||||
{
|
||||
switch (container) {
|
||||
#if defined(Q_OS_WINDOWS) || defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
|
||||
case DockerContainer::Ipsec: return new Ikev2Protocol(configuration);
|
||||
#endif
|
||||
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) and !defined MACOS_NE || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
||||
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) && !defined(MACOS_NE) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
||||
case DockerContainer::OpenVpn: return new OpenVpnProtocol(configuration);
|
||||
case DockerContainer::Cloak: return new OpenVpnOverCloakProtocol(configuration);
|
||||
case DockerContainer::ShadowSocks: return new ShadowSocksVpnProtocol(configuration);
|
||||
@@ -121,6 +122,7 @@ VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject &
|
||||
case DockerContainer::Awg: return new WireguardProtocol(configuration);
|
||||
case DockerContainer::Xray: return new XrayProtocol(configuration);
|
||||
case DockerContainer::SSXray: return new XrayProtocol(configuration);
|
||||
case DockerContainer::Ipsec: return new Ikev2Protocol(configuration);
|
||||
#endif
|
||||
default: return nullptr;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# Exempt the tunnel interface(s) used by the VPN connection
|
||||
|
||||
utunInterfaces = "{ \
|
||||
utun0, utun1, utun2, utun3, utun4, utun5, utun6, utun7, utun8, utun9, utun10, \
|
||||
utun11, utun12, utun13, utun14, utun15, utun16, utun17, utun18, utun19, utun20, \
|
||||
utun21, utun22, utun23, utun24, utun25, utun26, utun27, utun28, utun29, utun30 \
|
||||
utunInterfaces = "{ \
|
||||
utun0, utun1, utun2, utun3, utun4, utun5, utun6, utun7, utun8, utun9, utun10, \
|
||||
utun11, utun12, utun13, utun14, utun15, utun16, utun17, utun18, utun19, utun20, \
|
||||
utun21, utun22, utun23, utun24, utun25, utun26, utun27, utun28, utun29, utun30, \
|
||||
ipsec0, ipsec1, ipsec2, ipsec3, ipsec4, ipsec5, ipsec6, ipsec7, ipsec8, ipsec9, \
|
||||
ipsec10, ipsec11, ipsec12, ipsec13, ipsec14, ipsec15, ipsec16, ipsec17, ipsec18, ipsec19 \
|
||||
}"
|
||||
|
||||
pass out on $utunInterfaces flags any no state
|
||||
|
||||
+2
-1
@@ -6,8 +6,9 @@
|
||||
#include <QObject>
|
||||
#include <QJsonArray>
|
||||
|
||||
#include "logger.h"
|
||||
#include "qjsonarray.h"
|
||||
#include "router.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include "killswitch.h"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user