snowflake.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. package main
  2. import (
  3. "bytes"
  4. "crypto/rand"
  5. "encoding/base64"
  6. "flag"
  7. "fmt"
  8. "io"
  9. "io/ioutil"
  10. "log"
  11. "net"
  12. "net/http"
  13. "net/url"
  14. "os"
  15. "regexp"
  16. "strings"
  17. "sync"
  18. "time"
  19. "git.torproject.org/pluggable-transports/snowflake.git/common/safelog"
  20. "github.com/keroserene/go-webrtc"
  21. "golang.org/x/net/websocket"
  22. )
  23. const defaultBrokerURL = "https://snowflake-broker.bamsoftware.com/"
  24. const defaultRelayURL = "wss://snowflake.bamsoftware.com/"
  25. const defaultSTUNURL = "stun:stun.l.google.com:19302"
  26. const pollInterval = 5 * time.Second
  27. //amount of time after sending an SDP answer before the proxy assumes the
  28. //client is not going to connect
  29. const dataChannelTimeout = 20 * time.Second
  30. const readLimit = 100000 //Maximum number of bytes to be read from an HTTP request
  31. var brokerURL *url.URL
  32. var relayURL string
  33. const (
  34. sessionIDLength = 16
  35. )
  36. var (
  37. tokens chan bool
  38. config *webrtc.Configuration
  39. client http.Client
  40. )
  41. var remoteIPPatterns = []*regexp.Regexp{
  42. /* IPv4 */
  43. regexp.MustCompile(`(?m)^c=IN IP4 ([\d.]+)(?:(?:\/\d+)?\/\d+)?(:? |\r?\n)`),
  44. /* IPv6 */
  45. regexp.MustCompile(`(?m)^c=IN IP6 ([0-9A-Fa-f:.]+)(?:\/\d+)?(:? |\r?\n)`),
  46. }
  47. // https://tools.ietf.org/html/rfc4566#section-5.7
  48. func remoteIPFromSDP(sdp string) net.IP {
  49. for _, pattern := range remoteIPPatterns {
  50. m := pattern.FindStringSubmatch(sdp)
  51. if m != nil {
  52. // Ignore parsing errors, ParseIP returns nil.
  53. return net.ParseIP(m[1])
  54. }
  55. }
  56. return nil
  57. }
  58. type webRTCConn struct {
  59. dc *webrtc.DataChannel
  60. pc *webrtc.PeerConnection
  61. pr *io.PipeReader
  62. lock sync.Mutex // Synchronization for DataChannel destruction
  63. once sync.Once // Synchronization for PeerConnection destruction
  64. }
  65. func (c *webRTCConn) Read(b []byte) (int, error) {
  66. return c.pr.Read(b)
  67. }
  68. func (c *webRTCConn) Write(b []byte) (int, error) {
  69. c.lock.Lock()
  70. defer c.lock.Unlock()
  71. // log.Printf("webrtc Write %d %+q", len(b), string(b))
  72. log.Printf("Write %d bytes --> WebRTC", len(b))
  73. if c.dc != nil {
  74. c.dc.Send(b)
  75. }
  76. return len(b), nil
  77. }
  78. func (c *webRTCConn) Close() (err error) {
  79. c.once.Do(func() {
  80. err = c.pc.Destroy()
  81. })
  82. return
  83. }
  84. func (c *webRTCConn) LocalAddr() net.Addr {
  85. return nil
  86. }
  87. func (c *webRTCConn) RemoteAddr() net.Addr {
  88. //Parse Remote SDP offer and extract client IP
  89. clientIP := remoteIPFromSDP(c.pc.RemoteDescription().Sdp)
  90. if clientIP == nil {
  91. return nil
  92. }
  93. return &net.IPAddr{IP: clientIP, Zone: ""}
  94. }
  95. func (c *webRTCConn) SetDeadline(t time.Time) error {
  96. return fmt.Errorf("SetDeadline not implemented")
  97. }
  98. func (c *webRTCConn) SetReadDeadline(t time.Time) error {
  99. return fmt.Errorf("SetReadDeadline not implemented")
  100. }
  101. func (c *webRTCConn) SetWriteDeadline(t time.Time) error {
  102. return fmt.Errorf("SetWriteDeadline not implemented")
  103. }
  104. func getToken() {
  105. <-tokens
  106. }
  107. func retToken() {
  108. tokens <- true
  109. }
  110. func genSessionID() string {
  111. buf := make([]byte, sessionIDLength)
  112. _, err := rand.Read(buf)
  113. if err != nil {
  114. panic(err.Error())
  115. }
  116. return strings.TrimRight(base64.StdEncoding.EncodeToString(buf), "=")
  117. }
  118. func limitedRead(r io.Reader, limit int64) ([]byte, error) {
  119. p, err := ioutil.ReadAll(&io.LimitedReader{R: r, N: limit + 1})
  120. if err != nil {
  121. return p, err
  122. } else if int64(len(p)) == limit+1 {
  123. return p[0:limit], io.ErrUnexpectedEOF
  124. }
  125. return p, err
  126. }
  127. func pollOffer(sid string) *webrtc.SessionDescription {
  128. broker := brokerURL.ResolveReference(&url.URL{Path: "proxy"})
  129. timeOfNextPoll := time.Now()
  130. for {
  131. // Sleep until we're scheduled to poll again.
  132. now := time.Now()
  133. time.Sleep(timeOfNextPoll.Sub(now))
  134. // Compute the next time to poll -- if it's in the past, that
  135. // means that the POST took longer than pollInterval, so we're
  136. // allowed to do another one immediately.
  137. timeOfNextPoll = timeOfNextPoll.Add(pollInterval)
  138. if timeOfNextPoll.Before(now) {
  139. timeOfNextPoll = now
  140. }
  141. req, _ := http.NewRequest("POST", broker.String(), bytes.NewBuffer([]byte(sid)))
  142. req.Header.Set("X-Session-ID", sid)
  143. resp, err := client.Do(req)
  144. if err != nil {
  145. log.Printf("error polling broker: %s", err)
  146. } else {
  147. defer resp.Body.Close()
  148. if resp.StatusCode != http.StatusOK {
  149. log.Printf("broker returns: %d", resp.StatusCode)
  150. } else {
  151. body, err := limitedRead(resp.Body, readLimit)
  152. if err != nil {
  153. log.Printf("error reading broker response: %s", err)
  154. } else {
  155. return webrtc.DeserializeSessionDescription(string(body))
  156. }
  157. }
  158. }
  159. }
  160. }
  161. func sendAnswer(sid string, pc *webrtc.PeerConnection) error {
  162. broker := brokerURL.ResolveReference(&url.URL{Path: "answer"})
  163. body := bytes.NewBuffer([]byte(pc.LocalDescription().Serialize()))
  164. req, _ := http.NewRequest("POST", broker.String(), body)
  165. req.Header.Set("X-Session-ID", sid)
  166. resp, err := client.Do(req)
  167. if err != nil {
  168. return err
  169. }
  170. if resp.StatusCode != http.StatusOK {
  171. return fmt.Errorf("broker returned %d", resp.StatusCode)
  172. }
  173. return nil
  174. }
  175. type timeoutConn struct {
  176. c net.Conn
  177. t time.Duration
  178. }
  179. func (tc timeoutConn) Read(buf []byte) (int, error) {
  180. tc.c.SetDeadline(time.Now().Add(tc.t))
  181. return tc.c.Read(buf)
  182. }
  183. func (tc timeoutConn) Write(buf []byte) (int, error) {
  184. tc.c.SetDeadline(time.Now().Add(tc.t))
  185. return tc.c.Write(buf)
  186. }
  187. func (tc timeoutConn) Close() error {
  188. return tc.c.Close()
  189. }
  190. func CopyLoopTimeout(c1 net.Conn, c2 net.Conn, timeout time.Duration) {
  191. tc1 := timeoutConn{c: c1, t: timeout}
  192. tc2 := timeoutConn{c: c2, t: timeout}
  193. var wg sync.WaitGroup
  194. copyer := func(dst io.ReadWriteCloser, src io.ReadWriteCloser) {
  195. defer wg.Done()
  196. io.Copy(dst, src)
  197. dst.Close()
  198. src.Close()
  199. }
  200. wg.Add(2)
  201. go copyer(tc1, tc2)
  202. go copyer(tc2, tc1)
  203. wg.Wait()
  204. }
  205. // We pass conn.RemoteAddr() as an additional parameter, rather than calling
  206. // conn.RemoteAddr() inside this function, as a workaround for a hang that
  207. // otherwise occurs inside of conn.pc.RemoteDescription() (called by
  208. // RemoteAddr). https://bugs.torproject.org/18628#comment:8
  209. func datachannelHandler(conn *webRTCConn, remoteAddr net.Addr) {
  210. defer conn.Close()
  211. defer retToken()
  212. u, err := url.Parse(relayURL)
  213. if err != nil {
  214. log.Fatalf("invalid relay url: %s", err)
  215. }
  216. // Retrieve client IP address
  217. if remoteAddr != nil {
  218. // Encode client IP address in relay URL
  219. q := u.Query()
  220. clientIP := remoteAddr.String()
  221. q.Set("client_ip", clientIP)
  222. u.RawQuery = q.Encode()
  223. } else {
  224. log.Printf("no remote address given in websocket")
  225. }
  226. wsConn, err := websocket.Dial(u.String(), "", relayURL)
  227. if err != nil {
  228. log.Printf("error dialing relay: %s", err)
  229. return
  230. }
  231. log.Printf("connected to relay")
  232. defer wsConn.Close()
  233. wsConn.PayloadType = websocket.BinaryFrame
  234. CopyLoopTimeout(conn, wsConn, time.Minute)
  235. log.Printf("datachannelHandler ends")
  236. }
  237. // Create a PeerConnection from an SDP offer. Blocks until the gathering of ICE
  238. // candidates is complete and the answer is available in LocalDescription.
  239. // Installs an OnDataChannel callback that creates a webRTCConn and passes it to
  240. // datachannelHandler.
  241. func makePeerConnectionFromOffer(sdp *webrtc.SessionDescription, config *webrtc.Configuration, dataChan chan struct{}) (*webrtc.PeerConnection, error) {
  242. pc, err := webrtc.NewPeerConnection(config)
  243. if err != nil {
  244. return nil, fmt.Errorf("accept: NewPeerConnection: %s", err)
  245. }
  246. pc.OnNegotiationNeeded = func() {
  247. panic("OnNegotiationNeeded")
  248. }
  249. pc.OnDataChannel = func(dc *webrtc.DataChannel) {
  250. log.Println("OnDataChannel")
  251. close(dataChan)
  252. pr, pw := io.Pipe()
  253. conn := &webRTCConn{pc: pc, dc: dc, pr: pr}
  254. dc.OnOpen = func() {
  255. log.Println("OnOpen channel")
  256. }
  257. dc.OnClose = func() {
  258. conn.lock.Lock()
  259. defer conn.lock.Unlock()
  260. log.Println("OnClose channel")
  261. conn.dc = nil
  262. pc.DeleteDataChannel(dc)
  263. pw.Close()
  264. }
  265. dc.OnMessage = func(msg []byte) {
  266. log.Printf("OnMessage <--- %d bytes", len(msg))
  267. n, err := pw.Write(msg)
  268. if err != nil {
  269. pw.CloseWithError(err)
  270. }
  271. if n != len(msg) {
  272. panic("short write")
  273. }
  274. }
  275. go datachannelHandler(conn, conn.RemoteAddr())
  276. }
  277. err = pc.SetRemoteDescription(sdp)
  278. if err != nil {
  279. pc.Destroy()
  280. return nil, fmt.Errorf("accept: SetRemoteDescription: %s", err)
  281. }
  282. log.Println("sdp offer successfully received.")
  283. log.Println("Generating answer...")
  284. answer, err := pc.CreateAnswer()
  285. // blocks on ICE gathering. we need to add a timeout if needed
  286. // not putting this in a separate go routine, because we need
  287. // SetLocalDescription(answer) to be called before sendAnswer
  288. if err != nil {
  289. pc.Destroy()
  290. return nil, err
  291. }
  292. if answer == nil {
  293. pc.Destroy()
  294. return nil, fmt.Errorf("Failed gathering ICE candidates.")
  295. }
  296. err = pc.SetLocalDescription(answer)
  297. if err != nil {
  298. pc.Destroy()
  299. return nil, err
  300. }
  301. return pc, nil
  302. }
  303. func runSession(sid string) {
  304. offer := pollOffer(sid)
  305. if offer == nil {
  306. log.Printf("bad offer from broker")
  307. retToken()
  308. return
  309. }
  310. dataChan := make(chan struct{})
  311. pc, err := makePeerConnectionFromOffer(offer, config, dataChan)
  312. if err != nil {
  313. log.Printf("error making WebRTC connection: %s", err)
  314. retToken()
  315. return
  316. }
  317. err = sendAnswer(sid, pc)
  318. if err != nil {
  319. log.Printf("error sending answer to client through broker: %s", err)
  320. pc.Destroy()
  321. retToken()
  322. return
  323. }
  324. // Set a timeout on peerconnection. If the connection state has not
  325. // advanced to PeerConnectionStateConnected in this time,
  326. // destroy the peer connection and return the token.
  327. select {
  328. case <-dataChan:
  329. log.Println("Connection successful.")
  330. case <-time.After(dataChannelTimeout):
  331. log.Println("Timed out waiting for client to open data channel.")
  332. pc.Destroy()
  333. retToken()
  334. }
  335. }
  336. func main() {
  337. var capacity uint
  338. var stunURL string
  339. var logFilename string
  340. var rawBrokerURL string
  341. flag.UintVar(&capacity, "capacity", 10, "maximum concurrent clients")
  342. flag.StringVar(&rawBrokerURL, "broker", defaultBrokerURL, "broker URL")
  343. flag.StringVar(&relayURL, "relay", defaultRelayURL, "websocket relay URL")
  344. flag.StringVar(&stunURL, "stun", defaultSTUNURL, "stun URL")
  345. flag.StringVar(&logFilename, "log", "", "log filename")
  346. flag.Parse()
  347. var logOutput io.Writer = os.Stderr
  348. log.SetFlags(log.LstdFlags | log.LUTC)
  349. if logFilename != "" {
  350. f, err := os.OpenFile(logFilename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
  351. if err != nil {
  352. log.Fatal(err)
  353. }
  354. defer f.Close()
  355. logOutput = io.MultiWriter(os.Stderr, f)
  356. }
  357. //We want to send the log output through our scrubber first
  358. log.SetOutput(&safelog.LogScrubber{Output: logOutput})
  359. log.Println("starting")
  360. var err error
  361. brokerURL, err = url.Parse(rawBrokerURL)
  362. if err != nil {
  363. log.Fatalf("invalid broker url: %s", err)
  364. }
  365. _, err = url.Parse(stunURL)
  366. if err != nil {
  367. log.Fatalf("invalid stun url: %s", err)
  368. }
  369. _, err = url.Parse(relayURL)
  370. if err != nil {
  371. log.Fatalf("invalid relay url: %s", err)
  372. }
  373. config = webrtc.NewConfiguration(webrtc.OptionIceServer(stunURL))
  374. tokens = make(chan bool, capacity)
  375. for i := uint(0); i < capacity; i++ {
  376. tokens <- true
  377. }
  378. for {
  379. getToken()
  380. sessionID := genSessionID()
  381. runSession(sessionID)
  382. }
  383. }