ss-abramchuk / OpenVPNAdapter

Objective-C wrapper for OpenVPN library. Compatible with iOS and macOS.
GNU Affero General Public License v3.0
482 stars 215 forks source link

PacketTunnelProvider implemetation on objective-c #201

Closed m1a7 closed 3 years ago

m1a7 commented 3 years ago

Good afternoon @ss-abramchuk ! Please, could you give a link/implementation code of (PacketTunnelProvider : NEPacketTunnelProvider) on objective-c ?

m1a7 commented 3 years ago

// .h

@import NetworkExtension;
@import OpenVPNAdapter;

NS_ASSUME_NONNULL_BEGIN

@interface PacketTunnelProvider : NEPacketTunnelProvider<OpenVPNAdapterDelegate>

@property(nonatomic,strong) OpenVPNAdapter *vpnAdapter;

@property(nonatomic,strong) OpenVPNReachability *openVpnReach;

typedef void(^StartHandler)(NSError * _Nullable);
typedef void(^StopHandler)(void);

@property(nonatomic,nullable, copy) StartHandler startHandler;

@property(nonatomic,nullable, copy) StopHandler stopHandler;

@end

NS_ASSUME_NONNULL_END

// .m

#import "PacketTunnelProvider.h"

@implementation PacketTunnelProvider

-(OpenVPNAdapter*)vpnAdapter{
    if(!_vpnAdapter){
        _vpnAdapter = [[OpenVPNAdapter alloc] init];
        _vpnAdapter.delegate = self;
    }
    return _vpnAdapter;
}

-(OpenVPNReachability*)openVpnReach{
    if(!_openVpnReach){
        _openVpnReach = [[OpenVPNReachability alloc] init];
    }
    return _openVpnReach;
}

-(void)startTunnelWithOptions:(NSDictionary<NSString *,NSObject *> *)options completionHandler:(void (^)(NSError * _Nullable))completionHandler
{
    NETunnelProviderProtocol*   protocolConfiguration = (NETunnelProviderProtocol*)self.protocolConfiguration;
    NSDictionary<NSString*,id>* providerConfiguration = protocolConfiguration.providerConfiguration;

    NSData* ovpnFileContent = providerConfiguration[@"ovpn"];
    if (!ovpnFileContent){
        @throw [NSException exceptionWithName:@"!ovpnFileContent" reason:nil userInfo:nil];
        return;
    }

    OpenVPNConfiguration *openVpnConfiguration = [[OpenVPNConfiguration alloc] init];
    openVpnConfiguration.fileContent = ovpnFileContent;
    openVpnConfiguration.settings = @{};

    NSError* errorEvaluation = nil;
    [self.vpnAdapter applyConfiguration:openVpnConfiguration error:&errorEvaluation];
    if (errorEvaluation){
         completionHandler(errorEvaluation); return;
     }

    __weak typeof(self) weak = self;
    [self.openVpnReach startTrackingWithCallback:^(OpenVPNReachabilityStatus status) {

        if(status==OpenVPNReachabilityStatusReachableViaWiFi){
            return;;
        }
        if(status==OpenVPNReachabilityStatusNotReachable){
            [weak.vpnAdapter reconnectAfterTimeInterval:5];
        }
    }];
    self.startHandler = completionHandler;
    [self.vpnAdapter connectUsingPacketFlow:(id<OpenVPNAdapterPacketFlow>)self.packetFlow];
}

-(void)stopTunnelWithReason:(NEProviderStopReason)reason completionHandler:(void (^)(void))completionHandler
{
    self.stopHandler = completionHandler;
    if ([self.openVpnReach isTracking]) {
        [self.openVpnReach stopTracking];
    }
    [self.vpnAdapter disconnect];
}

-(void)openVPNAdapter:(OpenVPNAdapter *)openVPNAdapter handleError:(NSError *)error{

    BOOL isOpen = (BOOL)[error userInfo][OpenVPNAdapterErrorFatalKey];
    if(isOpen){
        if (self.openVpnReach.isTracking) {
            [self.openVpnReach stopTracking];
        }
        if (self.startHandler){
            self.startHandler(error);
            self.startHandler = nil;
        }else {
            [self cancelTunnelWithError:error];
        }
    }
}

-(void)openVPNAdapter:(OpenVPNAdapter *)openVPNAdapter handleEvent:(OpenVPNAdapterEvent)event message:(NSString *)message{
    switch (event) {
        case OpenVPNAdapterEventConnected:
            if(self.reasserting){
                self.reasserting = NO;
            }
            if (!self.startHandler) return;
            self.startHandler(nil);
            self.startHandler = nil;
            break;
        case OpenVPNAdapterEventDisconnected:

            if (!self.stopHandler) return;

            if (self.openVpnReach.isTracking) {
                [self.openVpnReach stopTracking];
            }
            self.stopHandler();
            self.stopHandler = nil;
            break;
        case OpenVPNAdapterEventReconnecting:
            self.reasserting = YES;
            break;
        default:
            break;
    }
}

-(void)openVPNAdapter:(OpenVPNAdapter *)openVPNAdapter
configureTunnelWithNetworkSettings:(NEPacketTunnelNetworkSettings *)networkSettings
    completionHandler:(void (^)(NSError* _Nullable error))completionHandler
{
    networkSettings.DNSSettings.matchDomains = @[@""];
    [self setTunnelNetworkSettings:networkSettings completionHandler:completionHandler];
}

@end