snowflake.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. // Client transport plugin for the Snowflake pluggable transport.
  2. package main
  3. import (
  4. "flag"
  5. "io"
  6. "io/ioutil"
  7. "log"
  8. "net"
  9. "os"
  10. "os/signal"
  11. "path/filepath"
  12. "strings"
  13. "syscall"
  14. "time"
  15. pt "git.torproject.org/pluggable-transports/goptlib.git"
  16. sf "git.torproject.org/pluggable-transports/snowflake.git/client/lib"
  17. "git.torproject.org/pluggable-transports/snowflake.git/common/safelog"
  18. "github.com/pion/webrtc"
  19. )
  20. const (
  21. DefaultSnowflakeCapacity = 1
  22. )
  23. // Maintain |SnowflakeCapacity| number of available WebRTC connections, to
  24. // transfer to the Tor SOCKS handler when needed.
  25. func ConnectLoop(snowflakes sf.SnowflakeCollector) {
  26. for {
  27. // Check if ending is necessary.
  28. _, err := snowflakes.Collect()
  29. if nil != err {
  30. log.Println("WebRTC:", err,
  31. " Retrying in", sf.ReconnectTimeout, "seconds...")
  32. }
  33. select {
  34. case <-time.After(time.Second * sf.ReconnectTimeout):
  35. continue
  36. case <-snowflakes.Melted():
  37. log.Println("ConnectLoop: stopped.")
  38. return
  39. }
  40. }
  41. }
  42. // Accept local SOCKS connections and pass them to the handler.
  43. func socksAcceptLoop(ln *pt.SocksListener, snowflakes sf.SnowflakeCollector) {
  44. defer ln.Close()
  45. log.Println("Started SOCKS listener.")
  46. for {
  47. log.Println("SOCKS listening...")
  48. conn, err := ln.AcceptSocks()
  49. if err != nil {
  50. if e, ok := err.(net.Error); ok && e.Temporary() {
  51. continue
  52. }
  53. log.Printf("SOCKS accept error: %s", err)
  54. break
  55. }
  56. log.Println("SOCKS accepted: ", conn.Req)
  57. err = sf.Handler(conn, snowflakes)
  58. if err != nil {
  59. log.Printf("handler error: %s", err)
  60. }
  61. }
  62. }
  63. //s is a comma-separated list of ICE server URLs
  64. func parseIceServers(s string) []webrtc.ICEServer {
  65. var servers []webrtc.ICEServer
  66. log.Println(s)
  67. s = strings.TrimSpace(s)
  68. if len(s) == 0 {
  69. return nil
  70. }
  71. urls := strings.Split(s, ",")
  72. log.Printf("Using ICE Servers:")
  73. for _, url := range urls {
  74. log.Printf("url: %s", url)
  75. servers = append(servers, webrtc.ICEServer{
  76. URLs: []string{url},
  77. })
  78. }
  79. return servers
  80. }
  81. func main() {
  82. iceServersCommas := flag.String("ice", "", "comma-separated list of ICE servers")
  83. brokerURL := flag.String("url", "", "URL of signaling broker")
  84. frontDomain := flag.String("front", "", "front domain")
  85. logFilename := flag.String("log", "", "name of log file")
  86. logToStateDir := flag.Bool("logToStateDir", false, "resolve the log file relative to tor's pt state dir")
  87. max := flag.Int("max", DefaultSnowflakeCapacity,
  88. "capacity for number of multiplexed WebRTC peers")
  89. flag.Parse()
  90. log.SetFlags(log.LstdFlags | log.LUTC)
  91. // Don't write to stderr; versions of tor earlier than about
  92. // 0.3.5.6 do not read from the pipe, and eventually we will
  93. // deadlock because the buffer is full.
  94. // https://bugs.torproject.org/26360
  95. // https://bugs.torproject.org/25600#comment:14
  96. var logOutput = ioutil.Discard
  97. if *logFilename != "" {
  98. if *logToStateDir {
  99. stateDir, err := pt.MakeStateDir()
  100. if err != nil {
  101. log.Fatal(err)
  102. }
  103. *logFilename = filepath.Join(stateDir, *logFilename)
  104. }
  105. logFile, err := os.OpenFile(*logFilename,
  106. os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
  107. if err != nil {
  108. log.Fatal(err)
  109. }
  110. defer logFile.Close()
  111. logOutput = logFile
  112. }
  113. //We want to send the log output through our scrubber first
  114. log.SetOutput(&safelog.LogScrubber{Output: logOutput})
  115. log.Println("\n\n\n --- Starting Snowflake Client ---")
  116. iceServers := parseIceServers(*iceServersCommas)
  117. // Prepare to collect remote WebRTC peers.
  118. snowflakes := sf.NewPeers(*max)
  119. // Use potentially domain-fronting broker to rendezvous.
  120. broker := sf.NewBrokerChannel(*brokerURL, *frontDomain, sf.CreateBrokerTransport())
  121. snowflakes.Tongue = sf.NewWebRTCDialer(broker, iceServers)
  122. if nil == snowflakes.Tongue {
  123. log.Fatal("Unable to prepare rendezvous method.")
  124. return
  125. }
  126. // Use a real logger to periodically output how much traffic is happening.
  127. snowflakes.BytesLogger = &sf.BytesSyncLogger{
  128. InboundChan: make(chan int, 5),
  129. OutboundChan: make(chan int, 5),
  130. Inbound: 0,
  131. Outbound: 0,
  132. InEvents: 0,
  133. OutEvents: 0,
  134. }
  135. go snowflakes.BytesLogger.Log()
  136. go ConnectLoop(snowflakes)
  137. // Begin goptlib client process.
  138. ptInfo, err := pt.ClientSetup(nil)
  139. if err != nil {
  140. log.Fatal(err)
  141. }
  142. if ptInfo.ProxyURL != nil {
  143. if err := pt.ProxyError("proxy is not supported"); err != nil {
  144. log.Printf("call to pt.ProxyError generated error: %v", err)
  145. }
  146. os.Exit(1)
  147. }
  148. listeners := make([]net.Listener, 0)
  149. for _, methodName := range ptInfo.MethodNames {
  150. switch methodName {
  151. case "snowflake":
  152. // TODO: Be able to recover when SOCKS dies.
  153. ln, err := pt.ListenSocks("tcp", "127.0.0.1:0")
  154. if err != nil {
  155. if inerr := pt.CmethodError(methodName, err.Error()); inerr != nil {
  156. log.Printf("handling error generated by pt.ListenSocks with pt.CmethodError generated error: %v", inerr)
  157. }
  158. break
  159. }
  160. go socksAcceptLoop(ln, snowflakes)
  161. pt.Cmethod(methodName, ln.Version(), ln.Addr())
  162. listeners = append(listeners, ln)
  163. default:
  164. if err := pt.CmethodError(methodName, "no such method"); err != nil {
  165. log.Printf("calling pt.CmethodError generated error: %v", err)
  166. }
  167. }
  168. }
  169. pt.CmethodsDone()
  170. sigChan := make(chan os.Signal, 1)
  171. signal.Notify(sigChan, syscall.SIGTERM)
  172. if os.Getenv("TOR_PT_EXIT_ON_STDIN_CLOSE") == "1" {
  173. // This environment variable means we should treat EOF on stdin
  174. // just like SIGTERM: https://bugs.torproject.org/15435.
  175. go func() {
  176. if _, err := io.Copy(ioutil.Discard, os.Stdin); err != nil {
  177. log.Printf("calling io.Copy(ioutil.Discard, os.Stdin) returned error: %v", err)
  178. }
  179. log.Printf("synthesizing SIGTERM because of stdin close")
  180. sigChan <- syscall.SIGTERM
  181. }()
  182. }
  183. // keep track of handlers and wait for a signal
  184. <-sigChan
  185. // signal received, shut down
  186. for _, ln := range listeners {
  187. ln.Close()
  188. }
  189. snowflakes.End()
  190. log.Println("snowflake is done.")
  191. }