2019-03-14 09:30:04 +01:00
package tcp
import (
2020-11-17 13:04:04 +01:00
"fmt"
2019-03-14 09:30:04 +01:00
"io"
"net"
2019-09-13 17:46:04 +02:00
"time"
2019-03-14 09:30:04 +01:00
2020-11-17 13:04:04 +01:00
"github.com/pires/go-proxyproto"
"github.com/traefik/traefik/v2/pkg/config/dynamic"
2020-09-16 15:46:04 +02:00
"github.com/traefik/traefik/v2/pkg/log"
2019-03-14 09:30:04 +01:00
)
2020-05-11 12:06:07 +02:00
// Proxy forwards a TCP request to a TCP service.
2019-03-14 09:30:04 +01:00
type Proxy struct {
2020-11-13 08:48:04 -03:00
address string
2019-09-13 17:46:04 +02:00
target * net . TCPAddr
terminationDelay time . Duration
2020-11-17 13:04:04 +01:00
proxyProtocol * dynamic . ProxyProtocol
2020-11-13 08:48:04 -03:00
refreshTarget bool
2019-03-14 09:30:04 +01:00
}
2020-05-11 12:06:07 +02:00
// NewProxy creates a new Proxy.
2020-11-17 13:04:04 +01:00
func NewProxy ( address string , terminationDelay time . Duration , proxyProtocol * dynamic . ProxyProtocol ) ( * Proxy , error ) {
2019-03-14 09:30:04 +01:00
tcpAddr , err := net . ResolveTCPAddr ( "tcp" , address )
if err != nil {
return nil , err
}
2019-05-09 14:30:06 +02:00
2020-11-17 13:04:04 +01:00
if proxyProtocol != nil && ( proxyProtocol . Version < 1 || proxyProtocol . Version > 2 ) {
return nil , fmt . Errorf ( "unknown proxyProtocol version: %d" , proxyProtocol . Version )
}
2021-03-23 10:24:03 +00:00
// enable the refresh of the target only if the address in not an IP
2020-11-13 08:48:04 -03:00
refreshTarget := false
if host , _ , err := net . SplitHostPort ( address ) ; err == nil && net . ParseIP ( host ) == nil {
refreshTarget = true
}
return & Proxy {
address : address ,
target : tcpAddr ,
refreshTarget : refreshTarget ,
terminationDelay : terminationDelay ,
2020-11-20 11:30:07 +01:00
proxyProtocol : proxyProtocol ,
2020-11-13 08:48:04 -03:00
} , nil
2019-03-14 09:30:04 +01:00
}
2020-05-11 12:06:07 +02:00
// ServeTCP forwards the connection to a service.
2019-09-13 17:46:04 +02:00
func ( p * Proxy ) ServeTCP ( conn WriteCloser ) {
2021-03-23 10:24:03 +00:00
log . WithoutContext ( ) . Debugf ( "Handling connection from %s" , conn . RemoteAddr ( ) )
2019-09-13 17:46:04 +02:00
// needed because of e.g. server.trackedConnection
2019-03-14 09:30:04 +01:00
defer conn . Close ( )
2019-05-09 14:30:06 +02:00
2021-03-23 10:24:03 +00:00
connBackend , err := p . dialBackend ( )
2019-03-14 09:30:04 +01:00
if err != nil {
2021-03-23 10:24:03 +00:00
log . WithoutContext ( ) . Errorf ( "Error while connecting to backend: %v" , err )
2019-03-14 09:30:04 +01:00
return
}
2019-09-13 17:46:04 +02:00
// maybe not needed, but just in case
2019-03-14 09:30:04 +01:00
defer connBackend . Close ( )
2019-09-13 17:46:04 +02:00
errChan := make ( chan error )
2020-11-17 13:04:04 +01:00
if p . proxyProtocol != nil && p . proxyProtocol . Version > 0 && p . proxyProtocol . Version < 3 {
header := proxyproto . HeaderProxyFromAddrs ( byte ( p . proxyProtocol . Version ) , conn . RemoteAddr ( ) , conn . LocalAddr ( ) )
if _ , err := header . WriteTo ( connBackend ) ; err != nil {
log . WithoutContext ( ) . Errorf ( "Error while writing proxy protocol headers to backend connection: %v" , err )
return
}
}
2019-09-13 17:46:04 +02:00
go p . connCopy ( conn , connBackend , errChan )
go p . connCopy ( connBackend , conn , errChan )
2019-03-14 09:30:04 +01:00
err = <- errChan
if err != nil {
2019-09-13 17:46:04 +02:00
log . WithoutContext ( ) . Errorf ( "Error during connection: %v" , err )
2019-03-14 09:30:04 +01:00
}
2019-09-13 17:46:04 +02:00
<- errChan
2019-03-14 09:30:04 +01:00
}
2021-03-23 10:24:03 +00:00
func ( p Proxy ) dialBackend ( ) ( * net . TCPConn , error ) {
if ! p . refreshTarget {
return net . DialTCP ( "tcp" , nil , p . target )
}
conn , err := net . Dial ( "tcp" , p . address )
if err != nil {
return nil , err
}
return conn . ( * net . TCPConn ) , nil
}
2019-09-13 17:46:04 +02:00
func ( p Proxy ) connCopy ( dst , src WriteCloser , errCh chan error ) {
2019-03-14 09:30:04 +01:00
_ , err := io . Copy ( dst , src )
errCh <- err
2019-09-13 17:46:04 +02:00
errClose := dst . CloseWrite ( )
if errClose != nil {
2019-09-13 20:00:06 +02:00
log . WithoutContext ( ) . Debugf ( "Error while terminating connection: %v" , errClose )
return
2019-09-13 17:46:04 +02:00
}
if p . terminationDelay >= 0 {
err := dst . SetReadDeadline ( time . Now ( ) . Add ( p . terminationDelay ) )
if err != nil {
2019-09-13 20:00:06 +02:00
log . WithoutContext ( ) . Debugf ( "Error while setting deadline: %v" , err )
2019-09-13 17:46:04 +02:00
}
}
2019-03-14 09:30:04 +01:00
}