Generic Listener supporting unencrypted, encrypted, with TLS version detection before TLS Accept

- added support for context passing between the various functions
- support for withCancel, allowing administrative canceling, and a clean up of Go Routines.
- generic listener now supports a single port for both encrypted and clear text protocols.
- employee the buffered wedge connection for peaking into the protocol
- implementation of the oneListener.
- when TLS, leveraged the NewListener which uses oneListener as n inner lister.
- once the stream is decrypted, or if it was already clear text it is passed to handleStream which performs application detection.
This commit is contained in:
Henry Camacho 2017-02-25 23:17:39 -06:00
parent d611757b10
commit ebafa277df
5 changed files with 209 additions and 68 deletions

View File

@ -2,6 +2,7 @@ package connection
import "fmt" import "fmt"
import "time" import "time"
import "context"
const ( const (
initialDomains = 0 initialDomains = 0
@ -62,13 +63,18 @@ func (c *Table) reaper(delay int, idle int) {
} }
//Run -- Execute //Run -- Execute
func (c *Table) Run() { func (c *Table) Run(ctx context.Context) {
loginfo.Println("ConnectionTable starting") loginfo.Println("ConnectionTable starting")
go c.reaper(300, 60) go c.reaper(300, 60)
for { for {
select { select {
case <-ctx.Done():
loginfo.Println("Cancel signal hit")
return
case registration := <-c.register: case registration := <-c.register:
loginfo.Println("register fired") loginfo.Println("register fired")

View File

@ -1,89 +1,167 @@
package genericlistener package genericlistener
import ( import (
"net" "bytes"
"context"
"crypto/tls" "crypto/tls"
"encoding/hex"
"log"
"net"
"time"
"git.daplie.com/Daplie/go-rvpn-server/rvpn/connection" "git.daplie.com/Daplie/go-rvpn-server/rvpn/connection"
) )
//LaunchGenericServer -- used to lisen for any https traffic on 443 (8443)
//used to make sure customer devices can reach 443. wss or client
func LaunchGenericServer(connectionTable *connection.Table, secretKey string, serverBinding string, certbundle tls.Certificate) {
config := &tls.Config{Certificates: []tls.Certificate{certbundle}}
listener, err := tls.Listen("tcp", serverBinding, config)
if err != nil {
loginfo.Println("unable to bind ", serverBinding)
return
}
defer listener.Close()
for {
conn, err := listener.Accept()
if err != nil {
loginfo.Println("Bad accept ", err)
continue
}
go handleConnection(conn, connectionTable, secretKey)
}
}
type protocol int
//Family -- ENUM for Address Family
const ( const (
protoHTTP protocol = iota + 1 encryptNone int = iota
protoHTTPS encryptSSLV2
protoSSLV3 encryptSSLV3
protoTLSV1 encryptTLS10
protoTLSV11 encryptTLS11
protoTLSV2 encryptTLS12
) )
//State -- state of connection //GenericListenAndServe -- used to lisen for any https traffic on 443 (8443)
type State struct { // - setup generic TCP listener, unencrypted TCP, with a Deadtime out
Protocol protocol // - leaverage the wedgeConn to peek into the buffer.
// - if TLS, consume connection with TLS certbundle, pass to request identifier
// - else, just pass to the request identififer
func GenericListenAndServe(ctx context.Context, connectionTable *connection.Table, secretKey string, serverBinding string, certbundle tls.Certificate, deadTime int) {
config := &tls.Config{Certificates: []tls.Certificate{certbundle}}
listenAddr, err := net.ResolveTCPAddr("tcp", serverBinding)
if nil != err {
loginfo.Println(err)
return
}
ln, err := net.ListenTCP("tcp", listenAddr)
if err != nil {
loginfo.Println("unable to bind", err)
return
}
for {
select {
case <-ctx.Done():
loginfo.Println("Cancel signal hit")
return
default:
ln.SetDeadline(time.Now().Add(time.Duration(deadTime) * time.Second))
conn, err := ln.Accept()
loginfo.Println("Deadtime reached")
if nil != err {
if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
continue
}
log.Println(err)
return
}
wedgeConn := NewWedgeConn(conn)
go handleConnection(ctx, wedgeConn, connectionTable, secretKey, config)
}
}
} }
//NewState -- Constructor //handleConnection -
func NewState() (p *State) { // - accept a wedgeConnection along with all the other required attritvues
p = new(State) // - peek into the buffer, determine TLS or unencrypted
func handleConnection(ctx context.Context, wConn *WedgeConn, connectionTable *connection.Table, secretKey string, config *tls.Config) {
defer wConn.Close()
peekCnt := 10
encryptMode := encryptNone
loginfo.Println("conn", wConn, wConn.LocalAddr().String(), wConn.RemoteAddr().String())
peek, err := wConn.Peek(peekCnt)
if err != nil {
loginfo.Println("error while peeking")
return
}
loginfo.Println(hex.Dump(peek[0:peekCnt]))
loginfo.Println(hex.Dump(peek[2:4]))
loginfo.Println("after peek")
//take a look for a TLS header.
if bytes.Contains(peek[0:0], []byte{0x80}) && bytes.Contains(peek[2:4], []byte{0x01, 0x03}) {
encryptMode = encryptSSLV2
} else if bytes.Contains(peek[0:3], []byte{0x16, 0x03, 0x00}) {
encryptMode = encryptSSLV3
} else if bytes.Contains(peek[0:3], []byte{0x16, 0x03, 0x01}) {
encryptMode = encryptTLS10
loginfo.Println("TLS10")
} else if bytes.Contains(peek[0:3], []byte{0x16, 0x03, 0x02}) {
encryptMode = encryptTLS11
} else if bytes.Contains(peek[0:3], []byte{0x16, 0x03, 0x03}) {
encryptMode = encryptTLS12
}
oneConn := &oneConnListener{wConn}
if encryptMode == encryptSSLV2 {
loginfo.Println("SSLv2 is not accepted")
return
} else if encryptMode != encryptNone {
loginfo.Println("Handle Encryption")
tlsListener := tls.NewListener(oneConn, config)
conn, err := tlsListener.Accept()
if err != nil {
loginfo.Println(err)
return
}
loginfo.Println(conn)
handleStream(conn)
return
}
loginfo.Println("Handle Unencrypted")
handleStream(wConn)
return return
} }
func handleConnection(conn net.Conn, connectionTable *connection.Table, secretKey string) { func handleStream(conn net.Conn) {
defer conn.Close() var buf [512]byte
cnt, err := conn.Read(buf[0:])
loginfo.Println("conn", conn) if err != nil {
loginfo.Println("hank") loginfo.Println(err)
loginfo.Println("here", conn.LocalAddr().String(), conn.RemoteAddr().String())
//state := NewState()
//wConn := NewWedgeConnSize(conn, 512)
//var buffer [512]byte
// Peek for data to figure out what connection we have
//peekcnt := 32
//peek, err := wConn.Peek(peekcnt)
//if err != nil {
// loginfo.Println("error while peeking")
// return
// }
//loginfo.Println(hex.Dump(peek[0:peekcnt]))
//loginfo.Println("after peek")
// assume http websocket.
//loginfo.Println("wConn", wConn)
//wedgeListener := &WedgeListener{conn: conn}
//LaunchWssListener(connectionTable, &secretKey, wedgeListener)
return return
}
loginfo.Println(hex.Dump(buf[0:cnt]))
} }
//state := NewState()
//wConn := NewWedgeConnSize(conn, 512)
//var buffer [512]byte
// Peek for data to figure out what connection we have
//peekcnt := 32
//peek, err := wConn.Peek(peekcnt)
//if err != nil {
// loginfo.Println("error while peeking")
// return
// }
//loginfo.Println(hex.Dump(peek[0:peekcnt]))
//loginfo.Println("after peek")
// assume http websocket.
//loginfo.Println("wConn", wConn)
//wedgeListener := &WedgeListener{conn: conn}
//LaunchWssListener(connectionTable, &secretKey, wedgeListener)

View File

@ -9,6 +9,7 @@ import (
//WedgeListener -- used to hand off connections to other protocols via Listen //WedgeListener -- used to hand off connections to other protocols via Listen
type WedgeListener struct { type WedgeListener struct {
net.Listener
conn net.Conn conn net.Conn
once sync.Once once sync.Once
} }

View File

@ -0,0 +1,34 @@
package genericlistener
import (
"io"
"net"
)
type oneConnListener struct {
conn net.Conn
}
func (l *oneConnListener) Accept() (c net.Conn, err error) {
c = l.conn
if c == nil {
err = io.EOF
loginfo.Println("Accept")
return
}
err = nil
l.conn = nil
loginfo.Println("Accept", c.LocalAddr().String(), c.RemoteAddr().String())
return
}
func (l *oneConnListener) Close() error {
loginfo.Println("close")
return nil
}
func (l *oneConnListener) Addr() net.Addr {
loginfo.Println("addr")
return nil
}

View File

@ -1,10 +1,14 @@
package rvpnmain package rvpnmain
import ( import (
"crypto/tls"
"flag" "flag"
"fmt" "fmt"
"log" "log"
"os" "os"
"time"
"context"
"git.daplie.com/Daplie/go-rvpn-server/rvpn/connection" "git.daplie.com/Daplie/go-rvpn-server/rvpn/connection"
"git.daplie.com/Daplie/go-rvpn-server/rvpn/genericlistener" "git.daplie.com/Daplie/go-rvpn-server/rvpn/genericlistener"
@ -20,17 +24,18 @@ var (
argServerBinding string argServerBinding string
argServerAdminBinding string argServerAdminBinding string
argServerExternalBinding string argServerExternalBinding string
argDeadTime int
connectionTable *connection.Table connectionTable *connection.Table
wssMapping *xlate.WssMapping wssMapping *xlate.WssMapping
secretKey = "abc123" secretKey = "abc123"
) )
func init() { func init() {
flag.StringVar(&argGenericBinding, "ssl-listener", ":8443", "generic SSL Listener") flag.IntVar(&argDeadTime, "dead-time-counter", 5, "deadtime counter in seconds")
flag.StringVar(&argGenericBinding, "generic-listener", ":8443", "generic SSL Listener")
flag.StringVar(&argWssClientListener, "wss-client-listener", ":3502", "wss client listener address:port") flag.StringVar(&argWssClientListener, "wss-client-listener", ":3502", "wss client listener address:port")
flag.StringVar(&argServerAdminBinding, "admin-server-port", "127.0.0.2:8000", "admin server Bind listener") flag.StringVar(&argServerAdminBinding, "admin-server-port", "127.0.0.2:8000", "admin server Bind listener")
flag.StringVar(&argServerExternalBinding, "external-server-port", "127.0.0.1:8080", "external server Bind listener") flag.StringVar(&argServerExternalBinding, "external-server-port", "127.0.0.1:8080", "external server Bind listener")
} }
//Run -- main entry point //Run -- main entry point
@ -44,18 +49,35 @@ func Run() {
fmt.Println("-=-=-=-=-=-=-=-=-=-=") fmt.Println("-=-=-=-=-=-=-=-=-=-=")
// certbundle, err := tls.LoadX509KeyPair("certs/fullchain.pem", "certs/privkey.pem") certbundle, err := tls.LoadX509KeyPair("certs/fullchain.pem", "certs/privkey.pem")
// if err != nil { if err != nil {
// loginfo.Println(err) loginfo.Println(err)
// return return
// } }
// loginfo.Println(certbundle)
wssMapping = xlate.NewwssMapping() ctx, cancelContext := context.WithCancel(context.Background())
go wssMapping.Run() defer cancelContext()
connectionTable = connection.NewTable() connectionTable = connection.NewTable()
go connectionTable.Run() go connectionTable.Run(ctx)
// Setup for GenericListenServe.
// - establish context for the generic listener
// - startup listener
// - accept with peek buffer.
// - peek at the 1st 30 bytes.
// - check for tls
// - if tls, establish, protocol peek buffer, else decrypted
// - match protocol
go genericlistener.GenericListenAndServe(ctx, connectionTable, secretKey, argGenericBinding, certbundle, argDeadTime)
time.Sleep(300 * time.Second)
cancelContext()
time.Sleep(60 * time.Second)
//wssMapping = xlate.NewwssMapping()
//go wssMapping.Run()
//go client.LaunchClientListener(connectionTable, &secretKey, &argServerBinding) //go client.LaunchClientListener(connectionTable, &secretKey, &argServerBinding)
//go external.LaunchWebRequestExternalListener(&argServerExternalBinding, connectionTable) //go external.LaunchWebRequestExternalListener(&argServerExternalBinding, connectionTable)
@ -65,5 +87,5 @@ func Run() {
// loginfo.Println("LauchAdminListener failed: ", err) // loginfo.Println("LauchAdminListener failed: ", err)
//} //}
genericlistener.LaunchWssListener(connectionTable, secretKey, argWssClientListener, "certs/fullchain.pem", "certs/privkey.pem") //genericlistener.LaunchWssListener(connectionTable, secretKey, argWssClientListener, "certs/fullchain.pem", "certs/privkey.pem")
} }