proxy-go_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. package main
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "net"
  8. "net/http"
  9. "net/url"
  10. "strconv"
  11. "strings"
  12. "testing"
  13. "git.torproject.org/pluggable-transports/snowflake.git/common/messages"
  14. "git.torproject.org/pluggable-transports/snowflake.git/common/util"
  15. "github.com/pion/webrtc/v3"
  16. . "github.com/smartystreets/goconvey/convey"
  17. )
  18. // Set up a mock broker to communicate with
  19. type MockTransport struct {
  20. statusOverride int
  21. body []byte
  22. }
  23. // Just returns a response with fake SDP answer.
  24. func (m *MockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
  25. s := ioutil.NopCloser(bytes.NewReader(m.body))
  26. r := &http.Response{
  27. StatusCode: m.statusOverride,
  28. Body: s,
  29. }
  30. return r, nil
  31. }
  32. // Set up a mock faulty transport
  33. type FaultyTransport struct {
  34. statusOverride int
  35. body []byte
  36. }
  37. // Just returns a response with fake SDP answer.
  38. func (f *FaultyTransport) RoundTrip(req *http.Request) (*http.Response, error) {
  39. return nil, fmt.Errorf("TransportFailed")
  40. }
  41. func TestRemoteIPFromSDP(t *testing.T) {
  42. tests := []struct {
  43. sdp string
  44. expected net.IP
  45. }{
  46. // https://tools.ietf.org/html/rfc4566#section-5
  47. {`v=0
  48. o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5
  49. s=SDP Seminar
  50. i=A Seminar on the session description protocol
  51. u=http://www.example.com/seminars/sdp.pdf
  52. e=j.doe@example.com (Jane Doe)
  53. c=IN IP4 224.2.17.12/127
  54. t=2873397496 2873404696
  55. a=recvonly
  56. m=audio 49170 RTP/AVP 0
  57. m=video 51372 RTP/AVP 99
  58. a=rtpmap:99 h263-1998/90000
  59. `, net.ParseIP("224.2.17.12")},
  60. // local addresses only
  61. {`v=0
  62. o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5
  63. s=SDP Seminar
  64. i=A Seminar on the session description protocol
  65. u=http://www.example.com/seminars/sdp.pdf
  66. e=j.doe@example.com (Jane Doe)
  67. c=IN IP4 10.47.16.5/127
  68. t=2873397496 2873404696
  69. a=recvonly
  70. m=audio 49170 RTP/AVP 0
  71. m=video 51372 RTP/AVP 99
  72. a=rtpmap:99 h263-1998/90000
  73. `, nil},
  74. // Remote IP in candidate attribute only
  75. {`v=0
  76. o=- 4358805017720277108 2 IN IP4 0.0.0.0
  77. s=-
  78. t=0 0
  79. a=group:BUNDLE data
  80. a=msid-semantic: WMS
  81. m=application 56688 DTLS/SCTP 5000
  82. c=IN IP4 0.0.0.0
  83. a=candidate:3769337065 1 udp 2122260223 1.2.3.4 56688 typ host generation 0 network-id 1 network-cost 50
  84. a=ice-ufrag:aMAZ
  85. a=ice-pwd:jcHb08Jjgrazp2dzjdrvPPvV
  86. a=ice-options:trickle
  87. a=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
  88. a=setup:actpass
  89. a=mid:data
  90. a=sctpmap:5000 webrtc-datachannel 1024
  91. `, net.ParseIP("1.2.3.4")},
  92. // Unspecified address
  93. {`v=0
  94. o=jdoe 2890844526 2890842807 IN IP4 0.0.0.0
  95. s=SDP Seminar
  96. i=A Seminar on the session description protocol
  97. u=http://www.example.com/seminars/sdp.pdf
  98. e=j.doe@example.com (Jane Doe)
  99. t=2873397496 2873404696
  100. a=recvonly
  101. m=audio 49170 RTP/AVP 0
  102. m=video 51372 RTP/AVP 99
  103. a=rtpmap:99 h263-1998/90000
  104. `, nil},
  105. // Missing c= line
  106. {`v=0
  107. o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5
  108. s=SDP Seminar
  109. i=A Seminar on the session description protocol
  110. u=http://www.example.com/seminars/sdp.pdf
  111. e=j.doe@example.com (Jane Doe)
  112. t=2873397496 2873404696
  113. a=recvonly
  114. m=audio 49170 RTP/AVP 0
  115. m=video 51372 RTP/AVP 99
  116. a=rtpmap:99 h263-1998/90000
  117. `, nil},
  118. // Single line, IP address only
  119. {`v=0
  120. o=- 4358805017720277108 2 IN IP4 0.0.0.0
  121. s=-
  122. t=0 0
  123. a=group:BUNDLE data
  124. a=msid-semantic: WMS
  125. m=application 56688 DTLS/SCTP 5000
  126. c=IN IP4 224.2.1.1
  127. `, net.ParseIP("224.2.1.1")},
  128. // Same, with TTL
  129. {`v=0
  130. o=- 4358805017720277108 2 IN IP4 0.0.0.0
  131. s=-
  132. t=0 0
  133. a=group:BUNDLE data
  134. a=msid-semantic: WMS
  135. m=application 56688 DTLS/SCTP 5000
  136. c=IN IP4 224.2.1.1/127
  137. `, net.ParseIP("224.2.1.1")},
  138. // Same, with TTL and multicast addresses
  139. {`v=0
  140. o=- 4358805017720277108 2 IN IP4 0.0.0.0
  141. s=-
  142. t=0 0
  143. a=group:BUNDLE data
  144. a=msid-semantic: WMS
  145. m=application 56688 DTLS/SCTP 5000
  146. c=IN IP4 224.2.1.1/127/3
  147. `, net.ParseIP("224.2.1.1")},
  148. // IPv6, address only
  149. {`v=0
  150. o=- 4358805017720277108 2 IN IP4 0.0.0.0
  151. s=-
  152. t=0 0
  153. a=group:BUNDLE data
  154. a=msid-semantic: WMS
  155. m=application 56688 DTLS/SCTP 5000
  156. c=IN IP6 FF15::101
  157. `, net.ParseIP("ff15::101")},
  158. // Same, with multicast addresses
  159. {`v=0
  160. o=- 4358805017720277108 2 IN IP4 0.0.0.0
  161. s=-
  162. t=0 0
  163. a=group:BUNDLE data
  164. a=msid-semantic: WMS
  165. m=application 56688 DTLS/SCTP 5000
  166. c=IN IP6 FF15::101/3
  167. `, net.ParseIP("ff15::101")},
  168. // Multiple c= lines
  169. {`v=0
  170. o=- 4358805017720277108 2 IN IP4 0.0.0.0
  171. s=-
  172. t=0 0
  173. a=group:BUNDLE data
  174. a=msid-semantic: WMS
  175. m=application 56688 DTLS/SCTP 5000
  176. c=IN IP4 1.2.3.4
  177. c=IN IP4 5.6.7.8
  178. `, net.ParseIP("1.2.3.4")},
  179. // Modified from SDP sent by snowflake-client.
  180. {`v=0
  181. o=- 7860378660295630295 2 IN IP4 127.0.0.1
  182. s=-
  183. t=0 0
  184. a=group:BUNDLE data
  185. a=msid-semantic: WMS
  186. m=application 54653 DTLS/SCTP 5000
  187. c=IN IP4 1.2.3.4
  188. a=candidate:3581707038 1 udp 2122260223 192.168.0.1 54653 typ host generation 0 network-id 1 network-cost 50
  189. a=candidate:2617212910 1 tcp 1518280447 192.168.0.1 59673 typ host tcptype passive generation 0 network-id 1 network-cost 50
  190. 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
  191. a=ice-ufrag:IBdf
  192. a=ice-pwd:G3lTrrC9gmhQx481AowtkhYz
  193. 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
  194. a=setup:actpass
  195. a=mid:data
  196. a=sctpmap:5000 webrtc-datachannel 1024
  197. `, net.ParseIP("1.2.3.4")},
  198. // Improper character within IPv4
  199. {`v=0
  200. o=- 4358805017720277108 2 IN IP4 0.0.0.0
  201. s=-
  202. t=0 0
  203. a=group:BUNDLE data
  204. a=msid-semantic: WMS
  205. m=application 56688 DTLS/SCTP 5000
  206. c=IN IP4 224.2z.1.1
  207. `, nil},
  208. // Improper character within IPv6
  209. {`v=0
  210. o=- 4358805017720277108 2 IN IP4 0.0.0.0
  211. s=-
  212. t=0 0
  213. a=group:BUNDLE data
  214. a=msid-semantic: WMS
  215. m=application 56688 DTLS/SCTP 5000
  216. c=IN IP6 ff15:g::101
  217. `, nil},
  218. // Bogus "IP7" addrtype
  219. {`v=0
  220. o=- 4358805017720277108 2 IN IP4 0.0.0.0
  221. s=-
  222. t=0 0
  223. a=group:BUNDLE data
  224. a=msid-semantic: WMS
  225. m=application 56688 DTLS/SCTP 5000
  226. c=IN IP7 1.2.3.4
  227. `, nil},
  228. }
  229. for _, test := range tests {
  230. // https://tools.ietf.org/html/rfc4566#section-5: "The sequence
  231. // CRLF (0x0d0a) is used to end a record, although parsers
  232. // SHOULD be tolerant and also accept records terminated with a
  233. // single newline character." We represent the test cases with
  234. // LF line endings for convenience, and test them both that way
  235. // and with CRLF line endings.
  236. lfSDP := test.sdp
  237. crlfSDP := strings.Replace(lfSDP, "\n", "\r\n", -1)
  238. ip := remoteIPFromSDP(lfSDP)
  239. if !ip.Equal(test.expected) {
  240. t.Errorf("expected %q, got %q from %q", test.expected, ip, lfSDP)
  241. }
  242. ip = remoteIPFromSDP(crlfSDP)
  243. if !ip.Equal(test.expected) {
  244. t.Errorf("expected %q, got %q from %q", test.expected, ip, crlfSDP)
  245. }
  246. }
  247. }
  248. func TestSessionDescriptions(t *testing.T) {
  249. Convey("Session description deserialization", t, func() {
  250. for _, test := range []struct {
  251. msg string
  252. ret *webrtc.SessionDescription
  253. }{
  254. {
  255. "test",
  256. nil,
  257. },
  258. {
  259. `{"type":"answer"}`,
  260. nil,
  261. },
  262. {
  263. `{"sdp":"test"}`,
  264. nil,
  265. },
  266. {
  267. `{"type":"test", "sdp":"test"}`,
  268. nil,
  269. },
  270. {
  271. `{"type":"answer", "sdp":"test"}`,
  272. &webrtc.SessionDescription{
  273. Type: webrtc.SDPTypeAnswer,
  274. SDP: "test",
  275. },
  276. },
  277. {
  278. `{"type":"pranswer", "sdp":"test"}`,
  279. &webrtc.SessionDescription{
  280. Type: webrtc.SDPTypePranswer,
  281. SDP: "test",
  282. },
  283. },
  284. {
  285. `{"type":"rollback", "sdp":"test"}`,
  286. &webrtc.SessionDescription{
  287. Type: webrtc.SDPTypeRollback,
  288. SDP: "test",
  289. },
  290. },
  291. {
  292. `{"type":"offer", "sdp":"test"}`,
  293. &webrtc.SessionDescription{
  294. Type: webrtc.SDPTypeOffer,
  295. SDP: "test",
  296. },
  297. },
  298. } {
  299. desc, _ := util.DeserializeSessionDescription(test.msg)
  300. So(desc, ShouldResemble, test.ret)
  301. }
  302. })
  303. Convey("Session description serialization", t, func() {
  304. for _, test := range []struct {
  305. desc *webrtc.SessionDescription
  306. ret string
  307. }{
  308. {
  309. &webrtc.SessionDescription{
  310. Type: webrtc.SDPTypeOffer,
  311. SDP: "test",
  312. },
  313. `{"type":"offer","sdp":"test"}`,
  314. },
  315. } {
  316. msg, err := util.SerializeSessionDescription(test.desc)
  317. So(msg, ShouldResemble, test.ret)
  318. So(err, ShouldBeNil)
  319. }
  320. })
  321. }
  322. func TestBrokerInteractions(t *testing.T) {
  323. 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"`
  324. const sampleOffer = `{"type":"offer","sdp":` + sampleSDP + `}`
  325. const sampleAnswer = `{"type":"answer","sdp":` + sampleSDP + `}`
  326. Convey("Proxy connections to broker", t, func() {
  327. broker := new(SignalingServer)
  328. broker.url, _ = url.Parse("localhost")
  329. //Mock peerConnection
  330. config = webrtc.Configuration{
  331. ICEServers: []webrtc.ICEServer{
  332. {
  333. URLs: []string{"stun:stun.l.google.com:19302"},
  334. },
  335. },
  336. }
  337. pc, _ := webrtc.NewPeerConnection(config)
  338. offer, _ := util.DeserializeSessionDescription(sampleOffer)
  339. pc.SetRemoteDescription(*offer)
  340. answer, _ := pc.CreateAnswer(nil)
  341. pc.SetLocalDescription(answer)
  342. Convey("polls broker correctly", func() {
  343. var err error
  344. b, err := messages.EncodePollResponse(sampleOffer, true, "unknown")
  345. So(err, ShouldEqual, nil)
  346. broker.transport = &MockTransport{
  347. http.StatusOK,
  348. b,
  349. }
  350. sdp := broker.pollOffer(sampleOffer)
  351. expectedSDP, _ := strconv.Unquote(sampleSDP)
  352. So(sdp.SDP, ShouldResemble, expectedSDP)
  353. })
  354. Convey("handles poll error", func() {
  355. var err error
  356. b := []byte("test")
  357. So(err, ShouldEqual, nil)
  358. broker.transport = &MockTransport{
  359. http.StatusOK,
  360. b,
  361. }
  362. sdp := broker.pollOffer(sampleOffer)
  363. So(sdp, ShouldBeNil)
  364. })
  365. Convey("sends answer to broker", func() {
  366. var err error
  367. b, err := messages.EncodeAnswerResponse(true)
  368. So(err, ShouldEqual, nil)
  369. broker.transport = &MockTransport{
  370. http.StatusOK,
  371. b,
  372. }
  373. err = broker.sendAnswer(sampleAnswer, pc)
  374. So(err, ShouldEqual, nil)
  375. b, err = messages.EncodeAnswerResponse(false)
  376. So(err, ShouldEqual, nil)
  377. broker.transport = &MockTransport{
  378. http.StatusOK,
  379. b,
  380. }
  381. err = broker.sendAnswer(sampleAnswer, pc)
  382. So(err, ShouldNotBeNil)
  383. })
  384. Convey("handles answer error", func() {
  385. //Error if faulty transport
  386. broker.transport = &FaultyTransport{}
  387. err := broker.sendAnswer(sampleAnswer, pc)
  388. So(err, ShouldNotBeNil)
  389. //Error if status code is not ok
  390. broker.transport = &MockTransport{
  391. http.StatusGone,
  392. []byte(""),
  393. }
  394. err = broker.sendAnswer("test", pc)
  395. So(err, ShouldNotEqual, nil)
  396. So(err.Error(), ShouldResemble,
  397. "error sending answer to broker: remote returned status code 410")
  398. //Error if we can't parse broker message
  399. broker.transport = &MockTransport{
  400. http.StatusOK,
  401. []byte("test"),
  402. }
  403. err = broker.sendAnswer("test", pc)
  404. So(err, ShouldNotBeNil)
  405. //Error if broker message surpasses read limit
  406. broker.transport = &MockTransport{
  407. http.StatusOK,
  408. make([]byte, 100001),
  409. }
  410. err = broker.sendAnswer("test", pc)
  411. So(err, ShouldNotBeNil)
  412. })
  413. })
  414. }
  415. func TestUtilityFuncs(t *testing.T) {
  416. Convey("LimitedRead", t, func() {
  417. c, s := net.Pipe()
  418. Convey("Successful read", func() {
  419. go func() {
  420. bytes := make([]byte, 50)
  421. c.Write(bytes)
  422. c.Close()
  423. }()
  424. bytes, err := limitedRead(s, 60)
  425. So(len(bytes), ShouldEqual, 50)
  426. So(err, ShouldBeNil)
  427. })
  428. Convey("Large read", func() {
  429. go func() {
  430. bytes := make([]byte, 50)
  431. c.Write(bytes)
  432. c.Close()
  433. }()
  434. bytes, err := limitedRead(s, 49)
  435. So(len(bytes), ShouldEqual, 49)
  436. So(err, ShouldEqual, io.ErrUnexpectedEOF)
  437. })
  438. Convey("Failed read", func() {
  439. s.Close()
  440. bytes, err := limitedRead(s, 49)
  441. So(len(bytes), ShouldEqual, 0)
  442. So(err, ShouldEqual, io.ErrClosedPipe)
  443. })
  444. })
  445. Convey("Tokens", t, func() {
  446. tokens = make(chan bool, 2)
  447. for i := uint(0); i < 2; i++ {
  448. tokens <- true
  449. }
  450. So(len(tokens), ShouldEqual, 2)
  451. getToken()
  452. So(len(tokens), ShouldEqual, 1)
  453. retToken()
  454. So(len(tokens), ShouldEqual, 2)
  455. })
  456. Convey("SessionID Generation", t, func() {
  457. sid1 := genSessionID()
  458. sid2 := genSessionID()
  459. So(sid1, ShouldNotEqual, sid2)
  460. })
  461. Convey("CopyLoop", t, func() {
  462. c1, s1 := net.Pipe()
  463. c2, s2 := net.Pipe()
  464. go CopyLoop(s1, s2)
  465. go func() {
  466. bytes := []byte("Hello!")
  467. c1.Write(bytes)
  468. }()
  469. bytes := make([]byte, 6)
  470. n, err := c2.Read(bytes)
  471. So(n, ShouldEqual, 6)
  472. So(err, ShouldEqual, nil)
  473. So(bytes, ShouldResemble, []byte("Hello!"))
  474. s1.Close()
  475. //Check that copy loop has closed other connection
  476. _, err = s2.Write(bytes)
  477. So(err, ShouldNotBeNil)
  478. })
  479. }