mirror of
https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake.git
synced 2025-10-14 05:11:19 -04:00
Merge branch 'issue/21314'
This commit is contained in:
commit
2d43dd26b1
6 changed files with 89 additions and 65 deletions
|
@ -7,6 +7,9 @@ import (
|
||||||
// Interface for catching Snowflakes. (aka the remote dialer)
|
// Interface for catching Snowflakes. (aka the remote dialer)
|
||||||
type Tongue interface {
|
type Tongue interface {
|
||||||
Catch() (*WebRTCPeer, error)
|
Catch() (*WebRTCPeer, error)
|
||||||
|
|
||||||
|
// Get the maximum number of snowflakes
|
||||||
|
GetMax() int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interface for collecting some number of Snowflakes, for passing along
|
// Interface for collecting some number of Snowflakes, for passing along
|
||||||
|
|
|
@ -27,13 +27,19 @@ func (m *MockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type FakeDialer struct{}
|
type FakeDialer struct {
|
||||||
|
max int
|
||||||
|
}
|
||||||
|
|
||||||
func (w FakeDialer) Catch() (*WebRTCPeer, error) {
|
func (w FakeDialer) Catch() (*WebRTCPeer, error) {
|
||||||
fmt.Println("Caught a dummy snowflake.")
|
fmt.Println("Caught a dummy snowflake.")
|
||||||
return &WebRTCPeer{}, nil
|
return &WebRTCPeer{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w FakeDialer) GetMax() int {
|
||||||
|
return w.max
|
||||||
|
}
|
||||||
|
|
||||||
type FakeSocksConn struct {
|
type FakeSocksConn struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
rejected bool
|
rejected bool
|
||||||
|
@ -55,19 +61,19 @@ func TestSnowflakeClient(t *testing.T) {
|
||||||
|
|
||||||
Convey("Peers", t, func() {
|
Convey("Peers", t, func() {
|
||||||
Convey("Can construct", func() {
|
Convey("Can construct", func() {
|
||||||
p := NewPeers(1)
|
d := &FakeDialer{max: 1}
|
||||||
So(p.capacity, ShouldEqual, 1)
|
p, _ := NewPeers(d)
|
||||||
|
So(p.Tongue.GetMax(), ShouldEqual, 1)
|
||||||
So(p.snowflakeChan, ShouldNotBeNil)
|
So(p.snowflakeChan, ShouldNotBeNil)
|
||||||
So(cap(p.snowflakeChan), ShouldEqual, 1)
|
So(cap(p.snowflakeChan), ShouldEqual, 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Collecting a Snowflake requires a Tongue.", func() {
|
Convey("Collecting a Snowflake requires a Tongue.", func() {
|
||||||
p := NewPeers(1)
|
p, err := NewPeers(nil)
|
||||||
_, err := p.Collect()
|
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
So(p.Count(), ShouldEqual, 0)
|
|
||||||
// Set the dialer so that collection is possible.
|
// Set the dialer so that collection is possible.
|
||||||
p.Tongue = FakeDialer{}
|
d := &FakeDialer{max: 1}
|
||||||
|
p, err = NewPeers(d)
|
||||||
_, err = p.Collect()
|
_, err = p.Collect()
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(p.Count(), ShouldEqual, 1)
|
So(p.Count(), ShouldEqual, 1)
|
||||||
|
@ -77,8 +83,7 @@ func TestSnowflakeClient(t *testing.T) {
|
||||||
|
|
||||||
Convey("Collection continues until capacity.", func() {
|
Convey("Collection continues until capacity.", func() {
|
||||||
c := 5
|
c := 5
|
||||||
p := NewPeers(c)
|
p, _ := NewPeers(FakeDialer{max: c})
|
||||||
p.Tongue = FakeDialer{}
|
|
||||||
// Fill up to capacity.
|
// Fill up to capacity.
|
||||||
for i := 0; i < c; i++ {
|
for i := 0; i < c; i++ {
|
||||||
fmt.Println("Adding snowflake ", i)
|
fmt.Println("Adding snowflake ", i)
|
||||||
|
@ -104,8 +109,7 @@ func TestSnowflakeClient(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Count correctly purges peers marked for deletion.", func() {
|
Convey("Count correctly purges peers marked for deletion.", func() {
|
||||||
p := NewPeers(4)
|
p, _ := NewPeers(FakeDialer{max: 5})
|
||||||
p.Tongue = FakeDialer{}
|
|
||||||
p.Collect()
|
p.Collect()
|
||||||
p.Collect()
|
p.Collect()
|
||||||
p.Collect()
|
p.Collect()
|
||||||
|
@ -121,7 +125,7 @@ func TestSnowflakeClient(t *testing.T) {
|
||||||
|
|
||||||
Convey("End Closes all peers.", func() {
|
Convey("End Closes all peers.", func() {
|
||||||
cnt := 5
|
cnt := 5
|
||||||
p := NewPeers(cnt)
|
p, _ := NewPeers(FakeDialer{max: cnt})
|
||||||
for i := 0; i < cnt; i++ {
|
for i := 0; i < cnt; i++ {
|
||||||
p.activePeers.PushBack(&WebRTCPeer{})
|
p.activePeers.PushBack(&WebRTCPeer{})
|
||||||
}
|
}
|
||||||
|
@ -132,8 +136,7 @@ func TestSnowflakeClient(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Pop skips over closed peers.", func() {
|
Convey("Pop skips over closed peers.", func() {
|
||||||
p := NewPeers(4)
|
p, _ := NewPeers(FakeDialer{max: 4})
|
||||||
p.Tongue = FakeDialer{}
|
|
||||||
wc1, _ := p.Collect()
|
wc1, _ := p.Collect()
|
||||||
wc2, _ := p.Collect()
|
wc2, _ := p.Collect()
|
||||||
wc3, _ := p.Collect()
|
wc3, _ := p.Collect()
|
||||||
|
@ -157,11 +160,11 @@ func TestSnowflakeClient(t *testing.T) {
|
||||||
|
|
||||||
SkipConvey("Handler Grants correctly", func() {
|
SkipConvey("Handler Grants correctly", func() {
|
||||||
socks := &FakeSocksConn{}
|
socks := &FakeSocksConn{}
|
||||||
snowflakes := &FakePeers{}
|
broker := &BrokerChannel{Host: "test"}
|
||||||
|
d := NewWebRTCDialer(broker, nil, 1)
|
||||||
|
|
||||||
So(socks.rejected, ShouldEqual, false)
|
So(socks.rejected, ShouldEqual, false)
|
||||||
snowflakes.toRelease = nil
|
Handler(socks, d)
|
||||||
Handler(socks, snowflakes)
|
|
||||||
So(socks.rejected, ShouldEqual, true)
|
So(socks.rejected, ShouldEqual, true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -169,14 +172,14 @@ func TestSnowflakeClient(t *testing.T) {
|
||||||
Convey("Dialers", t, func() {
|
Convey("Dialers", t, func() {
|
||||||
Convey("Can construct WebRTCDialer.", func() {
|
Convey("Can construct WebRTCDialer.", func() {
|
||||||
broker := &BrokerChannel{Host: "test"}
|
broker := &BrokerChannel{Host: "test"}
|
||||||
d := NewWebRTCDialer(broker, nil)
|
d := NewWebRTCDialer(broker, nil, 1)
|
||||||
So(d, ShouldNotBeNil)
|
So(d, ShouldNotBeNil)
|
||||||
So(d.BrokerChannel, ShouldNotBeNil)
|
So(d.BrokerChannel, ShouldNotBeNil)
|
||||||
So(d.BrokerChannel.Host, ShouldEqual, "test")
|
So(d.BrokerChannel.Host, ShouldEqual, "test")
|
||||||
})
|
})
|
||||||
SkipConvey("WebRTCDialer can Catch a snowflake.", func() {
|
SkipConvey("WebRTCDialer can Catch a snowflake.", func() {
|
||||||
broker := &BrokerChannel{Host: "test"}
|
broker := &BrokerChannel{Host: "test"}
|
||||||
d := NewWebRTCDialer(broker, nil)
|
d := NewWebRTCDialer(broker, nil, 1)
|
||||||
conn, err := d.Catch()
|
conn, err := d.Catch()
|
||||||
So(conn, ShouldBeNil)
|
So(conn, ShouldBeNil)
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
|
|
|
@ -24,33 +24,37 @@ type Peers struct {
|
||||||
|
|
||||||
snowflakeChan chan *WebRTCPeer
|
snowflakeChan chan *WebRTCPeer
|
||||||
activePeers *list.List
|
activePeers *list.List
|
||||||
capacity int
|
|
||||||
|
|
||||||
melt chan struct{}
|
melt chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct a fresh container of remote peers.
|
// Construct a fresh container of remote peers.
|
||||||
func NewPeers(max int) *Peers {
|
func NewPeers(tongue Tongue) (*Peers, error) {
|
||||||
p := &Peers{capacity: max}
|
p := &Peers{}
|
||||||
// Use buffered go channel to pass snowflakes onwards to the SOCKS handler.
|
// Use buffered go channel to pass snowflakes onwards to the SOCKS handler.
|
||||||
p.snowflakeChan = make(chan *WebRTCPeer, max)
|
if tongue == nil {
|
||||||
|
return nil, errors.New("missing Tongue to catch Snowflakes with")
|
||||||
|
}
|
||||||
|
p.snowflakeChan = make(chan *WebRTCPeer, tongue.GetMax())
|
||||||
p.activePeers = list.New()
|
p.activePeers = list.New()
|
||||||
p.melt = make(chan struct{})
|
p.melt = make(chan struct{})
|
||||||
return p
|
p.Tongue = tongue
|
||||||
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// As part of |SnowflakeCollector| interface.
|
// As part of |SnowflakeCollector| interface.
|
||||||
func (p *Peers) Collect() (*WebRTCPeer, error) {
|
func (p *Peers) Collect() (*WebRTCPeer, error) {
|
||||||
cnt := p.Count()
|
|
||||||
s := fmt.Sprintf("Currently at [%d/%d]", cnt, p.capacity)
|
|
||||||
if cnt >= p.capacity {
|
|
||||||
return nil, fmt.Errorf("At capacity [%d/%d]", cnt, p.capacity)
|
|
||||||
}
|
|
||||||
log.Println("WebRTC: Collecting a new Snowflake.", s)
|
|
||||||
// Engage the Snowflake Catching interface, which must be available.
|
// Engage the Snowflake Catching interface, which must be available.
|
||||||
if nil == p.Tongue {
|
if nil == p.Tongue {
|
||||||
return nil, errors.New("missing Tongue to catch Snowflakes with")
|
return nil, errors.New("missing Tongue to catch Snowflakes with")
|
||||||
}
|
}
|
||||||
|
cnt := p.Count()
|
||||||
|
capacity := p.Tongue.GetMax()
|
||||||
|
s := fmt.Sprintf("Currently at [%d/%d]", cnt, capacity)
|
||||||
|
if cnt >= capacity {
|
||||||
|
return nil, fmt.Errorf("At capacity [%d/%d]", cnt, capacity)
|
||||||
|
}
|
||||||
|
log.Println("WebRTC: Collecting a new Snowflake.", s)
|
||||||
// BUG: some broker conflict here.
|
// BUG: some broker conflict here.
|
||||||
connection, err := p.Tongue.Catch()
|
connection, err := p.Tongue.Catch()
|
||||||
if nil != err {
|
if nil != err {
|
||||||
|
|
|
@ -155,15 +155,18 @@ func (bc *BrokerChannel) SetNATType(NATType string) {
|
||||||
type WebRTCDialer struct {
|
type WebRTCDialer struct {
|
||||||
*BrokerChannel
|
*BrokerChannel
|
||||||
webrtcConfig *webrtc.Configuration
|
webrtcConfig *webrtc.Configuration
|
||||||
|
max int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWebRTCDialer(broker *BrokerChannel, iceServers []webrtc.ICEServer) *WebRTCDialer {
|
func NewWebRTCDialer(broker *BrokerChannel, iceServers []webrtc.ICEServer, max int) *WebRTCDialer {
|
||||||
config := webrtc.Configuration{
|
config := webrtc.Configuration{
|
||||||
ICEServers: iceServers,
|
ICEServers: iceServers,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &WebRTCDialer{
|
return &WebRTCDialer{
|
||||||
BrokerChannel: broker,
|
BrokerChannel: broker,
|
||||||
webrtcConfig: &config,
|
webrtcConfig: &config,
|
||||||
|
max: max,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,3 +176,8 @@ func (w WebRTCDialer) Catch() (*WebRTCPeer, error) {
|
||||||
// TODO: [#25596] Consider TURN servers here too.
|
// TODO: [#25596] Consider TURN servers here too.
|
||||||
return NewWebRTCPeer(w.webrtcConfig, w.BrokerChannel)
|
return NewWebRTCPeer(w.webrtcConfig, w.BrokerChannel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the maximum number of snowflakes to collect
|
||||||
|
func (w WebRTCDialer) GetMax() int {
|
||||||
|
return w.max
|
||||||
|
}
|
||||||
|
|
|
@ -142,7 +142,19 @@ var sessionManager = sessionManager_{}
|
||||||
|
|
||||||
// Given an accepted SOCKS connection, establish a WebRTC connection to the
|
// Given an accepted SOCKS connection, establish a WebRTC connection to the
|
||||||
// remote peer and exchange traffic.
|
// remote peer and exchange traffic.
|
||||||
func Handler(socks net.Conn, snowflakes SnowflakeCollector) error {
|
func Handler(socks net.Conn, tongue Tongue) error {
|
||||||
|
// Prepare to collect remote WebRTC peers.
|
||||||
|
snowflakes, err := NewPeers(tongue)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use a real logger to periodically output how much traffic is happening.
|
||||||
|
snowflakes.BytesLogger = NewBytesSyncLogger()
|
||||||
|
|
||||||
|
log.Printf("---- Handler: begin collecting snowflakes ---")
|
||||||
|
go connectLoop(snowflakes)
|
||||||
|
|
||||||
// Return the global smux.Session.
|
// Return the global smux.Session.
|
||||||
sess, err := sessionManager.Get(snowflakes)
|
sess, err := sessionManager.Get(snowflakes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -160,9 +172,31 @@ func Handler(socks net.Conn, snowflakes SnowflakeCollector) error {
|
||||||
log.Printf("---- Handler: begin stream %v ---", stream.ID())
|
log.Printf("---- Handler: begin stream %v ---", stream.ID())
|
||||||
copyLoop(socks, stream)
|
copyLoop(socks, stream)
|
||||||
log.Printf("---- Handler: closed stream %v ---", stream.ID())
|
log.Printf("---- Handler: closed stream %v ---", stream.ID())
|
||||||
|
snowflakes.End()
|
||||||
|
log.Printf("---- Handler: end collecting snowflakes ---")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Maintain |SnowflakeCapacity| number of available WebRTC connections, to
|
||||||
|
// transfer to the Tor SOCKS handler when needed.
|
||||||
|
func connectLoop(snowflakes SnowflakeCollector) {
|
||||||
|
for {
|
||||||
|
// Check if ending is necessary.
|
||||||
|
_, err := snowflakes.Collect()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("WebRTC: %v Retrying in %v...",
|
||||||
|
err, ReconnectTimeout)
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-time.After(ReconnectTimeout):
|
||||||
|
continue
|
||||||
|
case <-snowflakes.Melted():
|
||||||
|
log.Println("ConnectLoop: stopped.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Exchanges bytes between two ReadWriters.
|
// Exchanges bytes between two ReadWriters.
|
||||||
// (In this case, between a SOCKS connection and smux stream.)
|
// (In this case, between a SOCKS connection and smux stream.)
|
||||||
func copyLoop(socks, stream io.ReadWriter) {
|
func copyLoop(socks, stream io.ReadWriter) {
|
||||||
|
|
|
@ -26,28 +26,8 @@ const (
|
||||||
DefaultSnowflakeCapacity = 1
|
DefaultSnowflakeCapacity = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
// Maintain |SnowflakeCapacity| number of available WebRTC connections, to
|
|
||||||
// transfer to the Tor SOCKS handler when needed.
|
|
||||||
func ConnectLoop(snowflakes sf.SnowflakeCollector) {
|
|
||||||
for {
|
|
||||||
// Check if ending is necessary.
|
|
||||||
_, err := snowflakes.Collect()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("WebRTC: %v Retrying in %v...",
|
|
||||||
err, sf.ReconnectTimeout)
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-time.After(sf.ReconnectTimeout):
|
|
||||||
continue
|
|
||||||
case <-snowflakes.Melted():
|
|
||||||
log.Println("ConnectLoop: stopped.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept local SOCKS connections and pass them to the handler.
|
// Accept local SOCKS connections and pass them to the handler.
|
||||||
func socksAcceptLoop(ln *pt.SocksListener, snowflakes sf.SnowflakeCollector) {
|
func socksAcceptLoop(ln *pt.SocksListener, tongue sf.Tongue) {
|
||||||
defer ln.Close()
|
defer ln.Close()
|
||||||
for {
|
for {
|
||||||
conn, err := ln.AcceptSocks()
|
conn, err := ln.AcceptSocks()
|
||||||
|
@ -68,7 +48,7 @@ func socksAcceptLoop(ln *pt.SocksListener, snowflakes sf.SnowflakeCollector) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = sf.Handler(conn, snowflakes)
|
err = sf.Handler(conn, tongue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("handler error: %s", err)
|
log.Printf("handler error: %s", err)
|
||||||
return
|
return
|
||||||
|
@ -158,9 +138,6 @@ func main() {
|
||||||
log.Printf("url: %v", strings.Join(server.URLs, " "))
|
log.Printf("url: %v", strings.Join(server.URLs, " "))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare to collect remote WebRTC peers.
|
|
||||||
snowflakes := sf.NewPeers(*max)
|
|
||||||
|
|
||||||
// Use potentially domain-fronting broker to rendezvous.
|
// Use potentially domain-fronting broker to rendezvous.
|
||||||
broker, err := sf.NewBrokerChannel(
|
broker, err := sf.NewBrokerChannel(
|
||||||
*brokerURL, *frontDomain, sf.CreateBrokerTransport(),
|
*brokerURL, *frontDomain, sf.CreateBrokerTransport(),
|
||||||
|
@ -170,12 +147,8 @@ func main() {
|
||||||
}
|
}
|
||||||
go updateNATType(iceServers, broker)
|
go updateNATType(iceServers, broker)
|
||||||
|
|
||||||
snowflakes.Tongue = sf.NewWebRTCDialer(broker, iceServers)
|
// Create a new WebRTCDialer to use as the |Tongue| to catch snowflakes
|
||||||
|
dialer := sf.NewWebRTCDialer(broker, iceServers, *max)
|
||||||
// Use a real logger to periodically output how much traffic is happening.
|
|
||||||
snowflakes.BytesLogger = sf.NewBytesSyncLogger()
|
|
||||||
|
|
||||||
go ConnectLoop(snowflakes)
|
|
||||||
|
|
||||||
// Begin goptlib client process.
|
// Begin goptlib client process.
|
||||||
ptInfo, err := pt.ClientSetup(nil)
|
ptInfo, err := pt.ClientSetup(nil)
|
||||||
|
@ -197,7 +170,7 @@ func main() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
log.Printf("Started SOCKS listener at %v.", ln.Addr())
|
log.Printf("Started SOCKS listener at %v.", ln.Addr())
|
||||||
go socksAcceptLoop(ln, snowflakes)
|
go socksAcceptLoop(ln, dialer)
|
||||||
pt.Cmethod(methodName, ln.Version(), ln.Addr())
|
pt.Cmethod(methodName, ln.Version(), ln.Addr())
|
||||||
listeners = append(listeners, ln)
|
listeners = append(listeners, ln)
|
||||||
default:
|
default:
|
||||||
|
@ -228,7 +201,6 @@ func main() {
|
||||||
for _, ln := range listeners {
|
for _, ln := range listeners {
|
||||||
ln.Close()
|
ln.Close()
|
||||||
}
|
}
|
||||||
snowflakes.End()
|
|
||||||
log.Println("snowflake is done.")
|
log.Println("snowflake is done.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue