tcp_proxy.go 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. package main
  2. import (
  3. "io"
  4. "log"
  5. "net"
  6. "sync"
  7. )
  8. // TCPProxy is a proxy for TCP connections. It implements the Proxy interface to
  9. // handle TCP traffic forwarding between the frontend and backend addresses.
  10. type TCPProxy struct {
  11. listener *net.TCPListener
  12. frontendAddr *net.TCPAddr
  13. backendAddr *net.TCPAddr
  14. }
  15. // NewTCPProxy creates a new TCPProxy.
  16. func NewTCPProxy(frontendAddr, backendAddr *net.TCPAddr) (*TCPProxy, error) {
  17. // detect version of hostIP to bind only to correct version
  18. ipVersion := ipv4
  19. if frontendAddr.IP.To4() == nil {
  20. ipVersion = ipv6
  21. }
  22. listener, err := net.ListenTCP("tcp"+string(ipVersion), frontendAddr)
  23. if err != nil {
  24. return nil, err
  25. }
  26. // If the port in frontendAddr was 0 then ListenTCP will have a picked
  27. // a port to listen on, hence the call to Addr to get that actual port:
  28. return &TCPProxy{
  29. listener: listener,
  30. frontendAddr: listener.Addr().(*net.TCPAddr),
  31. backendAddr: backendAddr,
  32. }, nil
  33. }
  34. func (proxy *TCPProxy) clientLoop(client *net.TCPConn, quit chan bool) {
  35. backend, err := net.DialTCP("tcp", nil, proxy.backendAddr)
  36. if err != nil {
  37. log.Printf("Can't forward traffic to backend tcp/%v: %s\n", proxy.backendAddr, err)
  38. client.Close()
  39. return
  40. }
  41. var wg sync.WaitGroup
  42. broker := func(to, from *net.TCPConn) {
  43. io.Copy(to, from)
  44. from.CloseRead()
  45. to.CloseWrite()
  46. wg.Done()
  47. }
  48. wg.Add(2)
  49. go broker(client, backend)
  50. go broker(backend, client)
  51. finish := make(chan struct{})
  52. go func() {
  53. wg.Wait()
  54. close(finish)
  55. }()
  56. select {
  57. case <-quit:
  58. case <-finish:
  59. }
  60. client.Close()
  61. backend.Close()
  62. <-finish
  63. }
  64. // Run starts forwarding the traffic using TCP.
  65. func (proxy *TCPProxy) Run() {
  66. quit := make(chan bool)
  67. defer close(quit)
  68. for {
  69. client, err := proxy.listener.Accept()
  70. if err != nil {
  71. log.Printf("Stopping proxy on tcp/%v for tcp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err)
  72. return
  73. }
  74. go proxy.clientLoop(client.(*net.TCPConn), quit)
  75. }
  76. }
  77. // Close stops forwarding the traffic.
  78. func (proxy *TCPProxy) Close() { proxy.listener.Close() }
  79. // FrontendAddr returns the TCP address on which the proxy is listening.
  80. func (proxy *TCPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
  81. // BackendAddr returns the TCP proxied address.
  82. func (proxy *TCPProxy) BackendAddr() net.Addr { return proxy.backendAddr }