Added support for a Listener Manager

- I anticipated having to bring up new listeners as tunnel-clients connect allowing different port use for a single domain
- completed the code, however did not see any port information coming back from WSS client.
- opened an issue with AJ.
- all listeners are now dynamically generated.  The run method takes an initial port, once functioning, the runner send down the channel a registration for the initial port.
This commit is contained in:
Henry Camacho 2017-02-26 17:27:38 -06:00
parent b122a6091b
commit 493477179b
5 changed files with 397 additions and 180 deletions

266
README.md
View File

@ -1,190 +1,134 @@
# RVPN Server # RVPN Server
Admin Interface ## restructured-http
---------------
Developing the API calls and buliding the structure behind it.
- build get mux router package `go get "github.com/gorilla/mux"` - connection handling has been totally rewritten.
- run `go-rvpn-server` - on a specific port RVPN can determine the following:
- execute stunnel (as below) - if a connection is encrypted or not encrypted
- browse to <https://127.0.0.2:8000/api/servers> - if a request is a wss_client
- if a request is an admin/api request
- if a request is a plain (to be forwarded) http request
- or if a request is a different protocol (perhaps SSH)
```json To accomplish the above RVPN uses raw TCP sockets, buffered readers, and a custom Listener. This allows protocol detection (multiple services on one port)
{"Servers":[{"ServerName":"0xc420019500","Domains":[{"Domain":"127.0.0.1","BytesIn":0,"BytesOut":2607},{"Domain":"hfc.daplie.me","BytesIn":0,"BytesOut":0},{"Domain":"test.hfc.daplie.me","BytesIn":0,"BytesOut":0}],"Duration":372.34454292,"BytesIn":65,"BytesOut":2607}]} If we expose 443 and 80 to maximize the ability for tunnel clients and south bound traffic, the RVPN is able to deal with this traffic on a limited number of ports, and the most popular ports.
It is possible now to meter any point of the connection (not Interface Level, rather TCP)
There is now a connection manager that dynamically allows new GenericListeners to start on different ports when needed....
```go
newListener := NewListenerRegistration(initialPort)
gl.register <- newListener
``` ```
The above is telemetry from the RVPN server. (now supports domain byte tracking) A new listener is created by sending a NewListenerRegistration on the channel.
Work is continuing, please review make sure it is what you are looking for. ```go
We will need to deal with server name, I am placing the point address for now, not sure how to handle the name. ln, err := net.ListenTCP("tcp", listenAddr)
if err != nil {
loginfo.Println("unable to bind", err)
listenerRegistration.status = listenerFault
listenerRegistration.err = err
listenerRegistration.commCh <- listenerRegistration
return
}
listenerRegistration.status = listenerAdded
listenerRegistration.commCh <- listenerRegistration
```
Once the lister is fired up, I sends back a regisration status to the manager along with the new Listener and status.
### Build
Traffic Testing
---------------
* run go-rvpn-server
* execute stunnel.js
```bash ```bash
bin/stunnel.js --locals http://hfc.daplie.me:8443,http://test.hfc.daplie.me:3001,http://127.0.0.1:8080 --stunneld wss://localhost.daplie.me:3502 --secret abc123
```
* Browse to http://127.0.0.1:8080 hcamacho@Hanks-MBP:go-rvpn-server $ go get
``` ```
INFO: connection: 2017/02/13 20:46:07.815340 connection.go:95: &{0xc420017350 0xc420181540 0xc42014acc0 127.0.0.1:49412 91 3390 0xc42014ad20 [hfc.daplie.me test.hfc.daplie.me 127.0.0.1]}
``` ### Execute RVPN
91, and 3390 are bytes in and bytes out.
```bash
hcamacho@Hanks-MBP:go-rvpn-server $ ./go-rvpn-server
INFO: packer: 2017/02/26 12:43:53.978133 run.go:48: startup
-=-=-=-=-=-=-=-=-=-=
INFO: connection: 2017/02/26 12:43:53.978959 connection_table.go:67: ConnectionTable starting
INFO: connection: 2017/02/26 12:43:53.979000 connection_table.go:50: Reaper waiting for 300 seconds
``` ```
INFO: external: 2017/02/13 20:46:07.814294 listener_webrequest.go:75: connState
&{{0xc42015e690}} 127.0.0.1:8080 127.0.0.1:49416 ### Connect Tunnel client
active
INFO: external: 2017/02/13 20:46:07.814327 listener_webrequest.go:24: handlerWebRequestExternal ```bash
INFO: external: 2017/02/13 20:46:07.814358 listener_webrequest.go:30: "GET /favicon.ico HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nAccept: image/webp,image/*,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate, sdch, br\r\nAccept-Language: en-US,en;q=0.8\r\nCache-Control: no-cache\r\nConnection: keep-alive\r\nPragma: no-cache\r\nReferer: http://127.0.0.1:8080/\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36\r\n\r\n"
INFO: external: 2017/02/13 20:46:07.814372 listener_webrequest.go:50: &{0xc420017350 0xc420181540 0xc42014acc0 127.0.0.1:49412 78 2963 0xc42014ad20 [hfc.daplie.me test.hfc.daplie.me 127.0.0.1]} 127.0.0.1 49416 hcamacho@Hanks-MBP:node-tunnel-client $ bin/stunnel.js --locals http://hfc.daplie.me:8443,http://test.hfc.daplie.me:3001,http://127.0.0.1:8080 --stunneld wss://localhost.daplie.me:8443 --secret abc123
header: ipv4,127.0.0.2,49416,398,na [local proxy] http://hfc.daplie.me:8443
meta: {[254 27] 0 [0 0 0 0] [254 27 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 0} [local proxy] http://test.hfc.daplie.me:3001
Data: GET /favicon.ico HTTP/1.1 [local proxy] http://127.0.0.1:8080
Host: 127.0.0.1:8080 [connect] 'wss://localhost.daplie.me:8443'
Accept: image/webp,image/*,*/*;q=0.8 [open] connected to 'wss://localhost.daplie.me:8443'
```
### Connect Admin
- add a host entry
```
127.0.0.1 tunnel.example.com rvpn.daplie.invalid
```
```bash
browse https://rvpn.daplie.invalid:8443
```
### Send some traffic
- run the RVPN (as above)
- run the tunnel client (as above)
- browse http://127.0.0.1:8443 && https://127.0.0.1:8443
- observe
```bash
hcamacho@Hanks-MBP:node-tunnel-client $ bin/stunnel.js --locals http://hfc.daplie.me:8443,http://test.hfc.daplie.me:3001,http://127.0.0.1:8080 --stunneld wss://localhost.daplie.me:8443 --secret abc123
[local proxy] http://hfc.daplie.me:8443
[local proxy] http://test.hfc.daplie.me:3001
[local proxy] http://127.0.0.1:8080
[connect] 'wss://localhost.daplie.me:8443'
[open] connected to 'wss://localhost.daplie.me:8443'
hello
fe1c495076342c3132372e302e302e312c383038302c3431332c68747470474554202f20485454502f312e310d0a486f73743a203132372e302e302e313a383434330d0a436f6e6e656374696f6e3a206b6565702d616c6976650d0a43616368652d436f6e74726f6c3a206d61782d6167653d300d0a557067726164652d496e7365637572652d52657175657374733a20310d0a557365722d4167656e743a204d6f7a696c6c612f352e3020284d6163696e746f73683b20496e74656c204d6163204f5320582031305f31325f3329204170706c655765624b69742f3533372e333620284b48544d4c2c206c696b65204765636b6f29204368726f6d652f35362e302e323932342e3837205361666172692f3533372e33360d0a4163636570743a20746578742f68746d6c2c6170706c69636174696f6e2f7868746d6c2b786d6c2c6170706c69636174696f6e2f786d6c3b713d302e392c696d6167652f776562702c2a2f2a3b713d302e380d0a4163636570742d456e636f64696e673a20677a69702c206465666c6174652c20736463682c2062720d0a4163636570742d4c616e67756167653a20656e2d55532c656e3b713d302e380d0a0d0a
<EFBFBD>IPv4,127.0.0.1,8080,413,httpGET / HTTP/1.1
Host: 127.0.0.1:8443
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch, br Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: en-US,en;q=0.8 Accept-Language: en-US,en;q=0.8
Cache-Control: no-cache
Connection: keep-alive
Pragma: no-cache [exit] loop closed 0
Referer: http://127.0.0.1:8080/
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
```
``` ```
[error] closing ',undefined,undefined' because 'unsupported service 'undefined'' (-1 clients)
[_onLocalClose]
<Buffer fe 0a 2c 2c 2c 31 2c 65 72 72 6f 72 00>
not v1 (or data is corrupt)
[error] closing ',undefined,undefined' because 'unsupported service 'undefined'' (-1 clients)
[_onLocalClose]
<Buffer fe 0a 2c 2c 2c 31 2c 65 72 72 6f 72 00>
not v1 (or data is corrupt)
```
Above is an attempt to connection...
``` Looks like it aborts for some reaon. I have this problem on on a new installation as well.
INFO: external: 2017/02/13 20:50:45.883267 listener_webrequest.go:75: connState
&{{0xc42015e690}} 127.0.0.1:8080 127.0.0.1:49416
active
INFO: external: 2017/02/13 20:50:45.883298 listener_webrequest.go:24: handlerWebRequestExternal
INFO: external: 2017/02/13 20:50:45.883331 listener_webrequest.go:30: "GET /favicon.ico HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nAccept: image/webp,image/*,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate, sdch, br\r\nAccept-Language: en-US,en;q=0.8\r\nConnection: keep-alive\r\nReferer: http://127.0.0.1:8080/\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36\r\n\r\n"
INFO: external: 2017/02/13 20:50:45.883338 listener_webrequest.go:46: unable to match 127.0.0.1 to an existing connection
INFO: external: 2017/02/13 20:50:45.883362 listener_webrequest.go:75: connState
&{{0xc42015e690}} 127.0.0.1:8080 127.0.0.1:49416
```
The above is a connection to a domain that was not registered.
Branch: restructured
--------------------
* restructure into various packages, removing items from package "main"
* used caddy source as a guide
* weird debugging issue introduced, will not halt, break point unverified.
* although a test project debugs just fin
listener_client — the WSS client
--------------------------------
- removed support for anything admin
- injected the domains from the claim
- domains are now included as initialDomains
- registration performans as normal but includes adding the domains to a map of domains, and a collection of domains on the connection.
- the system now supports look up fast in either direction, not sure if it will be needed.
- reads a chan during registration before allowing traffic, making sure all is well.
- registration returns a true on the channel if all is well. If it is not, false. Likely will add some text to pass back.
Connection
----------
- added support for boolean channel
- support for initial domains in a slice, these are brought back from the JWT as a interface and then are type asserted into the map
- removed all the old timer sender dwell stuff as a POC for traffic counts.
ConnectionTable
---------------
- added support for domain announcement after the WSS is connection. Not sure if we will need these. They have not been implemented.
- I assume all domains are registered with JWT unless I hear differently which would require a new WSS session
- expanded NewTable constructor
- populating domains into the domain map, and into the connection slice.
- added support for removing domains when a connection is removed.
Packer
------
- added support for a PackerHeader type and PackerData type
- these are connected in a Packer type
- support for calculated address family based on ip address property
- service field is set to "na"
Logging
-------
- unified package logging based on a package init. Will likely need to remove this
Tests
-----
- stared to structure project for tests.
Build Instructions -=-=-=-=-=-=
------------------
Create a subinterface:
```bash
sudo ifconfig lo0 alias 127.0.0.2 up
```
The above creates an alias the code is able to bind against for admin. Admin is still in progress.
Get the dependencies
```bash
go get github.com/gorilla/websocket
go get github.com/dgrijalva/jwt-go
git clone git@git.daplie.com:Daplie/localhost.daplie.me-certificates.git
ln -s localhost.daplie.me-certificates/certs/localhost.daplie.me certs
```
Run the VPN
```bash
go build && ./go-rvpn-server
```
In another terminal execute the client
``` bash
bin/stunnel.js --locals http:hfc.daplie.me:3000,http://test.hfc.daplie.me:3001 --stunneld wss://localhost.daplie.me:8000 --secret abc123
```
A good authentication
```
INFO: 2017/02/02 21:22:22 vpn-server.go:88: startup
INFO: 2017/02/02 21:22:22 vpn-server.go:90: :8000
INFO: 2017/02/02 21:22:22 vpn-server.go:73: starting Listener
INFO: 2017/02/02 21:22:22 connection_table.go:19: ConnectionTable starting
INFO: 2017/02/02 21:22:24 connection.go:113: websocket opening 127.0.0.1:55469
INFO: 2017/02/02 21:22:24 connection.go:127: access_token valid
INFO: 2017/02/02 21:22:24 connection.go:130: processing domains [hfc.daplie.me test.hfc.daplie.me]
```
Change the key on the tunnel client to test a valid secret
``` bash
INFO: 2017/02/02 21:24:13 vpn-server.go:88: startup
INFO: 2017/02/02 21:24:13 vpn-server.go:90: :8000
INFO: 2017/02/02 21:24:13 vpn-server.go:73: starting Listener
INFO: 2017/02/02 21:24:13 connection_table.go:19: ConnectionTable starting
INFO: 2017/02/02 21:24:15 connection.go:113: websocket opening 127.0.0.1:55487
INFO: 2017/02/02 21:24:15 connection.go:123: access_token invalid...closing connection
```
Connection to the External Interface.
http://127.0.0.1:8080
The request is dumped to stdio. This is in preparation of taking that request and sending it back to the designated WSS connection
The system needs to track the response coming back, decouple it, and place it back on the wire in the form of a response stream. Since
A Poor Man's Reverse VPN written in Go A Poor Man's Reverse VPN written in Go

View File

@ -30,14 +30,12 @@ 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 //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) { func (w *WedgeConn) Peek(n int) ([]byte, error) {
loginfo.Println("Peek") loginfo.Println("Peek")
loginfo.Println("buffered=", w.reader.Buffered())
return w.reader.Peek(n) return w.reader.Peek(n)
} }
//Read -- A normal reader. //Read -- A normal reader.
func (w *WedgeConn) Read(p []byte) (int, error) { func (w *WedgeConn) Read(p []byte) (int, error) {
loginfo.Println("read", w.Conn) loginfo.Println("Read")
cnt, err := w.reader.Read(p) cnt, err := w.reader.Read(p)
return cnt, err return cnt, err
} }

View File

@ -5,8 +5,12 @@ import (
"context" "context"
"crypto/tls" "crypto/tls"
"encoding/hex" "encoding/hex"
"encoding/json"
"fmt"
"log" "log"
"net" "net"
"strconv"
"strings"
"time" "time"
jwt "github.com/dgrijalva/jwt-go" jwt "github.com/dgrijalva/jwt-go"
@ -17,15 +21,19 @@ import (
"bufio" "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/connection"
"git.daplie.com/Daplie/go-rvpn-server/rvpn/packer"
) )
type contextKey string type contextKey string
const ( const (
ctxSecretKey contextKey = "secretKey" ctxSecretKey contextKey = "secretKey"
ctxConnectionTable contextKey = "connectionTable" ctxConnectionTable contextKey = "connectionTable"
ctxConfig contextKey = "config" ctxConfig contextKey = "config"
ctxDeadTime contextKey = "deadtime"
ctxListenerRegistration contextKey = "listenerRegistration"
) )
const ( const (
@ -42,14 +50,13 @@ const (
// - leaverage the wedgeConn to peek into the buffer. // - leaverage the wedgeConn to peek into the buffer.
// - if TLS, consume connection with TLS certbundle, pass to request identifier // - if TLS, consume connection with TLS certbundle, pass to request identifier
// - else, just pass to the request identififer // - else, just pass to the request identififer
func GenericListenAndServe(ctx context.Context, connectionTable *connection.Table, secretKey string, serverBinding string, certbundle tls.Certificate, deadTime int) { func GenericListenAndServe(ctx context.Context, listenerRegistration *ListenerRegistration) {
config := &tls.Config{Certificates: []tls.Certificate{certbundle}}
ctx = context.WithValue(ctx, ctxSecretKey, secretKey) loginfo.Println(":" + string(listenerRegistration.port))
ctx = context.WithValue(ctx, ctxConnectionTable, connectionTable)
ctx = context.WithValue(ctx, ctxConfig, config) listenAddr, err := net.ResolveTCPAddr("tcp", ":"+strconv.Itoa(listenerRegistration.port))
deadTime := ctx.Value(ctxDeadTime).(int)
listenAddr, err := net.ResolveTCPAddr("tcp", serverBinding)
if nil != err { if nil != err {
loginfo.Println(err) loginfo.Println(err)
return return
@ -58,9 +65,15 @@ func GenericListenAndServe(ctx context.Context, connectionTable *connection.Tabl
ln, err := net.ListenTCP("tcp", listenAddr) ln, err := net.ListenTCP("tcp", listenAddr)
if err != nil { if err != nil {
loginfo.Println("unable to bind", err) loginfo.Println("unable to bind", err)
listenerRegistration.status = listenerFault
listenerRegistration.err = err
listenerRegistration.commCh <- listenerRegistration
return return
} }
listenerRegistration.status = listenerAdded
listenerRegistration.commCh <- listenerRegistration
for { for {
select { select {
case <-ctx.Done(): case <-ctx.Done():
@ -188,9 +201,7 @@ func handleStream(ctx context.Context, wConn *WedgeConn) {
return return
} }
loginfo.Println(r) // do we have a valid wss_client?
//now we have a request. Check to see if it is one of our own
secretKey := ctx.Value(ctxSecretKey).(string) secretKey := ctx.Value(ctxSecretKey).(string)
tokenString := r.URL.Query().Get("access_token") tokenString := r.URL.Query().Get("access_token")
result, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { result, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
@ -201,8 +212,19 @@ func handleStream(ctx context.Context, wConn *WedgeConn) {
loginfo.Println("Valid WSS dected...sending to handler") loginfo.Println("Valid WSS dected...sending to handler")
oneConn := &oneConnListener{wConn} oneConn := &oneConnListener{wConn}
handleWssClient(ctx, oneConn) handleWssClient(ctx, oneConn)
//do we have a invalid domain indicating Admin?
//if yes, prep the oneConn and send it to the handler
} else if strings.Contains(r.Host, "rvpn.daplie.invalid") {
loginfo.Println("admin")
oneConn := &oneConnListener{wConn}
handleAdminClient(ctx, oneConn)
return
} else { } else {
loginfo.Println("not a valid WSS connection") loginfo.Println("default connection")
handleExternalHTTPRequest(ctx, wConn)
return
} }
} }
} }
@ -210,6 +232,132 @@ func handleStream(ctx context.Context, wConn *WedgeConn) {
loginfo.Println(hex.Dump(peek[0:])) 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()
connectionTable := ctx.Value(ctxConnectionTable).(*connection.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
}
hostname := r.Host
loginfo.Println("Host: ", hostname)
if strings.Contains(hostname, ":") {
arr := strings.Split(hostname, ":")
hostname = arr[0]
}
loginfo.Println("Remote: ", conn.RemoteAddr().String())
remoteSplit := strings.Split(conn.RemoteAddr().String(), ":")
rAddr := remoteSplit[0]
rPort := remoteSplit[1]
if conn, ok := connectionTable.ConnByDomain(hostname); !ok {
//matching connection can not be found based on ConnByDomain
loginfo.Println("unable to match ", hostname, " to an existing connection")
//http.Error(, "Domain not supported", http.StatusBadRequest)
} else {
loginfo.Println("Domain Accepted")
loginfo.Println(conn, rAddr, rPort)
p := packer.NewPacker()
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()
sendTrack := connection.NewSendTrack(buf.Bytes(), hostname)
conn.SendCh() <- sendTrack
}
}
}
//handleAdminClient -
// - 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)
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
loginfo.Println("HandleFunc /")
switch url := r.URL.Path; url {
case "/":
// check to see if we are using the administrative Host
if strings.Contains(r.Host, "rvpn.daplie.invalid") {
http.Redirect(w, r, "/admin", 301)
}
default:
http.Error(w, "Not Found", 404)
}
})
router.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprintln(w, "<html>Welcome..press <a href=/api/servers>Servers</a> to access stats</html>")
})
router.HandleFunc("/api/servers", func(w http.ResponseWriter, r *http.Request) {
fmt.Println("here")
serverContainer := admin.NewServerAPIContainer()
for c := range connectionTable.Connections() {
serverAPI := admin.NewServerAPI(c)
serverContainer.Servers = append(serverContainer.Servers, serverAPI)
}
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
json.NewEncoder(w).Encode(serverContainer)
})
s := &http.Server{
Addr: ":80",
Handler: router,
}
err := s.Serve(oneConn)
if err != nil {
loginfo.Println("Serve error: ", err)
}
select {
case <-ctx.Done():
loginfo.Println("Cancel signal hit")
return
}
}
//handleWssClient - //handleWssClient -
// - expecting an existing oneConnListener with a qualified wss client connected. // - expecting an existing oneConnListener with a qualified wss client connected.
// - auth will happen again since we were just peeking at the token. // - auth will happen again since we were just peeking at the token.
@ -241,6 +389,8 @@ func handleWssClient(ctx context.Context, oneConn *oneConnListener) {
claims := result.Claims.(jwt.MapClaims) claims := result.Claims.(jwt.MapClaims)
domains, ok := claims["domains"].([]interface{}) domains, ok := claims["domains"].([]interface{})
loginfo.Println("domains", domains)
var upgrader = websocket.Upgrader{ var upgrader = websocket.Upgrader{
ReadBufferSize: 1024, ReadBufferSize: 1024,
WriteBufferSize: 1024, WriteBufferSize: 1024,
@ -283,5 +433,4 @@ func handleWssClient(ctx context.Context, oneConn *oneConnListener) {
loginfo.Println("Cancel signal hit") loginfo.Println("Cancel signal hit")
return return
} }
} }

View File

@ -0,0 +1,123 @@
package genericlistener
import (
"context"
"crypto/tls"
"net"
"git.daplie.com/Daplie/go-rvpn-server/rvpn/connection"
)
//ListenerRegistrationStatus - post registration status
type ListenerRegistrationStatus int
const (
listenerAdded ListenerRegistrationStatus = iota
listenerExists
listenerFault
)
//ListenerRegistration -- A connection registration structure used to bring up a connection
//connection table will then handle additing and sdtarting up the various readers
//else error.
type ListenerRegistration struct {
// The websocket connection.
listener *net.Listener
// The listener port
port int
// The status
status ListenerRegistrationStatus
// The error
err error
// communications channel between go routines
commCh chan *ListenerRegistration
}
//NewListenerRegistration -- Constructor
func NewListenerRegistration(port int) (p *ListenerRegistration) {
p = new(ListenerRegistration)
p.port = port
p.commCh = make(chan *ListenerRegistration)
return
}
//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
}
//NewGenerListeners --
func NewGenerListeners(ctx context.Context, connectionTable *connection.Table, 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.secretKey = secretKey
p.certbundle = certbundle
p.deadTime = deadTime
p.register = make(chan *ListenerRegistration)
return
}
//Run -- Execute
// - execute the GenericLister
// - pass initial port, we'll announce that
func (gl *GenericListeners) Run(ctx context.Context, initialPort int) {
loginfo.Println("ConnectionTable starting")
config := &tls.Config{Certificates: []tls.Certificate{gl.certbundle}}
ctx = context.WithValue(ctx, ctxSecretKey, gl.secretKey)
ctx = context.WithValue(ctx, ctxConnectionTable, gl.connnectionTable)
ctx = context.WithValue(ctx, ctxConfig, config)
ctx = context.WithValue(ctx, ctxDeadTime, gl.deadTime)
ctx = context.WithValue(ctx, ctxListenerRegistration, gl.register)
go func(ctx context.Context) {
for {
select {
case <-ctx.Done():
loginfo.Println("Cancel signal hit")
return
case registration := <-gl.register:
loginfo.Println("register fired", registration.port)
// check to see if port is already running
for listener := range gl.listeners {
if gl.listeners[listener] == registration.port {
loginfo.Println("listener already running")
registration.status = listenerExists
registration.commCh <- registration
}
}
loginfo.Println("listener starting up ", registration.port)
go GenericListenAndServe(ctx, registration)
status := <-registration.commCh
if status.status == listenerAdded {
gl.listeners[status.listener] = status.port
} else if status.status == listenerFault {
loginfo.Println("Unable to create a new listerer", registration.port)
}
}
}
}(ctx)
newListener := NewListenerRegistration(initialPort)
gl.register <- newListener
}

View File

@ -70,9 +70,12 @@ func Run() {
// - if tls, establish, protocol peek buffer, else decrypted // - if tls, establish, protocol peek buffer, else decrypted
// - match protocol // - match protocol
go genericlistener.GenericListenAndServe(ctx, connectionTable, secretKey, argGenericBinding, certbundle, argDeadTime) genericListeners := genericlistener.NewGenerListeners(ctx, connectionTable, secretKey, certbundle, argDeadTime)
go genericListeners.Run(ctx, 8443)
time.Sleep(20 * time.Second) //go genericlistener.GenericListenAndServe(ctx, connectionTable, secretKey, argGenericBinding, certbundle, argDeadTime)
time.Sleep(300 * time.Second)
cancelContext() cancelContext()
time.Sleep(60 * time.Second) time.Sleep(60 * time.Second)