Files
OpenVPNAdapter/OpenVPN Adapter/OpenVPNAdapter.mm
2017-10-11 13:39:41 +01:00

819 lines
37 KiB
Plaintext

//
// OpenVPNAdapter.m
// OpenVPN Adapter
//
// Created by Sergey Abramchuk on 11.02.17.
//
//
#import "OpenVPNAdapter.h"
#define OPENVPN_EXTERN extern
#import <client/ovpncli.hpp>
#import <openvpn/tun/client/tunbase.hpp>
#import <openvpn/ip/ip.hpp>
#import <openvpn/addr/ipv4.hpp>
#import <NetworkExtension/NetworkExtension.h>
#import "OpenVPNAdapterEvent.h"
#import "OpenVPNCredentials+Internal.h"
#import "OpenVPNConfiguration+Internal.h"
#import "OpenVPNConnectionInfo+Internal.h"
#import "OpenVPNError.h"
#import "OpenVPNInterfaceStats+Internal.h"
#import "OpenVPNProperties+Internal.h"
#import "OpenVPNSessionToken+Internal.h"
#import "OpenVPNTransportStats+Internal.h"
class Client;
@interface OpenVPNAdapter () {
CFSocketRef _tunSocket;
CFSocketRef _vpnSocket;
}
@property (nonatomic) Client *client;
@property (nonatomic) NEPacketTunnelFlow *packetFlow;
@property (nonatomic) NSString *remoteAddress;
@property (nonatomic) NSString *ipv4DefaultGateway;
@property (nonatomic) NSString *ipv6DefaultGateway;
@property (nonatomic) NSNumber *mtu;
@property (nonatomic) NSMutableArray<NSString *> *ipv4LocalAddresses;
@property (nonatomic) NSMutableArray<NSString *> *ipv4SubnetMasks;
@property (nonatomic) NSMutableArray<NEIPv4Route *> *ipv4IncludedRoutes;
@property (nonatomic) NSMutableArray<NEIPv4Route *> *ipv4ExcludedRoutes;
@property (nonatomic) NSMutableArray<NSString *> *ipv6LocalAddresses;
@property (nonatomic) NSMutableArray<NSNumber *> *ipv6NetworkPrefixLengths;
@property (nonatomic) NSMutableArray<NEIPv6Route *> *ipv6IncludedRoutes;
@property (nonatomic) NSMutableArray<NEIPv6Route *> *ipv6ExcludedRoutes;
@property (nonatomic) NSMutableArray<NSString *> *dnsServers;
@property (nonatomic) NSMutableArray<NSString *> *searchDomains;
@property (nonatomic) NSMutableArray<NSString *> *proxyExceptionList;
@property (nonatomic) NSString *sessionName;
@property (nonatomic) BOOL autoProxyConfigurationEnabled;
@property (nonatomic) NSURL *proxyAutoConfigurationURL;
@property (nonatomic) BOOL httpProxyServerEnabled;
@property (nonatomic) NEProxyServer *httpProxyServer;
@property (nonatomic) BOOL httpsProxyServerEnabled;
@property (nonatomic) NEProxyServer *httpsProxyServer;
@property (nonatomic, readonly) NEPacketTunnelNetworkSettings *networkSettings;
- (CFSocketNativeHandle)configureSockets;
- (void)readTUNPackets;
- (void)teardownTunnel:(BOOL)disconnect;
- (OpenVPNAdapterError)errorByName:(NSString *)errorName;
- (OpenVPNAdapterEvent)eventByName:(NSString *)errorName;
- (NSString *)reasonForError:(OpenVPNAdapterError)error;
@end
using namespace openvpn;
class Client : public ClientAPI::OpenVPNClient {
public:
Client(OpenVPNAdapter *client) {
this->client = client;
}
bool tun_builder_set_remote_address(const std::string& address, bool ipv6) override {
NSString *remoteAddress = [[NSString alloc] initWithUTF8String:address.c_str()];
client.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) {
client.ipv6DefaultGateway = defaultGateway;
[client.ipv6LocalAddresses addObject:localAddress];
[client.ipv6NetworkPrefixLengths addObject:@(prefix_length)];
} else {
NSString *subnetMask = [[NSString alloc] initWithUTF8String:IPv4::Addr::netmask_from_prefix_len(prefix_length).to_string().c_str()];
client.ipv4DefaultGateway = defaultGateway;
[client.ipv4LocalAddresses addObject:localAddress];
[client.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 = client.ipv4DefaultGateway;
[client.ipv4IncludedRoutes addObject:includedRoute];
}
if (ipv6) {
NEIPv6Route *includedRoute = [NEIPv6Route defaultRoute];
includedRoute.gatewayAddress = client.ipv6DefaultGateway;
[client.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 = client.ipv6DefaultGateway;
[client.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 = client.ipv4DefaultGateway;
[client.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)];
[client.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];
[client.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()];
[client.dnsServers addObject:dnsAddress];
return true;
}
bool tun_builder_add_search_domain(const std::string& domain) override {
NSString *searchDomain = [[NSString alloc] initWithUTF8String:domain.c_str()];
[client.searchDomains addObject:searchDomain];
return true;
}
bool tun_builder_set_mtu(int mtu) override {
client.mtu = @(mtu);
return true;
}
bool tun_builder_set_session_name(const std::string& name) override {
client.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()];
[client.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()]];
client.autoProxyConfigurationEnabled = url != nil;
client.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()];
client.httpProxyServerEnabled = YES;
client.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()];
client.httpsProxyServerEnabled = YES;
client.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_network_parameters();
return true;
}
int tun_builder_establish() override {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[client.delegate openVPNAdapter:client configureTunnelWithNetworkSettings:client.networkSettings completionHandler:^(NEPacketTunnelFlow * _Nullable packetFlow) {
client.packetFlow = packetFlow;
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC));
if (client.packetFlow) {
[client readTUNPackets];
return [client configureSockets];
} else {
return -1;
}
}
void tun_builder_teardown(bool disconnect) override {
reset_network_parameters();
[client teardownTunnel:disconnect];
}
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 = [client errorByName:name];
NSString *errorReason = [client reasonForError:errorCode];
NSError *error = [NSError errorWithDomain:OpenVPNAdapterErrorDomain code:errorCode userInfo:@{NSLocalizedDescriptionKey: @"OpenVPN error occured.", NSLocalizedFailureReasonErrorKey: errorReason, OpenVPNAdapterErrorMessageKey: message != nil ? message : @"", OpenVPNAdapterErrorFatalKey: @(event.fatal)}];
[client.delegate openVPNAdapter:client handleError:error];
} else {
OpenVPNAdapterEvent eventIdentifier = [client eventByName:name];
[client.delegate openVPNAdapter:client handleEvent:eventIdentifier message:message.length ? message : nil];
}
}
void log(const ClientAPI::LogInfo& log) override {
if ([client.delegate respondsToSelector:@selector(openVPNAdapter:handleLogMessage:)]) {
[client.delegate openVPNAdapter:client handleLogMessage:[[NSString alloc] initWithUTF8String:log.text.c_str()]];
}
}
void clock_tick() override {
if ([client.delegate respondsToSelector:@selector(openVPNAdapterDidReceiveClockTick:)]) {
[client.delegate openVPNAdapterDidReceiveClockTick:client];
}
}
void reset_network_parameters() {
client.remoteAddress = nil;
client.ipv4DefaultGateway = nil;
client.ipv6DefaultGateway = nil;
client.mtu = nil;
client.ipv4LocalAddresses = nil;
client.ipv4SubnetMasks = nil;
client.ipv4IncludedRoutes = nil;
client.ipv4ExcludedRoutes = nil;
client.ipv6LocalAddresses = nil;
client.ipv6NetworkPrefixLengths = nil;
client.ipv6IncludedRoutes = nil;
client.ipv6ExcludedRoutes = nil;
client.dnsServers = nil;
client.searchDomains = nil;
client.sessionName = nil;
client.proxyExceptionList = nil;
client.autoProxyConfigurationEnabled = NO;
client.proxyAutoConfigurationURL = nil;
client.httpProxyServerEnabled = NO;
client.httpProxyServer = nil;
client.httpsProxyServerEnabled = NO;
client.httpsProxyServer = nil;
}
private:
OpenVPNAdapter *client;
};
@implementation OpenVPNAdapter
- (instancetype)init {
if ((self = [super init])) {
self.client = new Client(self);
}
return self;
}
static inline void SocketCallback(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) {
if (type == kCFSocketDataCallBack) {
[(__bridge OpenVPNAdapter *)info readVPNPacket:(__bridge NSData *)data];
}
}
- (CFSocketNativeHandle)configureSockets {
[self invalidateSockets];
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)]);
return NO;
}
if (![self configureBufferSizeForSocket: sockets[0]] || ![self configureBufferSizeForSocket: sockets[1]]) {
NSLog(@"Failed to configure buffer size of the sockets");
return NO;
}
CFSocketContext socketCtxt = {0, (__bridge void *)self, NULL, NULL, NULL};
_vpnSocket = CFSocketCreateWithNative(kCFAllocatorDefault, sockets[0], kCFSocketDataCallBack, SocketCallback, &socketCtxt);
_tunSocket = CFSocketCreateWithNative(kCFAllocatorDefault, sockets[1], kCFSocketNoCallBack, NULL, NULL);
if (!_vpnSocket || !_tunSocket) {
[self invalidateSockets];
NSLog(@"Failed to create core foundation sockets from native sockets");
return NO;
}
CFRunLoopSourceRef tunSocketSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _vpnSocket, 0);
CFRunLoopAddSource(CFRunLoopGetMain(), tunSocketSource, kCFRunLoopDefaultMode);
CFRelease(tunSocketSource);
return CFSocketGetNative(_tunSocket);
}
- (void)teardownTunnel:(BOOL)disconnect {
[self invalidateSockets];
}
- (void)invalidateSockets {
if (_vpnSocket) {
CFSocketInvalidate(_vpnSocket);
CFRelease(_vpnSocket);
_vpnSocket = nil;
}
if (_tunSocket) {
CFSocketInvalidate(_tunSocket);
CFRelease(_tunSocket);
_tunSocket = nil;
}
}
- (BOOL)configureBufferSizeForSocket:(int)socket {
int buf_value = 65536;
socklen_t buf_len = sizeof(buf_value);
if (setsockopt(socket, SOL_SOCKET, SO_RCVBUF, &buf_value, buf_len) == -1) {
NSLog(@"Failed to setup buffer size for input: %@", [NSString stringWithUTF8String:strerror(errno)]);
return NO;
}
if (setsockopt(socket, SOL_SOCKET, SO_SNDBUF, &buf_value, buf_len) == -1) {
NSLog(@"Failed to setup buffer size for output: %@", [NSString stringWithUTF8String:strerror(errno)]);
return NO;
}
return YES;
}
- (void)readTUNPackets {
[self.packetFlow readPacketsWithCompletionHandler:^(NSArray<NSData *> * _Nonnull packets, NSArray<NSNumber *> * _Nonnull protocols) {
[self writeVPNPackets:packets protocols:protocols];
[self readTUNPackets];
}];
}
- (void)writeVPNPackets:(NSArray<NSData *> *)packets protocols:(NSArray<NSNumber *> *)protocols {
[packets enumerateObjectsUsingBlock:^(NSData *data, NSUInteger idx, BOOL *stop) {
if (!_vpnSocket) {
*stop = YES;
return;
}
// Prepare data for sending
NSData *packet = [self prepareVPNPacket:data protocol:protocols[idx]];
// Send data to the VPN server
CFSocketSendData(_vpnSocket, NULL, (CFDataRef)packet, 0.05);
}];
}
- (NSData *)prepareVPNPacket:(NSData *)packet protocol:(NSNumber *)protocol {
NSMutableData *data = [NSMutableData new];
#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)[protocol unsignedIntegerValue]);
[data appendBytes:&prefix length:sizeof(prefix)];
#endif
[data appendData:packet];
return [data copy];
}
- (void)readVPNPacket:(NSData *)packet {
#if TARGET_OS_IPHONE
// Get network protocol from prefix
NSUInteger prefixSize = sizeof(uint32_t);
if (packet.length < prefixSize) {
NSLog(@"Incorrect OpenVPN packet size");
return;
}
uint32_t protocol = PF_UNSPEC;
[packet getBytes:&protocol length:prefixSize];
protocol = CFSwapInt32BigToHost(protocol);
NSRange range = NSMakeRange(prefixSize, packet.length - prefixSize);
NSData *data = [packet subdataWithRange:range];
#else
// Get network protocol from header
uint8_t header = 0;
[packet getBytes:&header length:1];
uint32_t version = openvpn::IPHeader::version(header);
uint8_t protocol = [self protocolFamilyForVersion:version];
NSData *data = packet;
#endif
// Send the packet to the TUN interface
if (![self.packetFlow writePackets:@[data] withProtocols:@[@(protocol)]]) {
NSLog(@"Failed to send OpenVPN packet to the TUN interface");
}
}
- (uint8_t)protocolFamilyForVersion:(uint32_t)version {
switch (version) {
case 4: return PF_INET;
case 6: return PF_INET6;
default: return PF_UNSPEC;
}
}
- (NEPacketTunnelNetworkSettings *)networkSettings {
NEPacketTunnelNetworkSettings *networkSettings = [[NEPacketTunnelNetworkSettings alloc] initWithTunnelRemoteAddress:self.remoteAddress];
NEIPv4Settings *ipv4Settings = [[NEIPv4Settings alloc] initWithAddresses:self.ipv4LocalAddresses subnetMasks:self.ipv4SubnetMasks];
ipv4Settings.includedRoutes = self.ipv4IncludedRoutes;
ipv4Settings.excludedRoutes = self.ipv4ExcludedRoutes;
networkSettings.IPv4Settings = ipv4Settings;
NEIPv6Settings *ipv6Settings = [[NEIPv6Settings alloc] initWithAddresses:self.ipv6LocalAddresses networkPrefixLengths:self.ipv6NetworkPrefixLengths];
ipv6Settings.includedRoutes = self.ipv6IncludedRoutes;
ipv6Settings.excludedRoutes = self.ipv6ExcludedRoutes;
networkSettings.IPv6Settings = ipv6Settings;
NEDNSSettings *dnsSettings = [[NEDNSSettings alloc] initWithServers:self.dnsServers];
dnsSettings.searchDomains = self.searchDomains;
networkSettings.DNSSettings = dnsSettings;
NEProxySettings *proxySettings = [[NEProxySettings alloc] init];
proxySettings.autoProxyConfigurationEnabled = self.autoProxyConfigurationEnabled;
proxySettings.proxyAutoConfigurationURL = self.proxyAutoConfigurationURL;
proxySettings.exceptionList = self.proxyExceptionList;
proxySettings.HTTPServer = self.httpProxyServer;
proxySettings.HTTPEnabled = self.httpProxyServerEnabled;
proxySettings.HTTPSServer = self.httpsProxyServer;
proxySettings.HTTPSEnabled = self.httpsProxyServerEnabled;
networkSettings.MTU = self.mtu;
return networkSettings;
}
+ (NSString *)copyright {
return [[NSString alloc] initWithUTF8String:Client::copyright().c_str()];
}
+ (NSString *)platform {
return [[NSString alloc] initWithUTF8String:Client::platform().c_str()];
}
- (OpenVPNConnectionInfo *)connectionInformation {
ClientAPI::ConnectionInfo information = self.client->connection_info();
return information.defined ? [[OpenVPNConnectionInfo alloc] initWithConnectionInfo:information] : nil;
}
- (OpenVPNInterfaceStats *)interfaceStatistics {
return [[OpenVPNInterfaceStats alloc] initWithInterfaceStats:self.client->tun_stats()];
}
- (OpenVPNSessionToken *)sessionToken {
ClientAPI::SessionToken token;
return self.client->session_token(token) ? [[OpenVPNSessionToken alloc] initWithSessionToken:token] : nil;
}
- (OpenVPNTransportStats *)transportStatistics {
return [[OpenVPNTransportStats alloc] initWithTransportStats:self.client->transport_stats()];
}
- (OpenVPNProperties *)applyConfiguration:(OpenVPNConfiguration *)configuration error:(NSError * _Nullable __autoreleasing *)error {
ClientAPI::EvalConfig eval = self.client->eval_config(configuration.config);
if (eval.error) {
if (error) {
NSString *errorReason = [self reasonForError:OpenVPNAdapterErrorConfigurationFailure];
*error = [NSError errorWithDomain:OpenVPNAdapterErrorDomain code:OpenVPNAdapterErrorConfigurationFailure userInfo:@{NSLocalizedDescriptionKey: @"Failed to apply OpenVPN configuration.", NSLocalizedFailureReasonErrorKey: errorReason, OpenVPNAdapterErrorMessageKey: [[NSString alloc] initWithUTF8String:eval.message.c_str()], OpenVPNAdapterErrorFatalKey: @YES}];
}
return nil;
}
return [[OpenVPNProperties alloc] initWithEvalConfig:eval];
}
- (BOOL)provideCredentials:(OpenVPNCredentials *)credentials error:(NSError * _Nullable __autoreleasing *)error {
ClientAPI::Status status = self.client->provide_creds(credentials.credentials);
if (status.error) {
if (error) {
OpenVPNAdapterError errorCode = !status.status.empty() ? [self errorByName:[[NSString alloc] initWithUTF8String:status.status.c_str()]] : OpenVPNAdapterErrorCredentialsFailure;
NSString *errorReason = [self reasonForError:errorCode];
*error = [NSError errorWithDomain:OpenVPNAdapterErrorDomain code:errorCode userInfo:@{NSLocalizedDescriptionKey: @"Failed to provide OpenVPN credentials.", NSLocalizedFailureReasonErrorKey: errorReason, OpenVPNAdapterErrorMessageKey: [[NSString alloc] initWithUTF8String:status.message.c_str()], OpenVPNAdapterErrorFatalKey: @YES}];
}
return NO;
}
return YES;
}
- (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_async(connectQueue, ^{
Client::init_process();
ClientAPI::Status status = self.client->connect();
[self handleStatus:status];
Client::uninit_process();
});
}
- (void)pauseWithReason:(NSString *)reason {
self.client->pause(std::string(reason.UTF8String));
}
- (void)resume {
self.client->resume();
}
- (void)reconnectAfterTimeInterval:(NSTimeInterval)timeInterval {
self.client->reconnect(timeInterval);
}
- (void)disconnect {
self.client->stop();
}
- (void)handleStatus:(ClientAPI::Status)status {
if (!status.error) {
return;
}
OpenVPNAdapterError errorCode = !status.status.empty() ? [self errorByName:[[NSString alloc] initWithUTF8String:status.status.c_str()]] : OpenVPNAdapterErrorUnknown;
NSString *errorReason = [self reasonForError:errorCode];
NSError *error = [NSError errorWithDomain:OpenVPNAdapterErrorDomain code:errorCode userInfo:@{NSLocalizedDescriptionKey: @"Failed to establish connection with OpenVPN server.", NSLocalizedFailureReasonErrorKey: errorReason, OpenVPNAdapterErrorMessageKey: [[NSString alloc] initWithUTF8String:status.message.c_str()], OpenVPNAdapterErrorFatalKey: @YES}];
[self.delegate openVPNAdapter:self handleError:error];
}
- (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)};
OpenVPNAdapterEvent event = events[eventName] != nil ? (OpenVPNAdapterEvent)[events[eventName] integerValue] : OpenVPNAdapterEventUnknown;
return event;
}
- (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)};
OpenVPNAdapterError error = errors[errorName] != nil ? (OpenVPNAdapterError)[errors[errorName] integerValue] : OpenVPNAdapterErrorUnknown;
return error;
}
- (NSString *)reasonForError:(OpenVPNAdapterError)error {
switch (error) {
case OpenVPNAdapterErrorConfigurationFailure: return @"See OpenVPN error message for more details.";
case OpenVPNAdapterErrorCredentialsFailure: return @"See OpenVPN error message for more details.";
case OpenVPNAdapterErrorNetworkRecvError: return @"Errors receiving on network socket.";
case OpenVPNAdapterErrorNetworkEOFError: return @"EOF received on TCP network socket.";
case OpenVPNAdapterErrorNetworkSendError: return @"Errors sending on network socket";
case OpenVPNAdapterErrorNetworkUnavailable: return @"Network unavailable.";
case OpenVPNAdapterErrorDecryptError: return @"Data channel encrypt/decrypt error.";
case OpenVPNAdapterErrorHMACError: return @"HMAC verification failure.";
case OpenVPNAdapterErrorReplayError: return @"Error from PacketIDReceive.";
case OpenVPNAdapterErrorBufferError: return @"Exception thrown in Buffer methods.";
case OpenVPNAdapterErrorCCError: return @"General control channel errors.";
case OpenVPNAdapterErrorBadSrcAddr: return @"Packet from unknown source address.";
case OpenVPNAdapterErrorCompressError: return @"Compress/Decompress errors on data channel.";
case OpenVPNAdapterErrorResolveError: return @"DNS resolution error.";
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.";
case OpenVPNAdapterErrorTUNFramingError: return @"Error with tun PF_INET/PF_INET6 prefix.";
case OpenVPNAdapterErrorTUNSetupFailed: return @"Error setting up TUN/TAP interface.";
case OpenVPNAdapterErrorTUNIfaceCreate: return @"Error creating TUN/TAP interface.";
case OpenVPNAdapterErrorTUNIfaceDisabled: return @"TUN/TAP interface is disabled.";
case OpenVPNAdapterErrorTUNError: return @"General tun error.";
case OpenVPNAdapterErrorTAPNotSupported: return @"Dev TAP is present in profile but not supported.";
case OpenVPNAdapterErrorRerouteGatewayNoDns: return @"redirect-gateway specified without alt DNS servers.";
case OpenVPNAdapterErrorTransportError: return @"General transport error";
case OpenVPNAdapterErrorTCPOverflow: return @"TCP output queue overflow.";
case OpenVPNAdapterErrorTCPSizeError: return @"Bad embedded uint16_t TCP packet size.";
case OpenVPNAdapterErrorTCPConnectError: return @"Client error on TCP connect.";
case OpenVPNAdapterErrorUDPConnectError: return @"Client error on UDP connect.";
case OpenVPNAdapterErrorSSLError: return @"Errors resulting from read/write on SSL object.";
case OpenVPNAdapterErrorSSLPartialWrite: return @"SSL object did not process all written cleartext.";
case OpenVPNAdapterErrorEncapsulationError: return @"Exceptions thrown during packet encapsulation.";
case OpenVPNAdapterErrorEPKICertError: return @"Error obtaining certificate from External PKI provider.";
case OpenVPNAdapterErrorEPKISignError: return @"Error obtaining RSA signature from External PKI provider.";
case OpenVPNAdapterErrorHandshakeTimeout: return @"Handshake failed to complete within given time frame.";
case OpenVPNAdapterErrorKeepaliveTimeout: return @"Lost contact with peer.";
case OpenVPNAdapterErrorInactiveTimeout: return @"Disconnected due to inactive timer.";
case OpenVPNAdapterErrorConnectionTimeout: return @"Connection failed to establish within given time.";
case OpenVPNAdapterErrorPrimaryExpire: return @"Primary key context expired.";
case OpenVPNAdapterErrorTLSVersionMin: return @"Peer cannot handshake at our minimum required TLS version.";
case OpenVPNAdapterErrorTLSAuthFail: return @"tls-auth HMAC verification failed.";
case OpenVPNAdapterErrorCertVerifyFail: return @"Peer certificate verification failure.";
case OpenVPNAdapterErrorPEMPasswordFail: return @"Incorrect or missing PEM private key decryption password.";
case OpenVPNAdapterErrorAuthFailed: return @"General authentication failure";
case OpenVPNAdapterErrorClientHalt: return @"HALT message from server received.";
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 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 OpenVPNAdapterErrorUnknown: return @"Unknown error.";
}
}
- (NSMutableArray<NSString *> *)ipv4LocalAddresses {
if (!_ipv4LocalAddresses) {
_ipv4LocalAddresses = [[NSMutableArray alloc] init];
}
return _ipv4LocalAddresses;
}
- (NSMutableArray<NSString *> *)ipv4SubnetMasks {
if (!_ipv4SubnetMasks) {
_ipv4SubnetMasks = [[NSMutableArray alloc] init];
}
return _ipv4SubnetMasks;
}
- (NSMutableArray<NEIPv4Route *> *)ipv4IncludedRoutes {
if (!_ipv4IncludedRoutes) {
_ipv4IncludedRoutes = [[NSMutableArray alloc] init];
}
return _ipv4IncludedRoutes;
}
- (NSMutableArray<NEIPv4Route *> *)ipv4ExcludedRoutes {
if (!_ipv4ExcludedRoutes) {
_ipv4ExcludedRoutes = [[NSMutableArray alloc] init];
}
return _ipv4ExcludedRoutes;
}
- (NSMutableArray<NSString *> *)ipv6LocalAddresses {
if (!_ipv6LocalAddresses) {
_ipv6LocalAddresses = [[NSMutableArray alloc] init];
}
return _ipv6LocalAddresses;
}
- (NSMutableArray<NSNumber *> *)ipv6NetworkPrefixLengths {
if (!_ipv6NetworkPrefixLengths) {
_ipv6NetworkPrefixLengths = [[NSMutableArray alloc] init];
}
return _ipv6NetworkPrefixLengths;
}
- (NSMutableArray<NEIPv6Route *> *)ipv6IncludedRoutes {
if (!_ipv6IncludedRoutes) {
_ipv6IncludedRoutes = [[NSMutableArray alloc] init];
}
return _ipv6IncludedRoutes;
}
- (NSMutableArray<NEIPv6Route *> *)ipv6ExcludedRoutes {
if (!_ipv6ExcludedRoutes) {
_ipv6ExcludedRoutes = [[NSMutableArray alloc] init];
}
return _ipv6ExcludedRoutes;
}
- (NSMutableArray<NSString *> *)dnsServers {
if (!_dnsServers) {
_dnsServers = [[NSMutableArray alloc] init];
}
return _dnsServers;
}
- (NSMutableArray<NSString *> *)searchDomains {
if (!_searchDomains) {
_searchDomains = [[NSMutableArray alloc] init];
}
return _searchDomains;
}
- (NSMutableArray<NSString *> *)proxyExceptionList {
if (!_proxyExceptionList) {
_proxyExceptionList = [[NSMutableArray alloc] init];
}
return _proxyExceptionList;
}
- (void)dealloc {
delete _client;
}
@end