diff --git a/client/platforms/ios/PacketTunnelProvider+WireGuard.swift b/client/platforms/ios/PacketTunnelProvider+WireGuard.swift index 18200c7f..7a5c125b 100644 --- a/client/platforms/ios/PacketTunnelProvider+WireGuard.swift +++ b/client/platforms/ios/PacketTunnelProvider+WireGuard.swift @@ -111,10 +111,20 @@ extension PacketTunnelProvider { settingsDictionary[pair[0]] = pair[1] } } + + let lastHandshakeString = settingsDictionary["last_handshake_time_sec"] + let lastHandshake: Int64 + + if let lastHandshakeValue = lastHandshakeString, let handshakeValue = Int64(lastHandshakeValue) { + lastHandshake = handshakeValue + } else { + lastHandshake = -2 + } let response: [String: Any] = [ "rx_bytes": settingsDictionary["rx_bytes"] ?? "0", - "tx_bytes": settingsDictionary["tx_bytes"] ?? "0" + "tx_bytes": settingsDictionary["tx_bytes"] ?? "0", + "last_handshake_time_sec": lastHandshake ] completionHandler(try? JSONSerialization.data(withJSONObject: response, options: [])) diff --git a/client/platforms/ios/ios_controller.h b/client/platforms/ios/ios_controller.h index 85580769..543b8e41 100644 --- a/client/platforms/ios/ios_controller.h +++ b/client/platforms/ios/ios_controller.h @@ -55,6 +55,9 @@ public: QString openFile(); void requestInetAccess(); + + void stopForHandshake(); + void waitForHandshake(); signals: void connectionStateChanged(Vpn::ConnectionState state); void bytesChanged(quint64 receivedBytes, quint64 sentBytes); @@ -62,7 +65,6 @@ signals: void importBackupFromOutside(const QString); void finished(); - protected slots: private: diff --git a/client/platforms/ios/ios_controller.mm b/client/platforms/ios/ios_controller.mm index 85fb50b7..1a685aa0 100644 --- a/client/platforms/ios/ios_controller.mm +++ b/client/platforms/ios/ios_controller.mm @@ -7,7 +7,7 @@ #include #include #include - +#include #include "../protocols/vpnprotocol.h" #import "ios_controller_wrapper.h" @@ -58,6 +58,7 @@ Vpn::ConnectionState iosStatusToState(NEVPNStatus status) { namespace { IosController* s_instance = nullptr; +QTimer *m_handshakeTimer = nullptr; } IosController::IosController() : QObject() @@ -237,7 +238,6 @@ void IosController::disconnectVpn() } } - void IosController::checkStatus() { NSString *actionKey = [NSString stringWithUTF8String:MessageKey::action]; @@ -258,10 +258,15 @@ void IosController::checkStatus() void IosController::vpnStatusDidChange(void *pNotification) { NETunnelProviderSession *session = (NETunnelProviderSession *)pNotification; - + NETunnelProviderProtocol *tunnelProtocol = (NETunnelProviderProtocol *)m_currentTunnel.protocolConfiguration; if (session /* && session == TunnelManager.session */ ) { qDebug() << "IosController::vpnStatusDidChange" << iosStatusToState(session.status) << session; - + if (tunnelProtocol.providerConfiguration[@"wireguard"] != nil && + (session.status == NEVPNStatusConnected || session.status == NEVPNStatusConnecting)) + { + // use last_handshake_time for check status connected for WireGuard + return; + } if (session.status == NEVPNStatusDisconnected) { if (@available(iOS 16.0, *)) { [session fetchLastDisconnectErrorWithCompletionHandler:^(NSError * _Nullable error) { @@ -356,6 +361,7 @@ void IosController::vpnStatusDidChange(void *pNotification) } else { qDebug() << "Disconnect error is unavailable on iOS < 16.0"; } + stopForHandshake(); } emit connectionStateChanged(iosStatusToState(session.status)); @@ -641,6 +647,7 @@ bool IosController::startWireGuard(const QString &config) m_currentTunnel.protocolConfiguration = tunnelProtocol; + waitForHandshake(); startTunnel(); } @@ -667,6 +674,8 @@ void IosController::startTunnel() protocolName = @"WireGuard"; } else if (tunnelProtocol.providerConfiguration[@"ovpn"] != nil) { protocolName = @"OpenVPN"; + } else if (tunnelProtocol.providerConfiguration[@"xray"] != nil) { + protocolName = @"XRay"; } m_rxBytes = 0; @@ -863,3 +872,83 @@ void IosController::requestInetAccess() { }]; [task resume]; } + +void IosController::stopForHandshake() { + if (m_handshakeTimer) { + if (QThread::currentThread() != m_handshakeTimer->thread()) { + QMetaObject::invokeMethod(m_handshakeTimer, "stop", Qt::QueuedConnection); + QMetaObject::invokeMethod(m_handshakeTimer, "deleteLater", Qt::QueuedConnection); + m_handshakeTimer = nullptr; + return; + } + if (m_handshakeTimer->isActive()) { + m_handshakeTimer->stop(); + } + m_handshakeTimer->deleteLater(); + m_handshakeTimer = nullptr; + + qDebug() << "Handshake monitoring stopped."; + } else { + qDebug() << "No active handshake monitoring to stop."; + } +} + +void IosController::waitForHandshake() { + qDebug() << "Waiting for last_handshake_time_sec to be greater than 0..."; + + // Initialize the timer if it's null + if (!m_handshakeTimer) { + m_handshakeTimer = new QTimer(this); + + // Connect the timer's timeout signal to perform handshake checking + connect(m_handshakeTimer, &QTimer::timeout, this, [this]() { + // Prepare the message to check status + NSString *actionKey = [NSString stringWithUTF8String:MessageKey::action]; + NSString *actionValue = [NSString stringWithUTF8String:Action::getStatus]; + NSString *tunnelIdKey = [NSString stringWithUTF8String:MessageKey::tunnelId]; + NSString *tunnelIdValue = !m_tunnelId.isEmpty() ? m_tunnelId.toNSString() : @""; + + NSDictionary *message = @{actionKey: actionValue, tunnelIdKey: tunnelIdValue}; + + // Lambda to handle the response + auto checkHandshake = [this](NSDictionary *response) { + uint64_t last_handshake_time_sec = 0; + if (response && response[@"last_handshake_time_sec"] && + ![response[@"last_handshake_time_sec"] isKindOfClass:[NSNull class]]) { + last_handshake_time_sec = [response[@"last_handshake_time_sec"] unsignedLongLongValue]; + } + + qDebug() << "last_handshake_time_sec:" << last_handshake_time_sec; + + if (last_handshake_time_sec > 0) { + // Handshake successful, update state + qDebug() << "Handshake detected, updating state to CONNECTED."; + emit connectionStateChanged(Vpn::ConnectionState::Connected); + stopForHandshake(); + return; + } else { + if (last_handshake_time_sec == 0) { + // Keep retrying + emit connectionStateChanged(Vpn::ConnectionState::Connecting); + } else { + // Handle handshake failure and stop monitoring + emit connectionStateChanged(Vpn::ConnectionState::Disconnected); + stopForHandshake(); + return; + } + } + }; + + // Send the message to the VPN extension + sendVpnExtensionMessage(message, checkHandshake); + }); + + qDebug() << "Handshake timer initialized."; + } + + // Start the timer only if it's not already active + if (m_handshakeTimer && !m_handshakeTimer->isActive()) { + m_handshakeTimer->start(1000); // Retry every 1 second + qDebug() << "Handshake timer Retry every 1 second"; + } +}