snowflake.go 5.44 KB
Newer Older
1
// Client transport plugin for the Snowflake pluggable transport.
2
3
4
package main

import (
5
	"errors"
6
	"flag"
7
8
9
10
11
12
13
	"io"
	"log"
	"net"
	"os"
	"os/signal"
	"sync"
	"syscall"
14
	"time"
15

16
	"git.torproject.org/pluggable-transports/goptlib.git"
17
18
19
	"github.com/keroserene/go-webrtc"
)

20
const (
21
22
	ReconnectTimeout         = 10
	DefaultSnowflakeCapacity = 1
23
	SnowflakeTimeout         = 30
24
25
)

26
27
28
29
// When a connection handler starts, +1 is written to this channel; when it
// ends, -1 is written.
var handlerChan = make(chan int)

30
31
// Maintain |SnowflakeCapacity| number of available WebRTC connections, to
// transfer to the Tor SOCKS handler when needed.
32
func ConnectLoop(snowflakes SnowflakeCollector) {
33
	for {
34
		// Check if ending is necessary.
35
		_, err := snowflakes.Collect()
36
		if nil != err {
37
			log.Println("WebRTC:", err,
38
				" Retrying in", ReconnectTimeout, "seconds...")
39
40
41
		}
		select {
		case <-time.After(time.Second * ReconnectTimeout):
42
			continue
43
44
45
		case <-snowflakes.Melted():
			log.Println("ConnectLoop: stopped.")
			return
46
47
48
49
		}
	}
}

50
// Accept local SOCKS connections and pass them to the handler.
51
func socksAcceptLoop(ln *pt.SocksListener, snowflakes SnowflakeCollector) error {
52
	defer ln.Close()
53
	log.Println("Started SOCKS listener.")
54
	for {
55
		log.Println("SOCKS listening...")
56
		conn, err := ln.AcceptSocks()
57
		log.Println("SOCKS accepted: ", conn.Req)
58
59
60
61
62
63
64
65
66
67
		if err != nil {
			if e, ok := err.(net.Error); ok && e.Temporary() {
				continue
			}
			return err
		}
		err = handler(conn, snowflakes)
		if err != nil {
			log.Printf("handler error: %s", err)
		}
68
	}
69
70
}

71
72
73
// Given an accepted SOCKS connection, establish a WebRTC connection to the
// remote peer and exchange traffic.
func handler(socks SocksConnector, snowflakes SnowflakeCollector) error {
74
75
76
77
	handlerChan <- 1
	defer func() {
		handlerChan <- -1
	}()
78
79
	// Obtain an available WebRTC remote. May block.
	snowflake := snowflakes.Pop()
80
81
	if nil == snowflake {
		socks.Reject()
82
		return errors.New("handler: Received invalid Snowflake")
83
	}
84
	defer socks.Close()
85
	defer snowflake.Close()
86
	log.Println("---- Handler: snowflake assigned ----")
87
	err := socks.Grant(&net.TCPAddr{IP: net.IPv4zero, Port: 0})
88
89
90
91
	if err != nil {
		return err
	}

92
	go func() {
93
		// When WebRTC resets, close the SOCKS connection too.
94
95
96
		snowflake.WaitForReset()
		socks.Close()
	}()
97

98
99
	// Begin exchanging data. Either WebRTC or localhost SOCKS will close first.
	// In eithercase, this closes the handler and induces a new handler.
100
101
	copyLoop(socks, snowflake)
	log.Println("---- Handler: closed ---")
102
103
104
	return nil
}

105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// 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")
}

122
func main() {
123
	webrtc.SetLoggingVerbosity(1)
124
125
	logFile, err := os.OpenFile("snowflake.log",
		os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
126
127
128
129
130
	if err != nil {
		log.Fatal(err)
	}
	defer logFile.Close()
	log.SetOutput(logFile)
131

132
133
134
135
	var iceServers IceServerList
	log.Println("\n\n\n --- Starting Snowflake Client ---")

	flag.Var(&iceServers, "ice", "comma-separated list of ICE servers")
136
137
138
139
	brokerURL := flag.String("url", "", "URL of signaling broker")
	frontDomain := flag.String("front", "", "front domain")
	max := flag.Int("max", DefaultSnowflakeCapacity,
		"capacity for number of multiplexed WebRTC peers")
140
141
	flag.Parse()

142
	// Prepare to collect remote WebRTC peers.
143
	snowflakes := NewPeers(*max)
144
145
146
147
148
149
150
151
	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)
	}
152
153
154
155
	if nil == snowflakes.Tongue {
		log.Fatal("Unable to prepare rendezvous method.")
		return
	}
156
	// Use a real logger to periodically output how much traffic is happening.
157
	snowflakes.BytesLogger = &BytesSyncLogger{
158
159
160
		inboundChan: make(chan int, 5), outboundChan: make(chan int, 5),
		inbound: 0, outbound: 0, inEvents: 0, outEvents: 0,
	}
161
	go snowflakes.BytesLogger.Log()
162

163
	go ConnectLoop(snowflakes)
164

165
166
	// Begin goptlib client process.
	ptInfo, err := pt.ClientSetup(nil)
167
	if err != nil {
David Fifield's avatar
David Fifield committed
168
		log.Fatal(err)
169
170
171
172
173
174
175
176
	}
	if ptInfo.ProxyURL != nil {
		pt.ProxyError("proxy is not supported")
		os.Exit(1)
	}
	listeners := make([]net.Listener, 0)
	for _, methodName := range ptInfo.MethodNames {
		switch methodName {
Arlo Breault's avatar
Arlo Breault committed
177
		case "snowflake":
178
			// TODO: Be able to recover when SOCKS dies.
179
180
181
182
183
			ln, err := pt.ListenSocks("tcp", "127.0.0.1:0")
			if err != nil {
				pt.CmethodError(methodName, err.Error())
				break
			}
184
			go socksAcceptLoop(ln, snowflakes)
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
			pt.Cmethod(methodName, ln.Version(), ln.Addr())
			listeners = append(listeners, ln)
		default:
			pt.CmethodError(methodName, "no such method")
		}
	}
	pt.CmethodsDone()

	var numHandlers int = 0
	var sig os.Signal
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	// wait for first signal
	sig = nil
	for sig == nil {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case sig = <-sigChan:
		}
	}
	for _, ln := range listeners {
		ln.Close()
	}
210
	snowflakes.End()
211
212
213
214
215
216
217
218
219
	// wait for second signal or no more handlers
	sig = nil
	for sig == nil && numHandlers != 0 {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case sig = <-sigChan:
		}
	}
220
	log.Println("snowflake is done.")
221
}