本站源代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

194 lines
4.5KB

  1. package ssh
  2. import (
  3. "io"
  4. "log"
  5. "net"
  6. "strconv"
  7. "sync"
  8. gossh "golang.org/x/crypto/ssh"
  9. )
  10. const (
  11. forwardedTCPChannelType = "forwarded-tcpip"
  12. )
  13. // direct-tcpip data struct as specified in RFC4254, Section 7.2
  14. type localForwardChannelData struct {
  15. DestAddr string
  16. DestPort uint32
  17. OriginAddr string
  18. OriginPort uint32
  19. }
  20. // DirectTCPIPHandler can be enabled by adding it to the server's
  21. // ChannelHandlers under direct-tcpip.
  22. func DirectTCPIPHandler(srv *Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx Context) {
  23. d := localForwardChannelData{}
  24. if err := gossh.Unmarshal(newChan.ExtraData(), &d); err != nil {
  25. newChan.Reject(gossh.ConnectionFailed, "error parsing forward data: "+err.Error())
  26. return
  27. }
  28. if srv.LocalPortForwardingCallback == nil || !srv.LocalPortForwardingCallback(ctx, d.DestAddr, d.DestPort) {
  29. newChan.Reject(gossh.Prohibited, "port forwarding is disabled")
  30. return
  31. }
  32. dest := net.JoinHostPort(d.DestAddr, strconv.FormatInt(int64(d.DestPort), 10))
  33. var dialer net.Dialer
  34. dconn, err := dialer.DialContext(ctx, "tcp", dest)
  35. if err != nil {
  36. newChan.Reject(gossh.ConnectionFailed, err.Error())
  37. return
  38. }
  39. ch, reqs, err := newChan.Accept()
  40. if err != nil {
  41. dconn.Close()
  42. return
  43. }
  44. go gossh.DiscardRequests(reqs)
  45. go func() {
  46. defer ch.Close()
  47. defer dconn.Close()
  48. io.Copy(ch, dconn)
  49. }()
  50. go func() {
  51. defer ch.Close()
  52. defer dconn.Close()
  53. io.Copy(dconn, ch)
  54. }()
  55. }
  56. type remoteForwardRequest struct {
  57. BindAddr string
  58. BindPort uint32
  59. }
  60. type remoteForwardSuccess struct {
  61. BindPort uint32
  62. }
  63. type remoteForwardCancelRequest struct {
  64. BindAddr string
  65. BindPort uint32
  66. }
  67. type remoteForwardChannelData struct {
  68. DestAddr string
  69. DestPort uint32
  70. OriginAddr string
  71. OriginPort uint32
  72. }
  73. // ForwardedTCPHandler can be enabled by creating a ForwardedTCPHandler and
  74. // adding the HandleSSHRequest callback to the server's RequestHandlers under
  75. // tcpip-forward and cancel-tcpip-forward.
  76. type ForwardedTCPHandler struct {
  77. forwards map[string]net.Listener
  78. sync.Mutex
  79. }
  80. func (h *ForwardedTCPHandler) HandleSSHRequest(ctx Context, srv *Server, req *gossh.Request) (bool, []byte) {
  81. h.Lock()
  82. if h.forwards == nil {
  83. h.forwards = make(map[string]net.Listener)
  84. }
  85. h.Unlock()
  86. conn := ctx.Value(ContextKeyConn).(*gossh.ServerConn)
  87. switch req.Type {
  88. case "tcpip-forward":
  89. var reqPayload remoteForwardRequest
  90. if err := gossh.Unmarshal(req.Payload, &reqPayload); err != nil {
  91. // TODO: log parse failure
  92. return false, []byte{}
  93. }
  94. if srv.ReversePortForwardingCallback == nil || !srv.ReversePortForwardingCallback(ctx, reqPayload.BindAddr, reqPayload.BindPort) {
  95. return false, []byte("port forwarding is disabled")
  96. }
  97. addr := net.JoinHostPort(reqPayload.BindAddr, strconv.Itoa(int(reqPayload.BindPort)))
  98. ln, err := net.Listen("tcp", addr)
  99. if err != nil {
  100. // TODO: log listen failure
  101. return false, []byte{}
  102. }
  103. _, destPortStr, _ := net.SplitHostPort(ln.Addr().String())
  104. destPort, _ := strconv.Atoi(destPortStr)
  105. h.Lock()
  106. h.forwards[addr] = ln
  107. h.Unlock()
  108. go func() {
  109. <-ctx.Done()
  110. h.Lock()
  111. ln, ok := h.forwards[addr]
  112. h.Unlock()
  113. if ok {
  114. ln.Close()
  115. }
  116. }()
  117. go func() {
  118. for {
  119. c, err := ln.Accept()
  120. if err != nil {
  121. // TODO: log accept failure
  122. break
  123. }
  124. originAddr, orignPortStr, _ := net.SplitHostPort(c.RemoteAddr().String())
  125. originPort, _ := strconv.Atoi(orignPortStr)
  126. payload := gossh.Marshal(&remoteForwardChannelData{
  127. DestAddr: reqPayload.BindAddr,
  128. DestPort: uint32(destPort),
  129. OriginAddr: originAddr,
  130. OriginPort: uint32(originPort),
  131. })
  132. go func() {
  133. ch, reqs, err := conn.OpenChannel(forwardedTCPChannelType, payload)
  134. if err != nil {
  135. // TODO: log failure to open channel
  136. log.Println(err)
  137. c.Close()
  138. return
  139. }
  140. go gossh.DiscardRequests(reqs)
  141. go func() {
  142. defer ch.Close()
  143. defer c.Close()
  144. io.Copy(ch, c)
  145. }()
  146. go func() {
  147. defer ch.Close()
  148. defer c.Close()
  149. io.Copy(c, ch)
  150. }()
  151. }()
  152. }
  153. h.Lock()
  154. delete(h.forwards, addr)
  155. h.Unlock()
  156. }()
  157. return true, gossh.Marshal(&remoteForwardSuccess{uint32(destPort)})
  158. case "cancel-tcpip-forward":
  159. var reqPayload remoteForwardCancelRequest
  160. if err := gossh.Unmarshal(req.Payload, &reqPayload); err != nil {
  161. // TODO: log parse failure
  162. return false, []byte{}
  163. }
  164. addr := net.JoinHostPort(reqPayload.BindAddr, strconv.Itoa(int(reqPayload.BindPort)))
  165. h.Lock()
  166. ln, ok := h.forwards[addr]
  167. h.Unlock()
  168. if ok {
  169. ln.Close()
  170. }
  171. return true, nil
  172. default:
  173. return false, nil
  174. }
  175. }
上海开阖软件有限公司 沪ICP备12045867号-1