From 3eba7cc615ee1788c4577c548f0413be48622caf Mon Sep 17 00:00:00 2001 From: Sergey Abramchuk Date: Tue, 16 Jan 2018 17:00:41 +0300 Subject: [PATCH] Refactor implementation of OpenVPNPacketFlowBridge class --- OpenVPN Adapter/OpenVPNPacketFlowBridge.h | 12 +- OpenVPN Adapter/OpenVPNPacketFlowBridge.mm | 210 +++++++++++---------- 2 files changed, 115 insertions(+), 107 deletions(-) diff --git a/OpenVPN Adapter/OpenVPNPacketFlowBridge.h b/OpenVPN Adapter/OpenVPNPacketFlowBridge.h index 438e881..e025354 100644 --- a/OpenVPN Adapter/OpenVPNPacketFlowBridge.h +++ b/OpenVPN Adapter/OpenVPNPacketFlowBridge.h @@ -3,21 +3,25 @@ // OpenVPN Adapter // // Created by Jonathan Downing on 12/10/2017. +// Modified by Sergey Abramchuk on 15/01/2018. // #import NS_ASSUME_NONNULL_BEGIN -@class NEPacketTunnelFlow; +@protocol OpenVPNAdapterPacketFlow; -@interface OpenVPNPacketFlowBridge : NSObject +@interface OpenVPNPacketFlowBridge: NSObject -@property (nonatomic, readonly) CFSocketNativeHandle socketHandle; +@property (nonatomic, readonly) CFSocketRef openVPNSocket; +@property (nonatomic, readonly) CFSocketRef packetFlowSocket; - (instancetype)init NS_UNAVAILABLE; +- (nullable instancetype)initWithPacketFlow:(id)packetFlow NS_DESIGNATED_INITIALIZER; -- (nullable instancetype)initWithPacketFlow:(NEPacketTunnelFlow *)packetFlow NS_DESIGNATED_INITIALIZER; +- (BOOL)configureSocketsWithError:(NSError **)error; +- (void)startReading; @end diff --git a/OpenVPN Adapter/OpenVPNPacketFlowBridge.mm b/OpenVPN Adapter/OpenVPNPacketFlowBridge.mm index e4efd36..d979643 100644 --- a/OpenVPN Adapter/OpenVPNPacketFlowBridge.mm +++ b/OpenVPN Adapter/OpenVPNPacketFlowBridge.mm @@ -3,65 +3,85 @@ // OpenVPN Adapter // // Created by Jonathan Downing on 12/10/2017. +// Modified by Sergey Abramchuk on 15/01/2018. // -#import -#import #import "OpenVPNPacketFlowBridge.h" -@interface OpenVPNPacketFlowBridge () { - CFSocketRef _openVPNClientSocket; - CFSocketRef _packetFlowSocket; -} +#include +#include -@property (nonatomic) NEPacketTunnelFlow *packetFlow; +#import "OpenVPNError.h" +#import "OpenVPNPacket.h" +#import "OpenVPNAdapterPacketFlow.h" + +@interface OpenVPNPacketFlowBridge () + +@property (nonatomic) id packetFlow; @end @implementation OpenVPNPacketFlowBridge -- (CFSocketNativeHandle)socketHandle { - return CFSocketGetNative(_openVPNClientSocket); -} - -- (instancetype)initWithPacketFlow:(NEPacketTunnelFlow *)packetFlow { - if ((self = [super init])) { - self.packetFlow = packetFlow; - - if (![self configureSockets]) { - return nil; - } - - [self readPacketFlowPackets]; +- (instancetype)initWithPacketFlow:(id)packetFlow { + if (self = [super init]) { + _packetFlow = packetFlow; } return self; } -static inline void PacketFlowSocketCallback(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *adapter) { - [(__bridge OpenVPNPacketFlowBridge *)adapter writeDataToPacketFlow:(__bridge NSData *)data]; +#pragma mark - Sockets Configuration + +static void SocketCallback(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *obj) { + if (type != kCFSocketDataCallBack) { return; } + + OpenVPNPacket *packet = [[OpenVPNPacket alloc] initWithVPNData:(__bridge NSData *)data]; + + OpenVPNPacketFlowBridge *bridge = (__bridge OpenVPNPacketFlowBridge *)obj; + [bridge writePackets:@[packet] toPacketFlow:bridge.packetFlow]; } -- (BOOL)configureSockets { +- (BOOL)configureSocketsWithError:(NSError **)error { int sockets[2]; if (socketpair(PF_LOCAL, SOCK_DGRAM, IPPROTO_IP, sockets) == -1) { - NSLog(@"Failed to create a pair of connected sockets: %@", [NSString stringWithUTF8String:strerror(errno)]); + if (error) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: @"Failed to create a pair of connected sockets", + NSLocalizedFailureReasonErrorKey: [NSString stringWithUTF8String:strerror(errno)], + OpenVPNAdapterErrorFatalKey: @(YES) + }; + + *error = [NSError errorWithDomain:OpenVPNAdapterErrorDomain + code:OpenVPNAdapterErrorTUNSetupFailed + userInfo:userInfo]; + } + return NO; } CFSocketContext socketCtxt = {0, (__bridge void *)self, NULL, NULL, NULL}; - _packetFlowSocket = CFSocketCreateWithNative(kCFAllocatorDefault, sockets[0], kCFSocketDataCallBack, PacketFlowSocketCallback, &socketCtxt); - _openVPNClientSocket = CFSocketCreateWithNative(kCFAllocatorDefault, sockets[1], kCFSocketNoCallBack, NULL, NULL); + _packetFlowSocket = CFSocketCreateWithNative(kCFAllocatorDefault, sockets[0], kCFSocketDataCallBack, + SocketCallback, &socketCtxt); + _openVPNSocket = CFSocketCreateWithNative(kCFAllocatorDefault, sockets[1], kCFSocketNoCallBack, NULL, NULL); - if (!(_packetFlowSocket && _openVPNClientSocket)) { - NSLog(@"Failed to create core foundation sockets from native sockets"); + if (!(_packetFlowSocket && _openVPNSocket)) { + if (error) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: @"Failed to create core foundation sockets from native sockets", + OpenVPNAdapterErrorFatalKey: @(YES) + }; + + *error = [NSError errorWithDomain:OpenVPNAdapterErrorDomain + code:OpenVPNAdapterErrorTUNSetupFailed + userInfo:userInfo]; + } + return NO; } - if (!([self configureOptionsForSocket:_packetFlowSocket] && [self configureOptionsForSocket:_openVPNClientSocket])) { - NSLog(@"Failed to configure buffer size of the sockets"); - return NO; - } + if (!([self configureOptionsForSocket:_packetFlowSocket error:error] && + [self configureOptionsForSocket:_openVPNSocket error:error])) { return NO; } CFRunLoopSourceRef packetFlowSocketSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _packetFlowSocket, 0); CFRunLoopAddSource(CFRunLoopGetMain(), packetFlowSocketSource, kCFRunLoopDefaultMode); @@ -70,104 +90,88 @@ static inline void PacketFlowSocketCallback(CFSocketRef socket, CFSocketCallBack return YES; } -- (BOOL)configureOptionsForSocket:(CFSocketRef)socket { +- (BOOL)configureOptionsForSocket:(CFSocketRef)socket error:(NSError **)error { CFSocketNativeHandle socketHandle = CFSocketGetNative(socket); int buf_value = 65536; socklen_t buf_len = sizeof(buf_value); if (setsockopt(socketHandle, SOL_SOCKET, SO_RCVBUF, &buf_value, buf_len) == -1) { - NSLog(@"Failed to setup buffer size for input: %@", [NSString stringWithUTF8String:strerror(errno)]); + if (error) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: @"Failed to setup buffer size for input", + NSLocalizedFailureReasonErrorKey: [NSString stringWithUTF8String:strerror(errno)], + OpenVPNAdapterErrorFatalKey: @(YES) + }; + + *error = [NSError errorWithDomain:OpenVPNAdapterErrorDomain + code:OpenVPNAdapterErrorTUNSetupFailed + userInfo:userInfo]; + } + return NO; } if (setsockopt(socketHandle, SOL_SOCKET, SO_SNDBUF, &buf_value, buf_len) == -1) { - NSLog(@"Failed to setup buffer size for output: %@", [NSString stringWithUTF8String:strerror(errno)]); + if (error) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: @"Failed to setup buffer size for output", + NSLocalizedFailureReasonErrorKey: [NSString stringWithUTF8String:strerror(errno)], + OpenVPNAdapterErrorFatalKey: @(YES) + }; + + *error = [NSError errorWithDomain:OpenVPNAdapterErrorDomain + code:OpenVPNAdapterErrorTUNSetupFailed + userInfo:userInfo]; + } + return NO; } return YES; } -- (void)readPacketFlowPackets { +- (void)startReading { __weak typeof(self) weakSelf = self; - [self.packetFlow readPacketObjectsWithCompletionHandler:^(NSArray * _Nonnull packets) { + + [self.packetFlow readPacketsWithCompletionHandler:^(NSArray *packets, NSArray *protocols) { __strong typeof(self) self = weakSelf; - - [self writeVPNPacketObjects:packets]; - [self readPacketFlowPackets]; + + [self writePackets:packets protocols:protocols toSocket:self.packetFlowSocket]; + [self startReading]; }]; } -- (void)writeVPNPacketObjects:(NSArray *)packets { - for (NEPacket *packet in packets) { - CFSocketSendData(_packetFlowSocket, NULL, (CFDataRef)[self dataFromPacket:packet], 0.05); - } +#pragma mark - + +- (void)writePackets:(NSArray *)packets protocols:(NSArray *)protocols toSocket:(CFSocketRef)socket { + [packets enumerateObjectsUsingBlock:^(NSData *data, NSUInteger idx, BOOL *stop) { + NSNumber *protocolFamily = protocols[idx]; + OpenVPNPacket *packet = [[OpenVPNPacket alloc] initWithPacketFlowData:data protocolFamily:protocolFamily]; + + CFSocketSendData(socket, NULL, (CFDataRef)packet.vpnData, 0.05); + }]; } -- (NSData *)dataFromPacket:(NEPacket *)packet { -#if TARGET_OS_IPHONE - // Prepend data with network protocol. It should be done because OpenVPN on iOS uses uint32_t prefixes containing network protocol. - uint32_t prefix = CFSwapInt32HostToBig((uint32_t)packet.protocolFamily); - NSMutableData *data = [[NSMutableData alloc] initWithCapacity:sizeof(prefix) + packet.data.length]; - [data appendBytes:&prefix length:sizeof(prefix)]; - [data appendData:packet.data]; - return data; -#else - return packet.data; -#endif +#pragma mark - + +- (void)writePackets:(NSArray *)packets toPacketFlow:(id)packetFlow { + NSMutableArray *flowPackets = [[NSMutableArray alloc] init]; + NSMutableArray *protocols = [[NSMutableArray alloc] init]; + + [packets enumerateObjectsUsingBlock:^(OpenVPNPacket * _Nonnull packet, NSUInteger idx, BOOL * _Nonnull stop) { + [flowPackets addObject:packet.packetFlowData]; + [protocols addObject:packet.protocolFamily]; + }]; + + [packetFlow writePackets:flowPackets withProtocols:protocols]; } -- (NEPacket *)packetFromData:(NSData *)data { -#if TARGET_OS_IPHONE - // Get network protocol from prefix - NSUInteger prefixSize = sizeof(uint32_t); - - if (data.length < prefixSize) { - return nil; - } - - uint32_t protocol = PF_UNSPEC; - [data getBytes:&protocol length:prefixSize]; - protocol = CFSwapInt32BigToHost(protocol); - - NSRange range = NSMakeRange(prefixSize, data.length - prefixSize); - NSData *packetData = [data subdataWithRange:range]; -#else - // Get network protocol from header - uint8_t header = 0; - [data getBytes:&header length:1]; - - uint32_t version = openvpn::IPHeader::version(header); - sa_family_t protocol = [self protocolFamilyForVersion:version]; - - NSData *packetData = data; -#endif - - return [[NEPacket alloc] initWithData:packetData protocolFamily:protocol]; -} - -- (void)writeDataToPacketFlow:(NSData *)data { - NEPacket *packet = [self packetFromData:data]; - - if (!packet) { - return; - } - - [self.packetFlow writePacketObjects:@[packet]]; -} - -- (sa_family_t)protocolFamilyForVersion:(uint32_t)version { - switch (version) { - case 4: return PF_INET; - case 6: return PF_INET6; - default: return PF_UNSPEC; - } -} +#pragma mark - - (void)dealloc { - CFSocketInvalidate(_openVPNClientSocket); - CFRelease(_openVPNClientSocket); + CFSocketInvalidate(_openVPNSocket); + CFRelease(_openVPNSocket); CFSocketInvalidate(_packetFlowSocket); CFRelease(_packetFlowSocket);