Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ios: observe vpn connect status #681

Merged
merged 1 commit into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/ios/YassAppDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
enum YASSState { STARTED, STARTING, START_FAILED, STOPPING, STOPPED };

@interface YassAppDelegate : UIResponder <UIApplicationDelegate>
- (void) reloadState;
- (enum YASSState) getState;
- (NSString*) getStatus;
- (void) OnStart:(BOOL) quiet;
Expand Down
188 changes: 103 additions & 85 deletions src/ios/YassAppDelegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@

@interface YassAppDelegate ()
- (std::string)SaveConfig;
- (void)OnStarted;
- (void)OnStartSaveAndLoadInstance:(NETunnelProviderManager*)vpn_manager;
- (void)OnStartInstanceFailed:(NSError* _Nullable)error;
- (void)OnStarting;
- (void)OnStarted;
- (void)OnStartFailedWithNSError:(NSError* _Nullable)error;
- (void)OnStartFailed:(std::string)error_msg;
- (void)OnStopping;
- (void)OnStopped;
@end

Expand Down Expand Up @@ -60,6 +62,26 @@ - (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<

#pragma mark - Application

- (void)reloadState {
[NETunnelProviderManager loadAllFromPreferencesWithCompletionHandler:^(NSArray<NETunnelProviderManager *> * _Nullable managers, NSError * _Nullable error) {
if (error) {
vpn_manager_ = nil;
[self didChangeVpnStatus:nil];
return;
}
NETunnelProviderManager* vpn_manager;
if ([managers count] == 0) {
vpn_manager_ = nil;
[self didChangeVpnStatus:nil];
return;
} else {
vpn_manager = managers[0];
}
vpn_manager_ = vpn_manager.enabled ? vpn_manager : nil;
[self didChangeVpnStatus:nil];
}];
}

- (enum YASSState)getState {
return state_;
}
Expand Down Expand Up @@ -96,9 +118,13 @@ - (NSString*)getStatus {
std::ostringstream ss;
if (state_ == STARTED) {
ss << gurl_base::SysNSStringToUTF8(status_) << ":";
} else if (state_ == STARTING) {
ss << gurl_base::SysNSStringToUTF8(NSLocalizedString(@"CONNECTING", @"Connecting"));
} else if (state_ == START_FAILED) {
NSString *prefixMessage = NSLocalizedString(@"FAILED_TO_CONNECT_DUE_TO", @"Failed to connect due to ");
ss << gurl_base::SysNSStringToUTF8(prefixMessage) << error_msg_.c_str();
} else if (state_ == STOPPING) {
ss << gurl_base::SysNSStringToUTF8(NSLocalizedString(@"DISCONNECTING", @"Disconnecting"));
} else {
NSString *prefixMessage = NSLocalizedString(@"DISCONNECTED_WITH", @"Disconnected with ");
ss << gurl_base::SysNSStringToUTF8(prefixMessage) << absl::GetFlag(FLAGS_server_host);
Expand All @@ -108,7 +134,8 @@ - (NSString*)getStatus {
}

- (void)OnStart:(BOOL)quiet {
state_ = STARTING;
[self OnStarting];

if (!connectedToNetwork()) {
NSString *message = NSLocalizedString(@"NETWORK_UNREACHABLE", @"Network unreachable");
[self OnStartFailed:gurl_base::SysNSStringToUTF8(message)];
Expand All @@ -120,22 +147,35 @@ - (void)OnStart:(BOOL)quiet {
return;
}

[self OnStarted];
config::SaveConfig();

[NETunnelProviderManager loadAllFromPreferencesWithCompletionHandler:^(NSArray<NETunnelProviderManager *> * _Nullable managers, NSError * _Nullable error) {
if (error != nil) {
[self OnStartFailedWithNSError:error];
return;
}
NETunnelProviderManager* vpn_manager;
if ([managers count] == 0) {
vpn_manager = [[NETunnelProviderManager alloc] init];
} else {
vpn_manager = managers[0];
}
[self OnStartSaveAndLoadInstance:vpn_manager];
}];
}

- (void)OnStop:(BOOL)quiet {
state_ = STOPPING;

if (vpn_manager_ != nil) {
// state will reflect in NEVPNStatusDidChangeNotification
[vpn_manager_.connection stopVPNTunnel];
vpn_manager_ = nil;
[self didChangeVpnStatus:nil];
} else {
[self OnStopped];
}

[self OnStopped];
}

- (void)FetchTelemetryData {
if (vpn_manager_ != nil) {
if (vpn_manager_ != nil && state_ == STARTED) {
NETunnelProviderSession* session = (NETunnelProviderSession*)vpn_manager_.connection;
NSData *requestData = [@(kAppMessageGetTelemetry) dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
Expand Down Expand Up @@ -166,59 +206,6 @@ - (void)FetchTelemetryData {
}
}

- (void)OnStarted {
state_ = STARTED;
config::SaveConfig();

refresh_timer_ =
[NSTimer scheduledTimerWithTimeInterval:NSTimeInterval(1.0)
target:self
selector:@selector(FetchTelemetryData)
userInfo:nil
repeats:YES];

[NETunnelProviderManager loadAllFromPreferencesWithCompletionHandler:^(NSArray<NETunnelProviderManager *> * _Nullable managers, NSError * _Nullable error) {
if (error) {
[self OnStartInstanceFailed: error];
return;
}
NETunnelProviderManager* vpn_manager;
if ([managers count] == 0) {
vpn_manager = [[NETunnelProviderManager alloc] init];
} else {
vpn_manager = managers[0];
#if 0
// Compare the configuration with new one
// Remove old one once the configuration is changed (to prevent stall configuration).
NETunnelProviderProtocol* tunnelProtocol = (NETunnelProviderProtocol*)vpn_manager.protocolConfiguration;

YassViewController* viewController = [self getRootViewController];

NSDictionary* new_configuration = @{
@"server_host": viewController.serverHost.text,
@"server_port": viewController.serverPort.text,
@"username": viewController.username.text,
@"password": viewController.password.text,
@"method_string": [viewController getCipher],
@"connect_timeout": viewController.timeout.text,
};

if (![new_configuration isEqualToDictionary:tunnelProtocol.providerConfiguration]) {
[vpn_manager removeFromPreferencesWithCompletionHandler:^(NSError * _Nullable error) {
if (error) {
[self OnStartInstanceFailed: error];
return;
}
[self OnStartSaveAndLoadInstance:vpn_manager];
}];
return;
}
#endif
}
[self OnStartSaveAndLoadInstance:vpn_manager];
}];
}

- (void)OnStartSaveAndLoadInstance:(NETunnelProviderManager*) vpn_manager {
NETunnelProviderProtocol* tunnelProtocol;
if (!vpn_manager.protocolConfiguration) {
Expand Down Expand Up @@ -260,95 +247,126 @@ - (void)OnStartSaveAndLoadInstance:(NETunnelProviderManager*) vpn_manager {

[vpn_manager saveToPreferencesWithCompletionHandler:^(NSError * _Nullable error) {
if (error != nil) {
[self OnStartInstanceFailed: error];
[self OnStartFailedWithNSError:error];
return;
}
[vpn_manager loadFromPreferencesWithCompletionHandler:^(NSError * _Nullable error) {
if (error != nil) {
[self OnStartInstanceFailed: error];
[self OnStartFailedWithNSError:error];
return;
}
vpn_manager_ = vpn_manager;
BOOL ret = [vpn_manager.connection startVPNTunnelAndReturnError:&error];
if (ret == TRUE) {
[self didChangeVpnStatus: nil];

YassViewController* viewController = [self getRootViewController];
[viewController Started];
vpn_manager_ = vpn_manager;
// state will reflect in NEVPNStatusDidChangeNotification
[self didChangeVpnStatus:nil];
} else {
vpn_manager_ = nil;
[self OnStartInstanceFailed:error];
[self OnStartFailedWithNSError:error];
}
}];
}];
}

- (void)OnStartInstanceFailed:(NSError* _Nullable)error {
DCHECK_EQ(vpn_manager_, nil);
std::string err_msg = gurl_base::SysNSStringToUTF8([error localizedDescription]);
[self OnStop:true];
[self OnStartFailed:err_msg];
}

#pragma mark - Notification

- (void)didChangeVpnStatus:(NSNotification *)notification {
if (!vpn_manager_) {
status_ = NSLocalizedString(@"DISCONNECTED", @"Disconnected");
[self OnStopped];
return;
}
NEVPNStatus status = vpn_manager_.connection.status;

switch (status) {
case NEVPNStatusConnecting:
NSLog(@"Connecting...");
status_ = NSLocalizedString(@"CONNECTING", @"Connecting");
[self OnStarting];
break;
case NEVPNStatusConnected:
NSLog(@"Connected...");
status_ = NSLocalizedString(@"CONNECTED", @"Connected");
[self OnStarted];
break;
case NEVPNStatusDisconnecting:
NSLog(@"Disconnecting...");
status_ = NSLocalizedString(@"DISCONNECTING", @"Disconnecting");
[self OnStopping];
break;
case NEVPNStatusDisconnected:
NSLog(@"Disconnected...");
status_ = NSLocalizedString(@"DISCONNECTED", @"Disconnected");
[self OnStopped];
break;
case NEVPNStatusInvalid:
NSLog(@"Invalid");
status_ = NSLocalizedString(@"INVALID", @"Invalid");
[self OnStopped];
break;
case NEVPNStatusReasserting:
NSLog(@"Reasserting...");
status_ = NSLocalizedString(@"REASSERTING", @"Reasserting");
[self OnStopped];
break;
default:
NSLog(@"Unknown status... %@", @(status));
status_ = NSLocalizedString(@"UNKNOWN", @"Unknown");
[self OnStopped];
break;
}
}

- (void)OnStarting {
state_ = STARTING;

YassViewController* viewController = [self getRootViewController];
[viewController Starting];
}

- (void)OnStarted {
state_ = STARTED;

YassViewController* viewController = [self getRootViewController];
[viewController UpdateStatusBar];
[viewController Started];

refresh_timer_ =
[NSTimer scheduledTimerWithTimeInterval:NSTimeInterval(1.0)
target:self
selector:@selector(FetchTelemetryData)
userInfo:nil
repeats:YES];
}

- (void)OnStartFailedWithNSError:(NSError* _Nullable)error {
std::string err_msg = gurl_base::SysNSStringToUTF8([error localizedDescription]);
[self OnStartFailed:err_msg];
}

- (void)OnStartFailed:(std::string)error_msg {
state_ = START_FAILED;
error_msg_ = error_msg; // required by viewController

error_msg_ = error_msg;
LOG(ERROR) << "worker failed due to: " << error_msg_;
YassViewController* viewController = [self getRootViewController];
[viewController StartFailed];

UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Start Failed" message:@(error_msg.c_str()) preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* action = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) {}];
[alert addAction:action];
[viewController presentViewController:alert animated:YES completion:nil];
[refresh_timer_ invalidate];
refresh_timer_ = nil;
}

- (void)OnStopping {
state_ = STOPPING;

YassViewController* viewController = [self getRootViewController];
[viewController Stopping];
}

- (void)OnStopped {
state_ = STOPPED;

YassViewController* viewController = [self getRootViewController];
[viewController Stopped];

[refresh_timer_ invalidate];
refresh_timer_ = nil;
}
Expand Down
2 changes: 2 additions & 0 deletions src/ios/YassViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
@interface YassViewController : UIViewController
- (void)OnStart;
- (void)OnStop;
- (void)Starting;
- (void)Started;
- (void)StartFailed;
- (void)Stopping;
- (void)Stopped;
- (void)UpdateStatusBar;
@property (weak, nonatomic) IBOutlet UIButton *startButton;
Expand Down
Loading
Loading