add full SNI peeking, fix TLS routing

This commit is contained in:
AJ ONeal 2020-06-09 04:41:38 -06:00
parent 1872302d66
commit 2b48a8b8b9
6 changed files with 100 additions and 23 deletions

View File

@ -208,8 +208,8 @@ func main() {
} }
mux.HandleTLS("*", acme, mux) mux.HandleTLS("*", acme, mux)
for _, fwd := range forwards { for _, fwd := range forwards {
mux.ForwardTCP("*", "localhost:"+fwd.port, 120*time.Second) //mux.ForwardTCP("*", "localhost:"+fwd.port, 120*time.Second)
//mux.ForwardTCP(fwd.pattern, "localhost:"+fwd.port, 120*time.Second) mux.ForwardTCP(fwd.pattern, "localhost:"+fwd.port, 120*time.Second)
} }
done := make(chan error) done := make(chan error)

View File

@ -83,13 +83,11 @@ func (c *Conn) LocalAddr() net.Addr {
// LocalAddr returns the local network address. // LocalAddr returns the local network address.
func (c *Conn) LocalAddr() net.Addr { func (c *Conn) LocalAddr() net.Addr {
// TODO is this the right one?
return &c.relaySourceAddr return &c.relaySourceAddr
} }
// RemoteAddr returns the remote network address. // RemoteAddr returns the remote network address.
func (c *Conn) RemoteAddr() net.Addr { func (c *Conn) RemoteAddr() net.Addr {
// TODO is this the right one?
return &c.relayTargetAddr return &c.relayTargetAddr
} }

View File

@ -2,17 +2,22 @@ package telebit
import ( import (
"bufio" "bufio"
"fmt"
"net" "net"
"time" "time"
"git.coolaj86.com/coolaj86/go-telebitd/sni"
) )
// ConnWrap is just a cheap way to DRY up some switch conn.(type) statements to handle special features of Conn // ConnWrap is just a cheap way to DRY up some switch conn.(type) statements to handle special features of Conn
type ConnWrap struct { type ConnWrap struct {
// TODO use io.MultiReader to unbuffer the peeker // TODO use io.MultiReader to unbuffer the peeker
//Conn net.Conn //Conn net.Conn
peeker *bufio.Reader peeker *bufio.Reader
Conn net.Conn servername string
Plain net.Conn scheme string
Conn net.Conn
Plain net.Conn
} }
type Peeker interface { type Peeker interface {
@ -60,11 +65,19 @@ func (c *ConnWrap) Close() error {
// Scheme returns one of "https", "http", "tcp", "tls", or "" // Scheme returns one of "https", "http", "tcp", "tls", or ""
func (c *ConnWrap) Scheme() string { func (c *ConnWrap) Scheme() string {
if nil != c.Plain { if "" != c.scheme {
tlsConn := &ConnWrap{Conn: c.Plain} return c.scheme
return tlsConn.Scheme()
} }
/*
if nil != c.Plain {
tlsConn := &ConnWrap{Conn: c.Plain}
// TODO upgrade tls+http => https
c.scheme = tlsConn.Scheme()
return c.scheme
}
*/
switch conn := c.Conn.(type) { switch conn := c.Conn.(type) {
case *ConnWrap: case *ConnWrap:
return conn.Scheme() return conn.Scheme()
@ -74,20 +87,34 @@ func (c *ConnWrap) Scheme() string {
return "" return ""
} }
/*
func (c *ConnWrap) SetServername(name string) {
c.servername = name
}
*/
// Servername may return Servername or Hostname as hinted by a tunnel or buffered peeking // Servername may return Servername or Hostname as hinted by a tunnel or buffered peeking
func (c *ConnWrap) Servername() string { func (c *ConnWrap) Servername() string {
if "" != c.servername {
return c.servername
}
if nil != c.Plain { if nil != c.Plain {
tlsConn := &ConnWrap{Conn: c.Plain} tlsConn := &ConnWrap{Conn: c.Plain}
return tlsConn.Servername() c.servername = tlsConn.Servername()
return c.servername
} }
switch conn := c.Conn.(type) { switch conn := c.Conn.(type) {
case *ConnWrap: case *ConnWrap:
return conn.Scheme() //c.servername = conn.Servername()
return conn.Servername()
case *Conn: case *Conn:
return string(conn.relaySourceAddr.scheme) // TODO XXX
//c.servername = string(conn.relayTargetAddr.addr)
return string(conn.relayTargetAddr.addr)
} }
return "" return c.servername
} }
// isTerminated returns true if net.Conn is either a ConnWrap{ tls.Conn }, // isTerminated returns true if net.Conn is either a ConnWrap{ tls.Conn },
@ -104,16 +131,24 @@ func (c *ConnWrap) isTerminated() bool {
c.SetDeadline(time.Now().Add(5 * time.Second)) c.SetDeadline(time.Now().Add(5 * time.Second))
n := 6 n := 6
b, _ := c.Peek(n) b, _ := c.Peek(n)
fmt.Println("Peek(n)", b)
defer c.SetDeadline(time.Time{}) defer c.SetDeadline(time.Time{})
if len(b) >= n { if len(b) >= n {
// SSL v3.x / TLS v1.x // SSL v3.x / TLS v1.x
// 0: TLS Byte // 0: TLS Byte
// 1: Major Version // 1: Major Version
// 2: 0-Indexed Minor Version // 2: Minor Version - 1
// 3-4: Header Length // 3-4: Header Length
// Payload
// 5: TLS Client Hello Marker Byte // 5: TLS Client Hello Marker Byte
if 0x16 == b[0] && 0x03 == b[1] && 0x01 == b[5] { if 0x16 == b[0] && 0x03 == b[1] && 0x01 == b[5] {
//length := (int(b[3]) << 8) + int(b[4]) length := (int(b[3]) << 8) + int(b[4])
b, err := c.Peek(n - 1 + length)
if nil != err {
return true
}
c.servername, _ = sni.GetHostname(b)
return false return false
} }
} }

View File

@ -6,6 +6,7 @@ import (
"io" "io"
"net" "net"
"net/http" "net/http"
"strings"
) )
// A Listener transforms a multiplexed websocket connection into individual net.Conn-like connections. // A Listener transforms a multiplexed websocket connection into individual net.Conn-like connections.
@ -76,7 +77,7 @@ func Serve(listener net.Listener, mux Handler) error {
go func() { go func() {
err = mux.Serve(client) err = mux.Serve(client)
if nil != err { if nil != err {
if io.EOF != err { if io.EOF != err && io.ErrClosedPipe != err && !strings.Contains(err.Error(), errNetClosing) {
fmt.Printf("client could not be served: %q\n", err.Error()) fmt.Printf("client could not be served: %q\n", err.Error())
} }
} }

View File

@ -2,6 +2,7 @@ package telebit
import ( import (
"fmt" "fmt"
"io"
"net" "net"
"strconv" "strconv"
"strings" "strings"
@ -65,7 +66,11 @@ func (m *RouteMux) Serve(client net.Conn) error {
for _, meta := range m.routes { for _, meta := range m.routes {
// TODO '*.example.com' // TODO '*.example.com'
fmt.Println("Meta:", meta.addr) if meta.terminate && "" == servername {
wconn.isTerminated()
servername = wconn.servername
}
fmt.Println("Meta:", meta.addr, servername)
if servername == meta.addr || "*" == meta.addr || port == meta.addr { if servername == meta.addr || "*" == meta.addr || port == meta.addr {
//fmt.Println("[debug] test of route:", meta) //fmt.Println("[debug] test of route:", meta)
if err := meta.handler.Serve(wconn); nil != err { if err := meta.handler.Serve(wconn); nil != err {
@ -124,7 +129,11 @@ func (m *RouteMux) HandleTLS(servername string, acme *ACME, handler Handler) err
//NewTerminator(acme, handler)(client) //NewTerminator(acme, handler)(client)
//return handler.Serve(client) //return handler.Serve(client)
return handler.Serve(TerminateTLS(wconn, acme)) err := handler.Serve(TerminateTLS(wconn, acme))
if nil == err || io.EOF == err {
return io.EOF
}
return err
}), }),
}) })
return nil return nil

View File

@ -180,11 +180,15 @@ type ACME struct {
var acmecert *certmagic.Config = nil var acmecert *certmagic.Config = nil
func NewTerminator(acme *ACME, handler Handler) HandlerFunc { /*
func NewTerminator(servername string, acme *ACME, handler Handler) HandlerFunc {
return func(client net.Conn) error { return func(client net.Conn) error {
return handler.Serve(TerminateTLS(client, acme)) return handler.Serve(TerminateTLS("", client, acme))
} }
} }
*/
//func TerminateTLS(client *ConnWrap, acme *ACME) net.Conn
func TerminateTLS(client net.Conn, acme *ACME) net.Conn { func TerminateTLS(client net.Conn, acme *ACME) net.Conn {
var magic *certmagic.Config = nil var magic *certmagic.Config = nil
@ -231,10 +235,40 @@ func TerminateTLS(client net.Conn, acme *ACME) net.Conn {
}, },
} }
var servername string
var scheme string
// I think this must always be ConnWrap, but I'm not sure
switch conn := client.(type) {
case *ConnWrap:
servername = conn.Servername()
scheme = conn.Scheme()
client = conn
default:
wconn := &ConnWrap{
Conn: client,
}
wconn.isTerminated()
servername = wconn.Servername()
scheme = wconn.Scheme()
client = wconn
}
/*
// TODO ?
if "" == scheme {
scheme = "tls"
}
if "http" == scheme {
scheme = "https"
}
*/
tlsconn := tls.Server(client, tlsConfig) tlsconn := tls.Server(client, tlsConfig)
return &ConnWrap{ return &ConnWrap{
Conn: tlsconn, Conn: tlsconn,
Plain: client, Plain: client,
servername: servername,
scheme: scheme,
} }
} }