From 493477179bef158cee5c63a5bf379ad4d1f93f7f Mon Sep 17 00:00:00 2001 From: Henry Camacho Date: Sun, 26 Feb 2017 17:27:38 -0600 Subject: [PATCH] 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. --- README.md | 266 +++++++++-------------- rvpn/genericlistener/conn_wedge.go | 4 +- rvpn/genericlistener/listener_generic.go | 177 +++++++++++++-- rvpn/genericlistener/manager.go | 123 +++++++++++ rvpn/rvpnmain/run.go | 7 +- 5 files changed, 397 insertions(+), 180 deletions(-) create mode 100644 rvpn/genericlistener/manager.go diff --git a/README.md b/README.md index 03e8575..03f376b 100644 --- a/README.md +++ b/README.md @@ -1,190 +1,134 @@ # RVPN Server -Admin Interface ---------------- -Developing the API calls and buliding the structure behind it. +## restructured-http -- build get mux router package `go get "github.com/gorilla/mux"` -- run `go-rvpn-server` -- execute stunnel (as below) -- browse to +- connection handling has been totally rewritten. +- on a specific port RVPN can determine the following: + - if a connection is encrypted or not encrypted + - 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 -{"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}]} +To accomplish the above RVPN uses raw TCP sockets, buffered readers, and a custom Listener. This allows protocol detection (multiple services on one port) +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. - -Traffic Testing ---------------- -* run go-rvpn-server -* execute stunnel.js +### Build ```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]} -``` -91, and 3390 are bytes in and bytes out. + +### Execute RVPN + +```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 -active -INFO: external: 2017/02/13 20:46:07.814327 listener_webrequest.go:24: handlerWebRequestExternal -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 -header: ipv4,127.0.0.2,49416,398,na -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} -Data: GET /favicon.ico HTTP/1.1 -Host: 127.0.0.1:8080 -Accept: image/webp,image/*,*/*;q=0.8 + +### Connect Tunnel client + +```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' + +``` + +### 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 +�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-Language: en-US,en;q=0.8 -Cache-Control: no-cache -Connection: keep-alive -Pragma: no-cache -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 -``` + + +[exit] loop closed 0 ``` -[error] closing ',undefined,undefined' because 'unsupported service 'undefined'' (-1 clients) -[_onLocalClose] - -not v1 (or data is corrupt) -[error] closing ',undefined,undefined' because 'unsupported service 'undefined'' (-1 clients) -[_onLocalClose] - -not v1 (or data is corrupt) -``` -Above is an attempt to connection... -``` -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. +Looks like it aborts for some reaon. I have this problem on on a new installation as well. -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 diff --git a/rvpn/genericlistener/conn_wedge.go b/rvpn/genericlistener/conn_wedge.go index 686896f..c0e7452 100644 --- a/rvpn/genericlistener/conn_wedge.go +++ b/rvpn/genericlistener/conn_wedge.go @@ -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 func (w *WedgeConn) Peek(n int) ([]byte, error) { loginfo.Println("Peek") - loginfo.Println("buffered=", w.reader.Buffered()) return w.reader.Peek(n) - } //Read -- A normal reader. func (w *WedgeConn) Read(p []byte) (int, error) { - loginfo.Println("read", w.Conn) + loginfo.Println("Read") cnt, err := w.reader.Read(p) return cnt, err } diff --git a/rvpn/genericlistener/listener_generic.go b/rvpn/genericlistener/listener_generic.go index c35a835..97f2d30 100644 --- a/rvpn/genericlistener/listener_generic.go +++ b/rvpn/genericlistener/listener_generic.go @@ -5,8 +5,12 @@ import ( "context" "crypto/tls" "encoding/hex" + "encoding/json" + "fmt" "log" "net" + "strconv" + "strings" "time" jwt "github.com/dgrijalva/jwt-go" @@ -17,15 +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 const ( - ctxSecretKey contextKey = "secretKey" - ctxConnectionTable contextKey = "connectionTable" - ctxConfig contextKey = "config" + ctxSecretKey contextKey = "secretKey" + ctxConnectionTable contextKey = "connectionTable" + ctxConfig contextKey = "config" + ctxDeadTime contextKey = "deadtime" + ctxListenerRegistration contextKey = "listenerRegistration" ) const ( @@ -42,14 +50,13 @@ const ( // - 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}} +func GenericListenAndServe(ctx context.Context, listenerRegistration *ListenerRegistration) { - ctx = context.WithValue(ctx, ctxSecretKey, secretKey) - ctx = context.WithValue(ctx, ctxConnectionTable, connectionTable) - ctx = context.WithValue(ctx, ctxConfig, config) + loginfo.Println(":" + string(listenerRegistration.port)) + + listenAddr, err := net.ResolveTCPAddr("tcp", ":"+strconv.Itoa(listenerRegistration.port)) + deadTime := ctx.Value(ctxDeadTime).(int) - listenAddr, err := net.ResolveTCPAddr("tcp", serverBinding) if nil != err { loginfo.Println(err) return @@ -58,9 +65,15 @@ func GenericListenAndServe(ctx context.Context, connectionTable *connection.Tabl 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 + for { select { case <-ctx.Done(): @@ -188,9 +201,7 @@ func handleStream(ctx context.Context, wConn *WedgeConn) { return } - loginfo.Println(r) - - //now we have a request. Check to see if it is one of our own + // do we have a valid wss_client? secretKey := ctx.Value(ctxSecretKey).(string) tokenString := r.URL.Query().Get("access_token") 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") oneConn := &oneConnListener{wConn} 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 { - 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:])) } +//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, "Welcome..press Servers to access stats") + }) + + 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 - // - expecting an existing oneConnListener with a qualified wss client connected. // - 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) domains, ok := claims["domains"].([]interface{}) + loginfo.Println("domains", domains) + var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, @@ -283,5 +433,4 @@ func handleWssClient(ctx context.Context, oneConn *oneConnListener) { loginfo.Println("Cancel signal hit") return } - } diff --git a/rvpn/genericlistener/manager.go b/rvpn/genericlistener/manager.go new file mode 100644 index 0000000..f71d6d7 --- /dev/null +++ b/rvpn/genericlistener/manager.go @@ -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 +} diff --git a/rvpn/rvpnmain/run.go b/rvpn/rvpnmain/run.go index 47fe43d..ccaf437 100644 --- a/rvpn/rvpnmain/run.go +++ b/rvpn/rvpnmain/run.go @@ -70,9 +70,12 @@ func Run() { // - if tls, establish, protocol peek buffer, else decrypted // - 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() time.Sleep(60 * time.Second)