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

View File

@ -1,67 +1,148 @@
package genericlistener
import (
"net"
"bytes"
"context"
"crypto/tls"
"encoding/hex"
"log"
"net"
"time"
"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 (
protoHTTP protocol = iota + 1
protoHTTPS
protoSSLV3
protoTLSV1
protoTLSV11
protoTLSV2
encryptNone int = iota
encryptSSLV2
encryptSSLV3
encryptTLS10
encryptTLS11
encryptTLS12
)
//State -- state of connection
type State struct {
Protocol protocol
}
//GenericListenAndServe -- used to lisen for any https traffic on 443 (8443)
// - setup generic TCP listener, unencrypted TCP, with a Deadtime out
// - 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}}
//NewState -- Constructor
func NewState() (p *State) {
p = new(State)
listenAddr, err := net.ResolveTCPAddr("tcp", serverBinding)
if nil != err {
loginfo.Println(err)
return
}
func handleConnection(conn net.Conn, connectionTable *connection.Table, secretKey string) {
defer conn.Close()
ln, err := net.ListenTCP("tcp", listenAddr)
if err != nil {
loginfo.Println("unable to bind", err)
return
}
loginfo.Println("conn", conn)
loginfo.Println("hank")
loginfo.Println("here", conn.LocalAddr().String(), conn.RemoteAddr().String())
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)
}
}
}
//handleConnection -
// - accept a wedgeConnection along with all the other required attritvues
// - 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
}
func handleStream(conn net.Conn) {
var buf [512]byte
cnt, err := conn.Read(buf[0:])
if err != nil {
loginfo.Println(err)
return
}
loginfo.Println(hex.Dump(buf[0:cnt]))
}
//state := NewState()
//wConn := NewWedgeConnSize(conn, 512)
@ -84,6 +165,3 @@ func handleConnection(conn net.Conn, connectionTable *connection.Table, secretKe
//wedgeListener := &WedgeListener{conn: conn}
//LaunchWssListener(connectionTable, &secretKey, wedgeListener)
return
}

View File

@ -9,6 +9,7 @@ import (
//WedgeListener -- used to hand off connections to other protocols via Listen
type WedgeListener struct {
net.Listener
conn net.Conn
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
import (
"crypto/tls"
"flag"
"fmt"
"log"
"os"
"time"
"context"
"git.daplie.com/Daplie/go-rvpn-server/rvpn/connection"
"git.daplie.com/Daplie/go-rvpn-server/rvpn/genericlistener"
@ -20,17 +24,18 @@ var (
argServerBinding string
argServerAdminBinding string
argServerExternalBinding string
argDeadTime int
connectionTable *connection.Table
wssMapping *xlate.WssMapping
secretKey = "abc123"
)
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(&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")
}
//Run -- main entry point
@ -44,18 +49,35 @@ func Run() {
fmt.Println("-=-=-=-=-=-=-=-=-=-=")
// certbundle, err := tls.LoadX509KeyPair("certs/fullchain.pem", "certs/privkey.pem")
// if err != nil {
// loginfo.Println(err)
// return
// }
// loginfo.Println(certbundle)
certbundle, err := tls.LoadX509KeyPair("certs/fullchain.pem", "certs/privkey.pem")
if err != nil {
loginfo.Println(err)
return
}
wssMapping = xlate.NewwssMapping()
go wssMapping.Run()
ctx, cancelContext := context.WithCancel(context.Background())
defer cancelContext()
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 external.LaunchWebRequestExternalListener(&argServerExternalBinding, connectionTable)
@ -65,5 +87,5 @@ func Run() {
// 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")
}