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

Add UDP port forward test coverage

Rod Hynes 9 лет назад
Родитель
Сommit
72d41aa4cc
2 измененных файлов с 108 добавлено и 5 удалено
  1. 101 0
      psiphon/server/server_test.go
  2. 7 5
      psiphon/server/udp.go

+ 101 - 0
psiphon/server/server_test.go

@@ -24,6 +24,7 @@ import (
 	"flag"
 	"fmt"
 	"io/ioutil"
+	"net"
 	"net/http"
 	"net/url"
 	"os"
@@ -34,6 +35,7 @@ import (
 
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
+	"golang.org/x/net/proxy"
 )
 
 func TestMain(m *testing.M) {
@@ -165,6 +167,8 @@ func runServer(t *testing.T, runConfig *runServerConfig) {
 	serverConfig.(map[string]interface{})["GeoIPDatabaseFilename"] = ""
 	serverConfig.(map[string]interface{})["PsinetDatabaseFilename"] = psinetFilename
 	serverConfig.(map[string]interface{})["TrafficRulesFilename"] = ""
+	serverConfig.(map[string]interface{})["LogLevel"] = "debug"
+
 	serverConfigJSON, _ = json.Marshal(serverConfig)
 
 	// run server
@@ -225,6 +229,7 @@ func runServer(t *testing.T, runConfig *runServerConfig) {
 
 	// TODO: currently, TargetServerEntry only works with one tunnel
 	numTunnels := 1
+	localSOCKSProxyPort := 1081
 	localHTTPProxyPort := 8081
 	establishTunnelPausePeriodSeconds := 1
 
@@ -245,6 +250,7 @@ func runServer(t *testing.T, runConfig *runServerConfig) {
 	clientConfig.EstablishTunnelPausePeriodSeconds = &establishTunnelPausePeriodSeconds
 	clientConfig.TargetServerEntry = string(encodedServerEntry)
 	clientConfig.TunnelProtocol = runConfig.tunnelProtocol
+	clientConfig.LocalSocksProxyPort = localSOCKSProxyPort
 	clientConfig.LocalHttpProxyPort = localHTTPProxyPort
 
 	err = psiphon.InitDataStore(clientConfig)
@@ -338,6 +344,16 @@ func runServer(t *testing.T, runConfig *runServerConfig) {
 
 	// Test: tunneled web site fetch
 
+	makeTunneledWebRequest(t, localHTTPProxyPort)
+
+	// Test: tunneled UDP packet
+
+	udpgwServerAddress := serverConfig.(map[string]interface{})["UDPInterceptUdpgwServerAddress"].(string)
+	makeTunneledDNSRequest(t, localSOCKSProxyPort, udpgwServerAddress)
+}
+
+func makeTunneledWebRequest(t *testing.T, localHTTPProxyPort int) {
+
 	testUrl := "https://psiphon.ca"
 	roundTripTimeout := 30 * time.Second
 
@@ -365,6 +381,91 @@ func runServer(t *testing.T, runConfig *runServerConfig) {
 	response.Body.Close()
 }
 
+func makeTunneledDNSRequest(t *testing.T, localSOCKSProxyPort int, udpgwServerAddress string) {
+
+	testHostname := "psiphon.ca"
+	timeout := 10 * time.Second
+
+	localUDPProxyAddress, err := net.ResolveUDPAddr("udp", "127.0.0.1:7301")
+	if err != nil {
+		t.Fatalf("ResolveUDPAddr failed: %s", err)
+	}
+
+	go func() {
+
+		serverUDPConn, err := net.ListenUDP("udp", localUDPProxyAddress)
+		if err != nil {
+			t.Fatalf("ListenUDP failed: %s", err)
+		}
+		defer serverUDPConn.Close()
+
+		udpgwPreambleSize := 11 // see writeUdpgwPreamble
+		buffer := make([]byte, udpgwProtocolMaxMessageSize)
+		packetSize, clientAddr, err := serverUDPConn.ReadFromUDP(
+			buffer[udpgwPreambleSize:len(buffer)])
+		if err != nil {
+			t.Fatalf("serverUDPConn.Read failed: %s", err)
+		}
+
+		socksProxyAddress := fmt.Sprintf("127.0.0.1:%d", localSOCKSProxyPort)
+
+		dialer, err := proxy.SOCKS5("tcp", socksProxyAddress, nil, proxy.Direct)
+		if err != nil {
+			t.Fatalf("proxy.SOCKS5 failed: %s", err)
+		}
+
+		socksTCPConn, err := dialer.Dial("tcp", udpgwServerAddress)
+		if err != nil {
+			t.Fatalf("dialer.Dial failed: %s", err)
+		}
+		defer socksTCPConn.Close()
+
+		err = writeUdpgwPreamble(
+			udpgwPreambleSize,
+			udpgwProtocolFlagDNS,
+			0,
+			make([]byte, 4), // ignored due to transparent DNS forwarding
+			53,
+			uint16(packetSize),
+			buffer)
+		if err != nil {
+			t.Fatalf("writeUdpgwPreamble failed: %s", err)
+		}
+
+		_, err = socksTCPConn.Write(buffer[0 : udpgwPreambleSize+packetSize])
+		if err != nil {
+			t.Fatalf("socksTCPConn.Write failed: %s", err)
+		}
+
+		updgwProtocolMessage, err := readUdpgwMessage(socksTCPConn, buffer)
+		if err != nil {
+			t.Fatalf("readUdpgwMessage failed: %s", err)
+		}
+
+		_, err = serverUDPConn.WriteToUDP(updgwProtocolMessage.packet, clientAddr)
+		if err != nil {
+			t.Fatalf("serverUDPConn.Write failed: %s", err)
+		}
+	}()
+
+	// TODO: properly synchronize with server startup
+	time.Sleep(1 * time.Second)
+
+	clientUDPConn, err := net.DialUDP("udp", nil, localUDPProxyAddress)
+	if err != nil {
+		t.Fatalf("DialUDP failed: %s", err)
+	}
+	defer clientUDPConn.Close()
+
+	clientUDPConn.SetReadDeadline(time.Now().Add(timeout))
+	clientUDPConn.SetWriteDeadline(time.Now().Add(timeout))
+
+	_, _, err = psiphon.ResolveIP(testHostname, clientUDPConn)
+	if err != nil {
+		t.Fatalf("ResolveIP failed: %s", err)
+	}
+}
+
 func pavePsinetDatabaseFile(t *testing.T, psinetFilename string) (string, string) {
 
 	sponsorID, _ := common.MakeRandomStringHex(8)

+ 7 - 5
psiphon/server/udp.go

@@ -324,6 +324,7 @@ func (portForward *udpPortForward) relayDownstream() {
 
 		err = writeUdpgwPreamble(
 			portForward.preambleSize,
+			0,
 			portForward.connID,
 			portForward.remoteIP,
 			portForward.remotePort,
@@ -377,7 +378,7 @@ const (
 	udpgwProtocolMaxMessageSize  = udpgwProtocolMaxPreambleSize + udpgwProtocolMaxPayloadSize
 )
 
-type udpProtocolMessage struct {
+type udpgwProtocolMessage struct {
 	connID              uint16
 	preambleSize        int
 	remoteIP            []byte
@@ -388,7 +389,7 @@ type udpProtocolMessage struct {
 }
 
 func readUdpgwMessage(
-	reader io.Reader, buffer []byte) (*udpProtocolMessage, error) {
+	reader io.Reader, buffer []byte) (*udpgwProtocolMessage, error) {
 
 	// udpgw message layout:
 	//
@@ -455,9 +456,9 @@ func readUdpgwMessage(
 		}
 
 		// Assemble message
-		// Note: udpProtocolMessage.packet references memory in the input buffer
+		// Note: udpgwProtocolMessage.packet references memory in the input buffer
 
-		message := &udpProtocolMessage{
+		message := &udpgwProtocolMessage{
 			connID:              connID,
 			preambleSize:        packetStart,
 			remoteIP:            remoteIP,
@@ -473,6 +474,7 @@ func readUdpgwMessage(
 
 func writeUdpgwPreamble(
 	preambleSize int,
+	flags uint8,
 	connID uint16,
 	remoteIP []byte,
 	remotePort uint16,
@@ -490,7 +492,7 @@ func writeUdpgwPreamble(
 	buffer[1] = byte(size >> 8)
 
 	// flags
-	buffer[2] = 0
+	buffer[2] = flags
 
 	// connID
 	buffer[3] = byte(connID & 0xFF)