Amir Khan 8 лет назад
Родитель
Сommit
c12f36ad2b

+ 12 - 0
MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel.xcodeproj/project.pbxproj

@@ -40,6 +40,7 @@
 		66BDB0661DC26CCC0079384C /* SBJson4StreamWriterState.m in Sources */ = {isa = PBXBuildFile; fileRef = 66BDB0571DC26CCC0079384C /* SBJson4StreamWriterState.m */; };
 		66BDB0661DC26CCC0079384C /* SBJson4StreamWriterState.m in Sources */ = {isa = PBXBuildFile; fileRef = 66BDB0571DC26CCC0079384C /* SBJson4StreamWriterState.m */; };
 		66BDB0671DC26CCC0079384C /* SBJson4Writer.h in Headers */ = {isa = PBXBuildFile; fileRef = 66BDB0581DC26CCC0079384C /* SBJson4Writer.h */; };
 		66BDB0671DC26CCC0079384C /* SBJson4Writer.h in Headers */ = {isa = PBXBuildFile; fileRef = 66BDB0581DC26CCC0079384C /* SBJson4Writer.h */; };
 		66BDB0681DC26CCC0079384C /* SBJson4Writer.m in Sources */ = {isa = PBXBuildFile; fileRef = 66BDB0591DC26CCC0079384C /* SBJson4Writer.m */; };
 		66BDB0681DC26CCC0079384C /* SBJson4Writer.m in Sources */ = {isa = PBXBuildFile; fileRef = 66BDB0591DC26CCC0079384C /* SBJson4Writer.m */; };
+		EFED7EBF1F587F6E0078980F /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = EFED7EBE1F587F6E0078980F /* libresolv.tbd */; };
 /* End PBXBuildFile section */
 /* End PBXBuildFile section */
 
 
 /* Begin PBXContainerItemProxy section */
 /* Begin PBXContainerItemProxy section */
@@ -101,6 +102,7 @@
 		66BDB0571DC26CCC0079384C /* SBJson4StreamWriterState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJson4StreamWriterState.m; sourceTree = "<group>"; };
 		66BDB0571DC26CCC0079384C /* SBJson4StreamWriterState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJson4StreamWriterState.m; sourceTree = "<group>"; };
 		66BDB0581DC26CCC0079384C /* SBJson4Writer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJson4Writer.h; sourceTree = "<group>"; };
 		66BDB0581DC26CCC0079384C /* SBJson4Writer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJson4Writer.h; sourceTree = "<group>"; };
 		66BDB0591DC26CCC0079384C /* SBJson4Writer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJson4Writer.m; sourceTree = "<group>"; };
 		66BDB0591DC26CCC0079384C /* SBJson4Writer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJson4Writer.m; sourceTree = "<group>"; };
+		EFED7EBE1F587F6E0078980F /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; };
 /* End PBXFileReference section */
 /* End PBXFileReference section */
 
 
 /* Begin PBXFrameworksBuildPhase section */
 /* Begin PBXFrameworksBuildPhase section */
@@ -108,6 +110,7 @@
 			isa = PBXFrameworksBuildPhase;
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			buildActionMask = 2147483647;
 			files = (
 			files = (
+				EFED7EBF1F587F6E0078980F /* libresolv.tbd in Frameworks */,
 				660E0B7A1E2D6EB6002BF5D4 /* Psi in Frameworks */,
 				660E0B7A1E2D6EB6002BF5D4 /* Psi in Frameworks */,
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
@@ -164,6 +167,7 @@
 				6685BDC31E2E881200F0E414 /* Psi */,
 				6685BDC31E2E881200F0E414 /* Psi */,
 				66BDB02D1DA6BFCC0079384C /* PsiphonTunnelTests */,
 				66BDB02D1DA6BFCC0079384C /* PsiphonTunnelTests */,
 				66BDB0211DA6BFCC0079384C /* Products */,
 				66BDB0211DA6BFCC0079384C /* Products */,
+				EFED7EBD1F587F6E0078980F /* Frameworks */,
 			);
 			);
 			sourceTree = "<group>";
 			sourceTree = "<group>";
 		};
 		};
@@ -229,6 +233,14 @@
 			path = "json-framework";
 			path = "json-framework";
 			sourceTree = "<group>";
 			sourceTree = "<group>";
 		};
 		};
+		EFED7EBD1F587F6E0078980F /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				EFED7EBE1F587F6E0078980F /* libresolv.tbd */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 /* End PBXGroup section */
 
 
 /* Begin PBXHeadersBuildPhase section */
 /* Begin PBXHeadersBuildPhase section */

+ 92 - 5
MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.m

@@ -27,9 +27,14 @@
 #import "PsiphonTunnel.h"
 #import "PsiphonTunnel.h"
 #import "json-framework/SBJson4.h"
 #import "json-framework/SBJson4.h"
 #import "JailbreakCheck/JailbreakCheck.h"
 #import "JailbreakCheck/JailbreakCheck.h"
-#include <ifaddrs.h>
+#import <ifaddrs.h>
+#import <resolv.h>
+#import <netdb.h>
 
 
 
 
+#define GOOGLE_DNS_1 @"8.8.4.4"
+#define GOOGLE_DNS_2 @"8.8.8.8"
+
 @interface PsiphonTunnel () <GoPsiPsiphonProvider>
 @interface PsiphonTunnel () <GoPsiPsiphonProvider>
 
 
 @property (weak) id <TunneledAppDelegate> tunneledAppDelegate;
 @property (weak) id <TunneledAppDelegate> tunneledAppDelegate;
@@ -50,6 +55,14 @@
     NetworkStatus previousNetworkStatus;
     NetworkStatus previousNetworkStatus;
 
 
     BOOL tunnelWholeDevice;
     BOOL tunnelWholeDevice;
+
+    // DNS
+    NSString *primaryGoogleDNS;
+    NSString *secondaryGoogleDNS;
+
+    // This cache becomes void if internetReachabilityChanged is called.
+    // Cache can be invalidated by setting it to nil.
+    NSArray<NSString *> *initialDNSCache;
 }
 }
 
 
 - (id)init {
 - (id)init {
@@ -65,6 +78,19 @@
     self->reachability = [Reachability reachabilityForInternetConnection];
     self->reachability = [Reachability reachabilityForInternetConnection];
     self->tunnelWholeDevice = FALSE;
     self->tunnelWholeDevice = FALSE;
 
 
+    // Randomize order of Google DNS servers on start,
+    // and consistently return in that fixed order.
+    if (arc4random_uniform(2) == 0) {
+        self->primaryGoogleDNS = GOOGLE_DNS_1;
+        self->secondaryGoogleDNS = GOOGLE_DNS_2;
+    } else {
+        self->primaryGoogleDNS = GOOGLE_DNS_2;
+        self->secondaryGoogleDNS = GOOGLE_DNS_1;
+    }
+
+    self->initialDNSCache = [self getDNSServers];
+
+
     return self;
     return self;
 }
 }
 
 
@@ -815,13 +841,23 @@
 - (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
     // TODO: Implement correctly
-    return @"8.8.8.8";
+
+    if (self->initialDNSCache && [initialDNSCache count] > 0) {
+        return self->initialDNSCache[0];
+    } else {
+        return self->primaryGoogleDNS;
+    }
 }
 }
 
 
 - (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
     // TODO: Implement correctly
-    return @"8.8.4.4";
+
+    if (self->initialDNSCache && [initialDNSCache count] > 1) {
+        return self->initialDNSCache[1];
+    } else {
+        return self->secondaryGoogleDNS;
+    }
 }
 }
 
 
 - (long)hasNetworkConnectivity {
 - (long)hasNetworkConnectivity {
@@ -862,6 +898,55 @@
 
 
 #pragma mark - Helpers (private)
 #pragma mark - Helpers (private)
 
 
+/**
+    @brief Returns NSString array of DNS addresses for current active
+           network interface using libresolv.
+    @return Array of DNS addresses, nil on failure.
+ */
+
+- (NSArray<NSString *> *)getDNSServers {
+    NSMutableArray<NSString *> *serverList = [NSMutableArray new];
+
+    res_state _state;
+    _state = malloc(sizeof(struct __res_state));
+
+    if (res_ninit(_state) < 0) {
+        NSLog(@"res_ninit failed.");
+        free(_state);
+        return nil;
+    }
+
+    union res_sockaddr_union servers[NI_MAXSERV];  // Default max 32
+
+    int numServersFound = res_getservers(_state, servers, NI_MAXSERV);
+
+    char hostBuf[NI_MAXHOST];
+    for (int i = 0; i < numServersFound; i++) {
+        union res_sockaddr_union s = servers[i];
+        if (s.sin.sin_len > 0) {
+            int ret_code = getnameinfo((struct sockaddr *)&s.sin,
+              (socklen_t)s.sin.sin_len,
+              (char *)&hostBuf,
+              sizeof(hostBuf),
+              nil,
+              0,
+              NI_NUMERICHOST); // Flag "numeric form of hostname"
+
+            if (EXIT_SUCCESS == ret_code) {
+                [serverList addObject:[NSString stringWithUTF8String:hostBuf]];
+            } else {
+                NSLog(@"getnameinfo failed. Retcode: %d", ret_code);
+            }
+        }
+    }
+
+    // Clear memory used by res_ninit
+    res_ndestroy(_state);
+    free(_state);
+
+    return serverList;
+}
+
 - (void)logMessage:(NSString *)message {
 - (void)logMessage:(NSString *)message {
     if ([self.tunneledAppDelegate respondsToSelector:@selector(onDiagnosticMessage:)]) {
     if ([self.tunneledAppDelegate respondsToSelector:@selector(onDiagnosticMessage:)]) {
         dispatch_async(self->callbackQueue, ^{
         dispatch_async(self->callbackQueue, ^{
@@ -964,8 +1049,10 @@
     [[NSNotificationCenter defaultCenter] removeObserver:self name:kReachabilityChangedNotification object:nil];
     [[NSNotificationCenter defaultCenter] removeObserver:self name:kReachabilityChangedNotification object:nil];
 }
 }
 
 
-- (void)internetReachabilityChanged:(NSNotification *)note
-{
+- (void)internetReachabilityChanged:(NSNotification *)note {
+    // Invalidate initialDNSCache.
+    self->initialDNSCache = nil;
+
     // If we lose network while connected, we're going to force a reconnect in
     // If we lose network while connected, we're going to force a reconnect in
     // order to trigger the waiting-for-network state. The reason we don't wait
     // order to trigger the waiting-for-network state. The reason we don't wait
     // for the tunnel to notice the network loss is that it might take 30 seconds.
     // for the tunnel to notice the network loss is that it might take 30 seconds.