Working version of RVPN

- got too cute with the package names, needed to bring everything into one package, except for packer.
- system is passing traffic now, ran a load test generating 1000 connections, seems ok.
- removed a lot of message logging since traffic is passing.
This commit is contained in:
Henry Camacho 2017-03-02 18:47:59 -06:00
parent 5baa7a0601
commit 8f2e4f58c0
20 changed files with 177 additions and 150 deletions

View File

@ -47,6 +47,7 @@ Once the lister is fired up, I sends back a regisration status to the manager al
```bash
hcamacho@Hanks-MBP:go-rvpn-server $ go get
hcamacho@Hanks-MBP:go-rvpn-server $ go build
```

View File

@ -1,60 +0,0 @@
package admin
import (
"encoding/json"
"fmt"
"net/http"
"git.daplie.com/Daplie/go-rvpn-server/rvpn/connection"
"github.com/gorilla/mux"
)
var (
connTable *connection.Table
)
//LaunchAdminListener - starts up http listeners and handles various URI paths
func LaunchAdminListener(serverBinding *string, connectionTable *connection.Table) (err error) {
loginfo.Println("starting launchAdminListener", *serverBinding)
connTable = connectionTable
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/", index)
router.HandleFunc("/api/servers", apiServers)
s := &http.Server{
Addr: *serverBinding,
Handler: router,
}
err = s.ListenAndServeTLS("certs/fullchain.pem", "certs/privkey.pem")
if err != nil {
loginfo.Println("ListenAndServe: ", err)
}
return
}
func index(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Welcome!")
}
func apiServers(w http.ResponseWriter, r *http.Request) {
fmt.Println("here")
serverContainer := NewServerAPIContainer()
for c := range connTable.Connections() {
serverAPI := NewServerAPI(c)
serverContainer.Servers = append(serverContainer.Servers, serverAPI)
}
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
json.NewEncoder(w).Encode(serverContainer)
}
func handleRequest(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Domain not supported", http.StatusBadRequest)
}

View File

@ -0,0 +1,17 @@
package connectiontrack
import (
"log"
"os"
)
var (
loginfo *log.Logger
logdebug *log.Logger
logFlags = log.Ldate | log.Lmicroseconds | log.Lshortfile
)
func init() {
loginfo = log.New(os.Stdout, "INFO: connectiontrack: ", logFlags)
logdebug = log.New(os.Stdout, "DEBUG: connectiontrack:", logFlags)
}

View File

@ -0,0 +1,68 @@
package genericlistener
import "net"
import "context"
import "fmt"
//Tracking --
type Tracking struct {
connections map[string]net.Conn
register chan net.Conn
unregister chan net.Conn
}
//NewTracking -- Constructor
func NewTracking() (p *Tracking) {
p = new(Tracking)
p.connections = make(map[string]net.Conn)
p.register = make(chan net.Conn)
p.unregister = make(chan net.Conn)
return
}
//Run -
func (p *Tracking) Run(ctx context.Context) {
loginfo.Println("Tracking Running")
for {
select {
case <-ctx.Done():
loginfo.Println("Cancel signal hit")
return
case connection := <-p.register:
key := connection.RemoteAddr().String()
loginfo.Println("register fired", key)
p.connections[key] = connection
p.list()
case connection := <-p.unregister:
key := connection.RemoteAddr().String()
loginfo.Println("unregister fired", key)
p.connections[key] = connection
if _, ok := p.connections[key]; ok {
delete(p.connections, key)
}
p.list()
}
}
}
func (p *Tracking) list() {
for c := range p.connections {
loginfo.Println(c)
}
}
//Lookup --
// - get connection from key
func (p *Tracking) Lookup(key string) (c net.Conn, err error) {
if _, ok := p.connections[key]; ok {
c = p.connections[key]
} else {
err = fmt.Errorf("Lookup failed for %s", key)
c = nil
}
return
}

View File

@ -29,13 +29,11 @@ func NewWedgeConnSize(c net.Conn, size int) (p *WedgeConn) {
//Peek - Get a number of bytes outof the buffer, but allow the buffer to be replayed once read
func (w *WedgeConn) Peek(n int) ([]byte, error) {
loginfo.Println("Peek")
return w.reader.Peek(n)
}
//Read -- A normal reader.
func (w *WedgeConn) Read(p []byte) (int, error) {
loginfo.Println("Read")
cnt, err := w.reader.Read(p)
return cnt, err
}
@ -49,7 +47,6 @@ func (w *WedgeConn) Buffered() int {
// - get all the chars available
// - pass then back
func (w *WedgeConn) PeekAll() (buf []byte, err error) {
loginfo.Println("PeekAll")
_, err = w.Peek(1)
if err != nil {

View File

@ -1,7 +1,7 @@
package connection
package genericlistener
import (
"encoding/hex"
"strconv"
"time"
"git.daplie.com/Daplie/go-rvpn-server/rvpn/packer"
@ -10,6 +10,8 @@ import (
"io"
"context"
"github.com/gorilla/websocket"
)
@ -55,12 +57,14 @@ type Connection struct {
//initialDomains - a list of domains from the JWT
initialDomains []interface{}
connectionTrack *Tracking
///wssState tracks a highlevel status of the connection, false means do nothing.
wssState bool
}
//NewConnection -- Constructor
func NewConnection(connectionTable *Table, conn *websocket.Conn, remoteAddress string, initialDomains []interface{}) (p *Connection) {
func NewConnection(connectionTable *Table, conn *websocket.Conn, remoteAddress string, initialDomains []interface{}, connectionTrack *Tracking) (p *Connection) {
p = new(Connection)
p.mutex = &sync.Mutex{}
p.connectionTable = connectionTable
@ -71,6 +75,7 @@ func NewConnection(connectionTable *Table, conn *websocket.Conn, remoteAddress s
p.send = make(chan *SendTrack)
p.connectTime = time.Now()
p.initialDomains = initialDomains
p.connectionTrack = connectionTrack
p.DomainTrack = make(map[string]*DomainTrack)
for _, domain := range initialDomains {
@ -181,7 +186,9 @@ func (c *Connection) Write(w io.WriteCloser, message []byte) (cnt int, err error
}
//Reader -- export the reader function
func (c *Connection) Reader() {
func (c *Connection) Reader(ctx context.Context) {
connectionTrack := c.connectionTrack
defer func() {
c.connectionTable.unregister <- c
c.conn.Close()
@ -195,28 +202,28 @@ func (c *Connection) Reader() {
msgType, message, err := c.conn.ReadMessage()
loginfo.Println("ReadMessage", msgType, err)
loginfo.Println(hex.Dump(message))
loginfo.Println(message)
c.Update()
if err != nil {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
c.State(false)
loginfo.Printf("error: %v", err)
//loginfo.Println(c.conn)
}
break
}
// unpack the message.
_, _ = packer.ReadMessage(message)
p, err := packer.ReadMessage(message)
key := p.Header.Address().String() + ":" + strconv.Itoa(p.Header.Port)
test, err := connectionTrack.Lookup(key)
// p.Header.SetAddress(rAddr)
// p.Header.Port, err = strconv.Atoi(rPort)
// p.Header.Port = 8080
// p.Header.Service = "http"
// p.Data.AppendBytes(buffer[0:cnt])
// buf := p.PackV1()
if err != nil {
loginfo.Println("Unable to locate Tracking for ", key)
continue
}
test.Write(p.Data.Data())
c.addIn(int64(len(message)))
loginfo.Println("end of read")

View File

@ -1,4 +1,4 @@
package connection
package genericlistener
import "github.com/gorilla/websocket"
@ -17,15 +17,18 @@ type Registration struct {
//initialDomains - a list of domains from the JWT
initialDomains []interface{}
connectionTrack *Tracking
}
//NewRegistration -- Constructor
func NewRegistration(conn *websocket.Conn, remoteAddress string, initialDomains []interface{}) (p *Registration) {
func NewRegistration(conn *websocket.Conn, remoteAddress string, initialDomains []interface{}, connectionTrack *Tracking) (p *Registration) {
p = new(Registration)
p.conn = conn
p.source = remoteAddress
p.commCh = make(chan bool)
p.initialDomains = initialDomains
p.connectionTrack = connectionTrack
return
}

View File

@ -1,4 +1,4 @@
package connection
package genericlistener
import "fmt"
import "time"
@ -78,7 +78,7 @@ func (c *Table) Run(ctx context.Context) {
case registration := <-c.register:
loginfo.Println("register fired")
connection := NewConnection(c, registration.conn, registration.source, registration.initialDomains)
connection := NewConnection(c, registration.conn, registration.source, registration.initialDomains, registration.connectionTrack)
c.connections[connection] = make([]string, initialDomains)
registration.commCh <- true
@ -87,7 +87,7 @@ func (c *Table) Run(ctx context.Context) {
// add to the domains regirstation
newDomain := string(domain.(string))
loginfo.Println("adding domain ", newDomain, " to connection ", connection)
loginfo.Println("adding domain ", newDomain, " to connection ", connection.conn.RemoteAddr().String())
c.domains[newDomain] = connection
// add to the connection domain list
@ -95,11 +95,10 @@ func (c *Table) Run(ctx context.Context) {
c.connections[connection] = append(s, newDomain)
}
go connection.Writer()
go connection.Reader()
loginfo.Println("register exiting")
go connection.Reader(ctx)
case connection := <-c.unregister:
loginfo.Println("closing connection ", connection)
loginfo.Println("closing connection ", connection.conn.RemoteAddr().String())
if _, ok := c.connections[connection]; ok {
for _, domain := range c.connections[connection] {
fmt.Println("removing domain ", domain)
@ -122,8 +121,6 @@ func (c *Table) Run(ctx context.Context) {
//}
}
fmt.Println("domain ", c.domains)
fmt.Println("connections ", c.connections)
}
}

View File

@ -1,4 +1,4 @@
package admin
package genericlistener
//DomainAPI -- Structure to hold the domain tracking for JSON
type DomainAPI struct {

View File

@ -1,4 +1,4 @@
package connection
package genericlistener
//DomainMapping --
type DomainMapping struct {

View File

@ -1,4 +1,4 @@
package connection
package genericlistener
//DomainTrack -- Tracking specifics for domains
type DomainTrack struct {

View File

@ -9,9 +9,6 @@ import (
jwt "github.com/dgrijalva/jwt-go"
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
"git.daplie.com/Daplie/go-rvpn-server/rvpn/admin"
"git.daplie.com/Daplie/go-rvpn-server/rvpn/connection"
)
//LaunchWssListener - obtains a onetime connection from wedge listener

View File

@ -0,0 +1 @@
package genericlistener

View File

@ -21,19 +21,19 @@ import (
"bufio"
"git.daplie.com/Daplie/go-rvpn-server/rvpn/admin"
"git.daplie.com/Daplie/go-rvpn-server/rvpn/connection"
"git.daplie.com/Daplie/go-rvpn-server/rvpn/packer"
)
type contextKey string
//CtxConnectionTrack
const (
ctxSecretKey contextKey = "secretKey"
ctxConnectionTable contextKey = "connectionTable"
ctxConfig contextKey = "config"
ctxDeadTime contextKey = "deadtime"
ctxListenerRegistration contextKey = "listenerRegistration"
ctxConnectionTrack contextKey = "connectionTrack"
)
const (
@ -118,9 +118,6 @@ func handleConnection(ctx context.Context, wConn *WedgeConn) {
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}) {
@ -181,8 +178,6 @@ func handleStream(ctx context.Context, wConn *WedgeConn) {
loginfo.Println("conn", wConn, wConn.LocalAddr().String(), wConn.RemoteAddr().String())
peek, err := wConn.PeekAll()
loginfo.Println(hex.Dump(peek[0:]))
if err != nil {
loginfo.Println("error while peeking")
loginfo.Println(hex.Dump(peek[0:]))
@ -228,34 +223,32 @@ func handleStream(ctx context.Context, wConn *WedgeConn) {
}
}
}
loginfo.Println(hex.Dump(peek[0:]))
}
//handleExternalHTTPRequest -
// - get a wConn and start processing requests
func handleExternalHTTPRequest(ctx context.Context, conn net.Conn) {
defer conn.Close()
connectionTracking := ctx.Value(ctxConnectionTrack).(*Tracking)
connectionTracking.register <- conn
connectionTable := ctx.Value(ctxConnectionTable).(*connection.Table)
defer func() {
connectionTracking.unregister <- conn
conn.Close()
}()
connectionTable := ctx.Value(ctxConnectionTable).(*Table)
var buffer [512]byte
for {
cnt, err := conn.Read(buffer[0:])
if err != nil {
return
}
loginfo.Println("conn ", conn)
loginfo.Println("byte read", cnt)
readBuffer := bytes.NewBuffer(buffer[0:cnt])
reader := bufio.NewReader(readBuffer)
r, err := http.ReadRequest(reader)
loginfo.Println(r)
if err != nil {
loginfo.Println("error parsing request")
return
@ -284,8 +277,7 @@ func handleExternalHTTPRequest(ctx context.Context, conn net.Conn) {
return
}
loginfo.Println("Domain Accepted")
loginfo.Println(conn, rAddr, rPort)
loginfo.Println("Domain Accepted", conn, rAddr, rPort)
p := packer.NewPacker()
p.Header.SetAddress(rAddr)
p.Header.Port, err = strconv.Atoi(rPort)
@ -298,7 +290,7 @@ func handleExternalHTTPRequest(ctx context.Context, conn net.Conn) {
p.Data.AppendBytes(buffer[0:cnt])
buf := p.PackV1()
sendTrack := connection.NewSendTrack(buf.Bytes(), hostname)
sendTrack := NewSendTrack(buf.Bytes(), hostname)
conn.SendCh() <- sendTrack
}
}
@ -307,7 +299,7 @@ func handleExternalHTTPRequest(ctx context.Context, conn net.Conn) {
// - expecting an existing oneConnListener with a qualified wss client connected.
// - auth will happen again since we were just peeking at the token.
func handleAdminClient(ctx context.Context, oneConn *oneConnListener) {
connectionTable := ctx.Value(ctxConnectionTable).(*connection.Table)
connectionTable := ctx.Value(ctxConnectionTable).(*Table)
router := mux.NewRouter().StrictSlash(true)
@ -332,10 +324,10 @@ func handleAdminClient(ctx context.Context, oneConn *oneConnListener) {
router.HandleFunc("/api/servers", func(w http.ResponseWriter, r *http.Request) {
fmt.Println("here")
serverContainer := admin.NewServerAPIContainer()
serverContainer := NewServerAPIContainer()
for c := range connectionTable.Connections() {
serverAPI := admin.NewServerAPI(c)
serverAPI := NewServerAPI(c)
serverContainer.Servers = append(serverContainer.Servers, serverAPI)
}
@ -367,7 +359,7 @@ func handleAdminClient(ctx context.Context, oneConn *oneConnListener) {
// - auth will happen again since we were just peeking at the token.
func handleWssClient(ctx context.Context, oneConn *oneConnListener) {
secretKey := ctx.Value(ctxSecretKey).(string)
connectionTable := ctx.Value(ctxConnectionTable).(*connection.Table)
connectionTable := ctx.Value(ctxConnectionTable).(*Table)
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
@ -388,13 +380,9 @@ func handleWssClient(ctx context.Context, oneConn *oneConnListener) {
return
}
loginfo.Println("help access_token valid")
claims := result.Claims.(jwt.MapClaims)
domains, ok := claims["domains"].([]interface{})
loginfo.Println("domains", domains)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
@ -410,7 +398,8 @@ func handleWssClient(ctx context.Context, oneConn *oneConnListener) {
//newConnection := connection.NewConnection(connectionTable, conn, r.RemoteAddr, domains)
newRegistration := connection.NewRegistration(conn, r.RemoteAddr, domains)
connectionTrack := ctx.Value(ctxConnectionTrack).(*Tracking)
newRegistration := NewRegistration(conn, r.RemoteAddr, domains, connectionTrack)
connectionTable.Register() <- newRegistration
ok = <-newRegistration.CommCh()
if !ok {

View File

@ -4,8 +4,6 @@ import (
"context"
"crypto/tls"
"net"
"git.daplie.com/Daplie/go-rvpn-server/rvpn/connection"
)
//ListenerRegistrationStatus - post registration status
@ -47,22 +45,24 @@ func NewListenerRegistration(port int) (p *ListenerRegistration) {
//GenericListeners -
type GenericListeners struct {
listeners map[*net.Listener]int
ctx context.Context
connnectionTable *connection.Table
secretKey string
certbundle tls.Certificate
deadTime int
register chan *ListenerRegistration
genericListeners *GenericListeners
listeners map[*net.Listener]int
ctx context.Context
connnectionTable *Table
connectionTracking *Tracking
secretKey string
certbundle tls.Certificate
deadTime int
register chan *ListenerRegistration
genericListeners *GenericListeners
}
//NewGenerListeners --
func NewGenerListeners(ctx context.Context, connectionTable *connection.Table, secretKey string, certbundle tls.Certificate, deadTime int) (p *GenericListeners) {
func NewGenerListeners(ctx context.Context, connectionTable *Table, connectionTrack *Tracking, secretKey string, certbundle tls.Certificate, deadTime int) (p *GenericListeners) {
p = new(GenericListeners)
p.listeners = make(map[*net.Listener]int)
p.ctx = ctx
p.connnectionTable = connectionTable
p.connectionTracking = connectionTrack
p.secretKey = secretKey
p.certbundle = certbundle
p.deadTime = deadTime
@ -80,6 +80,10 @@ func (gl *GenericListeners) Run(ctx context.Context, initialPort int) {
ctx = context.WithValue(ctx, ctxSecretKey, gl.secretKey)
ctx = context.WithValue(ctx, ctxConnectionTable, gl.connnectionTable)
loginfo.Println(gl.connectionTracking)
ctx = context.WithValue(ctx, ctxConnectionTrack, gl.connectionTracking)
ctx = context.WithValue(ctx, ctxConfig, config)
ctx = context.WithValue(ctx, ctxDeadTime, gl.deadTime)
ctx = context.WithValue(ctx, ctxListenerRegistration, gl.register)
@ -104,6 +108,7 @@ func (gl *GenericListeners) Run(ctx context.Context, initialPort int) {
}
}
loginfo.Println("listener starting up ", registration.port)
loginfo.Println(ctx.Value(ctxConnectionTrack).(*Tracking))
go GenericListenAndServe(ctx, registration)
status := <-registration.commCh

View File

@ -1,4 +1,4 @@
package connection
package genericlistener
//SendTrack -- Used as a channel communication to id domain asssociated to domain for outbound WSS
type SendTrack struct {

View File

@ -1,10 +1,8 @@
package admin
package genericlistener
import (
"fmt"
"time"
"git.daplie.com/Daplie/go-rvpn-server/rvpn/connection"
)
//ServerAPI -- Structure to support the server API
@ -17,7 +15,7 @@ type ServerAPI struct {
}
//NewServerAPI - Constructor
func NewServerAPI(c *connection.Connection) (s *ServerAPI) {
func NewServerAPI(c *Connection) (s *ServerAPI) {
s = new(ServerAPI)
s.ServerName = fmt.Sprintf("%p", c)
s.Domains = make([]*DomainAPI, 0)

View File

@ -95,12 +95,11 @@ func ReadMessage(b []byte) (p *Packer, err error) {
//handle Service
pos = pos + end + 1
end = pos + int(p.Header.HeaderLen)
p.Header.Service = string(b[pos:end])
p.Header.Service = string(b[pos : p.Header.HeaderLen+2])
//handle payload
pos = pos + end + 1
loginfo.Println(p.Header.Port)
pos = int(p.Header.HeaderLen + 2)
p.Data.AppendBytes(b[pos:])
} else {
err = fmt.Errorf("Version %d not supported", b[0:0])

View File

@ -14,12 +14,18 @@ func newPackerData() (p *packerData) {
return
}
func (p packerData) AppendString(dataString string) (n int, err error) {
func (p *packerData) AppendString(dataString string) (n int, err error) {
n, err = p.buffer.WriteString(dataString)
return
}
func (p packerData) AppendBytes(dataBytes []byte) (n int, err error) {
func (p *packerData) AppendBytes(dataBytes []byte) (n int, err error) {
n, err = p.buffer.Write(dataBytes)
return
}
//Data --
func (p *packerData) Data() (b []byte) {
b = p.buffer.Bytes()
return
}

View File

@ -10,7 +10,6 @@ import (
"context"
"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/xlate"
)
@ -25,7 +24,7 @@ var (
argServerAdminBinding string
argServerExternalBinding string
argDeadTime int
connectionTable *connection.Table
connectionTable *genericlistener.Table
wssMapping *xlate.WssMapping
secretKey = "abc123"
)
@ -58,9 +57,6 @@ func Run() {
ctx, cancelContext := context.WithCancel(context.Background())
defer cancelContext()
connectionTable = connection.NewTable()
go connectionTable.Run(ctx)
// Setup for GenericListenServe.
// - establish context for the generic listener
// - startup listener
@ -70,7 +66,13 @@ func Run() {
// - if tls, establish, protocol peek buffer, else decrypted
// - match protocol
genericListeners := genericlistener.NewGenerListeners(ctx, connectionTable, secretKey, certbundle, argDeadTime)
connectionTracking := genericlistener.NewTracking()
go connectionTracking.Run(ctx)
connectionTable = genericlistener.NewTable()
go connectionTable.Run(ctx)
genericListeners := genericlistener.NewGenerListeners(ctx, connectionTable, connectionTracking, secretKey, certbundle, argDeadTime)
go genericListeners.Run(ctx, 8443)
//go genericlistener.GenericListenAndServe(ctx, connectionTable, secretKey, argGenericBinding, certbundle, argDeadTime)