proxy-go_test.go 10.2 KB
Newer Older
1
2
3
package main

import (
4
5
	"bytes"
	"fmt"
6
	"io"
7
	"io/ioutil"
8
	"net"
9
10
	"net/http"
	"net/url"
Cecylia Bocovich's avatar
Cecylia Bocovich committed
11
	"strconv"
12
13
	"strings"
	"testing"
14

15
	"git.torproject.org/pluggable-transports/snowflake.git/common/messages"
16
	"git.torproject.org/pluggable-transports/snowflake.git/common/util"
17
	"github.com/pion/webrtc/v2"
18
	. "github.com/smartystreets/goconvey/convey"
19
20
)

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// Set up a mock broker to communicate with
type MockTransport struct {
	statusOverride int
	body           []byte
}

// Just returns a response with fake SDP answer.
func (m *MockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	s := ioutil.NopCloser(bytes.NewReader(m.body))
	r := &http.Response{
		StatusCode: m.statusOverride,
		Body:       s,
	}
	return r, nil
}

// Set up a mock faulty transport
type FaultyTransport struct {
	statusOverride int
	body           []byte
}

// Just returns a response with fake SDP answer.
func (f *FaultyTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	return nil, fmt.Errorf("TransportFailed")
}

48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
func TestRemoteIPFromSDP(t *testing.T) {
	tests := []struct {
		sdp      string
		expected net.IP
	}{
		// https://tools.ietf.org/html/rfc4566#section-5
		{`v=0
o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5
s=SDP Seminar
i=A Seminar on the session description protocol
u=http://www.example.com/seminars/sdp.pdf
e=j.doe@example.com (Jane Doe)
c=IN IP4 224.2.17.12/127
t=2873397496 2873404696
a=recvonly
m=audio 49170 RTP/AVP 0
m=video 51372 RTP/AVP 99
a=rtpmap:99 h263-1998/90000
`, net.ParseIP("224.2.17.12")},
		// Missing c= line
		{`v=0
o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5
s=SDP Seminar
i=A Seminar on the session description protocol
u=http://www.example.com/seminars/sdp.pdf
e=j.doe@example.com (Jane Doe)
t=2873397496 2873404696
a=recvonly
m=audio 49170 RTP/AVP 0
m=video 51372 RTP/AVP 99
a=rtpmap:99 h263-1998/90000
`, nil},
		// Single line, IP address only
		{`c=IN IP4 224.2.1.1
`, net.ParseIP("224.2.1.1")},
		// Same, with TTL
		{`c=IN IP4 224.2.1.1/127
`, net.ParseIP("224.2.1.1")},
		// Same, with TTL and multicast addresses
		{`c=IN IP4 224.2.1.1/127/3
`, net.ParseIP("224.2.1.1")},
		// IPv6, address only
		{`c=IN IP6 FF15::101
`, net.ParseIP("ff15::101")},
		// Same, with multicast addresses
		{`c=IN IP6 FF15::101/3
`, net.ParseIP("ff15::101")},
		// Multiple c= lines
		{`c=IN IP4 1.2.3.4
c=IN IP4 5.6.7.8
`, net.ParseIP("1.2.3.4")},
		// Modified from SDP sent by snowflake-client.
		{`v=0
o=- 7860378660295630295 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE data
a=msid-semantic: WMS
m=application 54653 DTLS/SCTP 5000
c=IN IP4 1.2.3.4
a=candidate:3581707038 1 udp 2122260223 192.168.0.1 54653 typ host generation 0 network-id 1 network-cost 50
a=candidate:2617212910 1 tcp 1518280447 192.168.0.1 59673 typ host tcptype passive generation 0 network-id 1 network-cost 50
a=candidate:2082671819 1 udp 1686052607 1.2.3.4 54653 typ srflx raddr 192.168.0.1 rport 54653 generation 0 network-id 1 network-cost 50
a=ice-ufrag:IBdf
a=ice-pwd:G3lTrrC9gmhQx481AowtkhYz
a=fingerprint:sha-256 53:F8:84:D9:3C:1F:A0:44:AA:D6:3C:65:80:D3:CB:6F:23:90:17:41:06:F9:9C:10:D8:48:4A:A8:B6:FA:14:A1
a=setup:actpass
a=mid:data
a=sctpmap:5000 webrtc-datachannel 1024
`, net.ParseIP("1.2.3.4")},
		// Improper character within IPv4
		{`c=IN IP4 224.2z.1.1
`, nil},
		// Improper character within IPv6
		{`c=IN IP6 ff15:g::101
`, nil},
		// Bogus "IP7" addrtype
		{`c=IN IP7 1.2.3.4
`, nil},
	}

	for _, test := range tests {
		// https://tools.ietf.org/html/rfc4566#section-5: "The sequence
		// CRLF (0x0d0a) is used to end a record, although parsers
		// SHOULD be tolerant and also accept records terminated with a
		// single newline character." We represent the test cases with
		// LF line endings for convenience, and test them both that way
		// and with CRLF line endings.
		lfSDP := test.sdp
		crlfSDP := strings.Replace(lfSDP, "\n", "\r\n", -1)

		ip := remoteIPFromSDP(lfSDP)
		if !ip.Equal(test.expected) {
			t.Errorf("expected %q, got %q from %q", test.expected, ip, lfSDP)
		}
		ip = remoteIPFromSDP(crlfSDP)
		if !ip.Equal(test.expected) {
			t.Errorf("expected %q, got %q from %q", test.expected, ip, crlfSDP)
		}
	}
}
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200

func TestSessionDescriptions(t *testing.T) {
	Convey("Session description deserialization", t, func() {
		for _, test := range []struct {
			msg string
			ret *webrtc.SessionDescription
		}{
			{
				"test",
				nil,
			},
			{
				`{"type":"answer"}`,
				nil,
			},
			{
				`{"sdp":"test"}`,
				nil,
			},
			{
				`{"type":"test", "sdp":"test"}`,
				nil,
			},
			{
				`{"type":"answer", "sdp":"test"}`,
				&webrtc.SessionDescription{
					Type: webrtc.SDPTypeAnswer,
					SDP:  "test",
				},
			},
			{
				`{"type":"pranswer", "sdp":"test"}`,
				&webrtc.SessionDescription{
					Type: webrtc.SDPTypePranswer,
					SDP:  "test",
				},
			},
			{
				`{"type":"rollback", "sdp":"test"}`,
				&webrtc.SessionDescription{
					Type: webrtc.SDPTypeRollback,
					SDP:  "test",
				},
			},
			{
				`{"type":"offer", "sdp":"test"}`,
				&webrtc.SessionDescription{
					Type: webrtc.SDPTypeOffer,
					SDP:  "test",
				},
			},
		} {
201
			desc := util.DeserializeSessionDescription(test.msg)
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
			So(desc, ShouldResemble, test.ret)
		}
	})
	Convey("Session description serialization", t, func() {
		for _, test := range []struct {
			desc *webrtc.SessionDescription
			ret  string
		}{
			{
				&webrtc.SessionDescription{
					Type: webrtc.SDPTypeOffer,
					SDP:  "test",
				},
				`{"type":"offer","sdp":"test"}`,
			},
		} {
218
			msg := util.SerializeSessionDescription(test.desc)
219
220
221
222
223
			So(msg, ShouldResemble, test.ret)
		}
	})
}

224
func TestBrokerInteractions(t *testing.T) {
Cecylia Bocovich's avatar
Cecylia Bocovich committed
225
226
227
228
229
	const sampleSDP = `"v=0\r\no=- 4358805017720277108 2 IN IP4 8.8.8.8\r\ns=-\r\nt=0 0\r\na=group:BUNDLE data\r\na=msid-semantic: WMS\r\nm=application 56688 DTLS/SCTP 5000\r\nc=IN IP4 8.8.8.8\r\na=candidate:3769337065 1 udp 2122260223 8.8.8.8 56688 typ host generation 0 network-id 1 network-cost 50\r\na=candidate:2921887769 1 tcp 1518280447 8.8.8.8 35441 typ host tcptype passive generation 0 network-id 1 network-cost 50\r\na=ice-ufrag:aMAZ\r\na=ice-pwd:jcHb08Jjgrazp2dzjdrvPPvV\r\na=ice-options:trickle\r\na=fingerprint:sha-256 C8:88:EE:B9:E7:02:2E:21:37:ED:7A:D1:EB:2B:A3:15:A2:3B:5B:1C:3D:D4:D5:1F:06:CF:52:40:03:F8:DD:66\r\na=setup:actpass\r\na=mid:data\r\na=sctpmap:5000 webrtc-datachannel 1024\r\n"`

	const sampleOffer = `{"type":"offer","sdp":` + sampleSDP + `}`
	const sampleAnswer = `{"type":"answer","sdp":` + sampleSDP + `}`

230
231
232
233
234
235
236
237
238
239
240
241
242
	Convey("Proxy connections to broker", t, func() {
		broker := new(Broker)
		broker.url, _ = url.Parse("localhost")

		//Mock peerConnection
		config = webrtc.Configuration{
			ICEServers: []webrtc.ICEServer{
				{
					URLs: []string{"stun:stun.l.google.com:19302"},
				},
			},
		}
		pc, _ := webrtc.NewPeerConnection(config)
243
		offer := util.DeserializeSessionDescription(sampleOffer)
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
		pc.SetRemoteDescription(*offer)
		answer, _ := pc.CreateAnswer(nil)
		pc.SetLocalDescription(answer)

		Convey("polls broker correctly", func() {
			var err error

			b, err := messages.EncodePollResponse(sampleOffer, true)
			So(err, ShouldEqual, nil)
			broker.transport = &MockTransport{
				http.StatusOK,
				b,
			}

			sdp := broker.pollOffer(sampleOffer)
Cecylia Bocovich's avatar
Cecylia Bocovich committed
259
260
			expectedSDP, _ := strconv.Unquote(sampleSDP)
			So(sdp.SDP, ShouldResemble, expectedSDP)
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
		})
		Convey("handles poll error", func() {
			var err error

			b := []byte("test")
			So(err, ShouldEqual, nil)
			broker.transport = &MockTransport{
				http.StatusOK,
				b,
			}

			sdp := broker.pollOffer(sampleOffer)
			So(sdp, ShouldBeNil)
		})
		Convey("sends answer to broker", func() {
			var err error

			b, err := messages.EncodeAnswerResponse(true)
			So(err, ShouldEqual, nil)
			broker.transport = &MockTransport{
				http.StatusOK,
				b,
			}

			err = broker.sendAnswer(sampleAnswer, pc)
			So(err, ShouldEqual, nil)

			b, err = messages.EncodeAnswerResponse(false)
			So(err, ShouldEqual, nil)
			broker.transport = &MockTransport{
				http.StatusOK,
				b,
			}

			err = broker.sendAnswer(sampleAnswer, pc)
			So(err, ShouldNotBeNil)
		})
		Convey("handles answer error", func() {
			//Error if faulty transport
			broker.transport = &FaultyTransport{}
			err := broker.sendAnswer(sampleAnswer, pc)
			So(err, ShouldNotBeNil)

			//Error if status code is not ok
			broker.transport = &MockTransport{
				http.StatusGone,
				[]byte(""),
			}
			err = broker.sendAnswer("test", pc)
			So(err, ShouldNotEqual, nil)
			So(err.Error(), ShouldResemble, "broker returned 410")

			//Error if we can't parse broker message
			broker.transport = &MockTransport{
				http.StatusOK,
				[]byte("test"),
			}
			err = broker.sendAnswer("test", pc)
			So(err, ShouldNotBeNil)

			//Error if broker message surpasses read limit
			broker.transport = &MockTransport{
				http.StatusOK,
				make([]byte, 100001),
			}
			err = broker.sendAnswer("test", pc)
			So(err, ShouldNotBeNil)
		})
	})
}

332
333
func TestUtilityFuncs(t *testing.T) {
	Convey("LimitedRead", t, func() {
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
		c, s := net.Pipe()
		Convey("Successful read", func() {
			go func() {
				bytes := make([]byte, 50)
				c.Write(bytes)
				c.Close()
			}()
			bytes, err := limitedRead(s, 60)
			So(len(bytes), ShouldEqual, 50)
			So(err, ShouldBeNil)
		})
		Convey("Large read", func() {
			go func() {
				bytes := make([]byte, 50)
				c.Write(bytes)
				c.Close()
			}()
			bytes, err := limitedRead(s, 49)
			So(len(bytes), ShouldEqual, 49)
			So(err, ShouldEqual, io.ErrUnexpectedEOF)
		})
		Convey("Failed read", func() {
			s.Close()
			bytes, err := limitedRead(s, 49)
			So(len(bytes), ShouldEqual, 0)
			So(err, ShouldEqual, io.ErrClosedPipe)
		})
	})
	Convey("Tokens", t, func() {
		tokens = make(chan bool, 2)
		for i := uint(0); i < 2; i++ {
			tokens <- true
		}
		So(len(tokens), ShouldEqual, 2)
		getToken()
		So(len(tokens), ShouldEqual, 1)
		retToken()
		So(len(tokens), ShouldEqual, 2)
	})
	Convey("SessionID Generation", t, func() {
		sid1 := genSessionID()
		sid2 := genSessionID()
		So(sid1, ShouldNotEqual, sid2)
	})
Arlo Breault's avatar
Arlo Breault committed
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
	Convey("CopyLoop", t, func() {
		c1, s1 := net.Pipe()
		c2, s2 := net.Pipe()
		go CopyLoop(s1, s2)
		go func() {
			bytes := []byte("Hello!")
			c1.Write(bytes)
		}()
		bytes := make([]byte, 6)
		n, err := c2.Read(bytes)
		So(n, ShouldEqual, 6)
		So(err, ShouldEqual, nil)
		So(bytes, ShouldResemble, []byte("Hello!"))
		s1.Close()

		//Check that copy loop has closed other connection
		_, err = s2.Write(bytes)
		So(err, ShouldNotBeNil)
	})
397
}