Просмотр исходного кода

Added IPv6Synthesizer interface which allows the core tunnel to call into the host application to synthesize IPv6 addresses from IPv4 ones. This is used to correctly lookup IPs on DNS64/NAT64 networks.

Miro Kuratczyk 9 лет назад
Родитель
Сommit
778398d30c

+ 5 - 1
MobileLibrary/Android/PsiphonTunnel/PsiphonTunnel.java

@@ -335,6 +335,9 @@ public class PsiphonTunnel extends Psi.PsiphonProvider.Stub {
         return DEFAULT_SECONDARY_DNS_SERVER;
         return DEFAULT_SECONDARY_DNS_SERVER;
     }
     }
 
 
+    @Override
+    public String IPv6Synthesize(String IPv4Addr) { return IPv4Addr; }
+
     //----------------------------------------------------------------------------------------------
     //----------------------------------------------------------------------------------------------
     // Psiphon Tunnel Core
     // Psiphon Tunnel Core
     //----------------------------------------------------------------------------------------------
     //----------------------------------------------------------------------------------------------
@@ -347,7 +350,8 @@ public class PsiphonTunnel extends Psi.PsiphonProvider.Stub {
                     loadPsiphonConfig(mHostService.getContext()),
                     loadPsiphonConfig(mHostService.getContext()),
                     embeddedServerEntries,
                     embeddedServerEntries,
                     this,
                     this,
-                    isVpnMode());
+                    isVpnMode(),
+                    false /* Do not use IPv6 synthesizer for android */);
         } catch (java.lang.Exception e) {
         } catch (java.lang.Exception e) {
             throw new Exception("failed to start Psiphon library", e);
             throw new Exception("failed to start Psiphon library", e);
         }
         }

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

@@ -7,6 +7,8 @@
 	objects = {
 	objects = {
 
 
 /* Begin PBXBuildFile section */
 /* Begin PBXBuildFile section */
+		4E89F7FE1E2ED3CE00005F4C /* LookupIPv6.c in Sources */ = {isa = PBXBuildFile; fileRef = 4E89F7FC1E2ED3CE00005F4C /* LookupIPv6.c */; };
+		4E89F7FF1E2ED3CE00005F4C /* LookupIPv6.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E89F7FD1E2ED3CE00005F4C /* LookupIPv6.h */; };
 		660E0B7A1E2D6EB6002BF5D4 /* Psi in Frameworks */ = {isa = PBXBuildFile; fileRef = 660E0B791E2D6EB6002BF5D4 /* Psi */; };
 		660E0B7A1E2D6EB6002BF5D4 /* Psi in Frameworks */ = {isa = PBXBuildFile; fileRef = 660E0B791E2D6EB6002BF5D4 /* Psi */; };
 		662659271DD270E900872F6C /* Reachability.h in Headers */ = {isa = PBXBuildFile; fileRef = 662659251DD270E900872F6C /* Reachability.h */; };
 		662659271DD270E900872F6C /* Reachability.h in Headers */ = {isa = PBXBuildFile; fileRef = 662659251DD270E900872F6C /* Reachability.h */; };
 		662659281DD270E900872F6C /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 662659261DD270E900872F6C /* Reachability.m */; };
 		662659281DD270E900872F6C /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 662659261DD270E900872F6C /* Reachability.m */; };
@@ -60,6 +62,8 @@
 /* End PBXCopyFilesBuildPhase section */
 /* End PBXCopyFilesBuildPhase section */
 
 
 /* Begin PBXFileReference section */
 /* Begin PBXFileReference section */
+		4E89F7FC1E2ED3CE00005F4C /* LookupIPv6.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = LookupIPv6.c; sourceTree = "<group>"; };
+		4E89F7FD1E2ED3CE00005F4C /* LookupIPv6.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LookupIPv6.h; sourceTree = "<group>"; };
 		660E0B791E2D6EB6002BF5D4 /* Psi */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = Psi; path = PsiphonTunnel/Psi.framework/Versions/A/Psi; sourceTree = "<group>"; };
 		660E0B791E2D6EB6002BF5D4 /* Psi */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = Psi; path = PsiphonTunnel/Psi.framework/Versions/A/Psi; sourceTree = "<group>"; };
 		662659251DD270E900872F6C /* Reachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reachability.h; sourceTree = "<group>"; };
 		662659251DD270E900872F6C /* Reachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reachability.h; sourceTree = "<group>"; };
 		662659261DD270E900872F6C /* Reachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = "<group>"; };
 		662659261DD270E900872F6C /* Reachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = "<group>"; };
@@ -118,6 +122,8 @@
 			children = (
 			children = (
 				662659251DD270E900872F6C /* Reachability.h */,
 				662659251DD270E900872F6C /* Reachability.h */,
 				662659261DD270E900872F6C /* Reachability.m */,
 				662659261DD270E900872F6C /* Reachability.m */,
+				4E89F7FC1E2ED3CE00005F4C /* LookupIPv6.c */,
+				4E89F7FD1E2ED3CE00005F4C /* LookupIPv6.h */,
 			);
 			);
 			path = Reachability;
 			path = Reachability;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -214,6 +220,7 @@
 			buildActionMask = 2147483647;
 			buildActionMask = 2147483647;
 			files = (
 			files = (
 				6685BDCB1E2E882800F0E414 /* ref.h in Headers */,
 				6685BDCB1E2E882800F0E414 /* ref.h in Headers */,
+				4E89F7FF1E2ED3CE00005F4C /* LookupIPv6.h in Headers */,
 				66BDB05D1DC26CCC0079384C /* SBJson4StreamParser.h in Headers */,
 				66BDB05D1DC26CCC0079384C /* SBJson4StreamParser.h in Headers */,
 				6685BDD41E2EBB1000F0E414 /* GoPsi.objc.h in Headers */,
 				6685BDD41E2EBB1000F0E414 /* GoPsi.objc.h in Headers */,
 				6685BDD51E2EBB1000F0E414 /* Universe.objc.h in Headers */,
 				6685BDD51E2EBB1000F0E414 /* Universe.objc.h in Headers */,
@@ -337,6 +344,7 @@
 				66BDB0641DC26CCC0079384C /* SBJson4StreamWriter.m in Sources */,
 				66BDB0641DC26CCC0079384C /* SBJson4StreamWriter.m in Sources */,
 				66BDB0661DC26CCC0079384C /* SBJson4StreamWriterState.m in Sources */,
 				66BDB0661DC26CCC0079384C /* SBJson4StreamWriterState.m in Sources */,
 				66BDB05C1DC26CCC0079384C /* SBJson4Parser.m in Sources */,
 				66BDB05C1DC26CCC0079384C /* SBJson4Parser.m in Sources */,
+				4E89F7FE1E2ED3CE00005F4C /* LookupIPv6.c in Sources */,
 				66BDB0681DC26CCC0079384C /* SBJson4Writer.m in Sources */,
 				66BDB0681DC26CCC0079384C /* SBJson4Writer.m in Sources */,
 				66BDB0621DC26CCC0079384C /* SBJson4StreamTokeniser.m in Sources */,
 				66BDB0621DC26CCC0079384C /* SBJson4StreamTokeniser.m in Sources */,
 				66BDB0441DA6C7DD0079384C /* PsiphonTunnel.m in Sources */,
 				66BDB0441DA6C7DD0079384C /* PsiphonTunnel.m in Sources */,

+ 16 - 0
MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.m

@@ -19,6 +19,7 @@
 
 
 #import <CoreTelephony/CTTelephonyNetworkInfo.h>
 #import <CoreTelephony/CTTelephonyNetworkInfo.h>
 #import <CoreTelephony/CTCarrier.h>
 #import <CoreTelephony/CTCarrier.h>
+#import "LookupIPv6.h"
 #import "Psi-meta.h"
 #import "Psi-meta.h"
 #import "PsiphonTunnel.h"
 #import "PsiphonTunnel.h"
 #import "Reachability.h"
 #import "Reachability.h"
@@ -62,6 +63,9 @@
 
 
         // Not supported on iOS.
         // Not supported on iOS.
         const BOOL useDeviceBinder = FALSE;
         const BOOL useDeviceBinder = FALSE;
+
+        // Must always use IPv6Synthesizer for iOS
+        const BOOL useIPv6Synthesizer = TRUE;
         
         
         NSString *configStr = [self getConfig];
         NSString *configStr = [self getConfig];
         if (configStr == nil) {
         if (configStr == nil) {
@@ -76,6 +80,7 @@
                            embeddedServerEntries,
                            embeddedServerEntries,
                            self,
                            self,
                            useDeviceBinder,
                            useDeviceBinder,
+                           useIPv6Synthesizer,
                            &e);
                            &e);
             
             
             [self logMessage:[NSString stringWithFormat: @"GoPsiStart: %@", res ? @"TRUE" : @"FALSE"]];
             [self logMessage:[NSString stringWithFormat: @"GoPsiStart: %@", res ? @"TRUE" : @"FALSE"]];
@@ -555,6 +560,17 @@
     return (netstat != NotReachable) ? 1 : 0;
     return (netstat != NotReachable) ? 1 : 0;
 }
 }
 
 
+- (NSString *)iPv6Synthesize:(NSString *)IPv4Addr {
+    // This function is called to synthesize an ipv6 address from an ipv4 one on a DNS64/NAT64 network
+    char *result = getIPv6ForIPv4([IPv4Addr UTF8String]);
+    if (result != NULL) {
+        NSString *IPv6Addr = [NSString stringWithUTF8String:result];
+        free(result);
+        return IPv6Addr;
+    }
+    return @"";
+}
+
 - (void)notice:(NSString *)noticeJSON {
 - (void)notice:(NSString *)noticeJSON {
     [self handlePsiphonNotice:noticeJSON];
     [self handlePsiphonNotice:noticeJSON];
 }
 }

+ 55 - 0
MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/Reachability/LookupIPv6.c

@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2016, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "LookupIPv6.h"
+
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+
+char *getIPv6ForIPv4(const char *ipv4_str) {
+    char *ipv6_str = NULL;
+    struct addrinfo hints, *res, *res0;
+    int error;
+    
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = PF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_flags = AI_DEFAULT;
+    error = getaddrinfo(ipv4_str, NULL, &hints, &res0);
+    if (error) {
+        /* NOTREACHED */
+        return NULL;
+    }
+
+    for (res = res0; res; res = res->ai_next) {
+        if (res->ai_addr->sa_family != AF_INET6) {
+            continue;
+        }
+        struct sockaddr_in6 *sockaddr = (struct sockaddr_in6*)res->ai_addr;
+        ipv6_str = (char *)malloc(sizeof(char)*(INET6_ADDRSTRLEN)); // INET6_ADDRSTRLEN includes null terminating character
+        inet_ntop(AF_INET6, &(sockaddr->sin6_addr), ipv6_str, INET6_ADDRSTRLEN);
+        break;
+    }
+    
+    freeaddrinfo(res0);
+    return ipv6_str;
+}

+ 25 - 0
MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/Reachability/LookupIPv6.h

@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef LookupIPv6_h
+#define LookupIPv6_h
+
+char *getIPv6ForIPv4(const char *ipv4_str);
+
+#endif /* LookupIPv6_h */

+ 6 - 1
MobileLibrary/psi/psi.go

@@ -37,6 +37,7 @@ type PsiphonProvider interface {
 	Notice(noticeJSON string)
 	Notice(noticeJSON string)
 	HasNetworkConnectivity() int
 	HasNetworkConnectivity() int
 	BindToDevice(fileDescriptor int) error
 	BindToDevice(fileDescriptor int) error
+	IPv6Synthesize(IPv4Addr string) string
 	GetPrimaryDnsServer() string
 	GetPrimaryDnsServer() string
 	GetSecondaryDnsServer() string
 	GetSecondaryDnsServer() string
 }
 }
@@ -49,7 +50,7 @@ var controllerWaitGroup *sync.WaitGroup
 func Start(
 func Start(
 	configJson, embeddedServerEntryList string,
 	configJson, embeddedServerEntryList string,
 	provider PsiphonProvider,
 	provider PsiphonProvider,
-	useDeviceBinder bool) error {
+	useDeviceBinder bool, useIPv6Synthesizer bool) error {
 
 
 	controllerMutex.Lock()
 	controllerMutex.Lock()
 	defer controllerMutex.Unlock()
 	defer controllerMutex.Unlock()
@@ -69,6 +70,10 @@ func Start(
 		config.DnsServerGetter = provider
 		config.DnsServerGetter = provider
 	}
 	}
 
 
+	if useIPv6Synthesizer {
+		config.IPv6Synthesizer = provider
+	}
+
 	psiphon.SetNoticeOutput(psiphon.NewNoticeReceiver(
 	psiphon.SetNoticeOutput(psiphon.NewNoticeReceiver(
 		func(notice []byte) {
 		func(notice []byte) {
 			provider.Notice(string(notice))
 			provider.Notice(string(notice))

+ 16 - 0
psiphon/TCPConn.go

@@ -104,6 +104,22 @@ func interruptibleTCPDial(addr string, config *DialConfig) (*TCPConn, error) {
 	// when tcpDial, amoung other things, when makes a blocking syscall.Connect()
 	// when tcpDial, amoung other things, when makes a blocking syscall.Connect()
 	// call.
 	// call.
 	go func() {
 	go func() {
+		if config.IPv6Synthesizer != nil {
+			// Synthesize an ipv6 address from an ipv4 one
+			// This is for compatibility on DNS64/NAT64 networks
+			host, port, err := net.SplitHostPort(addr)
+			if err != nil {
+				return
+			}
+			ip := net.ParseIP(host)
+			if ip != nil && len(ip) == net.IPv4len {
+				synthesizedAddr := config.IPv6Synthesizer.IPv6Synthesize(host)
+				if synthesizedAddr != "" {
+					addr = net.JoinHostPort(synthesizedAddr, port)
+				}
+			}
+		}
+
 		var netConn net.Conn
 		var netConn net.Conn
 		var err error
 		var err error
 		if config.UpstreamProxyUrl != "" {
 		if config.UpstreamProxyUrl != "" {

+ 27 - 6
psiphon/TCPConn_bind.go

@@ -78,12 +78,23 @@ func tcpDial(addr string, config *DialConfig, dialResult chan error) (net.Conn,
 		return nil, common.ContextError(err)
 		return nil, common.ContextError(err)
 	}
 	}
 
 
-	// TODO: IPv6 support
-	var ip [4]byte
-	copy(ip[:], ipAddrs[index].To4())
+	var ip []byte
+	var domain int
+	ipAddr := ipAddrs[index]
+
+	// Get address type (IPv4 or IPv6)
+	if len(ipAddr) == net.IPv4len {
+		ip = make([]byte, 4)
+		copy(ip[:], ipAddr.To4())
+		domain = syscall.AF_INET
+	} else if len(ipAddr) == net.IPv6len {
+		ip = make([]byte, 16)
+		copy(ip[:], ipAddr.To16())
+		domain = syscall.AF_INET6
+	}
 
 
 	// Create a socket and bind to device, when configured to do so
 	// Create a socket and bind to device, when configured to do so
-	socketFd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, 0)
+	socketFd, err := syscall.Socket(domain, syscall.SOCK_STREAM, 0)
 	if err != nil {
 	if err != nil {
 		return nil, common.ContextError(err)
 		return nil, common.ContextError(err)
 	}
 	}
@@ -100,8 +111,18 @@ func tcpDial(addr string, config *DialConfig, dialResult chan error) (net.Conn,
 		}
 		}
 	}
 	}
 
 
-	sockAddr := syscall.SockaddrInet4{Addr: ip, Port: port}
-	err = syscall.Connect(socketFd, &sockAddr)
+	// Connect socket fd to the address
+	if domain == syscall.AF_INET {
+		var inet4Addr [4]byte
+		copy(inet4Addr[:], ip)
+		servAddr := syscall.SockaddrInet4{Addr: inet4Addr, Port: port}
+		err = syscall.Connect(socketFd, &servAddr)
+	} else if domain == syscall.AF_INET6 {
+		var inet6Addr [16]byte
+		copy(inet6Addr[:], ip)
+		servAddr := syscall.SockaddrInet6{Addr: inet6Addr, Port: port}
+		err = syscall.Connect(socketFd, &servAddr)
+	}
 	if err != nil {
 	if err != nil {
 		syscall.Close(socketFd)
 		syscall.Close(socketFd)
 		return nil, common.ContextError(err)
 		return nil, common.ContextError(err)

+ 5 - 0
psiphon/config.go

@@ -230,6 +230,11 @@ type Config struct {
 	// deployments.
 	// deployments.
 	DeviceBinder DeviceBinder
 	DeviceBinder DeviceBinder
 
 
+	// IPv6Synthesizer is an interface that allows the core tunnel to call
+	// into the host application to synthesize IPv6 addresses from IPv4 ones. This
+	// is used to correctly lookup IPs on DNS64/NAT64 networks.
+	IPv6Synthesizer IPv6Synthesizer
+
 	// DnsServerGetter is an interface that enables the core tunnel to call
 	// DnsServerGetter is an interface that enables the core tunnel to call
 	// into the host application to discover the native network DNS server settings.
 	// into the host application to discover the native network DNS server settings.
 	// This parameter is only applicable to library deployments.
 	// This parameter is only applicable to library deployments.

+ 1 - 0
psiphon/controller.go

@@ -100,6 +100,7 @@ func NewController(config *Config) (controller *Controller, err error) {
 		PendingConns:                  untunneledPendingConns,
 		PendingConns:                  untunneledPendingConns,
 		DeviceBinder:                  config.DeviceBinder,
 		DeviceBinder:                  config.DeviceBinder,
 		DnsServerGetter:               config.DnsServerGetter,
 		DnsServerGetter:               config.DnsServerGetter,
+		IPv6Synthesizer:               config.IPv6Synthesizer,
 		UseIndistinguishableTLS:       config.UseIndistinguishableTLS,
 		UseIndistinguishableTLS:       config.UseIndistinguishableTLS,
 		TrustedCACertificatesFilename: config.TrustedCACertificatesFilename,
 		TrustedCACertificatesFilename: config.TrustedCACertificatesFilename,
 		DeviceRegion:                  config.DeviceRegion,
 		DeviceRegion:                  config.DeviceRegion,

+ 1 - 0
psiphon/feedback.go

@@ -113,6 +113,7 @@ func SendFeedback(configJson, diagnosticsJson, b64EncodedPublicKey, uploadServer
 		UpstreamProxyCustomHeaders:    config.UpstreamProxyCustomHeaders,
 		UpstreamProxyCustomHeaders:    config.UpstreamProxyCustomHeaders,
 		PendingConns:                  nil,
 		PendingConns:                  nil,
 		DeviceBinder:                  nil,
 		DeviceBinder:                  nil,
+		IPv6Synthesizer:               nil,
 		DnsServerGetter:               nil,
 		DnsServerGetter:               nil,
 		UseIndistinguishableTLS:       config.UseIndistinguishableTLS,
 		UseIndistinguishableTLS:       config.UseIndistinguishableTLS,
 		TrustedCACertificatesFilename: config.TrustedCACertificatesFilename,
 		TrustedCACertificatesFilename: config.TrustedCACertificatesFilename,

+ 6 - 0
psiphon/net.go

@@ -79,6 +79,7 @@ type DialConfig struct {
 	// current active untunneled network DNS server.
 	// current active untunneled network DNS server.
 	DeviceBinder    DeviceBinder
 	DeviceBinder    DeviceBinder
 	DnsServerGetter DnsServerGetter
 	DnsServerGetter DnsServerGetter
+	IPv6Synthesizer IPv6Synthesizer
 
 
 	// UseIndistinguishableTLS specifies whether to try to use an
 	// UseIndistinguishableTLS specifies whether to try to use an
 	// alternative stack for TLS. From a circumvention perspective,
 	// alternative stack for TLS. From a circumvention perspective,
@@ -123,6 +124,11 @@ type DnsServerGetter interface {
 	GetSecondaryDnsServer() string
 	GetSecondaryDnsServer() string
 }
 }
 
 
+// IPv6Synthesizer defines the interface to the external IPv6Synthesize provider
+type IPv6Synthesizer interface {
+	IPv6Synthesize(IPv4Addr string) string
+}
+
 // TimeoutError implements the error interface
 // TimeoutError implements the error interface
 type TimeoutError struct{}
 type TimeoutError struct{}
 
 

+ 1 - 0
psiphon/tunnel.go

@@ -629,6 +629,7 @@ func dialSsh(
 		PendingConns:                  pendingConns,
 		PendingConns:                  pendingConns,
 		DeviceBinder:                  config.DeviceBinder,
 		DeviceBinder:                  config.DeviceBinder,
 		DnsServerGetter:               config.DnsServerGetter,
 		DnsServerGetter:               config.DnsServerGetter,
+		IPv6Synthesizer:               config.IPv6Synthesizer,
 		UseIndistinguishableTLS:       config.UseIndistinguishableTLS,
 		UseIndistinguishableTLS:       config.UseIndistinguishableTLS,
 		TrustedCACertificatesFilename: config.TrustedCACertificatesFilename,
 		TrustedCACertificatesFilename: config.TrustedCACertificatesFilename,
 		DeviceRegion:                  config.DeviceRegion,
 		DeviceRegion:                  config.DeviceRegion,