[tor-commits] [snowflake/master] Implement NAT discover for go standalone proxies

cohosh at torproject.org cohosh at torproject.org
Mon Jul 6 17:27:06 UTC 2020


commit f6cf9a453b0cae189e5ad6b25d57241fe418de91
Author: Cecylia Bocovich <cohosh at torproject.org>
Date:   Tue Jun 16 17:10:56 2020 -0400

    Implement NAT discover for go standalone proxies
---
 broker/broker.go              |  2 +-
 common/messages/proxy.go      | 30 +++++++++++++++++++-----------
 common/messages/proxy_test.go | 26 ++++++++++++++++++++++----
 proxy/snowflake.go            | 36 +++++++++++++++++++++++++++++++++++-
 4 files changed, 77 insertions(+), 17 deletions(-)

diff --git a/broker/broker.go b/broker/broker.go
index d9ef111..2d3cd4b 100644
--- a/broker/broker.go
+++ b/broker/broker.go
@@ -170,7 +170,7 @@ func proxyPolls(ctx *BrokerContext, w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	sid, proxyType, err := messages.DecodePollRequest(body)
+	sid, proxyType, _, err := messages.DecodePollRequest(body)
 	if err != nil {
 		w.WriteHeader(http.StatusBadRequest)
 		return
diff --git a/common/messages/proxy.go b/common/messages/proxy.go
index 89dd43c..923189b 100644
--- a/common/messages/proxy.go
+++ b/common/messages/proxy.go
@@ -9,15 +9,16 @@ import (
 	"strings"
 )
 
-const version = "1.1"
+const version = "1.2"
 
-/* Version 1.1 specification:
+/* Version 1.2 specification:
 
 == ProxyPollRequest ==
 {
   Sid: [generated session id of proxy],
-  Version: 1.1,
+  Version: 1.2,
   Type: ["badge"|"webext"|"standalone"]
+  NAT: ["unknown"|"restricted"|"unrestricted"]
 }
 
 == ProxyPollResponse ==
@@ -44,7 +45,7 @@ HTTP 400 BadRequest
 == ProxyAnswerRequest ==
 {
   Sid: [generated session id of proxy],
-  Version: 1.1,
+  Version: 1.2,
   Answer:
   {
     type: answer,
@@ -76,37 +77,44 @@ type ProxyPollRequest struct {
 	Sid     string
 	Version string
 	Type    string
+	NAT     string
 }
 
-func EncodePollRequest(sid string, proxyType string) ([]byte, error) {
+func EncodePollRequest(sid string, proxyType string, natType string) ([]byte, error) {
 	return json.Marshal(ProxyPollRequest{
 		Sid:     sid,
 		Version: version,
 		Type:    proxyType,
+		NAT:     natType,
 	})
 }
 
 // Decodes a poll message from a snowflake proxy and returns the
 // sid and proxy type of the proxy on success and an error if it failed
-func DecodePollRequest(data []byte) (string, string, error) {
+func DecodePollRequest(data []byte) (string, string, string, error) {
 	var message ProxyPollRequest
 
 	err := json.Unmarshal(data, &message)
 	if err != nil {
-		return "", "", err
+		return "", "", "", err
 	}
 
 	majorVersion := strings.Split(message.Version, ".")[0]
 	if majorVersion != "1" {
-		return "", "", fmt.Errorf("using unknown version")
+		return "", "", "", fmt.Errorf("using unknown version")
 	}
 
 	// Version 1.x requires an Sid
 	if message.Sid == "" {
-		return "", "", fmt.Errorf("no supplied session id")
+		return "", "", "", fmt.Errorf("no supplied session id")
 	}
 
-	return message.Sid, message.Type, nil
+	natType := message.NAT
+	if natType == "" {
+		natType = "unknown"
+	}
+
+	return message.Sid, message.Type, natType, nil
 }
 
 type ProxyPollResponse struct {
@@ -159,7 +167,7 @@ type ProxyAnswerRequest struct {
 
 func EncodeAnswerRequest(answer string, sid string) ([]byte, error) {
 	return json.Marshal(ProxyAnswerRequest{
-		Version: "1.1",
+		Version: version,
 		Sid:     sid,
 		Answer:  answer,
 	})
diff --git a/common/messages/proxy_test.go b/common/messages/proxy_test.go
index 1570d4f..3aa67fb 100644
--- a/common/messages/proxy_test.go
+++ b/common/messages/proxy_test.go
@@ -13,6 +13,7 @@ func TestDecodeProxyPollRequest(t *testing.T) {
 		for _, test := range []struct {
 			sid       string
 			proxyType string
+			natType   string
 			data      string
 			err       error
 		}{
@@ -20,6 +21,7 @@ func TestDecodeProxyPollRequest(t *testing.T) {
 				//Version 1.0 proxy message
 				"ymbcCMto7KHNGYlp",
 				"",
+				"unknown",
 				`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0"}`,
 				nil,
 			},
@@ -27,44 +29,59 @@ func TestDecodeProxyPollRequest(t *testing.T) {
 				//Version 1.1 proxy message
 				"ymbcCMto7KHNGYlp",
 				"standalone",
+				"unknown",
 				`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.1","Type":"standalone"}`,
 				nil,
 			},
+			{
+				//Version 1.2 proxy message
+				"ymbcCMto7KHNGYlp",
+				"standalone",
+				"restricted",
+				`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.2","Type":"standalone", "NAT":"restricted"}`,
+				nil,
+			},
 			{
 				//Version 0.X proxy message:
 				"",
 				"",
-				"ymbcCMto7KHNGYlp",
+				"",
+				"",
 				&json.SyntaxError{},
 			},
 			{
+				"",
 				"",
 				"",
 				`{"Sid":"ymbcCMto7KHNGYlp"}`,
 				fmt.Errorf(""),
 			},
 			{
+				"",
 				"",
 				"",
 				"{}",
 				fmt.Errorf(""),
 			},
 			{
+				"",
 				"",
 				"",
 				`{"Version":"1.0"}`,
 				fmt.Errorf(""),
 			},
 			{
+				"",
 				"",
 				"",
 				`{"Version":"2.0"}`,
 				fmt.Errorf(""),
 			},
 		} {
-			sid, proxyType, err := DecodePollRequest([]byte(test.data))
+			sid, proxyType, natType, err := DecodePollRequest([]byte(test.data))
 			So(sid, ShouldResemble, test.sid)
 			So(proxyType, ShouldResemble, test.proxyType)
+			So(natType, ShouldResemble, test.natType)
 			So(err, ShouldHaveSameTypeAs, test.err)
 		}
 
@@ -73,11 +90,12 @@ func TestDecodeProxyPollRequest(t *testing.T) {
 
 func TestEncodeProxyPollRequests(t *testing.T) {
 	Convey("Context", t, func() {
-		b, err := EncodePollRequest("ymbcCMto7KHNGYlp", "standalone")
+		b, err := EncodePollRequest("ymbcCMto7KHNGYlp", "standalone", "unknown")
 		So(err, ShouldEqual, nil)
-		sid, proxyType, err := DecodePollRequest(b)
+		sid, proxyType, natType, err := DecodePollRequest(b)
 		So(sid, ShouldEqual, "ymbcCMto7KHNGYlp")
 		So(proxyType, ShouldEqual, "standalone")
+		So(natType, ShouldEqual, "unknown")
 		So(err, ShouldEqual, nil)
 	})
 }
diff --git a/proxy/snowflake.go b/proxy/snowflake.go
index 4877e6f..ac67748 100644
--- a/proxy/snowflake.go
+++ b/proxy/snowflake.go
@@ -19,6 +19,7 @@ import (
 	"time"
 
 	"git.torproject.org/pluggable-transports/snowflake.git/common/messages"
+	"git.torproject.org/pluggable-transports/snowflake.git/common/nat"
 	"git.torproject.org/pluggable-transports/snowflake.git/common/safelog"
 	"git.torproject.org/pluggable-transports/snowflake.git/common/util"
 	"git.torproject.org/pluggable-transports/snowflake.git/common/websocketconn"
@@ -30,6 +31,11 @@ const defaultBrokerURL = "https://snowflake-broker.bamsoftware.com/"
 const defaultRelayURL = "wss://snowflake.bamsoftware.com/"
 const defaultSTUNURL = "stun:stun.l.google.com:19302"
 const pollInterval = 5 * time.Second
+const (
+	NATUnknown      = "unknown"
+	NATRestricted   = "restricted"
+	NATUnrestricted = "unrestricted"
+)
 
 //amount of time after sending an SDP answer before the proxy assumes the
 //client is not going to connect
@@ -40,6 +46,8 @@ const readLimit = 100000 //Maximum number of bytes to be read from an HTTP reque
 var broker *Broker
 var relayURL string
 
+var currentNATType = NATUnknown
+
 const (
 	sessionIDLength = 16
 )
@@ -174,7 +182,7 @@ func (b *Broker) pollOffer(sid string) *webrtc.SessionDescription {
 			timeOfNextPoll = now
 		}
 
-		body, err := messages.EncodePollRequest(sid, "standalone")
+		body, err := messages.EncodePollRequest(sid, "standalone", currentNATType)
 		if err != nil {
 			log.Printf("Error encoding poll message: %s", err.Error())
 			return nil
@@ -485,9 +493,35 @@ func main() {
 		tokens <- true
 	}
 
+	// determine NAT type before polling
+	updateNATType(config.ICEServers)
+	log.Printf("NAT type: %s", currentNATType)
+
 	for {
 		getToken()
 		sessionID := genSessionID()
 		runSession(sessionID)
 	}
 }
+
+// use provided STUN server(s) to determine NAT type
+func updateNATType(servers []webrtc.ICEServer) {
+
+	var restrictedNAT bool
+	var err error
+	for _, server := range servers {
+		addr := strings.TrimPrefix(server.URLs[0], "stun:")
+		restrictedNAT, err = nat.CheckIfRestrictedNAT(addr)
+		if err == nil {
+			if restrictedNAT {
+				currentNATType = NATRestricted
+			} else {
+				currentNATType = NATUnrestricted
+			}
+			break
+		}
+	}
+	if err != nil {
+		currentNATType = NATUnknown
+	}
+}





More information about the tor-commits mailing list