commit 0fae4ee8ea487c3b4384217e193e5b9a9088e7de
Author: Arlo Breault <[email protected]>
Date:   Fri Jan 31 00:17:50 2020 -0500

    Remove local LAN address ICE candidates
    
    Unfortunately, the "public" RTCIceTransportPolicy was removed.
    
    
https://developer.mozilla.org/en-US/docs/Web/API/RTCConfiguration#RTCIceTransportPolicy_enum
    
    Trac: 19026
---
 client/lib/lib_test.go   | 35 +++++++++++++++++++++------
 client/lib/rendezvous.go | 63 ++++++++++++++++++++++++++++++++++++++++++++----
 client/snowflake.go      |  3 ++-
 client/torrc-localhost   |  1 +
 4 files changed, 89 insertions(+), 13 deletions(-)

diff --git a/client/lib/lib_test.go b/client/lib/lib_test.go
index 91a9809..adfc9ec 100644
--- a/client/lib/lib_test.go
+++ b/client/lib/lib_test.go
@@ -288,7 +288,7 @@ func TestSnowflakeClient(t *testing.T) {
                fakeOffer := 
deserializeSessionDescription(`{"type":"offer","sdp":"test"}`)
 
                Convey("Construct BrokerChannel with no front domain", func() {
-                       b, err := NewBrokerChannel("test.broker", "", transport)
+                       b, err := NewBrokerChannel("test.broker", "", 
transport, false)
                        So(b.url, ShouldNotBeNil)
                        So(err, ShouldBeNil)
                        So(b.url.Path, ShouldResemble, "test.broker")
@@ -296,7 +296,7 @@ func TestSnowflakeClient(t *testing.T) {
                })
 
                Convey("Construct BrokerChannel *with* front domain", func() {
-                       b, err := NewBrokerChannel("test.broker", "front", 
transport)
+                       b, err := NewBrokerChannel("test.broker", "front", 
transport, false)
                        So(b.url, ShouldNotBeNil)
                        So(err, ShouldBeNil)
                        So(b.url.Path, ShouldResemble, "test.broker")
@@ -305,7 +305,7 @@ func TestSnowflakeClient(t *testing.T) {
                })
 
                Convey("BrokerChannel.Negotiate responds with answer", func() {
-                       b, err := NewBrokerChannel("test.broker", "", transport)
+                       b, err := NewBrokerChannel("test.broker", "", 
transport, false)
                        So(err, ShouldBeNil)
                        answer, err := b.Negotiate(fakeOffer)
                        So(err, ShouldBeNil)
@@ -315,7 +315,8 @@ func TestSnowflakeClient(t *testing.T) {
 
                Convey("BrokerChannel.Negotiate fails with 503", func() {
                        b, err := NewBrokerChannel("test.broker", "",
-                               &MockTransport{http.StatusServiceUnavailable, 
[]byte("\n")})
+                               &MockTransport{http.StatusServiceUnavailable, 
[]byte("\n")},
+                               false)
                        So(err, ShouldBeNil)
                        answer, err := b.Negotiate(fakeOffer)
                        So(err, ShouldNotBeNil)
@@ -325,7 +326,8 @@ func TestSnowflakeClient(t *testing.T) {
 
                Convey("BrokerChannel.Negotiate fails with 400", func() {
                        b, err := NewBrokerChannel("test.broker", "",
-                               &MockTransport{http.StatusBadRequest, 
[]byte("\n")})
+                               &MockTransport{http.StatusBadRequest, 
[]byte("\n")},
+                               false)
                        So(err, ShouldBeNil)
                        answer, err := b.Negotiate(fakeOffer)
                        So(err, ShouldNotBeNil)
@@ -335,7 +337,8 @@ func TestSnowflakeClient(t *testing.T) {
 
                Convey("BrokerChannel.Negotiate fails with large read", func() {
                        b, err := NewBrokerChannel("test.broker", "",
-                               &MockTransport{http.StatusOK, make([]byte, 
100001, 100001)})
+                               &MockTransport{http.StatusOK, make([]byte, 
100001, 100001)},
+                               false)
                        So(err, ShouldBeNil)
                        answer, err := b.Negotiate(fakeOffer)
                        So(err, ShouldNotBeNil)
@@ -345,7 +348,7 @@ func TestSnowflakeClient(t *testing.T) {
 
                Convey("BrokerChannel.Negotiate fails with unexpected error", 
func() {
                        b, err := NewBrokerChannel("test.broker", "",
-                               &MockTransport{123, []byte("")})
+                               &MockTransport{123, []byte("")}, false)
                        So(err, ShouldBeNil)
                        answer, err := b.Negotiate(fakeOffer)
                        So(err, ShouldNotBeNil)
@@ -353,4 +356,22 @@ func TestSnowflakeClient(t *testing.T) {
                        So(err.Error(), ShouldResemble, BrokerErrorUnexpected)
                })
        })
+
+       Convey("Strip", t, func() {
+               const offerStart = `{"type":"offer","sdp":"v=0\r\no=- 
4358805017720277108 2 IN IP4 8.8.8.8\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 
data\r\na=msid-semantic: WMS\r\nm=application 56688 DTLS/SCTP 5000\r\nc=IN IP4 
8.8.8.8\r\n`
+               const goodCandidate = `a=candidate:3769337065 1 udp 2122260223 
8.8.8.8 56688 typ host generation 0 network-id 1 network-cost 50\r\n`
+               const offerEnd = 
`a=ice-ufrag:aMAZ\r\na=ice-pwd:jcHb08Jjgrazp2dzjdrvPPvV\r\na=ice-options:trickle\r\na=fingerprint:sha-256
 
C8:88:EE:B9:E7:02:2E:21:37:ED:7A:D1:EB:2B:A3:15:A2:3B:5B:1C:3D:D4:D5:1F:06:CF:52:40:03:F8:DD:66\r\na=setup:actpass\r\na=mid:data\r\na=sctpmap:5000
 webrtc-datachannel 1024\r\n"}`
+
+               offer := offerStart + goodCandidate +
+                       `a=candidate:3769337065 1 udp 2122260223 192.168.0.100 
56688 typ host generation 0 network-id 1 network-cost 50\r\n` + // IsLocal IPv4
+                       `a=candidate:3769337065 1 udp 2122260223 
fdf8:f53b:82e4::53 56688 typ host generation 0 network-id 1 network-cost 
50\r\n` + // IsLocal IPv6
+                       `a=candidate:3769337065 1 udp 2122260223 0.0.0.0 56688 
typ host generation 0 network-id 1 network-cost 50\r\n` + // IsUnspecified IPv4
+                       `a=candidate:3769337065 1 udp 2122260223 :: 56688 typ 
host generation 0 network-id 1 network-cost 50\r\n` + // IsUnspecified IPv6
+                       `a=candidate:3769337065 1 udp 2122260223 127.0.0.1 
56688 typ host generation 0 network-id 1 network-cost 50\r\n` + // IsLoopback 
IPv4
+                       `a=candidate:3769337065 1 udp 2122260223 ::1 56688 typ 
host generation 0 network-id 1 network-cost 50\r\n` + // IsLoopback IPv6
+                       offerEnd
+
+               So(stripLocalAddresses(offer), ShouldEqual, 
offerStart+goodCandidate+offerEnd)
+       })
+
 }
diff --git a/client/lib/rendezvous.go b/client/lib/rendezvous.go
index d117ebc..bd8ff00 100644
--- a/client/lib/rendezvous.go
+++ b/client/lib/rendezvous.go
@@ -14,9 +14,12 @@ import (
        "io"
        "io/ioutil"
        "log"
+       "net"
        "net/http"
        "net/url"
+       "regexp"
 
+       "github.com/pion/sdp"
        "github.com/pion/webrtc"
 )
 
@@ -31,9 +34,10 @@ const (
 type BrokerChannel struct {
        // The Host header to put in the HTTP request (optional and may be
        // different from the host name in URL).
-       Host      string
-       url       *url.URL
-       transport http.RoundTripper // Used to make all requests.
+       Host               string
+       url                *url.URL
+       transport          http.RoundTripper // Used to make all requests.
+       keepLocalAddresses bool
 }
 
 // We make a copy of DefaultTransport because we want the default Dial
@@ -48,7 +52,7 @@ func CreateBrokerTransport() http.RoundTripper {
 // Construct a new BrokerChannel, where:
 // |broker| is the full URL of the facilitating program which assigns proxies
 // to clients, and |front| is the option fronting domain.
-func NewBrokerChannel(broker string, front string, transport 
http.RoundTripper) (*BrokerChannel, error) {
+func NewBrokerChannel(broker string, front string, transport 
http.RoundTripper, keepLocalAddresses bool) (*BrokerChannel, error) {
        targetURL, err := url.Parse(broker)
        if err != nil {
                return nil, err
@@ -63,6 +67,7 @@ func NewBrokerChannel(broker string, front string, transport 
http.RoundTripper)
        }
 
        bc.transport = transport
+       bc.keepLocalAddresses = keepLocalAddresses
        return bc, nil
 }
 
@@ -76,6 +81,41 @@ func limitedRead(r io.Reader, limit int64) ([]byte, error) {
        return p, err
 }
 
+// Stolen from https://github.com/golang/go/pull/30278
+func IsLocal(ip net.IP) bool {
+       if ip4 := ip.To4(); ip4 != nil {
+               // Local IPv4 addresses are defined in 
https://tools.ietf.org/html/rfc1918
+               return ip4[0] == 10 ||
+                       (ip4[0] == 172 && ip4[1]&0xf0 == 16) ||
+                       (ip4[0] == 192 && ip4[1] == 168)
+       }
+       // Local IPv6 addresses are defined in 
https://tools.ietf.org/html/rfc4193
+       return len(ip) == net.IPv6len && ip[0]&0xfe == 0xfc
+}
+
+// Removes local LAN address ICE candidates
+func stripLocalAddresses(str string) string {
+       re := regexp.MustCompile(`a=candidate:.*?\\r\\n`)
+       return re.ReplaceAllStringFunc(str, func(s string) string {
+               t := s[len("a=candidate:") : len(s)-len("\\r\\n")]
+               var ice sdp.ICECandidate
+               err := ice.Unmarshal(t)
+               if err != nil {
+                       return s
+               }
+               if ice.Typ == "host" {
+                       ip := net.ParseIP(ice.Address)
+                       if ip == nil {
+                               return s
+                       }
+                       if IsLocal(ip) || ip.IsUnspecified() || ip.IsLoopback() 
{
+                               return ""
+                       }
+               }
+               return s
+       })
+}
+
 // Roundtrip HTTP POST using WebRTC SessionDescriptions.
 //
 // Send an SDP offer to the broker, which assigns a proxy and responds
@@ -84,7 +124,20 @@ func (bc *BrokerChannel) Negotiate(offer 
*webrtc.SessionDescription) (
        *webrtc.SessionDescription, error) {
        log.Println("Negotiating via BrokerChannel...\nTarget URL: ",
                bc.Host, "\nFront URL:  ", bc.url.Host)
-       data := bytes.NewReader([]byte(serializeSessionDescription(offer)))
+       str := serializeSessionDescription(offer)
+       // Ideally, we could specify an `RTCIceTransportPolicy` that would 
handle
+       // this for us.  However, "public" was removed from the draft spec.
+       // See 
https://developer.mozilla.org/en-US/docs/Web/API/RTCConfiguration#RTCIceTransportPolicy_enum
+       //
+       // FIXME: We are stripping local addresses from the JSON serialized 
string,
+       // which is expedient but unsatisfying.  We could advocate upstream to
+       // implement a non-standard ICE transport policy, or to somehow alter
+       // APIs to avoid adding the undesirable candidates or a method to filter
+       // them from the marshalled session description.
+       if !bc.keepLocalAddresses {
+               str = stripLocalAddresses(str)
+       }
+       data := bytes.NewReader([]byte(str))
        // Suffix with broker's client registration handler.
        clientURL := bc.url.ResolveReference(&url.URL{Path: "client"})
        request, err := http.NewRequest("POST", clientURL.String(), data)
diff --git a/client/snowflake.go b/client/snowflake.go
index edcbd4a..7cf8a9d 100644
--- a/client/snowflake.go
+++ b/client/snowflake.go
@@ -90,6 +90,7 @@ func main() {
        frontDomain := flag.String("front", "", "front domain")
        logFilename := flag.String("log", "", "name of log file")
        logToStateDir := flag.Bool("logToStateDir", false, "resolve the log 
file relative to tor's pt state dir")
+       keepLocalAddresses := flag.Bool("keepLocalAddresses", false, "keep 
local LAN address ICE candidates")
        max := flag.Int("max", DefaultSnowflakeCapacity,
                "capacity for number of multiplexed WebRTC peers")
        flag.Parse()
@@ -133,7 +134,7 @@ func main() {
        snowflakes := sf.NewPeers(*max)
 
        // Use potentially domain-fronting broker to rendezvous.
-       broker, err := sf.NewBrokerChannel(*brokerURL, *frontDomain, 
sf.CreateBrokerTransport())
+       broker, err := sf.NewBrokerChannel(*brokerURL, *frontDomain, 
sf.CreateBrokerTransport(), *keepLocalAddresses)
        if err != nil {
                log.Fatalf("parsing broker URL: %v", err)
        }
diff --git a/client/torrc-localhost b/client/torrc-localhost
index 7d539fb..9afb033 100644
--- a/client/torrc-localhost
+++ b/client/torrc-localhost
@@ -3,5 +3,6 @@ DataDirectory datadir
 
 ClientTransportPlugin snowflake exec ./client \
 -url http://localhost:8080/ \
+-keepLocalAddresses
 
 Bridge snowflake 0.0.3.0:1



_______________________________________________
tor-commits mailing list
[email protected]
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits

Reply via email to