2019-03-14 09:30:04 +01:00
package tcp
import (
"bufio"
"bytes"
"crypto/tls"
"io"
"net"
"net/http"
"strings"
2020-01-06 16:56:05 +01:00
"time"
2019-03-14 09:30:04 +01:00
2020-09-16 15:46:04 +02:00
"github.com/traefik/traefik/v2/pkg/log"
"github.com/traefik/traefik/v2/pkg/types"
2019-03-14 09:30:04 +01:00
)
2020-05-11 12:06:07 +02:00
// Router is a TCP router.
2019-03-14 09:30:04 +01:00
type Router struct {
2019-06-17 18:14:08 +02: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 09:30:04 +01:00
}
2020-05-11 12:06:07 +02:00
// ServeTCP forwards the connection to the right TCP/HTTP handler.
2019-09-13 17:46:04 +02:00
func ( r * Router ) ServeTCP ( conn WriteCloser ) {
2019-03-14 09:30:04 +01:00
// FIXME -- Check if ProxyProtocol changes the first bytes of the request
2019-11-14 16:40:05 +01:00
if r . catchAllNoTLS != nil && len ( r . routingTable ) == 0 {
2019-06-07 19:30:07 +02:00
r . catchAllNoTLS . ServeTCP ( conn )
return
}
2019-03-14 09:30:04 +01:00
br := bufio . NewReader ( conn )
2020-01-06 16:56:05 +01:00
serverName , tls , peeked , err := clientHelloServerName ( br )
if err != nil {
conn . Close ( )
return
}
// Remove read/write deadline and delegate this to underlying tcp server (for now only handled by HTTP Server)
err = conn . SetReadDeadline ( time . Time { } )
if err != nil {
log . WithoutContext ( ) . Errorf ( "Error while setting read deadline: %v" , err )
}
err = conn . SetWriteDeadline ( time . Time { } )
if err != nil {
log . WithoutContext ( ) . Errorf ( "Error while setting write deadline: %v" , err )
}
2019-03-14 09:30:04 +01:00
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
2020-07-20 18:32:03 +02:00
serverName = types . CanonicalDomain ( serverName )
2019-03-14 09:30:04 +01:00
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 ( )
}
}
2020-05-11 12:06:07 +02:00
// AddRoute defines a handler for a given sniHost (* is the only valid option).
2019-03-14 09:30:04 +01:00
func ( r * Router ) AddRoute ( sniHost string , target Handler ) {
if r . routingTable == nil {
r . routingTable = map [ string ] Handler { }
}
r . routingTable [ strings . ToLower ( sniHost ) ] = target
}
2020-05-11 12:06:07 +02:00
// AddRouteTLS defines a handler for a given sniHost and sets the matching tlsConfig.
2019-03-14 09:30:04 +01:00
func ( r * Router ) AddRouteTLS ( sniHost string , target Handler , config * tls . Config ) {
r . AddRoute ( sniHost , & TLSHandler {
Next : target ,
Config : config ,
} )
}
2020-07-13 17:58:03 +02:00
// AddRouteHTTPTLS defines a handler for a given sniHost and sets the matching tlsConfig.
2019-06-17 18:14:08 +02:00
func ( r * Router ) AddRouteHTTPTLS ( sniHost string , config * tls . Config ) {
if r . hostHTTPTLSConfig == nil {
r . hostHTTPTLSConfig = map [ string ] * tls . Config { }
}
r . hostHTTPTLSConfig [ sniHost ] = config
}
2020-05-11 12:06:07 +02:00
// AddCatchAllNoTLS defines the fallback tcp handler.
2019-03-14 09:30:04 +01:00
func ( r * Router ) AddCatchAllNoTLS ( handler Handler ) {
r . catchAllNoTLS = handler
}
2020-05-11 12:06:07 +02:00
// GetConn creates a connection proxy with a peeked string.
2019-09-13 17:46:04 +02:00
func ( r * Router ) GetConn ( conn WriteCloser , peeked string ) WriteCloser {
2019-03-14 09:30:04 +01:00
// FIXME should it really be on Router ?
conn = & Conn {
2019-09-13 17:46:04 +02:00
Peeked : [ ] byte ( peeked ) ,
WriteCloser : conn ,
2019-03-14 09:30:04 +01:00
}
return conn
}
2020-05-11 12:06:07 +02:00
// GetHTTPHandler gets the attached http handler.
2019-03-14 09:30:04 +01:00
func ( r * Router ) GetHTTPHandler ( ) http . Handler {
return r . httpHandler
}
2020-05-11 12:06:07 +02:00
// GetHTTPSHandler gets the attached https handler.
2019-03-14 09:30:04 +01:00
func ( r * Router ) GetHTTPSHandler ( ) http . Handler {
return r . httpsHandler
}
2020-05-11 12:06:07 +02:00
// HTTPForwarder sets the tcp handler that will forward the connections to an http handler.
2019-03-14 09:30:04 +01:00
func ( r * Router ) HTTPForwarder ( handler Handler ) {
r . httpForwarder = handler
}
2020-05-11 12:06:07 +02:00
// HTTPSForwarder sets the tcp handler that will forward the TLS connections to an http handler.
2019-03-14 09:30:04 +01:00
func ( r * Router ) HTTPSForwarder ( handler Handler ) {
2019-06-17 18:14:08 +02:00
for sniHost , tlsConf := range r . hostHTTPTLSConfig {
r . AddRouteTLS ( sniHost , handler , tlsConf )
}
2019-03-14 09:30:04 +01:00
r . httpsForwarder = & TLSHandler {
Next : handler ,
Config : r . httpsTLSConfig ,
}
}
2020-05-11 12:06:07 +02:00
// HTTPHandler attaches http handlers on the router.
2019-03-14 09:30:04 +01:00
func ( r * Router ) HTTPHandler ( handler http . Handler ) {
r . httpHandler = handler
}
2020-05-11 12:06:07 +02:00
// HTTPSHandler attaches https handlers on the router.
2019-03-14 09:30:04 +01:00
func ( r * Router ) HTTPSHandler ( handler http . Handler , config * tls . Config ) {
r . httpsHandler = handler
r . httpsTLSConfig = config
}
2020-05-11 12:06:07 +02:00
// Conn is a connection proxy that handles Peeked bytes.
2019-03-14 09:30:04 +01:00
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 17:46:04 +02:00
WriteCloser
2019-03-14 09:30:04 +01:00
}
2020-05-11 12:06:07 +02:00
// Read reads bytes from the connection (using the buffer prior to actually reading).
2019-03-14 09:30:04 +01:00
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 17:46:04 +02:00
return c . WriteCloser . Read ( p )
2019-03-14 09:30:04 +01: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.
2020-01-06 16:56:05 +01:00
func clientHelloServerName ( br * bufio . Reader ) ( string , bool , string , error ) {
2019-03-14 09:30:04 +01:00
hdr , err := br . Peek ( 1 )
if err != nil {
2020-01-06 16:56:05 +01:00
opErr , ok := err . ( * net . OpError )
if err != io . EOF && ( ! ok || ! opErr . Timeout ( ) ) {
2020-02-10 18:54:05 +01:00
log . WithoutContext ( ) . Debugf ( "Error while Peeking first byte: %s" , err )
2019-04-08 12:24:05 +02:00
}
2020-01-06 16:56:05 +01:00
return "" , false , "" , err
2019-03-14 09:30:04 +01:00
}
2019-11-14 16:40:05 +01:00
2020-02-25 17:50:05 +01:00
// No valid TLS record has a type of 0x80, however SSLv2 handshakes
// start with a uint16 length where the MSB is set and the first record
// is always < 256 bytes long. Therefore typ == 0x80 strongly suggests
// an SSLv2 client.
const recordTypeSSLv2 = 0x80
2019-03-14 09:30:04 +01:00
const recordTypeHandshake = 0x16
if hdr [ 0 ] != recordTypeHandshake {
2020-02-25 17:50:05 +01:00
if hdr [ 0 ] == recordTypeSSLv2 {
// we consider SSLv2 as TLS and it will be refuse by real TLS handshake.
return "" , true , getPeeked ( br ) , nil
}
2020-01-06 16:56:05 +01:00
return "" , false , getPeeked ( br ) , nil // Not TLS.
2019-03-14 09:30:04 +01:00
}
const recordHeaderLen = 5
hdr , err = br . Peek ( recordHeaderLen )
if err != nil {
log . Errorf ( "Error while Peeking hello: %s" , err )
2020-01-06 16:56:05 +01:00
return "" , false , getPeeked ( br ) , nil
2019-03-14 09:30:04 +01:00
}
2019-11-14 16:40:05 +01:00
2019-03-14 09:30:04 +01:00
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 )
2020-01-06 16:56:05 +01:00
return "" , true , getPeeked ( br ) , nil
2019-03-14 09:30:04 +01:00
}
2019-11-14 16:40:05 +01:00
2019-03-14 09:30:04 +01:00
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 ( )
2019-11-14 16:40:05 +01:00
2020-01-06 16:56:05 +01:00
return sni , true , getPeeked ( br ) , nil
2019-03-14 09:30:04 +01:00
}
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
}
2020-05-11 12:06:07 +02:00
// Read reads from the underlying reader.
2019-03-14 09:30:04 +01:00
func ( c sniSniffConn ) Read ( p [ ] byte ) ( int , error ) { return c . r . Read ( p ) }
2020-05-11 12:06:07 +02:00
// Write crashes all the time.
2019-03-14 09:30:04 +01:00
func ( sniSniffConn ) Write ( p [ ] byte ) ( int , error ) { return 0 , io . EOF }