mirror of
https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake.git
synced 2025-10-13 11:11:30 -04:00
client multiplexes AND recovers correctly! (close #31)
- stabilize rest of WebRTCPeer - use uid for the datachannel label - store a consistent WebRTC config in the dialer
This commit is contained in:
parent
2bf0e5457e
commit
e93c38f834
8 changed files with 50 additions and 35 deletions
|
@ -23,6 +23,7 @@ before_install:
|
|||
install:
|
||||
- go get -u github.com/smartystreets/goconvey
|
||||
- go get -u github.com/keroserene/go-webrtc
|
||||
- go get -u github.com/dchest/uniuri
|
||||
- go get -u git.torproject.org/pluggable-transports/goptlib.git
|
||||
- pushd proxy
|
||||
- npm install
|
||||
|
|
|
@ -49,7 +49,7 @@ type FakeDialer struct{}
|
|||
|
||||
func (w FakeDialer) Catch() (Snowflake, error) {
|
||||
fmt.Println("Caught a dummy snowflake.")
|
||||
return &webRTCConn{}, nil
|
||||
return &WebRTCPeer{}, nil
|
||||
}
|
||||
|
||||
type FakeSocksConn struct {
|
||||
|
@ -63,9 +63,9 @@ func (f FakeSocksConn) Reject() error {
|
|||
}
|
||||
func (f FakeSocksConn) Grant(addr *net.TCPAddr) error { return nil }
|
||||
|
||||
type FakePeers struct{ toRelease *webRTCConn }
|
||||
type FakePeers struct{ toRelease *WebRTCPeer }
|
||||
|
||||
func (f FakePeers) Collect() (Snowflake, error) { return &webRTCConn{}, nil }
|
||||
func (f FakePeers) Collect() (Snowflake, error) { return &WebRTCPeer{}, nil }
|
||||
func (f FakePeers) Pop() Snowflake { return nil }
|
||||
func (f FakePeers) Melted() <-chan struct{} { return nil }
|
||||
|
||||
|
@ -141,7 +141,7 @@ func TestSnowflakeClient(t *testing.T) {
|
|||
cnt := 5
|
||||
p := NewPeers(cnt)
|
||||
for i := 0; i < cnt; i++ {
|
||||
p.activePeers.PushBack(&webRTCConn{})
|
||||
p.activePeers.PushBack(&WebRTCPeer{})
|
||||
}
|
||||
So(p.Count(), ShouldEqual, cnt)
|
||||
p.End()
|
||||
|
|
|
@ -70,7 +70,7 @@ func (p *Peers) Pop() Snowflake {
|
|||
var ok bool
|
||||
for nil == snowflake {
|
||||
snowflake, ok = <-p.snowflakeChan
|
||||
conn := snowflake.(*webRTCConn)
|
||||
conn := snowflake.(*WebRTCPeer)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ func (p *Peers) Pop() Snowflake {
|
|||
}
|
||||
}
|
||||
// Set to use the same rate-limited traffic logger to keep consistency.
|
||||
snowflake.(*webRTCConn).BytesLogger = p.BytesLogger
|
||||
snowflake.(*WebRTCPeer).BytesLogger = p.BytesLogger
|
||||
return snowflake
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ func (p *Peers) Count() int {
|
|||
func (p *Peers) purgeClosedPeers() {
|
||||
for e := p.activePeers.Front(); e != nil; {
|
||||
next := e.Next()
|
||||
conn := e.Value.(*webRTCConn)
|
||||
conn := e.Value.(*WebRTCPeer)
|
||||
// Purge those marked for deletion.
|
||||
if conn.closed {
|
||||
p.activePeers.Remove(e)
|
||||
|
@ -115,7 +115,7 @@ func (p *Peers) End() {
|
|||
cnt := p.Count()
|
||||
for e := p.activePeers.Front(); e != nil; {
|
||||
next := e.Next()
|
||||
conn := e.Value.(*webRTCConn)
|
||||
conn := e.Value.(*WebRTCPeer)
|
||||
conn.Close()
|
||||
p.activePeers.Remove(e)
|
||||
e = next
|
||||
|
|
|
@ -115,17 +115,19 @@ func (bc *BrokerChannel) Negotiate(offer *webrtc.SessionDescription) (
|
|||
// Implements the |Tongue| interface to catch snowflakes, using BrokerChannel.
|
||||
type WebRTCDialer struct {
|
||||
*BrokerChannel
|
||||
// webrtcConfig *webrtc.Configuration
|
||||
iceServers IceServerList
|
||||
webrtcConfig *webrtc.Configuration
|
||||
}
|
||||
|
||||
func NewWebRTCDialer(
|
||||
broker *BrokerChannel, iceServers IceServerList) *WebRTCDialer {
|
||||
|
||||
config := webrtc.NewConfiguration(iceServers...)
|
||||
if nil == config {
|
||||
log.Println("Unable to prepare WebRTC configuration.")
|
||||
return nil
|
||||
}
|
||||
return &WebRTCDialer{
|
||||
BrokerChannel: broker,
|
||||
iceServers: iceServers,
|
||||
// webrtcConfig: config,
|
||||
webrtcConfig: config,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,8 +138,7 @@ func (w WebRTCDialer) Catch() (Snowflake, error) {
|
|||
}
|
||||
// TODO: [#3] Fetch ICE server information from Broker.
|
||||
// TODO: [#18] Consider TURN servers here too.
|
||||
config := webrtc.NewConfiguration(w.iceServers...)
|
||||
connection := NewWebRTCConnection(config, w.BrokerChannel)
|
||||
connection := NewWebRTCConnection(w.webrtcConfig, w.BrokerChannel)
|
||||
err := connection.Connect()
|
||||
return connection, err
|
||||
}
|
||||
|
@ -149,7 +150,7 @@ func (w WebRTCDialer) Catch() (Snowflake, error) {
|
|||
type CopyPasteDialer struct {
|
||||
webrtcConfig *webrtc.Configuration
|
||||
signal *os.File
|
||||
current *webRTCConn
|
||||
current *WebRTCPeer
|
||||
}
|
||||
|
||||
func NewCopyPasteDialer(iceServers IceServerList) *CopyPasteDialer {
|
||||
|
|
|
@ -75,7 +75,6 @@ func handler(socks SocksConnector, snowflakes SnowflakeCollector) error {
|
|||
handlerChan <- -1
|
||||
}()
|
||||
// Obtain an available WebRTC remote. May block.
|
||||
log.Println("handler: awaiting Snowflake...")
|
||||
snowflake := snowflakes.Pop()
|
||||
if nil == snowflake {
|
||||
socks.Reject()
|
||||
|
@ -148,6 +147,10 @@ func main() {
|
|||
// Otherwise, use manual copy and pasting of SDP messages.
|
||||
snowflakes.Tongue = NewCopyPasteDialer(iceServers)
|
||||
}
|
||||
if nil == snowflakes.Tongue {
|
||||
log.Fatal("Unable to prepare rendezvous method.")
|
||||
return
|
||||
}
|
||||
// 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),
|
||||
|
@ -212,4 +215,5 @@ func main() {
|
|||
case sig = <-sigChan:
|
||||
}
|
||||
}
|
||||
log.Println("snowflake is done.")
|
||||
}
|
||||
|
|
|
@ -5,6 +5,6 @@ ClientTransportPlugin snowflake exec ./client \
|
|||
-url https://snowflake-reg.appspot.com/ \
|
||||
-front www.google.com \
|
||||
-ice stun:stun.l.google.com:19302 \
|
||||
-max 1
|
||||
-max 4
|
||||
|
||||
Bridge snowflake 0.0.3.0:1
|
||||
|
|
|
@ -2,10 +2,11 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/keroserene/go-webrtc"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/keroserene/go-webrtc"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -3,16 +3,22 @@ package main
|
|||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"github.com/keroserene/go-webrtc"
|
||||
"io"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/dchest/uniuri"
|
||||
"github.com/keroserene/go-webrtc"
|
||||
)
|
||||
|
||||
// Remote WebRTC peer.
|
||||
// Implements the |Snowflake| interface, which includes
|
||||
// |io.ReadWriter|, |Resetter|, and |Connector|.
|
||||
type webRTCConn struct {
|
||||
//
|
||||
// Handles preparation of go-webrtc PeerConnection. Only ever has
|
||||
// one DataChannel.
|
||||
type WebRTCPeer struct {
|
||||
id string
|
||||
config *webrtc.Configuration
|
||||
pc *webrtc.PeerConnection
|
||||
transport SnowflakeDataChannel // Holds the WebRTC DataChannel.
|
||||
|
@ -33,13 +39,13 @@ type webRTCConn struct {
|
|||
|
||||
// Read bytes from local SOCKS.
|
||||
// As part of |io.ReadWriter|
|
||||
func (c *webRTCConn) Read(b []byte) (int, error) {
|
||||
func (c *WebRTCPeer) Read(b []byte) (int, error) {
|
||||
return c.recvPipe.Read(b)
|
||||
}
|
||||
|
||||
// Writes bytes out to remote WebRTC.
|
||||
// As part of |io.ReadWriter|
|
||||
func (c *webRTCConn) Write(b []byte) (int, error) {
|
||||
func (c *WebRTCPeer) Write(b []byte) (int, error) {
|
||||
c.BytesLogger.AddOutbound(len(b))
|
||||
if nil == c.transport {
|
||||
log.Printf("Buffered %d bytes --> WebRTC", len(b))
|
||||
|
@ -51,7 +57,7 @@ func (c *webRTCConn) Write(b []byte) (int, error) {
|
|||
}
|
||||
|
||||
// As part of |Snowflake|
|
||||
func (c *webRTCConn) Close() error {
|
||||
func (c *WebRTCPeer) Close() error {
|
||||
var err error = nil
|
||||
log.Printf("WebRTC: Closing")
|
||||
c.cleanup()
|
||||
|
@ -61,7 +67,7 @@ func (c *webRTCConn) Close() error {
|
|||
}
|
||||
|
||||
// As part of |Resetter|
|
||||
func (c *webRTCConn) Reset() {
|
||||
func (c *WebRTCPeer) Reset() {
|
||||
c.Close()
|
||||
go func() {
|
||||
c.reset <- struct{}{}
|
||||
|
@ -70,12 +76,13 @@ func (c *webRTCConn) Reset() {
|
|||
}
|
||||
|
||||
// As part of |Resetter|
|
||||
func (c *webRTCConn) WaitForReset() { <-c.reset }
|
||||
func (c *WebRTCPeer) WaitForReset() { <-c.reset }
|
||||
|
||||
// Construct a WebRTC PeerConnection.
|
||||
func NewWebRTCConnection(config *webrtc.Configuration,
|
||||
broker *BrokerChannel) *webRTCConn {
|
||||
connection := new(webRTCConn)
|
||||
broker *BrokerChannel) *WebRTCPeer {
|
||||
connection := new(WebRTCPeer)
|
||||
connection.id = "snowflake-" + uniuri.New()
|
||||
connection.config = config
|
||||
connection.broker = broker
|
||||
connection.offerChannel = make(chan *webrtc.SessionDescription, 1)
|
||||
|
@ -94,7 +101,8 @@ func NewWebRTCConnection(config *webrtc.Configuration,
|
|||
}
|
||||
|
||||
// As part of |Connector| interface.
|
||||
func (c *webRTCConn) Connect() error {
|
||||
func (c *WebRTCPeer) Connect() error {
|
||||
log.Println(c.id, " connecting...")
|
||||
// TODO: When go-webrtc is more stable, it's possible that a new
|
||||
// PeerConnection won't need to be re-prepared each time.
|
||||
err := c.preparePeerConnection()
|
||||
|
@ -113,7 +121,7 @@ func (c *webRTCConn) Connect() error {
|
|||
}
|
||||
|
||||
// Create and prepare callbacks on a new WebRTC PeerConnection.
|
||||
func (c *webRTCConn) preparePeerConnection() error {
|
||||
func (c *WebRTCPeer) preparePeerConnection() error {
|
||||
if nil != c.pc {
|
||||
c.pc.Close()
|
||||
c.pc = nil
|
||||
|
@ -161,11 +169,11 @@ func (c *webRTCConn) preparePeerConnection() error {
|
|||
}
|
||||
|
||||
// Create a WebRTC DataChannel locally.
|
||||
func (c *webRTCConn) establishDataChannel() error {
|
||||
func (c *WebRTCPeer) establishDataChannel() error {
|
||||
if c.transport != nil {
|
||||
panic("Unexpected datachannel already exists!")
|
||||
}
|
||||
dc, err := c.pc.CreateDataChannel("snowflake", webrtc.Init{})
|
||||
dc, err := c.pc.CreateDataChannel(c.id, webrtc.Init{})
|
||||
// Triggers "OnNegotiationNeeded" on the PeerConnection, which will prepare
|
||||
// an SDP offer while other goroutines operating on this struct handle the
|
||||
// signaling. Eventually fires "OnOpen".
|
||||
|
@ -220,7 +228,7 @@ func (c *webRTCConn) establishDataChannel() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *webRTCConn) sendOfferToBroker() {
|
||||
func (c *WebRTCPeer) sendOfferToBroker() {
|
||||
if nil == c.broker {
|
||||
return
|
||||
}
|
||||
|
@ -235,7 +243,7 @@ func (c *webRTCConn) sendOfferToBroker() {
|
|||
|
||||
// Block until an SDP offer is available, send it to either
|
||||
// the Broker or signal pipe, then await for the SDP answer.
|
||||
func (c *webRTCConn) exchangeSDP() error {
|
||||
func (c *WebRTCPeer) exchangeSDP() error {
|
||||
select {
|
||||
case offer := <-c.offerChannel:
|
||||
// Display for copy-paste when no broker available.
|
||||
|
@ -272,7 +280,7 @@ func (c *webRTCConn) exchangeSDP() error {
|
|||
}
|
||||
|
||||
// Close all channels and transports
|
||||
func (c *webRTCConn) cleanup() {
|
||||
func (c *WebRTCPeer) cleanup() {
|
||||
if nil != c.offerChannel {
|
||||
close(c.offerChannel)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue