2013-10-04 22:25:15 -04:00
|
|
|
package proxy
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io"
|
|
|
|
"net"
|
2016-05-10 15:24:43 -04:00
|
|
|
"sync"
|
2013-10-04 22:25:15 -04:00
|
|
|
"syscall"
|
2014-10-27 13:45:38 -04:00
|
|
|
|
2015-03-26 18:22:04 -04:00
|
|
|
"github.com/Sirupsen/logrus"
|
2013-10-04 22:25:15 -04:00
|
|
|
)
|
|
|
|
|
2015-07-25 04:35:07 -04:00
|
|
|
// TCPProxy is a proxy for TCP connections. It implements the Proxy interface to
|
|
|
|
// handle TCP traffic forwarding between the frontend and backend addresses.
|
2013-10-04 22:25:15 -04:00
|
|
|
type TCPProxy struct {
|
|
|
|
listener *net.TCPListener
|
|
|
|
frontendAddr *net.TCPAddr
|
|
|
|
backendAddr *net.TCPAddr
|
|
|
|
}
|
|
|
|
|
2015-07-25 04:35:07 -04:00
|
|
|
// NewTCPProxy creates a new TCPProxy.
|
2013-10-04 22:25:15 -04:00
|
|
|
func NewTCPProxy(frontendAddr, backendAddr *net.TCPAddr) (*TCPProxy, error) {
|
|
|
|
listener, err := net.ListenTCP("tcp", frontendAddr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// If the port in frontendAddr was 0 then ListenTCP will have a picked
|
|
|
|
// a port to listen on, hence the call to Addr to get that actual port:
|
|
|
|
return &TCPProxy{
|
|
|
|
listener: listener,
|
|
|
|
frontendAddr: listener.Addr().(*net.TCPAddr),
|
|
|
|
backendAddr: backendAddr,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (proxy *TCPProxy) clientLoop(client *net.TCPConn, quit chan bool) {
|
|
|
|
backend, err := net.DialTCP("tcp", nil, proxy.backendAddr)
|
|
|
|
if err != nil {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Printf("Can't forward traffic to backend tcp/%v: %s\n", proxy.backendAddr, err)
|
2013-10-04 22:25:15 -04:00
|
|
|
client.Close()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-05-10 15:24:43 -04:00
|
|
|
var wg sync.WaitGroup
|
2013-10-04 22:25:15 -04:00
|
|
|
var broker = func(to, from *net.TCPConn) {
|
2016-05-10 15:24:43 -04:00
|
|
|
if _, err := io.Copy(to, from); err != nil {
|
2013-10-04 22:25:15 -04:00
|
|
|
// If the socket we are writing to is shutdown with
|
|
|
|
// SHUT_WR, forward it to the other end of the pipe:
|
|
|
|
if err, ok := err.(*net.OpError); ok && err.Err == syscall.EPIPE {
|
|
|
|
from.CloseWrite()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
to.CloseRead()
|
2016-05-10 15:24:43 -04:00
|
|
|
wg.Done()
|
2013-10-04 22:25:15 -04:00
|
|
|
}
|
2014-02-14 18:25:25 -05:00
|
|
|
|
2016-05-10 15:24:43 -04:00
|
|
|
wg.Add(2)
|
2013-10-04 22:25:15 -04:00
|
|
|
go broker(client, backend)
|
|
|
|
go broker(backend, client)
|
|
|
|
|
2016-05-10 15:24:43 -04:00
|
|
|
finish := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
wg.Wait()
|
|
|
|
close(finish)
|
|
|
|
}()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-quit:
|
|
|
|
case <-finish:
|
2013-10-04 22:25:15 -04:00
|
|
|
}
|
|
|
|
client.Close()
|
|
|
|
backend.Close()
|
2016-05-10 15:24:43 -04:00
|
|
|
<-finish
|
2013-10-04 22:25:15 -04:00
|
|
|
}
|
|
|
|
|
2015-07-25 04:35:07 -04:00
|
|
|
// Run starts forwarding the traffic using TCP.
|
2013-10-04 22:25:15 -04:00
|
|
|
func (proxy *TCPProxy) Run() {
|
|
|
|
quit := make(chan bool)
|
|
|
|
defer close(quit)
|
|
|
|
for {
|
|
|
|
client, err := proxy.listener.Accept()
|
|
|
|
if err != nil {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Printf("Stopping proxy on tcp/%v for tcp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err)
|
2013-10-04 22:25:15 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
go proxy.clientLoop(client.(*net.TCPConn), quit)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-25 04:35:07 -04:00
|
|
|
// Close stops forwarding the traffic.
|
|
|
|
func (proxy *TCPProxy) Close() { proxy.listener.Close() }
|
|
|
|
|
|
|
|
// FrontendAddr returns the TCP address on which the proxy is listening.
|
2013-10-04 22:25:15 -04:00
|
|
|
func (proxy *TCPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
|
2015-07-25 04:35:07 -04:00
|
|
|
|
|
|
|
// BackendAddr returns the TCP proxied address.
|
|
|
|
func (proxy *TCPProxy) BackendAddr() net.Addr { return proxy.backendAddr }
|