mirror of
https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake.git
synced 2025-10-13 20:11:19 -04:00
Start refactoring out a client and library
This commit is contained in:
parent
7662ccb00c
commit
cce7ee64a7
8 changed files with 120 additions and 105 deletions
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package lib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package lib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -179,7 +179,7 @@ func TestSnowflakeClient(t *testing.T) {
|
||||||
|
|
||||||
So(socks.rejected, ShouldEqual, false)
|
So(socks.rejected, ShouldEqual, false)
|
||||||
snowflakes.toRelease = nil
|
snowflakes.toRelease = nil
|
||||||
handler(socks, snowflakes)
|
Handler(socks, snowflakes)
|
||||||
So(socks.rejected, ShouldEqual, true)
|
So(socks.rejected, ShouldEqual, true)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package lib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"container/list"
|
"container/list"
|
|
@ -9,7 +9,7 @@
|
||||||
//
|
//
|
||||||
// - Manual copy-paste signaling. User must create a signaling pipe.
|
// - Manual copy-paste signaling. User must create a signaling pipe.
|
||||||
// (The flags in torrc-manual allow this)
|
// (The flags in torrc-manual allow this)
|
||||||
package main
|
package lib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
69
client/lib/snowflake.go
Normal file
69
client/lib/snowflake.go
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ReconnectTimeout = 10
|
||||||
|
SnowflakeTimeout = 30
|
||||||
|
)
|
||||||
|
|
||||||
|
// When a connection handler starts, +1 is written to this channel; when it
|
||||||
|
// ends, -1 is written.
|
||||||
|
var HandlerChan = make(chan int)
|
||||||
|
|
||||||
|
// Given an accepted SOCKS connection, establish a WebRTC connection to the
|
||||||
|
// remote peer and exchange traffic.
|
||||||
|
func Handler(socks SocksConnector, snowflakes SnowflakeCollector) error {
|
||||||
|
HandlerChan <- 1
|
||||||
|
defer func() {
|
||||||
|
HandlerChan <- -1
|
||||||
|
}()
|
||||||
|
// Obtain an available WebRTC remote. May block.
|
||||||
|
snowflake := snowflakes.Pop()
|
||||||
|
if nil == snowflake {
|
||||||
|
socks.Reject()
|
||||||
|
return errors.New("handler: Received invalid Snowflake")
|
||||||
|
}
|
||||||
|
defer socks.Close()
|
||||||
|
defer snowflake.Close()
|
||||||
|
log.Println("---- Handler: snowflake assigned ----")
|
||||||
|
err := socks.Grant(&net.TCPAddr{IP: net.IPv4zero, Port: 0})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// When WebRTC resets, close the SOCKS connection too.
|
||||||
|
snowflake.WaitForReset()
|
||||||
|
socks.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Begin exchanging data. Either WebRTC or localhost SOCKS will close first.
|
||||||
|
// In eithercase, this closes the handler and induces a new handler.
|
||||||
|
copyLoop(socks, snowflake)
|
||||||
|
log.Println("---- Handler: closed ---")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exchanges bytes between two ReadWriters.
|
||||||
|
// (In this case, between a SOCKS and WebRTC connection.)
|
||||||
|
func copyLoop(a, b io.ReadWriter) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(2)
|
||||||
|
go func() {
|
||||||
|
io.Copy(b, a)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
io.Copy(a, b)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
wg.Wait()
|
||||||
|
log.Println("copy loop ended")
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package lib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -34,46 +34,46 @@ func (b BytesNullLogger) AddInbound(amount int) {}
|
||||||
// BytesSyncLogger uses channels to safely log from multiple sources with output
|
// BytesSyncLogger uses channels to safely log from multiple sources with output
|
||||||
// occuring at reasonable intervals.
|
// occuring at reasonable intervals.
|
||||||
type BytesSyncLogger struct {
|
type BytesSyncLogger struct {
|
||||||
outboundChan chan int
|
OutboundChan chan int
|
||||||
inboundChan chan int
|
InboundChan chan int
|
||||||
outbound int
|
Outbound int
|
||||||
inbound int
|
Inbound int
|
||||||
outEvents int
|
OutEvents int
|
||||||
inEvents int
|
InEvents int
|
||||||
isLogging bool
|
IsLogging bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BytesSyncLogger) Log() {
|
func (b *BytesSyncLogger) Log() {
|
||||||
b.isLogging = true
|
b.IsLogging = true
|
||||||
var amount int
|
var amount int
|
||||||
output := func() {
|
output := func() {
|
||||||
log.Printf("Traffic Bytes (in|out): %d | %d -- (%d OnMessages, %d Sends)",
|
log.Printf("Traffic Bytes (in|out): %d | %d -- (%d OnMessages, %d Sends)",
|
||||||
b.inbound, b.outbound, b.inEvents, b.outEvents)
|
b.Inbound, b.Outbound, b.InEvents, b.OutEvents)
|
||||||
b.outbound = 0
|
b.Outbound = 0
|
||||||
b.outEvents = 0
|
b.OutEvents = 0
|
||||||
b.inbound = 0
|
b.Inbound = 0
|
||||||
b.inEvents = 0
|
b.InEvents = 0
|
||||||
}
|
}
|
||||||
last := time.Now()
|
last := time.Now()
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case amount = <-b.outboundChan:
|
case amount = <-b.OutboundChan:
|
||||||
b.outbound += amount
|
b.Outbound += amount
|
||||||
b.outEvents++
|
b.OutEvents++
|
||||||
last := time.Now()
|
last := time.Now()
|
||||||
if time.Since(last) > time.Second*LogTimeInterval {
|
if time.Since(last) > time.Second*LogTimeInterval {
|
||||||
last = time.Now()
|
last = time.Now()
|
||||||
output()
|
output()
|
||||||
}
|
}
|
||||||
case amount = <-b.inboundChan:
|
case amount = <-b.InboundChan:
|
||||||
b.inbound += amount
|
b.Inbound += amount
|
||||||
b.inEvents++
|
b.InEvents++
|
||||||
if time.Since(last) > time.Second*LogTimeInterval {
|
if time.Since(last) > time.Second*LogTimeInterval {
|
||||||
last = time.Now()
|
last = time.Now()
|
||||||
output()
|
output()
|
||||||
}
|
}
|
||||||
case <-time.After(time.Second * LogTimeInterval):
|
case <-time.After(time.Second * LogTimeInterval):
|
||||||
if b.inEvents > 0 || b.outEvents > 0 {
|
if b.InEvents > 0 || b.OutEvents > 0 {
|
||||||
output()
|
output()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,15 +81,15 @@ func (b *BytesSyncLogger) Log() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BytesSyncLogger) AddOutbound(amount int) {
|
func (b *BytesSyncLogger) AddOutbound(amount int) {
|
||||||
if !b.isLogging {
|
if !b.IsLogging {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b.outboundChan <- amount
|
b.OutboundChan <- amount
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BytesSyncLogger) AddInbound(amount int) {
|
func (b *BytesSyncLogger) AddInbound(amount int) {
|
||||||
if !b.isLogging {
|
if !b.IsLogging {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b.inboundChan <- amount
|
b.InboundChan <- amount
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package lib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
|
@ -2,7 +2,6 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"flag"
|
"flag"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -12,36 +11,30 @@ import (
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.torproject.org/pluggable-transports/goptlib.git"
|
"git.torproject.org/pluggable-transports/goptlib.git"
|
||||||
|
sf "git.torproject.org/pluggable-transports/snowflake.git/client/lib"
|
||||||
"github.com/keroserene/go-webrtc"
|
"github.com/keroserene/go-webrtc"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ReconnectTimeout = 10
|
|
||||||
DefaultSnowflakeCapacity = 1
|
DefaultSnowflakeCapacity = 1
|
||||||
SnowflakeTimeout = 30
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// When a connection handler starts, +1 is written to this channel; when it
|
|
||||||
// ends, -1 is written.
|
|
||||||
var handlerChan = make(chan int)
|
|
||||||
|
|
||||||
// Maintain |SnowflakeCapacity| number of available WebRTC connections, to
|
// Maintain |SnowflakeCapacity| number of available WebRTC connections, to
|
||||||
// transfer to the Tor SOCKS handler when needed.
|
// transfer to the Tor SOCKS handler when needed.
|
||||||
func ConnectLoop(snowflakes SnowflakeCollector) {
|
func ConnectLoop(snowflakes sf.SnowflakeCollector) {
|
||||||
for {
|
for {
|
||||||
// Check if ending is necessary.
|
// Check if ending is necessary.
|
||||||
_, err := snowflakes.Collect()
|
_, err := snowflakes.Collect()
|
||||||
if nil != err {
|
if nil != err {
|
||||||
log.Println("WebRTC:", err,
|
log.Println("WebRTC:", err,
|
||||||
" Retrying in", ReconnectTimeout, "seconds...")
|
" Retrying in", sf.ReconnectTimeout, "seconds...")
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case <-time.After(time.Second * ReconnectTimeout):
|
case <-time.After(time.Second * sf.ReconnectTimeout):
|
||||||
continue
|
continue
|
||||||
case <-snowflakes.Melted():
|
case <-snowflakes.Melted():
|
||||||
log.Println("ConnectLoop: stopped.")
|
log.Println("ConnectLoop: stopped.")
|
||||||
|
@ -51,7 +44,7 @@ func ConnectLoop(snowflakes SnowflakeCollector) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 SnowflakeCollector) error {
|
func socksAcceptLoop(ln *pt.SocksListener, snowflakes sf.SnowflakeCollector) error {
|
||||||
defer ln.Close()
|
defer ln.Close()
|
||||||
log.Println("Started SOCKS listener.")
|
log.Println("Started SOCKS listener.")
|
||||||
for {
|
for {
|
||||||
|
@ -64,64 +57,13 @@ func socksAcceptLoop(ln *pt.SocksListener, snowflakes SnowflakeCollector) error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Println("SOCKS accepted: ", conn.Req)
|
log.Println("SOCKS accepted: ", conn.Req)
|
||||||
err = handler(conn, snowflakes)
|
err = sf.Handler(conn, snowflakes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("handler error: %s", err)
|
log.Printf("handler error: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given an accepted SOCKS connection, establish a WebRTC connection to the
|
|
||||||
// remote peer and exchange traffic.
|
|
||||||
func handler(socks SocksConnector, snowflakes SnowflakeCollector) error {
|
|
||||||
handlerChan <- 1
|
|
||||||
defer func() {
|
|
||||||
handlerChan <- -1
|
|
||||||
}()
|
|
||||||
// Obtain an available WebRTC remote. May block.
|
|
||||||
snowflake := snowflakes.Pop()
|
|
||||||
if nil == snowflake {
|
|
||||||
socks.Reject()
|
|
||||||
return errors.New("handler: Received invalid Snowflake")
|
|
||||||
}
|
|
||||||
defer socks.Close()
|
|
||||||
defer snowflake.Close()
|
|
||||||
log.Println("---- Handler: snowflake assigned ----")
|
|
||||||
err := socks.Grant(&net.TCPAddr{IP: net.IPv4zero, Port: 0})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
// When WebRTC resets, close the SOCKS connection too.
|
|
||||||
snowflake.WaitForReset()
|
|
||||||
socks.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Begin exchanging data. Either WebRTC or localhost SOCKS will close first.
|
|
||||||
// In eithercase, this closes the handler and induces a new handler.
|
|
||||||
copyLoop(socks, snowflake)
|
|
||||||
log.Println("---- Handler: closed ---")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exchanges bytes between two ReadWriters.
|
|
||||||
// (In this case, between a SOCKS and WebRTC connection.)
|
|
||||||
func copyLoop(a, b io.ReadWriter) {
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(2)
|
|
||||||
go func() {
|
|
||||||
io.Copy(b, a)
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
io.Copy(a, b)
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
wg.Wait()
|
|
||||||
log.Println("copy loop ended")
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
iceServersCommas := flag.String("ice", "", "comma-separated list of ICE servers")
|
iceServersCommas := flag.String("ice", "", "comma-separated list of ICE servers")
|
||||||
brokerURL := flag.String("url", "", "URL of signaling broker")
|
brokerURL := flag.String("url", "", "URL of signaling broker")
|
||||||
|
@ -156,30 +98,34 @@ func main() {
|
||||||
|
|
||||||
log.Println("\n\n\n --- Starting Snowflake Client ---")
|
log.Println("\n\n\n --- Starting Snowflake Client ---")
|
||||||
|
|
||||||
var iceServers IceServerList
|
var iceServers sf.IceServerList
|
||||||
if len(strings.TrimSpace(*iceServersCommas)) > 0 {
|
if len(strings.TrimSpace(*iceServersCommas)) > 0 {
|
||||||
option := webrtc.OptionIceServer(*iceServersCommas)
|
option := webrtc.OptionIceServer(*iceServersCommas)
|
||||||
iceServers = append(iceServers, option)
|
iceServers = append(iceServers, option)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare to collect remote WebRTC peers.
|
// Prepare to collect remote WebRTC peers.
|
||||||
snowflakes := NewPeers(*max)
|
snowflakes := sf.NewPeers(*max)
|
||||||
if "" != *brokerURL {
|
if "" != *brokerURL {
|
||||||
// Use potentially domain-fronting broker to rendezvous.
|
// Use potentially domain-fronting broker to rendezvous.
|
||||||
broker := NewBrokerChannel(*brokerURL, *frontDomain, CreateBrokerTransport())
|
broker := sf.NewBrokerChannel(*brokerURL, *frontDomain, sf.CreateBrokerTransport())
|
||||||
snowflakes.Tongue = NewWebRTCDialer(broker, iceServers)
|
snowflakes.Tongue = sf.NewWebRTCDialer(broker, iceServers)
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, use manual copy and pasting of SDP messages.
|
// Otherwise, use manual copy and pasting of SDP messages.
|
||||||
snowflakes.Tongue = NewCopyPasteDialer(iceServers)
|
snowflakes.Tongue = sf.NewCopyPasteDialer(iceServers)
|
||||||
}
|
}
|
||||||
if nil == snowflakes.Tongue {
|
if nil == snowflakes.Tongue {
|
||||||
log.Fatal("Unable to prepare rendezvous method.")
|
log.Fatal("Unable to prepare rendezvous method.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Use a real logger to periodically output how much traffic is happening.
|
// Use a real logger to periodically output how much traffic is happening.
|
||||||
snowflakes.BytesLogger = &BytesSyncLogger{
|
snowflakes.BytesLogger = &sf.BytesSyncLogger{
|
||||||
inboundChan: make(chan int, 5), outboundChan: make(chan int, 5),
|
InboundChan: make(chan int, 5),
|
||||||
inbound: 0, outbound: 0, inEvents: 0, outEvents: 0,
|
OutboundChan: make(chan int, 5),
|
||||||
|
Inbound: 0,
|
||||||
|
Outbound: 0,
|
||||||
|
InEvents: 0,
|
||||||
|
OutEvents: 0,
|
||||||
}
|
}
|
||||||
go snowflakes.BytesLogger.Log()
|
go snowflakes.BytesLogger.Log()
|
||||||
|
|
||||||
|
@ -232,7 +178,7 @@ func main() {
|
||||||
sig = nil
|
sig = nil
|
||||||
for sig == nil {
|
for sig == nil {
|
||||||
select {
|
select {
|
||||||
case n := <-handlerChan:
|
case n := <-sf.HandlerChan:
|
||||||
numHandlers += n
|
numHandlers += n
|
||||||
case sig = <-sigChan:
|
case sig = <-sigChan:
|
||||||
}
|
}
|
||||||
|
@ -244,7 +190,7 @@ func main() {
|
||||||
}
|
}
|
||||||
snowflakes.End()
|
snowflakes.End()
|
||||||
for numHandlers > 0 {
|
for numHandlers > 0 {
|
||||||
numHandlers += <-handlerChan
|
numHandlers += <-sf.HandlerChan
|
||||||
}
|
}
|
||||||
log.Println("snowflake is done.")
|
log.Println("snowflake is done.")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue