Start refactoring out a client and library

This commit is contained in:
Arlo Breault 2018-11-20 22:17:24 -05:00
parent 7662ccb00c
commit cce7ee64a7
8 changed files with 120 additions and 105 deletions

View file

@ -1,4 +1,4 @@
package main package lib
import ( import (
"io" "io"

View file

@ -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)
}) })

View file

@ -1,4 +1,4 @@
package main package lib
import ( import (
"container/list" "container/list"

View file

@ -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
View 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")
}

View file

@ -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
} }

View file

@ -1,4 +1,4 @@
package main package lib
import ( import (
"bytes" "bytes"

View file

@ -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.")
} }