[tor-commits] [snowflake/master] copy-paste rendezvous works again, but with new interface allowing seamless recovery for the first time

serene at torproject.org serene at torproject.org
Sun Jun 12 19:44:05 UTC 2016


commit 02562ba7504811659d801d06d9fdb1c3ecfa5d9b
Author: Serene Han <keroserene+git at gmail.com>
Date:   Sun Jun 12 12:43:24 2016 -0700

    copy-paste rendezvous works again, but with new interface allowing seamless recovery for the first time
---
 client/rendezvous.go | 112 +++++++++++++++++++++++++++++++++++++++++++++++++--
 client/snowflake.go  |  65 +++++-------------------------
 client/webrtc.go     |  32 +--------------
 3 files changed, 122 insertions(+), 87 deletions(-)

diff --git a/client/rendezvous.go b/client/rendezvous.go
index 3b25c89..0acee80 100644
--- a/client/rendezvous.go
+++ b/client/rendezvous.go
@@ -1,15 +1,26 @@
-// WebRTC Rendezvous requires the exchange of SessionDescriptions between
-// peers. This file contains the domain-fronted HTTP signaling mechanism
-// between the client and a desired Broker.
+// WebRTC rendezvous requires the exchange of SessionDescriptions between
+// peers in order to establish a PeerConnection.
+//
+// This file contains the two methods currently available to Snowflake:
+//
+// - Domain-fronted HTTP signaling. The Broker automatically exchange offers
+//   and answers between this client and some remote WebRTC proxy.
+//   (This is the recommended default, enabled via the flags in "torrc".)
+//
+// - Manual copy-paste signaling. User must create a signaling pipe.
+//   (The flags in torrc-manual allow this)
 package main
 
 import (
+	"bufio"
 	"bytes"
 	"errors"
 	"io/ioutil"
 	"log"
 	"net/http"
 	"net/url"
+	"os"
+	"syscall"
 
 	"github.com/keroserene/go-webrtc"
 )
@@ -100,3 +111,98 @@ func (bc *BrokerChannel) Negotiate(offer *webrtc.SessionDescription) (
 		return nil, errors.New(BrokerErrorUnexpected)
 	}
 }
+
+// Implements the |Tongue| interface to catch snowflakes, using BrokerChannel.
+type WebRTCDialer struct {
+	*BrokerChannel
+	webrtcConfig *webrtc.Configuration
+}
+
+func NewWebRTCDialer(
+	broker *BrokerChannel, iceServers IceServerList) *WebRTCDialer {
+	config := webrtc.NewConfiguration(iceServers...)
+	return &WebRTCDialer{
+		BrokerChannel: broker,
+		webrtcConfig:  config,
+	}
+}
+
+// Initialize a WebRTC Connection by signaling through the broker.
+func (w WebRTCDialer) Catch() (*webRTCConn, error) {
+	if nil == w.BrokerChannel {
+		return nil, errors.New("Cannot Dial WebRTC without a BrokerChannel.")
+	}
+	// TODO: [#3] Fetch ICE server information from Broker.
+	// TODO: [#18] Consider TURN servers here too.
+	connection := NewWebRTCConnection(w.webrtcConfig, w.BrokerChannel)
+	err := connection.Connect()
+	return connection, err
+}
+
+// CopyPasteDialer handles the interaction required to copy-paste the
+// offers and answers.
+// Implements |Tongue| interface to catch snowflakes manually.
+// Supports recovery of connections.
+type CopyPasteDialer struct {
+	webrtcConfig *webrtc.Configuration
+	signal       *os.File
+	current      *webRTCConn
+}
+
+func NewCopyPasteDialer(iceServers IceServerList) *CopyPasteDialer {
+	log.Println("No HTTP signaling detected. Using manual copy-paste signaling.")
+	log.Println("Waiting for a \"signal\" pipe...")
+	// This FIFO receives signaling messages.
+	err := syscall.Mkfifo("signal", 0600)
+	if err != nil {
+		if syscall.EEXIST != err.(syscall.Errno) {
+			log.Fatal(err)
+		}
+	}
+	signalFile, err := os.OpenFile("signal", os.O_RDONLY, 0600)
+	if nil != err {
+		log.Fatal(err)
+		return nil
+	}
+	config := webrtc.NewConfiguration(iceServers...)
+	dialer := &CopyPasteDialer{
+		webrtcConfig: config,
+		signal:       signalFile,
+	}
+	go dialer.readSignals()
+	return dialer
+}
+
+// Initialize a WebRTC connection via manual copy-paste.
+func (d *CopyPasteDialer) Catch() (*webRTCConn, error) {
+	if nil == d.signal {
+		return nil, errors.New("Cannot copy-paste dial without signal pipe.")
+	}
+	connection := NewWebRTCConnection(d.webrtcConfig, nil)
+	// Must keep track of pending new connection until copy-paste completes.
+	d.current = connection
+	// Outputs SDP offer to log, expecting user to copy-paste to the remote Peer.
+	// Blocks until user pastes back the answer.
+	err := connection.Connect()
+	d.current = nil
+	return connection, err
+}
+
+// Manual copy-paste signalling.
+func (d *CopyPasteDialer) readSignals() {
+	defer d.signal.Close()
+	log.Printf("CopyPasteDialer: reading messages from signal pipe.")
+	s := bufio.NewScanner(d.signal)
+	for s.Scan() {
+		msg := s.Text()
+		sdp := webrtc.DeserializeSessionDescription(msg)
+		if sdp == nil {
+			log.Printf("CopyPasteDialer: ignoring invalid signal message %+q", msg)
+			continue
+		}
+		d.current.answerChannel <- sdp
+	}
+	if err := s.Err(); err != nil {
+		log.Printf("signal FIFO: %s", err)
+	}
+}
diff --git a/client/snowflake.go b/client/snowflake.go
index 948b862..7c06b17 100644
--- a/client/snowflake.go
+++ b/client/snowflake.go
@@ -2,7 +2,6 @@
 package main
 
 import (
-	"bufio"
 	"errors"
 	"flag"
 	"io"
@@ -110,45 +109,6 @@ func handler(socks SocksConnector, snowflakes SnowflakeCollector) error {
 	return nil
 }
 
-func setupCopyPaste() {
-	log.Println("No HTTP signaling detected. Waiting for a \"signal\" pipe...")
-	// This FIFO receives signaling messages.
-	err := syscall.Mkfifo("signal", 0600)
-	if err != nil {
-		if syscall.EEXIST != err.(syscall.Errno) {
-			log.Fatal(err)
-		}
-	}
-	signalFile, err := os.OpenFile("signal", os.O_RDONLY, 0600)
-	if nil != err {
-		log.Fatal(err)
-	}
-	defer signalFile.Close()
-	go readSignalingMessages(signalFile)
-}
-
-// Manual copy-paste signalling.
-// TODO: Needs fix since multiplexing changes access to the remotes.
-func readSignalingMessages(f *os.File) {
-	log.Printf("readSignalingMessages")
-	s := bufio.NewScanner(f)
-	for s.Scan() {
-		msg := s.Text()
-		log.Printf("readSignalingMessages loop %+q", msg)
-		sdp := webrtc.DeserializeSessionDescription(msg)
-		if sdp == nil {
-			log.Printf("ignoring invalid signal message %+q", msg)
-			continue
-		}
-		// webrtcRemotes[0].answerChannel <- sdp
-	}
-	log.Printf("close answerChannel")
-	// close(webrtcRemotes[0].answerChannel)
-	if err := s.Err(); err != nil {
-		log.Printf("signal FIFO: %s", err)
-	}
-}
-
 func main() {
 	webrtc.SetLoggingVerbosity(1)
 	logFile, err := os.OpenFile("snowflake.log",
@@ -169,24 +129,24 @@ func main() {
 		"capacity for number of multiplexed WebRTC peers")
 	flag.Parse()
 
-	// TODO: Maybe just get rid of copy-paste option entirely.
-	if "" == *brokerURL {
-		setupCopyPaste()
-	}
-
-	// Prepare WebRTC SnowflakeCollector, Broker, then accumulate connections.
+	// Prepare to collect remote WebRTC peers.
 	snowflakes := NewPeers(*max)
-	broker := NewBrokerChannel(*brokerURL, *frontDomain, CreateBrokerTransport())
-	snowflakes.Tongue = NewWebRTCDialer(broker, iceServers)
-
-	// Use a real logger for traffic.
+	if "" != *brokerURL {
+		// Use potentially domain-fronting broker to rendezvous.
+		broker := NewBrokerChannel(*brokerURL, *frontDomain, CreateBrokerTransport())
+		snowflakes.Tongue = NewWebRTCDialer(broker, iceServers)
+	} else {
+		// Otherwise, use manual copy and pasting of SDP messages.
+		snowflakes.Tongue = NewCopyPasteDialer(iceServers)
+	}
+	// Use a real logger to periodically output how much traffic is happening.
 	snowflakes.BytesLogger = &BytesSyncLogger{
 		inboundChan: make(chan int, 5), outboundChan: make(chan int, 5),
 		inbound: 0, outbound: 0, inEvents: 0, outEvents: 0,
 	}
+	go snowflakes.BytesLogger.Log()
 
 	go ConnectLoop(snowflakes)
-	go snowflakes.BytesLogger.Log()
 
 	// Begin goptlib client process.
 	ptInfo, err := pt.ClientSetup(nil)
@@ -197,7 +157,6 @@ func main() {
 		pt.ProxyError("proxy is not supported")
 		os.Exit(1)
 	}
-
 	listeners := make([]net.Listener, 0)
 	for _, methodName := range ptInfo.MethodNames {
 		switch methodName {
@@ -234,9 +193,7 @@ func main() {
 	for _, ln := range listeners {
 		ln.Close()
 	}
-
 	snowflakes.End()
-
 	// wait for second signal or no more handlers
 	sig = nil
 	for sig == nil && numHandlers != 0 {
diff --git a/client/webrtc.go b/client/webrtc.go
index 7647cf6..2d26b7a 100644
--- a/client/webrtc.go
+++ b/client/webrtc.go
@@ -11,34 +11,6 @@ import (
 	"time"
 )
 
-// Implements the |Tongue| interface to catch snowflakes, using a BrokerChannel.
-type WebRTCDialer struct {
-	*BrokerChannel
-	webrtcConfig *webrtc.Configuration
-}
-
-func NewWebRTCDialer(
-	broker *BrokerChannel, iceServers IceServerList) *WebRTCDialer {
-
-	config := webrtc.NewConfiguration(iceServers...)
-	return &WebRTCDialer{
-		BrokerChannel: broker,
-		webrtcConfig:  config,
-	}
-}
-
-// Initialize a WebRTC Connection by signaling through the broker.
-func (w WebRTCDialer) Catch() (*webRTCConn, error) {
-	if nil == w.BrokerChannel {
-		return nil, errors.New("Cannot Dial WebRTC without a BrokerChannel.")
-	}
-	// TODO: [#3] Fetch ICE server information from Broker.
-	// TODO: [#18] Consider TURN servers here too.
-	connection := NewWebRTCConnection(w.webrtcConfig, w.BrokerChannel)
-	err := connection.Connect()
-	return connection, err
-}
-
 // Remote WebRTC peer.  Implements the |net.Conn| interface.
 type webRTCConn struct {
 	config    *webrtc.Configuration
@@ -282,11 +254,11 @@ func (c *webRTCConn) sendOfferToBroker() {
 func (c *webRTCConn) exchangeSDP() error {
 	select {
 	case offer := <-c.offerChannel:
-		// Display for copy-paste, when no broker available.
+		// Display for copy-paste when no broker available.
 		if nil == c.broker {
 			log.Printf("Please Copy & Paste the following to the peer:")
 			log.Printf("----------------")
-			log.Printf("\n" + offer.Serialize() + "\n")
+			log.Printf("\n\n" + offer.Serialize() + "\n\n")
 			log.Printf("----------------")
 		}
 	case err := <-c.errorChannel:



More information about the tor-commits mailing list