|
@@ -29,7 +29,7 @@
|
|
|
#import "JailbreakCheck/JailbreakCheck.h"
|
|
#import "JailbreakCheck/JailbreakCheck.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
-@interface PsiphonTunnel () <GoPsiPsiphonProvider>
|
|
|
|
|
|
|
+@interface PsiphonTunnel () <GoPsiPsiphonProvider, GoPsiPacketTunnelDeviceSender>
|
|
|
|
|
|
|
|
@property (weak) id <TunneledAppDelegate> tunneledAppDelegate;
|
|
@property (weak) id <TunneledAppDelegate> tunneledAppDelegate;
|
|
|
|
|
|
|
@@ -43,6 +43,9 @@
|
|
|
|
|
|
|
|
Reachability* reachability;
|
|
Reachability* reachability;
|
|
|
NetworkStatus previousNetworkStatus;
|
|
NetworkStatus previousNetworkStatus;
|
|
|
|
|
+
|
|
|
|
|
+ BOOL tunnelWholeDevice;
|
|
|
|
|
+ GoPsiPacketTunnelDeviceBridge* packetTunnelDeviceBridge; // only used in whole device mode
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- (id)init {
|
|
- (id)init {
|
|
@@ -50,6 +53,8 @@
|
|
|
atomic_init(&localSocksProxyPort, 0);
|
|
atomic_init(&localSocksProxyPort, 0);
|
|
|
atomic_init(&localHttpProxyPort, 0);
|
|
atomic_init(&localHttpProxyPort, 0);
|
|
|
reachability = [Reachability reachabilityForInternetConnection];
|
|
reachability = [Reachability reachabilityForInternetConnection];
|
|
|
|
|
+ tunnelWholeDevice = FALSE;
|
|
|
|
|
+ packetTunnelDeviceBridge = NULL;
|
|
|
|
|
|
|
|
return self;
|
|
return self;
|
|
|
}
|
|
}
|
|
@@ -57,7 +62,7 @@
|
|
|
#pragma mark - PsiphonTunnel public methods
|
|
#pragma mark - PsiphonTunnel public methods
|
|
|
|
|
|
|
|
// See comment in header
|
|
// See comment in header
|
|
|
-+(PsiphonTunnel * _Nonnull) newPsiphonTunnel:(id<TunneledAppDelegate> _Nonnull)tunneledAppDelegate {
|
|
|
|
|
|
|
++ (PsiphonTunnel * _Nonnull)newPsiphonTunnel:(id<TunneledAppDelegate> _Nonnull)tunneledAppDelegate {
|
|
|
@synchronized (PsiphonTunnel.self) {
|
|
@synchronized (PsiphonTunnel.self) {
|
|
|
// Only one PsiphonTunnel instance may exist at a time, as the underlying
|
|
// Only one PsiphonTunnel instance may exist at a time, as the underlying
|
|
|
// go.psi.Psi and tun2socks implementations each contain global state.
|
|
// go.psi.Psi and tun2socks implementations each contain global state.
|
|
@@ -67,7 +72,7 @@
|
|
|
dispatch_once(&onceToken, ^{
|
|
dispatch_once(&onceToken, ^{
|
|
|
sharedInstance = [[self alloc] init];
|
|
sharedInstance = [[self alloc] init];
|
|
|
});
|
|
});
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
[sharedInstance stop];
|
|
[sharedInstance stop];
|
|
|
sharedInstance.tunneledAppDelegate = tunneledAppDelegate;
|
|
sharedInstance.tunneledAppDelegate = tunneledAppDelegate;
|
|
|
|
|
|
|
@@ -76,7 +81,7 @@
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// See comment in header
|
|
// See comment in header
|
|
|
--(BOOL) start:(BOOL)ifNeeded {
|
|
|
|
|
|
|
+- (BOOL)start:(BOOL)ifNeeded {
|
|
|
if (ifNeeded) {
|
|
if (ifNeeded) {
|
|
|
return [self startIfNeeded];
|
|
return [self startIfNeeded];
|
|
|
}
|
|
}
|
|
@@ -84,14 +89,11 @@
|
|
|
return [self start];
|
|
return [self start];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
--(BOOL) start {
|
|
|
|
|
|
|
+- (BOOL)start {
|
|
|
@synchronized (PsiphonTunnel.self) {
|
|
@synchronized (PsiphonTunnel.self) {
|
|
|
[self stop];
|
|
[self stop];
|
|
|
[self logMessage:@"Starting Psiphon library"];
|
|
[self logMessage:@"Starting Psiphon library"];
|
|
|
|
|
|
|
|
- // Not supported on iOS.
|
|
|
|
|
- const BOOL useDeviceBinder = FALSE;
|
|
|
|
|
-
|
|
|
|
|
// Must always use IPv6Synthesizer for iOS
|
|
// Must always use IPv6Synthesizer for iOS
|
|
|
const BOOL useIPv6Synthesizer = TRUE;
|
|
const BOOL useIPv6Synthesizer = TRUE;
|
|
|
|
|
|
|
@@ -116,7 +118,7 @@
|
|
|
configStr,
|
|
configStr,
|
|
|
embeddedServerEntries,
|
|
embeddedServerEntries,
|
|
|
self,
|
|
self,
|
|
|
- useDeviceBinder,
|
|
|
|
|
|
|
+ tunnelWholeDevice, // useDeviceBinder
|
|
|
useIPv6Synthesizer,
|
|
useIPv6Synthesizer,
|
|
|
&e);
|
|
&e);
|
|
|
|
|
|
|
@@ -142,7 +144,7 @@
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
--(BOOL) startIfNeeded {
|
|
|
|
|
|
|
+- (BOOL)startIfNeeded {
|
|
|
PsiphonConnectionState connState = [self getConnectionState];
|
|
PsiphonConnectionState connState = [self getConnectionState];
|
|
|
BOOL localProxyAlive = [self isLocalProxyAlive];
|
|
BOOL localProxyAlive = [self isLocalProxyAlive];
|
|
|
|
|
|
|
@@ -164,7 +166,7 @@
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// See comment in header.
|
|
// See comment in header.
|
|
|
--(void) stop {
|
|
|
|
|
|
|
+- (void)stop {
|
|
|
@synchronized (PsiphonTunnel.self) {
|
|
@synchronized (PsiphonTunnel.self) {
|
|
|
[self logMessage: @"Stopping Psiphon library"];
|
|
[self logMessage: @"Stopping Psiphon library"];
|
|
|
|
|
|
|
@@ -176,26 +178,47 @@
|
|
|
|
|
|
|
|
atomic_store(&localSocksProxyPort, 0);
|
|
atomic_store(&localSocksProxyPort, 0);
|
|
|
atomic_store(&localHttpProxyPort, 0);
|
|
atomic_store(&localHttpProxyPort, 0);
|
|
|
|
|
+ packetTunnelDeviceBridge = NULL;
|
|
|
|
|
|
|
|
[self changeConnectionStateTo:PsiphonConnectionStateDisconnected evenIfSameState:NO];
|
|
[self changeConnectionStateTo:PsiphonConnectionStateDisconnected evenIfSameState:NO];
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// See comment in header.
|
|
// See comment in header.
|
|
|
--(PsiphonConnectionState) getConnectionState {
|
|
|
|
|
|
|
+- (PsiphonConnectionState)getConnectionState {
|
|
|
return atomic_load(&connectionState);
|
|
return atomic_load(&connectionState);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// See comment in header.
|
|
// See comment in header.
|
|
|
--(NSInteger) getLocalSocksProxyPort {
|
|
|
|
|
|
|
+- (NSInteger)getLocalSocksProxyPort {
|
|
|
return atomic_load(&localSocksProxyPort);
|
|
return atomic_load(&localSocksProxyPort);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// See comment in header.
|
|
// See comment in header.
|
|
|
--(NSInteger) getLocalHttpProxyPort {
|
|
|
|
|
|
|
+- (NSInteger)getLocalHttpProxyPort {
|
|
|
return atomic_load(&localHttpProxyPort);
|
|
return atomic_load(&localHttpProxyPort);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// See comment in header.
|
|
|
|
|
+- (long)getPacketTunnelMTU {
|
|
|
|
|
+ return GoPsiGetPacketTunnelMTU();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// See comment in header.
|
|
|
|
|
+- (NSString * _Nonnull)getPacketTunnelDNSResolverIPv4Address {
|
|
|
|
|
+ return GoPsiGetPacketTunnelDNSResolverIPv4Address();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// See comment in header.
|
|
|
|
|
+- (NSString * _Nonnull)getPacketTunnelDNSResolverIPv6Address {
|
|
|
|
|
+ return GoPsiGetPacketTunnelDNSResolverIPv6Address();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// See comment in header.
|
|
|
|
|
+- (void)receivedFromDevice:(NSMutableData * _Nonnull)packet {
|
|
|
|
|
+ [packetTunnelDeviceBridge receivedFromDevice:packet];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// See comment in header.
|
|
// See comment in header.
|
|
|
- (void)sendFeedback:(NSString * _Nonnull)feedbackJson
|
|
- (void)sendFeedback:(NSString * _Nonnull)feedbackJson
|
|
|
publicKey:(NSString * _Nonnull)b64EncodedPublicKey
|
|
publicKey:(NSString * _Nonnull)b64EncodedPublicKey
|
|
@@ -215,7 +238,7 @@
|
|
|
Build the config string for the tunnel.
|
|
Build the config string for the tunnel.
|
|
|
@returns String containing the JSON config. `nil` on error.
|
|
@returns String containing the JSON config. `nil` on error.
|
|
|
*/
|
|
*/
|
|
|
--(NSString * _Nullable)getConfig {
|
|
|
|
|
|
|
+- (NSString * _Nullable)getConfig {
|
|
|
// tunneledAppDelegate is a weak reference, so check it.
|
|
// tunneledAppDelegate is a weak reference, so check it.
|
|
|
if (self.tunneledAppDelegate == nil) {
|
|
if (self.tunneledAppDelegate == nil) {
|
|
|
[self logMessage:@"tunneledApp delegate lost"];
|
|
[self logMessage:@"tunneledApp delegate lost"];
|
|
@@ -374,9 +397,19 @@
|
|
|
[self logMessage:[NSString stringWithFormat: @"UpgradeDownloadFilename overridden from '%@' to '%@'", defaultUpgradeDownloadFilename, config[@"UpgradeDownloadFilename"]]];
|
|
[self logMessage:[NSString stringWithFormat: @"UpgradeDownloadFilename overridden from '%@' to '%@'", defaultUpgradeDownloadFilename, config[@"UpgradeDownloadFilename"]]];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ //
|
|
|
|
|
+ // Tunnel Whole Device (defaults to not whole device)
|
|
|
|
|
+ //
|
|
|
|
|
+
|
|
|
|
|
+ // We'll record our state about what mode we're in.
|
|
|
|
|
+ tunnelWholeDevice = ([config[@"TunnelWholeDevice"] integerValue] == 1);
|
|
|
|
|
+ if (tunnelWholeDevice && ![self.tunneledAppDelegate respondsToSelector:@selector(sendToDevice:)]) {
|
|
|
|
|
+ [self logMessage:@"If TunnelWholeDevice is desired, then sendToDevice must be implemented"];
|
|
|
|
|
+ return nil;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// Other optional fields not being altered. If not set, their defaults will be used:
|
|
// Other optional fields not being altered. If not set, their defaults will be used:
|
|
|
// * EstablishTunnelTimeoutSeconds
|
|
// * EstablishTunnelTimeoutSeconds
|
|
|
- // * TunnelWholeDevice
|
|
|
|
|
// * LocalSocksProxyPort
|
|
// * LocalSocksProxyPort
|
|
|
// * LocalHttpProxyPort
|
|
// * LocalHttpProxyPort
|
|
|
// * UpstreamProxyUrl
|
|
// * UpstreamProxyUrl
|
|
@@ -669,10 +702,10 @@
|
|
|
#pragma mark - GoPsiPsiphonProvider protocol implementation (private)
|
|
#pragma mark - GoPsiPsiphonProvider protocol implementation (private)
|
|
|
|
|
|
|
|
- (BOOL)bindToDevice:(long)fileDescriptor error:(NSError **)error {
|
|
- (BOOL)bindToDevice:(long)fileDescriptor error:(NSError **)error {
|
|
|
- // This function is only called in TunnelWholeDevice mode
|
|
|
|
|
-
|
|
|
|
|
- // TODO: Does this function ever get called?
|
|
|
|
|
-
|
|
|
|
|
|
|
+ if (!tunnelWholeDevice) {
|
|
|
|
|
+ return FALSE;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// TODO: Determine if this is robust.
|
|
// TODO: Determine if this is robust.
|
|
|
unsigned int interfaceIndex = if_nametoindex("ap1");
|
|
unsigned int interfaceIndex = if_nametoindex("ap1");
|
|
|
|
|
|
|
@@ -687,11 +720,13 @@
|
|
|
|
|
|
|
|
- (NSString *)getPrimaryDnsServer {
|
|
- (NSString *)getPrimaryDnsServer {
|
|
|
// This function is only called when BindToDevice is used/supported.
|
|
// This function is only called when BindToDevice is used/supported.
|
|
|
|
|
+ // TODO: Implement correctly
|
|
|
return @"8.8.8.8";
|
|
return @"8.8.8.8";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- (NSString *)getSecondaryDnsServer {
|
|
- (NSString *)getSecondaryDnsServer {
|
|
|
// This function is only called when BindToDevice is used/supported.
|
|
// This function is only called when BindToDevice is used/supported.
|
|
|
|
|
+ // TODO: Implement correctly
|
|
|
return @"8.8.4.4";
|
|
return @"8.8.4.4";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -722,6 +757,22 @@
|
|
|
[self handlePsiphonNotice:noticeJSON];
|
|
[self handlePsiphonNotice:noticeJSON];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+- (GoPsiPacketTunnelDeviceBridge*)getPacketTunnelDeviceBridge {
|
|
|
|
|
+ if (!packetTunnelDeviceBridge) {
|
|
|
|
|
+ packetTunnelDeviceBridge = GoPsiNewPacketTunnelDeviceBridge(self);
|
|
|
|
|
+ }
|
|
|
|
|
+ return packetTunnelDeviceBridge;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+#pragma mark - GoPsiPacketTunnelDeviceWriter protocol implementation (private)
|
|
|
|
|
+
|
|
|
|
|
+- (void)sendToDevice:(NSMutableData *)packet {
|
|
|
|
|
+ // The check to see if the delegate responds to this optional selector was
|
|
|
|
|
+ // done when processing the config, so we're not going to do it again for every packet.
|
|
|
|
|
+ [self.tunneledAppDelegate sendToDevice:packet];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
|
|
|
#pragma mark - Helpers (private)
|
|
#pragma mark - Helpers (private)
|
|
|
|
|
|