main.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. // main.go - Tor Pluggable Transport implementation.
  2. // Copyright (C) 2016 Yawning Angel.
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Affero General Public License as
  6. // published by the Free Software Foundation, either version 3 of the
  7. // License, or (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Affero General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Affero General Public License
  15. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. // Tor Pluggable Transport that uses the `basket2` protocol. Only works as a
  17. // managed client/server.
  18. package main
  19. import (
  20. "flag"
  21. "fmt"
  22. "io"
  23. "io/ioutil"
  24. golog "log"
  25. "net"
  26. "os"
  27. "path"
  28. "strings"
  29. "sync"
  30. "syscall"
  31. "git.schwanenlied.me/yawning/basket2.git"
  32. "git.schwanenlied.me/yawning/basket2.git/basket2proxy/internal/log"
  33. "git.schwanenlied.me/yawning/basket2.git/basket2proxy/internal/ptextras"
  34. "git.schwanenlied.me/yawning/basket2.git/handshake"
  35. "git.torproject.org/pluggable-transports/goptlib.git"
  36. )
  37. const (
  38. basket2proxyVersion = "0.0.1-dev"
  39. basket2proxyLogFile = "basket2proxy.log"
  40. transportName = "basket2"
  41. socksAddr = "127.0.0.1:0"
  42. transportArg = "basket2params"
  43. )
  44. var (
  45. stateDir string
  46. termMon *ptextras.TermMonitor
  47. termHooks []func()
  48. defaultPaddingMethods = []basket2.PaddingMethod{
  49. basket2.PaddingObfs4BurstIAT,
  50. basket2.PaddingObfs4Burst,
  51. basket2.PaddingNull,
  52. }
  53. enabledKEXMethods []handshake.KEXMethod
  54. )
  55. func isEnabledKEXMethod(m handshake.KEXMethod) bool {
  56. for _, v := range enabledKEXMethods {
  57. if m == v {
  58. return true
  59. }
  60. }
  61. return false
  62. }
  63. func getVersion() string {
  64. return "basket2proxy - " + basket2proxyVersion
  65. }
  66. func runTermHooks() {
  67. for _, fn := range termHooks {
  68. fn()
  69. }
  70. }
  71. func closeListeners(listeners []net.Listener) {
  72. for _, l := range listeners {
  73. l.Close()
  74. }
  75. }
  76. func copyLoop(bConn, orConn net.Conn, addrStr string) {
  77. errChan := make(chan error, 2)
  78. var wg sync.WaitGroup
  79. wg.Add(2)
  80. go func() {
  81. defer wg.Done()
  82. defer orConn.Close()
  83. defer bConn.Close()
  84. _, err := io.Copy(orConn, bConn)
  85. errChan <- err
  86. }()
  87. go func() {
  88. defer wg.Done()
  89. defer bConn.Close()
  90. defer orConn.Close()
  91. _, err := io.Copy(bConn, orConn)
  92. errChan <- err
  93. }()
  94. wg.Wait()
  95. var err error
  96. if len(errChan) > 0 {
  97. err = <-errChan
  98. }
  99. if err != nil {
  100. log.Warnf("%s: Closed connection: %v", addrStr, log.ElideError(err))
  101. } else {
  102. log.Infof("%s: Closed connection", addrStr)
  103. }
  104. }
  105. func overrideKEXMethods(s string) error {
  106. if s == "" {
  107. return nil
  108. }
  109. var methods []handshake.KEXMethod
  110. for _, mStr := range strings.Split(s, ",") {
  111. m := handshake.KEXMethodFromString(mStr)
  112. if m == handshake.KEXInvalid {
  113. return handshake.ErrInvalidKEXMethod
  114. }
  115. methods = append(methods, m)
  116. }
  117. if len(methods) == 0 {
  118. return fmt.Errorf("no valid KEX methods provided")
  119. }
  120. enabledKEXMethods = methods
  121. return nil
  122. }
  123. func main() {
  124. termMon = ptextras.NewTermMonitor()
  125. _, execName := path.Split(os.Args[0])
  126. // Parse and act on the command line arguments.
  127. showVersion := flag.Bool("version", false, "Show version and exit")
  128. enableLogging := flag.Bool("enableLogging", false, "Log to TOR_PT_STATE_LOCATION/"+basket2proxyLogFile)
  129. logLevelStr := flag.String("logLevel", "ERROR", "Log level (ERROR/WARN/INFO/DEBUG)")
  130. showAlgorithms := flag.Bool("algorithms", false, "Show supported algorithms and exit")
  131. kexMethodsStr := flag.String("kexMethods", "", "Key exchange methods")
  132. flag.Parse()
  133. // Populate the lists of supported algorithms.
  134. enabledKEXMethods = handshake.SupportedKEXMethods()
  135. if *showVersion {
  136. fmt.Printf("%s\n", getVersion())
  137. os.Exit(0)
  138. }
  139. if *showAlgorithms {
  140. fmt.Printf("%s\n", getVersion())
  141. fmt.Printf("\n Key Exchange Methods:\n")
  142. for _, m := range enabledKEXMethods {
  143. fmt.Printf(" %s - %s\n", m.ToHexString(), m.ToString())
  144. }
  145. os.Exit(0)
  146. }
  147. // XXX: Support for overriding the padding algorithms etc.
  148. if err := overrideKEXMethods(*kexMethodsStr); err != nil {
  149. golog.Fatalf("%s: [ERROR]: Failed to set KEX methods: %v", execName, err)
  150. }
  151. // Common PT initialization (primarily for the stateDir).
  152. isClient, err := ptextras.IsClient()
  153. if err != nil {
  154. golog.Fatalf("%s: [ERROR]: Must be run as a managed transport", execName)
  155. }
  156. if stateDir, err = pt.MakeStateDir(); err != nil {
  157. golog.Fatalf("%s: [ERROR]: No state directory: %v", execName, err)
  158. }
  159. // Bring file backed logging online.
  160. if *enableLogging {
  161. logFilePath := path.Join(stateDir, basket2proxyLogFile)
  162. logWriter, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
  163. if err != nil {
  164. golog.Fatalf("%s: [ERROR]: Failed to open log file: %v", execName, err)
  165. }
  166. logger := golog.New(logWriter, execName+": ", golog.LstdFlags)
  167. log.SetLogger(logger)
  168. if err = log.SetLogLevel(*logLevelStr); err != nil {
  169. golog.Fatalf("%s: [ERROR]: Failed to set log level: %v", execName, err)
  170. }
  171. // Nothing should use the go log package, but redirect the output to
  172. // the writer so I can use it for development/testing.
  173. golog.SetOutput(logWriter)
  174. } else {
  175. golog.SetOutput(ioutil.Discard)
  176. }
  177. log.Noticef("%s - Launched", getVersion())
  178. defer func() {
  179. // Call the termination cleanup hooks.
  180. defer runTermHooks()
  181. log.Noticef("Terminated")
  182. }()
  183. // Initialize the listener(s) and complete the PT configuration protocol.
  184. var listeners []net.Listener
  185. if isClient {
  186. listeners = clientInit()
  187. } else {
  188. listeners = serverInit()
  189. }
  190. // If listeners were started, wait till the parent requests termination.
  191. if len(listeners) > 0 {
  192. defer closeListeners(listeners)
  193. if sig := termMon.Wait(false); sig == syscall.SIGTERM {
  194. return
  195. }
  196. // Explicitly close the listener(s) to block incoming connections.
  197. closeListeners(listeners)
  198. termMon.Wait(true)
  199. }
  200. }