Refactor implementation of OpenVPNAdapter class

This commit is contained in:
Sergey Abramchuk
2018-01-17 14:01:09 +03:00
parent c53f9e0d2f
commit 96d2ab1850
2 changed files with 354 additions and 397 deletions
+3 -27
View File
@@ -14,6 +14,9 @@ typedef NS_ENUM(NSInteger, OpenVPNAdapterEvent);
@class NEPacketTunnelFlow;
@class NEPacketTunnelNetworkSettings;
@protocol OpenVPNAdapterPacketFlow;
@class OpenVPNAdapter;
@class OpenVPNConfiguration;
@class OpenVPNConnectionInfo;
@@ -23,33 +26,6 @@ typedef NS_ENUM(NSInteger, OpenVPNAdapterEvent);
@class OpenVPNTransportStats;
@class OpenVPNSessionToken;
@protocol OpenVPNAdapterPacketFlow <NSObject>
/**
Read IP packets from the TUN interface.
@param completionHandler A block that is executed when some packets are read from the TUN interface. The packets that were
read are passed to this block in the packets array. The protocol numbers of the packets that were read are passed to this
block in the protocols array. Each packet has a protocol number in the corresponding index in the protocols array. The
protocol numbers are given in host byte order. Valid protocol numbers include PF_INET and PF_INET6. See /usr/include/sys/socket.h.
*/
- (void)readPacketsWithCompletionHandler:(void (^)(NSArray<NSData *> *packets, NSArray<NSNumber *> *protocols))completionHandler;
/**
Write IP packets to the TUN interface
@param packets An array of NSData objects containing the IP packets to the written.
@param protocols An array of NSNumber objects containing the protocol numbers (e.g. PF_INET or PF_INET6) of the IP packets
in packets in host byte order.
@discussion The number of NSData objects in packets must be exactly equal to the number of NSNumber objects in protocols.
@return YES on success, otherwise NO.
*/
- (BOOL)writePackets:(NSArray<NSData *> *)packets withProtocols:(NSArray<NSNumber *> *)protocols;
@end
@protocol OpenVPNAdapterDelegate <NSObject>
/**
+351 -370
View File
@@ -8,265 +8,42 @@
#define OPENVPN_EXTERN extern
#import "OpenVPNAdapter.h"
#import <NetworkExtension/NetworkExtension.h>
#import <client/ovpncli.hpp>
#import <openvpn/tun/client/tunbase.hpp>
#import "OpenVPNClient.h"
#import "OpenVPNError.h"
#import "OpenVPNAdapterEvent.h"
#import "OpenVPNCredentials+Internal.h"
#import "OpenVPNConfiguration+Internal.h"
#import "OpenVPNConnectionInfo+Internal.h"
#import "OpenVPNError.h"
#import "OpenVPNInterfaceStats+Internal.h"
#import "OpenVPNNetworkSettingsBuilder.h"
#import "OpenVPNPacketFlowBridge.h"
#import "OpenVPNProperties+Internal.h"
#import "OpenVPNSessionToken+Internal.h"
#import "OpenVPNTransportStats+Internal.h"
#import "OpenVPNAdapter.h"
#import "OpenVPNAdapterPacketFlow.h"
class Client;
@interface OpenVPNAdapter () <OpenVPNClientDelegate>
@interface OpenVPNAdapter ()
@property (nonatomic) Client *client;
@property (nonatomic) NSString *sessionName;
@property (nonatomic) OpenVPNNetworkSettingsBuilder *networkSettingsBuilder;
@property (nonatomic) OpenVPNClient *vpnClient;
@property (nonatomic) OpenVPNPacketFlowBridge *packetFlowBridge;
@property (nonatomic) OpenVPNNetworkSettingsBuilder *networkSettingsBuilder;
- (OpenVPNAdapterEvent)eventByName:(NSString *)eventName;
- (OpenVPNAdapterError)errorByName:(NSString *)errorName;
- (OpenVPNAdapterEvent)eventByName:(NSString *)errorName;
- (NSString *)reasonForError:(OpenVPNAdapterError)error;
@end
using namespace openvpn;
class Client : public ClientAPI::OpenVPNClient {
public:
Client(OpenVPNAdapter *adapter) {
this->adapter = adapter;
}
bool tun_builder_set_remote_address(const std::string& address, bool ipv6) override {
NSString *remoteAddress = [[NSString alloc] initWithUTF8String:address.c_str()];
adapter.networkSettingsBuilder.remoteAddress = remoteAddress;
return true;
}
bool tun_builder_add_address(const std::string& address, int prefix_length, const std::string& gateway, bool ipv6, bool net30) override {
NSString *localAddress = [[NSString alloc] initWithUTF8String:address.c_str()];
NSString *gatewayAddress = [[NSString alloc] initWithUTF8String:gateway.c_str()];
NSString *defaultGateway = gatewayAddress.length == 0 || [gatewayAddress isEqualToString:@"UNSPEC"] ? nil : gatewayAddress;
if (ipv6) {
adapter.networkSettingsBuilder.ipv6DefaultGateway = defaultGateway;
[adapter.networkSettingsBuilder.ipv6LocalAddresses addObject:localAddress];
[adapter.networkSettingsBuilder.ipv6NetworkPrefixLengths addObject:@(prefix_length)];
} else {
NSString *subnetMask = [[NSString alloc] initWithUTF8String:IPv4::Addr::netmask_from_prefix_len(prefix_length).to_string().c_str()];
adapter.networkSettingsBuilder.ipv4DefaultGateway = defaultGateway;
[adapter.networkSettingsBuilder.ipv4LocalAddresses addObject:localAddress];
[adapter.networkSettingsBuilder.ipv4SubnetMasks addObject:subnetMask];
}
return true;
}
bool tun_builder_reroute_gw(bool ipv4, bool ipv6, unsigned int flags) override {
if (ipv4) {
NEIPv4Route *includedRoute = [NEIPv4Route defaultRoute];
includedRoute.gatewayAddress = adapter.networkSettingsBuilder.ipv4DefaultGateway;
[adapter.networkSettingsBuilder.ipv4IncludedRoutes addObject:includedRoute];
}
if (ipv6) {
NEIPv6Route *includedRoute = [NEIPv6Route defaultRoute];
includedRoute.gatewayAddress = adapter.networkSettingsBuilder.ipv6DefaultGateway;
[adapter.networkSettingsBuilder.ipv6IncludedRoutes addObject:includedRoute];
}
return true;
}
bool tun_builder_add_route(const std::string& address, int prefix_length, int metric, bool ipv6) override {
NSString *route = [[NSString alloc] initWithUTF8String:address.c_str()];
if (ipv6) {
NEIPv6Route *includedRoute = [[NEIPv6Route alloc] initWithDestinationAddress:route networkPrefixLength:@(prefix_length)];
includedRoute.gatewayAddress = adapter.networkSettingsBuilder.ipv6DefaultGateway;
[adapter.networkSettingsBuilder.ipv6IncludedRoutes addObject:includedRoute];
} else {
NSString *subnetMask = [[NSString alloc] initWithUTF8String:IPv4::Addr::netmask_from_prefix_len(prefix_length).to_string().c_str()];
NEIPv4Route *includedRoute = [[NEIPv4Route alloc] initWithDestinationAddress:route subnetMask:subnetMask];
includedRoute.gatewayAddress = adapter.networkSettingsBuilder.ipv4DefaultGateway;
[adapter.networkSettingsBuilder.ipv4IncludedRoutes addObject:includedRoute];
}
return true;
}
bool tun_builder_exclude_route(const std::string& address, int prefix_length, int metric, bool ipv6) override {
NSString *route = [[NSString alloc] initWithUTF8String:address.c_str()];
if (ipv6) {
NEIPv6Route *excludedRoute = [[NEIPv6Route alloc] initWithDestinationAddress:route networkPrefixLength:@(prefix_length)];
[adapter.networkSettingsBuilder.ipv6ExcludedRoutes addObject:excludedRoute];
} else {
NSString *subnetMask = [[NSString alloc] initWithUTF8String:IPv4::Addr::netmask_from_prefix_len(prefix_length).to_string().c_str()];
NEIPv4Route *excludedRoute = [[NEIPv4Route alloc] initWithDestinationAddress:route subnetMask:subnetMask];
[adapter.networkSettingsBuilder.ipv4ExcludedRoutes addObject:excludedRoute];
}
return true;
}
bool tun_builder_add_dns_server(const std::string& address, bool ipv6) override {
NSString *dnsAddress = [[NSString alloc] initWithUTF8String:address.c_str()];
[adapter.networkSettingsBuilder.dnsServers addObject:dnsAddress];
return true;
}
bool tun_builder_add_search_domain(const std::string& domain) override {
NSString *searchDomain = [[NSString alloc] initWithUTF8String:domain.c_str()];
[adapter.networkSettingsBuilder.searchDomains addObject:searchDomain];
return true;
}
bool tun_builder_set_mtu(int mtu) override {
adapter.networkSettingsBuilder.mtu = @(mtu);
return true;
}
bool tun_builder_set_session_name(const std::string& name) override {
adapter.sessionName = [[NSString alloc] initWithUTF8String:name.c_str()];
return true;
}
bool tun_builder_add_proxy_bypass(const std::string& bypass_host) override {
NSString *bypassHost = [[NSString alloc] initWithUTF8String:bypass_host.c_str()];
[adapter.networkSettingsBuilder.proxyExceptionList addObject:bypassHost];
return true;
}
bool tun_builder_set_proxy_auto_config_url(const std::string& urlString) override {
NSURL *url = [[NSURL alloc] initWithString:[[NSString alloc] initWithUTF8String:urlString.c_str()]];
adapter.networkSettingsBuilder.autoProxyConfigurationEnabled = url != nil;
adapter.networkSettingsBuilder.proxyAutoConfigurationURL = url;
return true;
}
bool tun_builder_set_proxy_http(const std::string& host, int port) override {
NSString *address = [[NSString alloc] initWithUTF8String:host.c_str()];
adapter.networkSettingsBuilder.httpProxyServerEnabled = YES;
adapter.networkSettingsBuilder.httpProxyServer = [[NEProxyServer alloc] initWithAddress:address port:port];
return true;
}
bool tun_builder_set_proxy_https(const std::string& host, int port) override {
NSString *address = [[NSString alloc] initWithUTF8String:host.c_str()];
adapter.networkSettingsBuilder.httpsProxyServerEnabled = YES;
adapter.networkSettingsBuilder.httpsProxyServer = [[NEProxyServer alloc] initWithAddress:address port:port];
return true;
}
bool tun_builder_set_block_ipv6(bool block_ipv6) override {
return false;
}
bool tun_builder_new() override {
reset_tun();
return true;
}
int tun_builder_establish() override {
NEPacketTunnelNetworkSettings *networkSettings = adapter.networkSettingsBuilder.networkSettings;
if (!networkSettings) {
return -1;
}
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[adapter.delegate openVPNAdapter:adapter configureTunnelWithNetworkSettings:networkSettings completionHandler:^(NEPacketTunnelFlow * _Nullable flow) {
adapter.packetFlowBridge = [[OpenVPNPacketFlowBridge alloc] initWithPacketFlow:flow];
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC));
if (adapter.packetFlowBridge) {
return adapter.packetFlowBridge.socketHandle;
} else {
return -1;
}
}
void tun_builder_teardown(bool disconnect) override {
reset_tun();
}
bool tun_builder_persist() override {
return true;
}
bool socket_protect(int socket) override {
return true;
}
bool pause_on_connection_timeout() override {
return false;
}
void external_pki_cert_request(ClientAPI::ExternalPKICertRequest& certreq) override {
}
void external_pki_sign_request(ClientAPI::ExternalPKISignRequest& signreq) override {
}
void event(const ClientAPI::Event& event) override {
NSString *name = [[NSString alloc] initWithUTF8String:event.name.c_str()];
NSString *message = [[NSString alloc] initWithUTF8String:event.info.c_str()];
if (event.error) {
OpenVPNAdapterError errorCode = [adapter errorByName:name];
NSString *errorReason = [adapter reasonForError:errorCode];
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: @"OpenVPN error occured.",
NSLocalizedFailureReasonErrorKey: errorReason,
OpenVPNAdapterErrorMessageKey: message != nil ? message : @"",
OpenVPNAdapterErrorFatalKey: @(event.fatal) };
NSError *error = [NSError errorWithDomain:OpenVPNAdapterErrorDomain code:errorCode userInfo:userInfo];
[adapter.delegate openVPNAdapter:adapter handleError:error];
} else {
OpenVPNAdapterEvent eventIdentifier = [adapter eventByName:name];
[adapter.delegate openVPNAdapter:adapter handleEvent:eventIdentifier message:message.length ? message : nil];
}
}
void log(const ClientAPI::LogInfo& log) override {
if ([adapter.delegate respondsToSelector:@selector(openVPNAdapter:handleLogMessage:)]) {
[adapter.delegate openVPNAdapter:adapter handleLogMessage:[[NSString alloc] initWithUTF8String:log.text.c_str()]];
}
}
void clock_tick() override {
if ([adapter.delegate respondsToSelector:@selector(openVPNAdapterDidReceiveClockTick:)]) {
[adapter.delegate openVPNAdapterDidReceiveClockTick:adapter];
}
}
void reset_tun() {
adapter.packetFlowBridge = nil;
adapter.networkSettingsBuilder = nil;
adapter.sessionName = nil;
}
private:
OpenVPNAdapter *adapter;
};
@implementation OpenVPNAdapter
#pragma mark - Initialization
- (instancetype)init {
if ((self = [super init])) {
self.client = new Client(self);
if (self = [super init]) {
_vpnClient = new OpenVPNClient(self);
}
return self;
}
@@ -274,16 +51,24 @@ private:
#pragma mark - OpenVPNClient Lifecycle
- (OpenVPNProperties *)applyConfiguration:(OpenVPNConfiguration *)configuration error:(NSError * _Nullable __autoreleasing *)error {
ClientAPI::EvalConfig eval = self.client->eval_config(configuration.config);
ClientAPI::EvalConfig eval = self.vpnClient->eval_config(configuration.config);
if (eval.error) {
if (error) {
NSString *errorReason = [self reasonForError:OpenVPNAdapterErrorConfigurationFailure];
NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] initWithDictionary:@{
NSLocalizedDescriptionKey: @"Failed to apply OpenVPN configuration",
OpenVPNAdapterErrorFatalKey: @YES
}];
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: @"Failed to apply OpenVPN configuration.",
NSLocalizedFailureReasonErrorKey: errorReason,
OpenVPNAdapterErrorMessageKey: [[NSString alloc] initWithUTF8String:eval.message.c_str()],
OpenVPNAdapterErrorFatalKey: @YES };
NSString *errorReason = [self reasonForError:OpenVPNAdapterErrorConfigurationFailure];
if (errorReason) {
userInfo[NSLocalizedFailureReasonErrorKey] = errorReason;
}
NSString *message = [[NSString alloc] initWithUTF8String:eval.message.c_str()];
if (message.length) {
userInfo[OpenVPNAdapterErrorMessageKey] = message;
}
*error = [NSError errorWithDomain:OpenVPNAdapterErrorDomain code:OpenVPNAdapterErrorConfigurationFailure userInfo: userInfo];
}
@@ -294,7 +79,7 @@ private:
}
- (BOOL)provideCredentials:(OpenVPNCredentials *)credentials error:(NSError * _Nullable __autoreleasing *)error {
ClientAPI::Status status = self.client->provide_creds(credentials.credentials);
ClientAPI::Status status = self.vpnClient->provide_creds(credentials.credentials);
if (status.error) {
if (error) {
@@ -302,12 +87,20 @@ private:
[self errorByName:[[NSString alloc] initWithUTF8String:status.status.c_str()]] :
OpenVPNAdapterErrorCredentialsFailure;
NSString *errorReason = [self reasonForError:errorCode];
NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] initWithDictionary:@{
NSLocalizedDescriptionKey: @"Failed to provide OpenVPN credentials",
OpenVPNAdapterErrorFatalKey: @YES
}];
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: @"Failed to provide OpenVPN credentials.",
NSLocalizedFailureReasonErrorKey: errorReason,
OpenVPNAdapterErrorMessageKey: [[NSString alloc] initWithUTF8String:status.message.c_str()],
OpenVPNAdapterErrorFatalKey: @YES };
NSString *errorReason = [self reasonForError:errorCode];
if (errorReason) {
userInfo[NSLocalizedFailureReasonErrorKey] = errorReason;
}
NSString *message = [[NSString alloc] initWithUTF8String:status.message.c_str()];
if (message.length) {
userInfo[OpenVPNAdapterErrorMessageKey] = message;
}
*error = [NSError errorWithDomain:OpenVPNAdapterErrorDomain code:errorCode userInfo:userInfo];
}
@@ -320,43 +113,53 @@ private:
- (void)connect {
dispatch_queue_attr_t attributes = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0);
dispatch_queue_t connectQueue = dispatch_queue_create("com.openvpnadapter.connection", attributes);
dispatch_queue_t connectQueue = dispatch_queue_create("me.ss-abramchuk.openvpn-adapter.connection", attributes);
dispatch_async(connectQueue, ^{
Client::init_process();
ClientAPI::Status status = self.client->connect();
OpenVPNClient::init_process();
ClientAPI::Status status = self.vpnClient->connect();
[self handleConnectionStatus:status];
Client::uninit_process();
OpenVPNClient::uninit_process();
});
}
- (void)reconnectAfterTimeInterval:(NSTimeInterval)timeInterval {
self.client->reconnect(timeInterval);
self.vpnClient->reconnect(timeInterval);
}
- (void)disconnect {
self.client->stop();
self.vpnClient->stop();
}
- (void)pauseWithReason:(NSString *)reason {
self.client->pause(std::string(reason.UTF8String));
self.vpnClient->pause(std::string(reason.UTF8String));
}
- (void)resume {
self.client->resume();
self.vpnClient->resume();
}
- (void)handleConnectionStatus:(ClientAPI::Status)status {
if (!status.error) {
return;
if (!status.error) { return; }
OpenVPNAdapterError errorCode = !status.status.empty() ?
[self errorByName:[[NSString alloc] initWithUTF8String:status.status.c_str()]] : OpenVPNAdapterErrorUnknown;
NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] initWithDictionary:@{
NSLocalizedDescriptionKey: @"Failed to establish connection with OpenVPN server",
OpenVPNAdapterErrorFatalKey: @YES
}];
NSString *errorReason = [self reasonForError:errorCode];
if (errorReason) {
userInfo[NSLocalizedFailureReasonErrorKey] = errorReason;
}
OpenVPNAdapterError errorCode = !status.status.empty() ? [self errorByName:[[NSString alloc] initWithUTF8String:status.status.c_str()]] : OpenVPNAdapterErrorUnknown;
NSString *errorReason = [self reasonForError:errorCode];
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: @"Failed to establish connection with OpenVPN server.",
NSLocalizedFailureReasonErrorKey: errorReason,
OpenVPNAdapterErrorMessageKey: [[NSString alloc] initWithUTF8String:status.message.c_str()],
OpenVPNAdapterErrorFatalKey: @YES };
NSString *message = [[NSString alloc] initWithUTF8String:status.message.c_str()];
if (message.length) {
userInfo[OpenVPNAdapterErrorMessageKey] = message;
}
NSError *error = [NSError errorWithDomain:OpenVPNAdapterErrorDomain code:errorCode userInfo:userInfo];
[self.delegate openVPNAdapter:self handleError:error];
@@ -365,50 +168,52 @@ private:
#pragma mark - OpenVPNClient Information
+ (NSString *)copyright {
return [[NSString alloc] initWithUTF8String:Client::copyright().c_str()];
return [[NSString alloc] initWithUTF8String:OpenVPNClient::copyright().c_str()];
}
+ (NSString *)platform {
return [[NSString alloc] initWithUTF8String:Client::platform().c_str()];
return [[NSString alloc] initWithUTF8String:OpenVPNClient::platform().c_str()];
}
- (OpenVPNConnectionInfo *)connectionInformation {
ClientAPI::ConnectionInfo information = self.client->connection_info();
ClientAPI::ConnectionInfo information = self.vpnClient->connection_info();
return information.defined ? [[OpenVPNConnectionInfo alloc] initWithConnectionInfo:information] : nil;
}
- (OpenVPNInterfaceStats *)interfaceStatistics {
return [[OpenVPNInterfaceStats alloc] initWithInterfaceStats:self.client->tun_stats()];
return [[OpenVPNInterfaceStats alloc] initWithInterfaceStats:self.vpnClient->tun_stats()];
}
- (OpenVPNSessionToken *)sessionToken {
ClientAPI::SessionToken token;
return self.client->session_token(token) ? [[OpenVPNSessionToken alloc] initWithSessionToken:token] : nil;
return self.vpnClient->session_token(token) ? [[OpenVPNSessionToken alloc] initWithSessionToken:token] : nil;
}
- (OpenVPNTransportStats *)transportStatistics {
return [[OpenVPNTransportStats alloc] initWithTransportStats:self.client->transport_stats()];
return [[OpenVPNTransportStats alloc] initWithTransportStats:self.vpnClient->transport_stats()];
}
#pragma mark - OpenVPNAdapterEvent Helpers
- (OpenVPNAdapterEvent)eventByName:(NSString *)eventName {
NSDictionary *events = @{@"DISCONNECTED": @(OpenVPNAdapterEventDisconnected),
@"CONNECTED": @(OpenVPNAdapterEventConnected),
@"RECONNECTING": @(OpenVPNAdapterEventReconnecting),
@"RESOLVE": @(OpenVPNAdapterEventResolve),
@"WAIT": @(OpenVPNAdapterEventWait),
@"WAIT_PROXY": @(OpenVPNAdapterEventWaitProxy),
@"CONNECTING": @(OpenVPNAdapterEventConnecting),
@"GET_CONFIG": @(OpenVPNAdapterEventGetConfig),
@"ASSIGN_IP": @(OpenVPNAdapterEventAssignIP),
@"ADD_ROUTES": @(OpenVPNAdapterEventAddRoutes),
@"ECHO": @(OpenVPNAdapterEventEcho),
@"INFO": @(OpenVPNAdapterEventInfo),
@"PAUSE": @(OpenVPNAdapterEventPause),
@"RESUME": @(OpenVPNAdapterEventResume),
@"RELAY": @(OpenVPNAdapterEventRelay)};
NSDictionary *events = @{
@"DISCONNECTED": @(OpenVPNAdapterEventDisconnected),
@"CONNECTED": @(OpenVPNAdapterEventConnected),
@"RECONNECTING": @(OpenVPNAdapterEventReconnecting),
@"RESOLVE": @(OpenVPNAdapterEventResolve),
@"WAIT": @(OpenVPNAdapterEventWait),
@"WAIT_PROXY": @(OpenVPNAdapterEventWaitProxy),
@"CONNECTING": @(OpenVPNAdapterEventConnecting),
@"GET_CONFIG": @(OpenVPNAdapterEventGetConfig),
@"ASSIGN_IP": @(OpenVPNAdapterEventAssignIP),
@"ADD_ROUTES": @(OpenVPNAdapterEventAddRoutes),
@"ECHO": @(OpenVPNAdapterEventEcho),
@"INFO": @(OpenVPNAdapterEventInfo),
@"PAUSE": @(OpenVPNAdapterEventPause),
@"RESUME": @(OpenVPNAdapterEventResume),
@"RELAY": @(OpenVPNAdapterEventRelay)
};
OpenVPNAdapterEvent event = events[eventName] != nil ? (OpenVPNAdapterEvent)[events[eventName] integerValue] : OpenVPNAdapterEventUnknown;
return event;
}
@@ -416,76 +221,79 @@ private:
#pragma mark - OpenVPNAdapterError Helpers
- (OpenVPNAdapterError)errorByName:(NSString *)errorName {
NSDictionary *errors = @{@"NETWORK_RECV_ERROR": @(OpenVPNAdapterErrorNetworkRecvError),
@"NETWORK_EOF_ERROR": @(OpenVPNAdapterErrorNetworkEOFError),
@"NETWORK_SEND_ERROR": @(OpenVPNAdapterErrorNetworkSendError),
@"NETWORK_UNAVAILABLE": @(OpenVPNAdapterErrorNetworkUnavailable),
@"DECRYPT_ERROR": @(OpenVPNAdapterErrorDecryptError),
@"HMAC_ERROR": @(OpenVPNAdapterErrorDecryptError),
@"REPLAY_ERROR": @(OpenVPNAdapterErrorReplayError),
@"BUFFER_ERROR": @(OpenVPNAdapterErrorBufferError),
@"CC_ERROR": @(OpenVPNAdapterErrorCCError),
@"BAD_SRC_ADDR": @(OpenVPNAdapterErrorBadSrcAddr),
@"COMPRESS_ERROR": @(OpenVPNAdapterErrorCompressError),
@"RESOLVE_ERROR": @(OpenVPNAdapterErrorResolveError),
@"SOCKET_PROTECT_ERROR": @(OpenVPNAdapterErrorSocketProtectError),
@"TUN_READ_ERROR": @(OpenVPNAdapterErrorTUNReadError),
@"TUN_WRITE_ERROR": @(OpenVPNAdapterErrorTUNWriteError),
@"TUN_FRAMING_ERROR": @(OpenVPNAdapterErrorTUNFramingError),
@"TUN_SETUP_FAILED": @(OpenVPNAdapterErrorTUNSetupFailed),
@"TUN_IFACE_CREATE": @(OpenVPNAdapterErrorTUNIfaceCreate),
@"TUN_IFACE_DISABLED": @(OpenVPNAdapterErrorTUNIfaceDisabled),
@"TUN_ERROR": @(OpenVPNAdapterErrorTUNError),
@"TAP_NOT_SUPPORTED": @(OpenVPNAdapterErrorTAPNotSupported),
@"REROUTE_GW_NO_DNS": @(OpenVPNAdapterErrorRerouteGatewayNoDns),
@"TRANSPORT_ERROR": @(OpenVPNAdapterErrorTransportError),
@"TCP_OVERFLOW": @(OpenVPNAdapterErrorTCPOverflow),
@"TCP_SIZE_ERROR": @(OpenVPNAdapterErrorTCPSizeError),
@"TCP_CONNECT_ERROR": @(OpenVPNAdapterErrorTCPConnectError),
@"UDP_CONNECT_ERROR": @(OpenVPNAdapterErrorUDPConnectError),
@"SSL_ERROR": @(OpenVPNAdapterErrorSSLError),
@"SSL_PARTIAL_WRITE": @(OpenVPNAdapterErrorSSLPartialWrite),
@"ENCAPSULATION_ERROR": @(OpenVPNAdapterErrorEncapsulationError),
@"EPKI_CERT_ERROR": @(OpenVPNAdapterErrorEPKICertError),
@"EPKI_SIGN_ERROR": @(OpenVPNAdapterErrorEPKISignError),
@"HANDSHAKE_TIMEOUT": @(OpenVPNAdapterErrorHandshakeTimeout),
@"KEEPALIVE_TIMEOUT": @(OpenVPNAdapterErrorKeepaliveTimeout),
@"INACTIVE_TIMEOUT": @(OpenVPNAdapterErrorInactiveTimeout),
@"CONNECTION_TIMEOUT": @(OpenVPNAdapterErrorConnectionTimeout),
@"PRIMARY_EXPIRE": @(OpenVPNAdapterErrorPrimaryExpire),
@"TLS_VERSION_MIN": @(OpenVPNAdapterErrorTLSVersionMin),
@"TLS_AUTH_FAIL": @(OpenVPNAdapterErrorTLSAuthFail),
@"CERT_VERIFY_FAIL": @(OpenVPNAdapterErrorCertVerifyFail),
@"PEM_PASSWORD_FAIL": @(OpenVPNAdapterErrorPEMPasswordFail),
@"AUTH_FAILED": @(OpenVPNAdapterErrorAuthFailed),
@"CLIENT_HALT": @(OpenVPNAdapterErrorClientHalt),
@"CLIENT_RESTART": @(OpenVPNAdapterErrorClientRestart),
@"RELAY": @(OpenVPNAdapterErrorRelay),
@"RELAY_ERROR": @(OpenVPNAdapterErrorRelayError),
@"N_PAUSE": @(OpenVPNAdapterErrorPauseNumber),
@"N_RECONNECT": @(OpenVPNAdapterErrorReconnectNumber),
@"N_KEY_LIMIT_RENEG": @(OpenVPNAdapterErrorKeyLimitRenegNumber),
@"KEY_STATE_ERROR": @(OpenVPNAdapterErrorKeyStateError),
@"PROXY_ERROR": @(OpenVPNAdapterErrorProxyError),
@"PROXY_NEED_CREDS": @(OpenVPNAdapterErrorProxyNeedCreds),
@"KEV_NEGOTIATE_ERROR": @(OpenVPNAdapterErrorKevNegotiateError),
@"KEV_PENDING_ERROR": @(OpenVPNAdapterErrorKevPendingError),
@"N_KEV_EXPIRE": @(OpenVPNAdapterErrorKevExpireNumber),
@"PKTID_INVALID": @(OpenVPNAdapterErrorPKTIDInvalid),
@"PKTID_BACKTRACK": @(OpenVPNAdapterErrorPKTIDBacktrack),
@"PKTID_EXPIRE": @(OpenVPNAdapterErrorPKTIDExpire),
@"PKTID_REPLAY": @(OpenVPNAdapterErrorPKTIDReplay),
@"PKTID_TIME_BACKTRACK": @(OpenVPNAdapterErrorPKTIDTimeBacktrack),
@"DYNAMIC_CHALLENGE": @(OpenVPNAdapterErrorDynamicChallenge),
@"EPKI_ERROR": @(OpenVPNAdapterErrorEPKIError),
@"EPKI_INVALID_ALIAS": @(OpenVPNAdapterErrorEPKIInvalidAlias)};
NSDictionary *errors = @{
@"NETWORK_RECV_ERROR": @(OpenVPNAdapterErrorNetworkRecvError),
@"NETWORK_EOF_ERROR": @(OpenVPNAdapterErrorNetworkEOFError),
@"NETWORK_SEND_ERROR": @(OpenVPNAdapterErrorNetworkSendError),
@"NETWORK_UNAVAILABLE": @(OpenVPNAdapterErrorNetworkUnavailable),
@"DECRYPT_ERROR": @(OpenVPNAdapterErrorDecryptError),
@"HMAC_ERROR": @(OpenVPNAdapterErrorDecryptError),
@"REPLAY_ERROR": @(OpenVPNAdapterErrorReplayError),
@"BUFFER_ERROR": @(OpenVPNAdapterErrorBufferError),
@"CC_ERROR": @(OpenVPNAdapterErrorCCError),
@"BAD_SRC_ADDR": @(OpenVPNAdapterErrorBadSrcAddr),
@"COMPRESS_ERROR": @(OpenVPNAdapterErrorCompressError),
@"RESOLVE_ERROR": @(OpenVPNAdapterErrorResolveError),
@"SOCKET_PROTECT_ERROR": @(OpenVPNAdapterErrorSocketProtectError),
@"TUN_READ_ERROR": @(OpenVPNAdapterErrorTUNReadError),
@"TUN_WRITE_ERROR": @(OpenVPNAdapterErrorTUNWriteError),
@"TUN_FRAMING_ERROR": @(OpenVPNAdapterErrorTUNFramingError),
@"TUN_SETUP_FAILED": @(OpenVPNAdapterErrorTUNSetupFailed),
@"TUN_IFACE_CREATE": @(OpenVPNAdapterErrorTUNIfaceCreate),
@"TUN_IFACE_DISABLED": @(OpenVPNAdapterErrorTUNIfaceDisabled),
@"TUN_ERROR": @(OpenVPNAdapterErrorTUNError),
@"TAP_NOT_SUPPORTED": @(OpenVPNAdapterErrorTAPNotSupported),
@"REROUTE_GW_NO_DNS": @(OpenVPNAdapterErrorRerouteGatewayNoDns),
@"TRANSPORT_ERROR": @(OpenVPNAdapterErrorTransportError),
@"TCP_OVERFLOW": @(OpenVPNAdapterErrorTCPOverflow),
@"TCP_SIZE_ERROR": @(OpenVPNAdapterErrorTCPSizeError),
@"TCP_CONNECT_ERROR": @(OpenVPNAdapterErrorTCPConnectError),
@"UDP_CONNECT_ERROR": @(OpenVPNAdapterErrorUDPConnectError),
@"SSL_ERROR": @(OpenVPNAdapterErrorSSLError),
@"SSL_PARTIAL_WRITE": @(OpenVPNAdapterErrorSSLPartialWrite),
@"ENCAPSULATION_ERROR": @(OpenVPNAdapterErrorEncapsulationError),
@"EPKI_CERT_ERROR": @(OpenVPNAdapterErrorEPKICertError),
@"EPKI_SIGN_ERROR": @(OpenVPNAdapterErrorEPKISignError),
@"HANDSHAKE_TIMEOUT": @(OpenVPNAdapterErrorHandshakeTimeout),
@"KEEPALIVE_TIMEOUT": @(OpenVPNAdapterErrorKeepaliveTimeout),
@"INACTIVE_TIMEOUT": @(OpenVPNAdapterErrorInactiveTimeout),
@"CONNECTION_TIMEOUT": @(OpenVPNAdapterErrorConnectionTimeout),
@"PRIMARY_EXPIRE": @(OpenVPNAdapterErrorPrimaryExpire),
@"TLS_VERSION_MIN": @(OpenVPNAdapterErrorTLSVersionMin),
@"TLS_AUTH_FAIL": @(OpenVPNAdapterErrorTLSAuthFail),
@"CERT_VERIFY_FAIL": @(OpenVPNAdapterErrorCertVerifyFail),
@"PEM_PASSWORD_FAIL": @(OpenVPNAdapterErrorPEMPasswordFail),
@"AUTH_FAILED": @(OpenVPNAdapterErrorAuthFailed),
@"CLIENT_HALT": @(OpenVPNAdapterErrorClientHalt),
@"CLIENT_RESTART": @(OpenVPNAdapterErrorClientRestart),
@"RELAY": @(OpenVPNAdapterErrorRelay),
@"RELAY_ERROR": @(OpenVPNAdapterErrorRelayError),
@"N_PAUSE": @(OpenVPNAdapterErrorPauseNumber),
@"N_RECONNECT": @(OpenVPNAdapterErrorReconnectNumber),
@"N_KEY_LIMIT_RENEG": @(OpenVPNAdapterErrorKeyLimitRenegNumber),
@"KEY_STATE_ERROR": @(OpenVPNAdapterErrorKeyStateError),
@"PROXY_ERROR": @(OpenVPNAdapterErrorProxyError),
@"PROXY_NEED_CREDS": @(OpenVPNAdapterErrorProxyNeedCreds),
@"KEV_NEGOTIATE_ERROR": @(OpenVPNAdapterErrorKevNegotiateError),
@"KEV_PENDING_ERROR": @(OpenVPNAdapterErrorKevPendingError),
@"N_KEV_EXPIRE": @(OpenVPNAdapterErrorKevExpireNumber),
@"PKTID_INVALID": @(OpenVPNAdapterErrorPKTIDInvalid),
@"PKTID_BACKTRACK": @(OpenVPNAdapterErrorPKTIDBacktrack),
@"PKTID_EXPIRE": @(OpenVPNAdapterErrorPKTIDExpire),
@"PKTID_REPLAY": @(OpenVPNAdapterErrorPKTIDReplay),
@"PKTID_TIME_BACKTRACK": @(OpenVPNAdapterErrorPKTIDTimeBacktrack),
@"DYNAMIC_CHALLENGE": @(OpenVPNAdapterErrorDynamicChallenge),
@"EPKI_ERROR": @(OpenVPNAdapterErrorEPKIError),
@"EPKI_INVALID_ALIAS": @(OpenVPNAdapterErrorEPKIInvalidAlias)
};
OpenVPNAdapterError error = errors[errorName] != nil ?
(OpenVPNAdapterError)[errors[errorName] integerValue] : OpenVPNAdapterErrorUnknown;
OpenVPNAdapterError error = errors[errorName] != nil ? (OpenVPNAdapterError)[errors[errorName] integerValue] : OpenVPNAdapterErrorUnknown;
return error;
}
- (NSString *)reasonForError:(OpenVPNAdapterError)error {
// TODO: Add missing error reasons
switch (error) {
case OpenVPNAdapterErrorConfigurationFailure: return @"See OpenVPN error message for more details.";
case OpenVPNAdapterErrorCredentialsFailure: return @"See OpenVPN error message for more details.";
@@ -501,6 +309,7 @@ private:
case OpenVPNAdapterErrorBadSrcAddr: return @"Packet from unknown source address.";
case OpenVPNAdapterErrorCompressError: return @"Compress/Decompress errors on data channel.";
case OpenVPNAdapterErrorResolveError: return @"DNS resolution error.";
case OpenVPNAdapterErrorSocketSetupFailed: return nil;
case OpenVPNAdapterErrorSocketProtectError: return @"Error calling protect() method on socket.";
case OpenVPNAdapterErrorTUNReadError: return @"Read errors on TUN/TAP interface.";
case OpenVPNAdapterErrorTUNWriteError: return @"Write errors on TUN/TAP interface.";
@@ -535,23 +344,23 @@ private:
case OpenVPNAdapterErrorClientRestart: return @"RESTART message from server received.";
case OpenVPNAdapterErrorRelay: return @"RELAY message from server received.";
case OpenVPNAdapterErrorRelayError: return @"RELAY error.";
case OpenVPNAdapterErrorPauseNumber: return @"";
case OpenVPNAdapterErrorReconnectNumber: return @"";
case OpenVPNAdapterErrorKeyLimitRenegNumber: return @"";
case OpenVPNAdapterErrorPauseNumber: return nil;
case OpenVPNAdapterErrorReconnectNumber: return nil;
case OpenVPNAdapterErrorKeyLimitRenegNumber: return nil;
case OpenVPNAdapterErrorKeyStateError: return @"Received packet didn't match expected key state.";
case OpenVPNAdapterErrorProxyError: return @"HTTP proxy error.";
case OpenVPNAdapterErrorProxyNeedCreds: return @"HTTP proxy needs credentials.";
case OpenVPNAdapterErrorKevNegotiateError: return @"";
case OpenVPNAdapterErrorKevPendingError: return @"";
case OpenVPNAdapterErrorKevExpireNumber: return @"";
case OpenVPNAdapterErrorPKTIDInvalid: return @"";
case OpenVPNAdapterErrorPKTIDBacktrack: return @"";
case OpenVPNAdapterErrorPKTIDExpire: return @"";
case OpenVPNAdapterErrorPKTIDReplay: return @"";
case OpenVPNAdapterErrorPKTIDTimeBacktrack: return @"";
case OpenVPNAdapterErrorDynamicChallenge: return @"";
case OpenVPNAdapterErrorEPKIError: return @"";
case OpenVPNAdapterErrorEPKIInvalidAlias: return @"";
case OpenVPNAdapterErrorKevNegotiateError: return nil;
case OpenVPNAdapterErrorKevPendingError: return nil;
case OpenVPNAdapterErrorKevExpireNumber: return nil;
case OpenVPNAdapterErrorPKTIDInvalid: return nil;
case OpenVPNAdapterErrorPKTIDBacktrack: return nil;
case OpenVPNAdapterErrorPKTIDExpire: return nil;
case OpenVPNAdapterErrorPKTIDReplay: return nil;
case OpenVPNAdapterErrorPKTIDTimeBacktrack: return nil;
case OpenVPNAdapterErrorDynamicChallenge: return nil;
case OpenVPNAdapterErrorEPKIError: return nil;
case OpenVPNAdapterErrorEPKIInvalidAlias: return nil;
case OpenVPNAdapterErrorUnknown: return @"Unknown error.";
}
}
@@ -559,16 +368,188 @@ private:
#pragma mark - Lazy Initialization
- (OpenVPNNetworkSettingsBuilder *)networkSettingsBuilder {
if (!_networkSettingsBuilder) {
_networkSettingsBuilder = [[OpenVPNNetworkSettingsBuilder alloc] init];
}
if (!_networkSettingsBuilder) { _networkSettingsBuilder = [[OpenVPNNetworkSettingsBuilder alloc] init]; }
return _networkSettingsBuilder;
}
#pragma mark - Dealloc
#pragma mark - OpenVPNClientDelegate
- (BOOL)setRemoteAddress:(NSString *)address {
self.networkSettingsBuilder.remoteAddress = address;
return YES;
}
- (BOOL)addIPV4Address:(NSString *)address subnetMask:(NSString *)subnetMask gateway:(NSString *)gateway {
self.networkSettingsBuilder.ipv4DefaultGateway = gateway;
[self.networkSettingsBuilder.ipv4LocalAddresses addObject:address];
[self.networkSettingsBuilder.ipv4SubnetMasks addObject:subnetMask];
return YES;
}
- (BOOL)addIPV6Address:(NSString *)address prefixLength:(NSNumber *)prefixLength gateway:(NSString *)gateway {
self.networkSettingsBuilder.ipv6DefaultGateway = gateway;
[self.networkSettingsBuilder.ipv6LocalAddresses addObject:address];
[self.networkSettingsBuilder.ipv6NetworkPrefixLengths addObject:prefixLength];
return YES;
}
- (BOOL)addIPV4Route:(NEIPv4Route *)route {
route.gatewayAddress = self.networkSettingsBuilder.ipv4DefaultGateway;
[self.networkSettingsBuilder.ipv4IncludedRoutes addObject:route];
return YES;
}
- (BOOL)addIPV6Route:(NEIPv6Route *)route {
route.gatewayAddress = self.networkSettingsBuilder.ipv6DefaultGateway;
[self.networkSettingsBuilder.ipv6IncludedRoutes addObject:route];
return YES;
}
- (BOOL)excludeIPV4Route:(NEIPv4Route *)route {
[self.networkSettingsBuilder.ipv4ExcludedRoutes addObject:route];
return YES;
}
- (BOOL)excludeIPV6Route:(NEIPv6Route *)route {
[self.networkSettingsBuilder.ipv6ExcludedRoutes addObject:route];
return YES;
}
- (BOOL)addDNS:(NSString *)dns {
[self.networkSettingsBuilder.dnsServers addObject:dns];
return YES;
}
- (BOOL)addSearchDomain:(NSString *)domain {
[self.networkSettingsBuilder.searchDomains addObject:domain];
return YES;
}
- (BOOL)setMTU:(NSNumber *)mtu {
self.networkSettingsBuilder.mtu = mtu;
return YES;
}
- (BOOL)setSessionName:(NSString *)name {
_sessionName = name;
return YES;
}
- (BOOL)addProxyBypassHost:(NSString *)bypassHost {
[self.networkSettingsBuilder.proxyExceptionList addObject:bypassHost];
return YES;
}
- (BOOL)setProxyAutoConfigurationURL:(NSURL *)url {
self.networkSettingsBuilder.autoProxyConfigurationEnabled = YES;
self.networkSettingsBuilder.proxyAutoConfigurationURL = url;
return YES;
}
- (BOOL)setProxyServer:(NEProxyServer *)server protocol:(OpenVPNProxyServerProtocol)protocol {
switch (protocol) {
case OpenVPNProxyServerProtocolHTTP:
self.networkSettingsBuilder.httpProxyServerEnabled = YES;
self.networkSettingsBuilder.httpProxyServer = server;
break;
case OpenVPNProxyServerProtocolHTTPS:
self.networkSettingsBuilder.httpsProxyServerEnabled = YES;
self.networkSettingsBuilder.httpsProxyServer = server;
break;
}
return YES;
}
- (BOOL)establishTunnel {
NEPacketTunnelNetworkSettings *networkSettings = [self.networkSettingsBuilder networkSettings];
if (!networkSettings) { return NO; }
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__weak typeof(self) weakSelf = self;
void (^completionHandler)(id<OpenVPNAdapterPacketFlow> _Nullable) = ^(id<OpenVPNAdapterPacketFlow> flow) {
__strong typeof(self) self = weakSelf;
if (flow) {
self.packetFlowBridge = [[OpenVPNPacketFlowBridge alloc] initWithPacketFlow:flow];
}
dispatch_semaphore_signal(semaphore);
};
[self.delegate openVPNAdapter:self configureTunnelWithNetworkSettings:networkSettings completionHandler:completionHandler];
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC));
NSError *socketError;
if (self.packetFlowBridge && [self.packetFlowBridge configureSocketsWithError:&socketError]) {
[self.packetFlowBridge startReading];
return YES;
} else {
if (socketError) { [self.delegate openVPNAdapter:self handleError:socketError]; }
return NO;
}
}
- (CFSocketNativeHandle)socketHandle {
return CFSocketGetNative(self.packetFlowBridge.openVPNSocket);
}
- (void)clientEventName:(NSString *)eventName message:(NSString *)message {
OpenVPNAdapterEvent eventIdentifier = [self eventByName:eventName];
[self.delegate openVPNAdapter:self handleEvent:eventIdentifier message:message];
}
- (void)clientErrorName:(NSString *)errorName fatal:(BOOL)fatal message:(NSString *)message {
OpenVPNAdapterError errorCode = [self errorByName:errorName];
NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] initWithDictionary:@{
NSLocalizedDescriptionKey: fatal ? @"OpenVPN fatal error occured" : @"OpenVPN error occured",
OpenVPNAdapterErrorFatalKey: @(fatal)
}];
NSString *errorReason = [self reasonForError:errorCode];
if (errorReason) {
userInfo[NSLocalizedFailureReasonErrorKey] = errorReason;
}
if (message) {
userInfo[OpenVPNAdapterErrorMessageKey] = message;
}
NSError *error = [NSError errorWithDomain:OpenVPNAdapterErrorDomain code:errorCode userInfo:userInfo];
[self.delegate openVPNAdapter:self handleError:error];
}
- (void)clientLogMessage:(NSString *)logMessage {
if ([self.delegate respondsToSelector:@selector(openVPNAdapter:handleLogMessage:)]) {
[self.delegate openVPNAdapter:self handleLogMessage:logMessage];
}
}
- (void)tick {
if ([self.delegate respondsToSelector:@selector(openVPNAdapterDidReceiveClockTick:)]) {
[self.delegate openVPNAdapterDidReceiveClockTick:self];
}
}
- (void)resetSettings {
_sessionName = nil;
_packetFlowBridge = nil;
_networkSettingsBuilder = nil;
}
#pragma mark -
- (void)dealloc {
delete _client;
delete _vpnClient;
}
@end