snowflake.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. // Client transport plugin for the Snowflake pluggable transport.
  2. package main
  3. import (
  4. "flag"
  5. "fmt"
  6. "io"
  7. "log"
  8. "net"
  9. "os"
  10. "os/signal"
  11. "path/filepath"
  12. "strconv"
  13. "strings"
  14. "sync"
  15. "syscall"
  16. pt "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/goptlib"
  17. "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/ptutil/safelog"
  18. sf "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/client/lib"
  19. "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/event"
  20. "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/proxy"
  21. "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/version"
  22. )
  23. const (
  24. DefaultSnowflakeCapacity = 1
  25. )
  26. type ptEventLogger struct {
  27. }
  28. func NewPTEventLogger() event.SnowflakeEventReceiver {
  29. return &ptEventLogger{}
  30. }
  31. func (p ptEventLogger) OnNewSnowflakeEvent(e event.SnowflakeEvent) {
  32. pt.Log(pt.LogSeverityNotice, e.String())
  33. }
  34. // Exchanges bytes between two ReadWriters.
  35. // (In this case, between a SOCKS connection and a snowflake transport conn)
  36. func copyLoop(socks, sfconn io.ReadWriter) {
  37. done := make(chan struct{}, 2)
  38. go func() {
  39. if _, err := io.Copy(socks, sfconn); err != nil {
  40. log.Printf("copying Snowflake to SOCKS resulted in error: %v", err)
  41. }
  42. done <- struct{}{}
  43. }()
  44. go func() {
  45. if _, err := io.Copy(sfconn, socks); err != nil {
  46. log.Printf("copying SOCKS to Snowflake resulted in error: %v", err)
  47. }
  48. done <- struct{}{}
  49. }()
  50. <-done
  51. log.Println("copy loop ended")
  52. }
  53. // Accept local SOCKS connections and connect to a Snowflake connection
  54. func socksAcceptLoop(ln *pt.SocksListener, config sf.ClientConfig, shutdown chan struct{}, wg *sync.WaitGroup) {
  55. defer ln.Close()
  56. for {
  57. conn, err := ln.AcceptSocks()
  58. if err != nil {
  59. if err, ok := err.(net.Error); ok && err.Temporary() {
  60. continue
  61. }
  62. log.Printf("SOCKS accept error: %s", err)
  63. break
  64. }
  65. log.Printf("SOCKS accepted: %v", conn.Req)
  66. wg.Add(1)
  67. go func() {
  68. defer wg.Done()
  69. defer conn.Close()
  70. // Check to see if our command line options are overriden by SOCKS options
  71. if arg, ok := conn.Req.Args.Get("ampcache"); ok {
  72. config.AmpCacheURL = arg
  73. }
  74. if arg, ok := conn.Req.Args.Get("sqsqueue"); ok {
  75. config.SQSQueueURL = arg
  76. }
  77. if arg, ok := conn.Req.Args.Get("sqscreds"); ok {
  78. config.SQSCredsStr = arg
  79. }
  80. if arg, ok := conn.Req.Args.Get("fronts"); ok {
  81. if arg != "" {
  82. config.FrontDomains = strings.Split(strings.TrimSpace(arg), ",")
  83. }
  84. } else if arg, ok := conn.Req.Args.Get("front"); ok {
  85. config.FrontDomains = strings.Split(strings.TrimSpace(arg), ",")
  86. }
  87. if arg, ok := conn.Req.Args.Get("ice"); ok {
  88. config.ICEAddresses = strings.Split(strings.TrimSpace(arg), ",")
  89. }
  90. if arg, ok := conn.Req.Args.Get("max"); ok {
  91. max, err := strconv.Atoi(arg)
  92. if err != nil {
  93. conn.Reject()
  94. log.Println("Invalid SOCKS arg: max=", arg)
  95. return
  96. }
  97. config.Max = max
  98. }
  99. if arg, ok := conn.Req.Args.Get("url"); ok {
  100. config.BrokerURL = arg
  101. }
  102. if arg, ok := conn.Req.Args.Get("utls-nosni"); ok {
  103. switch strings.ToLower(arg) {
  104. case "true":
  105. fallthrough
  106. case "yes":
  107. config.UTLSRemoveSNI = true
  108. }
  109. }
  110. if arg, ok := conn.Req.Args.Get("utls-imitate"); ok {
  111. config.UTLSClientID = arg
  112. }
  113. if arg, ok := conn.Req.Args.Get("fingerprint"); ok {
  114. config.BridgeFingerprint = arg
  115. }
  116. transport, err := sf.NewSnowflakeClient(config)
  117. if err != nil {
  118. conn.Reject()
  119. log.Println("Failed to start snowflake transport: ", err)
  120. return
  121. }
  122. transport.AddSnowflakeEventListener(NewPTEventLogger())
  123. err = conn.Grant(&net.TCPAddr{IP: net.IPv4zero, Port: 0})
  124. if err != nil {
  125. log.Printf("conn.Grant error: %s", err)
  126. return
  127. }
  128. handler := make(chan struct{})
  129. go func() {
  130. defer close(handler)
  131. sconn, err := transport.Dial()
  132. if err != nil {
  133. log.Printf("dial error: %s", err)
  134. return
  135. }
  136. defer sconn.Close()
  137. // copy between the created Snowflake conn and the SOCKS conn
  138. copyLoop(conn, sconn)
  139. }()
  140. select {
  141. case <-shutdown:
  142. log.Println("Received shutdown signal")
  143. case <-handler:
  144. log.Println("Handler ended")
  145. }
  146. return
  147. }()
  148. }
  149. }
  150. func main() {
  151. iceServersCommas := flag.String("ice", "", "comma-separated list of ICE servers")
  152. brokerURL := flag.String("url", "", "URL of signaling broker")
  153. frontDomain := flag.String("front", "", "front domain")
  154. frontDomainsCommas := flag.String("fronts", "", "comma-separated list of front domains")
  155. ampCacheURL := flag.String("ampcache", "", "URL of AMP cache to use as a proxy for signaling")
  156. sqsQueueURL := flag.String("sqsqueue", "", "URL of SQS Queue to use as a proxy for signaling")
  157. sqsCredsStr := flag.String("sqscreds", "", "credentials to access SQS Queue")
  158. logFilename := flag.String("log", "", "name of log file")
  159. logToStateDir := flag.Bool("log-to-state-dir", false, "resolve the log file relative to tor's pt state dir")
  160. keepLocalAddresses := flag.Bool("keep-local-addresses", false, "keep local LAN address ICE candidates")
  161. unsafeLogging := flag.Bool("unsafe-logging", false, "prevent logs from being scrubbed")
  162. max := flag.Int("max", DefaultSnowflakeCapacity,
  163. "capacity for number of multiplexed WebRTC peers")
  164. versionFlag := flag.Bool("version", false, "display version info to stderr and quit")
  165. // Deprecated
  166. oldLogToStateDir := flag.Bool("logToStateDir", false, "use -log-to-state-dir instead")
  167. oldKeepLocalAddresses := flag.Bool("keepLocalAddresses", false, "use -keep-local-addresses instead")
  168. flag.Parse()
  169. if *versionFlag {
  170. fmt.Fprintf(os.Stderr, "snowflake-client %s", version.ConstructResult())
  171. os.Exit(0)
  172. }
  173. log.SetFlags(log.LstdFlags | log.LUTC)
  174. // Don't write to stderr; versions of tor earlier than about 0.3.5.6 do
  175. // not read from the pipe, and eventually we will deadlock because the
  176. // buffer is full.
  177. // https://bugs.torproject.org/26360
  178. // https://bugs.torproject.org/25600#comment:14
  179. var logOutput = io.Discard
  180. if *logFilename != "" {
  181. if *logToStateDir || *oldLogToStateDir {
  182. stateDir, err := pt.MakeStateDir()
  183. if err != nil {
  184. log.Fatal(err)
  185. }
  186. *logFilename = filepath.Join(stateDir, *logFilename)
  187. }
  188. logFile, err := os.OpenFile(*logFilename,
  189. os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
  190. if err != nil {
  191. log.Fatal(err)
  192. }
  193. defer logFile.Close()
  194. logOutput = logFile
  195. }
  196. if *unsafeLogging {
  197. log.SetOutput(logOutput)
  198. } else {
  199. // We want to send the log output through our scrubber first
  200. log.SetOutput(&safelog.LogScrubber{Output: logOutput})
  201. }
  202. log.Printf("snowflake-client %s\n", version.GetVersion())
  203. iceAddresses := strings.Split(strings.TrimSpace(*iceServersCommas), ",")
  204. var frontDomains []string
  205. if *frontDomainsCommas != "" {
  206. frontDomains = strings.Split(strings.TrimSpace(*frontDomainsCommas), ",")
  207. }
  208. // Maintain backwards compatability with legacy commandline option
  209. if (len(frontDomains) == 0) && (*frontDomain != "") {
  210. frontDomains = []string{*frontDomain}
  211. }
  212. config := sf.ClientConfig{
  213. BrokerURL: *brokerURL,
  214. AmpCacheURL: *ampCacheURL,
  215. SQSQueueURL: *sqsQueueURL,
  216. SQSCredsStr: *sqsCredsStr,
  217. FrontDomains: frontDomains,
  218. ICEAddresses: iceAddresses,
  219. KeepLocalAddresses: *keepLocalAddresses || *oldKeepLocalAddresses,
  220. Max: *max,
  221. }
  222. // Begin goptlib client process.
  223. ptInfo, err := pt.ClientSetup(nil)
  224. if err != nil {
  225. log.Fatal(err)
  226. }
  227. if ptInfo.ProxyURL != nil {
  228. if err := proxy.CheckProxyProtocolSupport(ptInfo.ProxyURL); err != nil {
  229. pt.ProxyError("proxy is not supported:" + err.Error())
  230. os.Exit(1)
  231. } else {
  232. config.CommunicationProxy = ptInfo.ProxyURL
  233. client := proxy.NewSocks5UDPClient(config.CommunicationProxy)
  234. conn, err := client.ListenPacket("udp", nil)
  235. if err != nil {
  236. pt.ProxyError("proxy test failure:" + err.Error())
  237. os.Exit(1)
  238. }
  239. conn.Close()
  240. pt.ProxyDone()
  241. }
  242. }
  243. listeners := make([]net.Listener, 0)
  244. shutdown := make(chan struct{})
  245. var wg sync.WaitGroup
  246. for _, methodName := range ptInfo.MethodNames {
  247. switch methodName {
  248. case "snowflake":
  249. // TODO: Be able to recover when SOCKS dies.
  250. ln, err := pt.ListenSocks("tcp", "127.0.0.1:0")
  251. if err != nil {
  252. pt.CmethodError(methodName, err.Error())
  253. break
  254. }
  255. log.Printf("Started SOCKS listener at %v.", ln.Addr())
  256. go socksAcceptLoop(ln, config, shutdown, &wg)
  257. pt.Cmethod(methodName, ln.Version(), ln.Addr())
  258. listeners = append(listeners, ln)
  259. default:
  260. pt.CmethodError(methodName, "no such method")
  261. }
  262. }
  263. pt.CmethodsDone()
  264. sigChan := make(chan os.Signal, 1)
  265. signal.Notify(sigChan, syscall.SIGTERM)
  266. if os.Getenv("TOR_PT_EXIT_ON_STDIN_CLOSE") == "1" {
  267. // This environment variable means we should treat EOF on stdin
  268. // just like SIGTERM: https://bugs.torproject.org/15435.
  269. go func() {
  270. if _, err := io.Copy(io.Discard, os.Stdin); err != nil {
  271. log.Printf("calling io.Copy(io.Discard, os.Stdin) returned error: %v", err)
  272. }
  273. log.Printf("synthesizing SIGTERM because of stdin close")
  274. sigChan <- syscall.SIGTERM
  275. }()
  276. }
  277. // Wait for a signal.
  278. <-sigChan
  279. log.Println("stopping snowflake")
  280. // Signal received, shut down.
  281. for _, ln := range listeners {
  282. ln.Close()
  283. }
  284. close(shutdown)
  285. wg.Wait()
  286. log.Println("snowflake is done.")
  287. }