socksProxy.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. /*
  2. * Copyright (c) 2015, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. package psiphon
  20. import (
  21. "net"
  22. "strconv"
  23. "strings"
  24. "sync"
  25. socks "github.com/Psiphon-Labs/goptlib"
  26. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  27. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
  28. )
  29. // SocksProxy is a SOCKS server that accepts local host connections
  30. // and, for each connection, establishes a port forward through
  31. // the tunnel SSH client and relays traffic through the port
  32. // forward.
  33. type SocksProxy struct {
  34. config *Config
  35. tunneler Tunneler
  36. listener *socks.SocksListener
  37. serveWaitGroup *sync.WaitGroup
  38. openConns *common.Conns
  39. stopListeningBroadcast chan struct{}
  40. }
  41. var _SOCKS_PROXY_TYPE = "SOCKS"
  42. // NewSocksProxy initializes a new SOCKS server. It begins listening for
  43. // connections, starts a goroutine that runs an accept loop, and returns
  44. // leaving the accept loop running.
  45. func NewSocksProxy(
  46. config *Config,
  47. tunneler Tunneler,
  48. listenIP string) (proxy *SocksProxy, err error) {
  49. listener, err := socks.ListenSocks(
  50. "tcp", net.JoinHostPort(listenIP, strconv.Itoa(config.LocalSocksProxyPort)))
  51. if err != nil {
  52. if IsAddressInUseError(err) {
  53. NoticeSocksProxyPortInUse(config.LocalSocksProxyPort)
  54. }
  55. return nil, errors.Trace(err)
  56. }
  57. proxy = &SocksProxy{
  58. config: config,
  59. tunneler: tunneler,
  60. listener: listener,
  61. serveWaitGroup: new(sync.WaitGroup),
  62. openConns: common.NewConns(),
  63. stopListeningBroadcast: make(chan struct{}),
  64. }
  65. proxy.serveWaitGroup.Add(1)
  66. go proxy.serve()
  67. NoticeListeningSocksProxyPort(proxy.listener.Addr().(*net.TCPAddr).Port)
  68. return proxy, nil
  69. }
  70. // Close terminates the listener and waits for the accept loop
  71. // goroutine to complete.
  72. func (proxy *SocksProxy) Close() {
  73. close(proxy.stopListeningBroadcast)
  74. proxy.listener.Close()
  75. proxy.serveWaitGroup.Wait()
  76. proxy.openConns.CloseAll()
  77. }
  78. func (proxy *SocksProxy) socksConnectionHandler(localConn *socks.SocksConn) (err error) {
  79. defer localConn.Close()
  80. defer proxy.openConns.Remove(localConn)
  81. proxy.openConns.Add(localConn)
  82. // Using downstreamConn so localConn.Close() will be called when remoteConn.Close() is called.
  83. // This ensures that the downstream client (e.g., web browser) doesn't keep waiting on the
  84. // open connection for data which will never arrive.
  85. remoteConn, err := proxy.tunneler.Dial(localConn.Req.Target, localConn)
  86. if err != nil {
  87. reason := byte(socks.SocksRepGeneralFailure)
  88. // "ssh: rejected" is the prefix of ssh.OpenChannelError
  89. // TODO: retain error type and check for ssh.OpenChannelError
  90. if strings.Contains(err.Error(), "ssh: rejected") {
  91. reason = byte(socks.SocksRepConnectionRefused)
  92. }
  93. _ = localConn.RejectReason(reason)
  94. return errors.Trace(err)
  95. }
  96. defer remoteConn.Close()
  97. err = localConn.Grant(&net.TCPAddr{IP: net.ParseIP("0.0.0.0"), Port: 0})
  98. if err != nil {
  99. return errors.Trace(err)
  100. }
  101. LocalProxyRelay(proxy.config, _SOCKS_PROXY_TYPE, localConn, remoteConn)
  102. return nil
  103. }
  104. func (proxy *SocksProxy) serve() {
  105. defer proxy.listener.Close()
  106. defer proxy.serveWaitGroup.Done()
  107. loop:
  108. for {
  109. // Note: will be interrupted by listener.Close() call made by proxy.Close()
  110. socksConnection, err := proxy.listener.AcceptSocks()
  111. // Can't check for the exact error that Close() will cause in Accept(),
  112. // (see: https://code.google.com/p/go/issues/detail?id=4373). So using an
  113. // explicit stop signal to stop gracefully.
  114. select {
  115. case <-proxy.stopListeningBroadcast:
  116. break loop
  117. default:
  118. }
  119. if err != nil {
  120. NoticeWarning("SOCKS proxy accept error: %s", err)
  121. if e, ok := err.(net.Error); ok && e.Temporary() {
  122. // Temporary error, keep running
  123. continue
  124. }
  125. // Fatal error, stop the proxy
  126. proxy.tunneler.SignalComponentFailure()
  127. break loop
  128. }
  129. go func() {
  130. err := proxy.socksConnectionHandler(socksConnection)
  131. if err != nil {
  132. NoticeLocalProxyError(_SOCKS_PROXY_TYPE, errors.Trace(err))
  133. }
  134. }()
  135. }
  136. NoticeInfo("SOCKS proxy stopped")
  137. }