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

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

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

@@ -7,6 +7,8 @@
 	objects = {
 
 /* 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 */; };
 		662659271DD270E900872F6C /* Reachability.h in Headers */ = {isa = PBXBuildFile; fileRef = 662659251DD270E900872F6C /* Reachability.h */; };
 		662659281DD270E900872F6C /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 662659261DD270E900872F6C /* Reachability.m */; };
@@ -60,6 +62,8 @@
 /* End PBXCopyFilesBuildPhase 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>"; };
 		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>"; };
@@ -118,6 +122,8 @@
 			children = (
 				662659251DD270E900872F6C /* Reachability.h */,
 				662659261DD270E900872F6C /* Reachability.m */,
+				4E89F7FC1E2ED3CE00005F4C /* LookupIPv6.c */,
+				4E89F7FD1E2ED3CE00005F4C /* LookupIPv6.h */,
 			);
 			path = Reachability;
 			sourceTree = "<group>";
@@ -214,6 +220,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				6685BDCB1E2E882800F0E414 /* ref.h in Headers */,
+				4E89F7FF1E2ED3CE00005F4C /* LookupIPv6.h in Headers */,
 				66BDB05D1DC26CCC0079384C /* SBJson4StreamParser.h in Headers */,
 				6685BDD41E2EBB1000F0E414 /* GoPsi.objc.h in Headers */,
 				6685BDD51E2EBB1000F0E414 /* Universe.objc.h in Headers */,
@@ -337,6 +344,7 @@
 				66BDB0641DC26CCC0079384C /* SBJson4StreamWriter.m in Sources */,
 				66BDB0661DC26CCC0079384C /* SBJson4StreamWriterState.m in Sources */,
 				66BDB05C1DC26CCC0079384C /* SBJson4Parser.m in Sources */,
+				4E89F7FE1E2ED3CE00005F4C /* LookupIPv6.c in Sources */,
 				66BDB0681DC26CCC0079384C /* SBJson4Writer.m in Sources */,
 				66BDB0621DC26CCC0079384C /* SBJson4StreamTokeniser.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/CTCarrier.h>
+#import "LookupIPv6.h"
 #import "Psi-meta.h"
 #import "PsiphonTunnel.h"
 #import "Reachability.h"
@@ -62,6 +63,9 @@
 
         // Not supported on iOS.
         const BOOL useDeviceBinder = FALSE;
+
+        // Must always use IPv6Synthesizer for iOS
+        const BOOL useIPv6Synthesizer = TRUE;
         
         NSString *configStr = [self getConfig];
         if (configStr == nil) {
@@ -76,6 +80,7 @@
                            embeddedServerEntries,
                            self,
                            useDeviceBinder,
+                           useIPv6Synthesizer,
                            &e);
             
             [self logMessage:[NSString stringWithFormat: @"GoPsiStart: %@", res ? @"TRUE" : @"FALSE"]];
@@ -555,6 +560,17 @@
     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 {
     [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)
 	HasNetworkConnectivity() int
 	BindToDevice(fileDescriptor int) error
+	IPv6Synthesize(IPv4Addr string) string
 	GetPrimaryDnsServer() string
 	GetSecondaryDnsServer() string
 }
@@ -49,7 +50,7 @@ var controllerWaitGroup *sync.WaitGroup
 func Start(
 	configJson, embeddedServerEntryList string,
 	provider PsiphonProvider,
-	useDeviceBinder bool) error {
+	useDeviceBinder bool, useIPv6Synthesizer bool) error {
 
 	controllerMutex.Lock()
 	defer controllerMutex.Unlock()
@@ -69,6 +70,10 @@ func Start(
 		config.DnsServerGetter = provider
 	}
 
+	if useIPv6Synthesizer {
+		config.IPv6Synthesizer = provider
+	}
+
 	psiphon.SetNoticeOutput(psiphon.NewNoticeReceiver(
 		func(notice []byte) {
 			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()
 	// call.
 	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 err error
 		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)
 	}
 
-	// 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
-	socketFd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, 0)
+	socketFd, err := syscall.Socket(domain, syscall.SOCK_STREAM, 0)
 	if err != nil {
 		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 {
 		syscall.Close(socketFd)
 		return nil, common.ContextError(err)

+ 5 - 0
psiphon/config.go

@@ -230,6 +230,11 @@ type Config struct {
 	// deployments.
 	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
 	// into the host application to discover the native network DNS server settings.
 	// 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,
 		DeviceBinder:                  config.DeviceBinder,
 		DnsServerGetter:               config.DnsServerGetter,
+		IPv6Synthesizer:               config.IPv6Synthesizer,
 		UseIndistinguishableTLS:       config.UseIndistinguishableTLS,
 		TrustedCACertificatesFilename: config.TrustedCACertificatesFilename,
 		DeviceRegion:                  config.DeviceRegion,

+ 1 - 0
psiphon/feedback.go

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

+ 6 - 0
psiphon/net.go

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

+ 1 - 0
psiphon/tunnel.go

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