2019-03-14 11:30:04 +03:00
package tcp
import (
"bufio"
"bytes"
"crypto/tls"
"io"
"net"
"net/http"
"strings"
2019-08-03 04:58:23 +03:00
"github.com/containous/traefik/v2/pkg/log"
2019-03-14 11:30:04 +03:00
)
// Router is a TCP router
type Router struct {
2019-06-17 19:14:08 +03:00
routingTable map [ string ] Handler
httpForwarder Handler
httpsForwarder Handler
httpHandler http . Handler
httpsHandler http . Handler
httpsTLSConfig * tls . Config // default TLS config
catchAllNoTLS Handler
hostHTTPTLSConfig map [ string ] * tls . Config // TLS configs keyed by SNI
2019-03-14 11:30:04 +03:00
}
// ServeTCP forwards the connection to the right TCP/HTTP handler
2019-09-13 18:46:04 +03:00
func ( r * Router ) ServeTCP ( conn WriteCloser ) {
2019-03-14 11:30:04 +03:00
// FIXME -- Check if ProxyProtocol changes the first bytes of the request
2019-06-07 20:30:07 +03:00
if r . catchAllNoTLS != nil && len ( r . routingTable ) == 0 && r . httpsHandler == nil {
r . catchAllNoTLS . ServeTCP ( conn )
return
}
2019-03-14 11:30:04 +03:00
br := bufio . NewReader ( conn )
serverName , tls , peeked := clientHelloServerName ( br )
if ! tls {
switch {
case r . catchAllNoTLS != nil :
r . catchAllNoTLS . ServeTCP ( r . GetConn ( conn , peeked ) )
case r . httpForwarder != nil :
r . httpForwarder . ServeTCP ( r . GetConn ( conn , peeked ) )
default :
conn . Close ( )
}
return
}
// FIXME Optimize and test the routing table before helloServerName
serverName = strings . ToLower ( serverName )
if r . routingTable != nil && serverName != "" {
if target , ok := r . routingTable [ serverName ] ; ok {
target . ServeTCP ( r . GetConn ( conn , peeked ) )
return
}
}
// FIXME Needs tests
if target , ok := r . routingTable [ "*" ] ; ok {
target . ServeTCP ( r . GetConn ( conn , peeked ) )
return
}
if r . httpsForwarder != nil {
r . httpsForwarder . ServeTCP ( r . GetConn ( conn , peeked ) )
} else {
conn . Close ( )
}
}
// AddRoute defines a handler for a given sniHost (* is the only valid option)
func ( r * Router ) AddRoute ( sniHost string , target Handler ) {
if r . routingTable == nil {
r . routingTable = map [ string ] Handler { }
}
r . routingTable [ strings . ToLower ( sniHost ) ] = target
}
// AddRouteTLS defines a handler for a given sniHost and sets the matching tlsConfig
func ( r * Router ) AddRouteTLS ( sniHost string , target Handler , config * tls . Config ) {
r . AddRoute ( sniHost , & TLSHandler {
Next : target ,
Config : config ,
} )
}
2019-06-17 19:14:08 +03:00
// AddRouteHTTPTLS defines a handler for a given sniHost and sets the matching tlsConfig
func ( r * Router ) AddRouteHTTPTLS ( sniHost string , config * tls . Config ) {
if r . hostHTTPTLSConfig == nil {
r . hostHTTPTLSConfig = map [ string ] * tls . Config { }
}
r . hostHTTPTLSConfig [ sniHost ] = config
}
2019-03-14 11:30:04 +03:00
// AddCatchAllNoTLS defines the fallback tcp handler
func ( r * Router ) AddCatchAllNoTLS ( handler Handler ) {
r . catchAllNoTLS = handler
}
// GetConn creates a connection proxy with a peeked string
2019-09-13 18:46:04 +03:00
func ( r * Router ) GetConn ( conn WriteCloser , peeked string ) WriteCloser {
2019-03-14 11:30:04 +03:00
// FIXME should it really be on Router ?
conn = & Conn {
2019-09-13 18:46:04 +03:00
Peeked : [ ] byte ( peeked ) ,
WriteCloser : conn ,
2019-03-14 11:30:04 +03:00
}
return conn
}
// GetHTTPHandler gets the attached http handler
func ( r * Router ) GetHTTPHandler ( ) http . Handler {
return r . httpHandler
}
// GetHTTPSHandler gets the attached https handler
func ( r * Router ) GetHTTPSHandler ( ) http . Handler {
return r . httpsHandler
}
// HTTPForwarder sets the tcp handler that will forward the connections to an http handler
func ( r * Router ) HTTPForwarder ( handler Handler ) {
r . httpForwarder = handler
}
// HTTPSForwarder sets the tcp handler that will forward the TLS connections to an http handler
func ( r * Router ) HTTPSForwarder ( handler Handler ) {
2019-06-17 19:14:08 +03:00
for sniHost , tlsConf := range r . hostHTTPTLSConfig {
r . AddRouteTLS ( sniHost , handler , tlsConf )
}
2019-03-14 11:30:04 +03:00
r . httpsForwarder = & TLSHandler {
Next : handler ,
Config : r . httpsTLSConfig ,
}
}
// HTTPHandler attaches http handlers on the router
func ( r * Router ) HTTPHandler ( handler http . Handler ) {
r . httpHandler = handler
}
// HTTPSHandler attaches https handlers on the router
func ( r * Router ) HTTPSHandler ( handler http . Handler , config * tls . Config ) {
r . httpsHandler = handler
r . httpsTLSConfig = config
}
// Conn is a connection proxy that handles Peeked bytes
type Conn struct {
// Peeked are the bytes that have been read from Conn for the
// purposes of route matching, but have not yet been consumed
// by Read calls. It set to nil by Read when fully consumed.
Peeked [ ] byte
// Conn is the underlying connection.
// It can be type asserted against *net.TCPConn or other types
// as needed. It should not be read from directly unless
// Peeked is nil.
2019-09-13 18:46:04 +03:00
WriteCloser
2019-03-14 11:30:04 +03:00
}
// Read reads bytes from the connection (using the buffer prior to actually reading)
func ( c * Conn ) Read ( p [ ] byte ) ( n int , err error ) {
if len ( c . Peeked ) > 0 {
n = copy ( p , c . Peeked )
c . Peeked = c . Peeked [ n : ]
if len ( c . Peeked ) == 0 {
c . Peeked = nil
}
return n , nil
}
2019-09-13 18:46:04 +03:00
return c . WriteCloser . Read ( p )
2019-03-14 11:30:04 +03:00
}
// clientHelloServerName returns the SNI server name inside the TLS ClientHello,
// without consuming any bytes from br.
// On any error, the empty string is returned.
func clientHelloServerName ( br * bufio . Reader ) ( string , bool , string ) {
hdr , err := br . Peek ( 1 )
if err != nil {
2019-04-08 13:24:05 +03:00
if err != io . EOF {
log . Errorf ( "Error while Peeking first byte: %s" , err )
}
2019-03-14 11:30:04 +03:00
return "" , false , ""
}
const recordTypeHandshake = 0x16
if hdr [ 0 ] != recordTypeHandshake {
// log.Errorf("Error not tls")
return "" , false , getPeeked ( br ) // Not TLS.
}
const recordHeaderLen = 5
hdr , err = br . Peek ( recordHeaderLen )
if err != nil {
log . Errorf ( "Error while Peeking hello: %s" , err )
return "" , false , getPeeked ( br )
}
recLen := int ( hdr [ 3 ] ) << 8 | int ( hdr [ 4 ] ) // ignoring version in hdr[1:3]
helloBytes , err := br . Peek ( recordHeaderLen + recLen )
if err != nil {
log . Errorf ( "Error while Hello: %s" , err )
return "" , true , getPeeked ( br )
}
sni := ""
server := tls . Server ( sniSniffConn { r : bytes . NewReader ( helloBytes ) } , & tls . Config {
GetConfigForClient : func ( hello * tls . ClientHelloInfo ) ( * tls . Config , error ) {
sni = hello . ServerName
return nil , nil
} ,
} )
_ = server . Handshake ( )
return sni , true , getPeeked ( br )
}
func getPeeked ( br * bufio . Reader ) string {
peeked , err := br . Peek ( br . Buffered ( ) )
if err != nil {
log . Errorf ( "Could not get anything: %s" , err )
return ""
}
return string ( peeked )
}
// sniSniffConn is a net.Conn that reads from r, fails on Writes,
// and crashes otherwise.
type sniSniffConn struct {
r io . Reader
net . Conn // nil; crash on any unexpected use
}
// Read reads from the underlying reader
func ( c sniSniffConn ) Read ( p [ ] byte ) ( int , error ) { return c . r . Read ( p ) }
// Write crashes all the time
func ( sniSniffConn ) Write ( p [ ] byte ) ( int , error ) { return 0 , io . EOF }