diff --git a/OpenVPN Adapter/OpenVPNAdapter.h b/OpenVPN Adapter/OpenVPNAdapter.h index fb8ec37..45b5ccb 100644 --- a/OpenVPN Adapter/OpenVPNAdapter.h +++ b/OpenVPN Adapter/OpenVPNAdapter.h @@ -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 - -/** - 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 *packets, NSArray *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 *)packets withProtocols:(NSArray *)protocols; - -@end - @protocol OpenVPNAdapterDelegate /** diff --git a/OpenVPN Adapter/OpenVPNAdapter.mm b/OpenVPN Adapter/OpenVPNAdapter.mm index e7f83d8..b4a367b 100644 --- a/OpenVPN Adapter/OpenVPNAdapter.mm +++ b/OpenVPN Adapter/OpenVPNAdapter.mm @@ -8,265 +8,42 @@ #define OPENVPN_EXTERN extern +#import "OpenVPNAdapter.h" + #import -#import -#import + +#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 () -@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 _Nullable) = ^(id 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