Skip to content

Commit

Permalink
ngrok.ListenAndForward and replace github.com/cakturk/go-netstat
Browse files Browse the repository at this point in the history
  • Loading branch information
abakum committed Nov 7, 2023
1 parent b434192 commit bef23a6
Show file tree
Hide file tree
Showing 6 changed files with 406 additions and 60 deletions.
248 changes: 248 additions & 0 deletions ListenAndForward.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
// try test ngrok.ListenAndForward
package main

import (
"bytes"
"context"
"crypto/tls"
"errors"
"fmt"
"io"
"net"
"net/http"
"net/url"
"strings"
"sync"

"github.com/inconshreveable/log15"
"golang.ngrok.com/ngrok"
"golang.ngrok.com/ngrok/config"
"golang.org/x/sync/errgroup"
)

// ListenAndForward creates a new [Forwarder] after connecting a new [Session], and
// then forwards all connections to the provided URL.
// This is a shortcut for calling [Connect] then [Session].ListenAndForward.
//
// Access to the underlying [Session] that was started automatically can be
// accessed via [Forwarder].Session.
//
// If an error is encountered during [Session].ListenAndForward, the [Session]
// object that was created will be closed automatically.
func ListenAndForward(ctx context.Context, backend *url.URL, tunnelConfig config.Tunnel, connectOpts ...ngrok.ConnectOption) (ngrok.Forwarder, error) {
sess, err := ngrok.Connect(ctx, connectOpts...)
if err != nil {
return nil, err
}
fwd, err := ListenAndForwardSession(ctx, backend, tunnelConfig, sess)
if err != nil {
_ = sess.Close()
return nil, err
}

return fwd, nil
}

func forwardTunnel(ctx context.Context, tun ngrok.Tunnel, url *url.URL) ngrok.Forwarder {
mainGroup, ctx := errgroup.WithContext(ctx)
fwdTasks := &sync.WaitGroup{}

// sess := tun.Session()
// sessImpl := sess.(*sessionImpl)
// logger := sessImpl.inner().Logger.New("task", "forward", "toUrl", url, "tunnelUrl", tun.URL())
ltf.Println("task", "forward", "toUrl", url, "tunnelUrl", tun.URL())

mainGroup.Go(func() error {
for {
if ctxErr := ctx.Err(); ctxErr != nil {
return ctxErr
}

conn, err := tun.Accept()
if err != nil {
return err
}
fwdTasks.Add(1)

go func() {
ngrokConn := conn.(ngrok.Conn)
// defer ngrokConn.Close()

backend, err := openBackend(ctx, nil, tun, ngrokConn, url)
if err != nil {
defer ngrokConn.Close()
// logger.Warn("failed to connect to backend url", "error", err)
ltf.Println("failed to connect to backend url", "error", err)
fwdTasks.Done()
return
}

// defer backend.Close()
join(ctx, ngrokConn, backend)
fwdTasks.Done()
}()
}
})

return &forwarder{
Tunnel: tun,
mainGroup: mainGroup,
}
}

// TODO: use an actual reverse proxy for http/s tunnels so that the host header gets set?
func openBackend(ctx context.Context, logger log15.Logger, tun ngrok.Tunnel, tunnelConn ngrok.Conn, url *url.URL) (net.Conn, error) {
host := url.Hostname()
port := url.Port()
if port == "" {
switch {
case usesTLS(url.Scheme):
port = "443"
case isHTTP(url.Scheme):
port = "80"
default:
return nil, fmt.Errorf("no default tcp port available for %s", url.Scheme)
}
// logger.Debug("set default port", "port", port)
ltf.Println("set default port", "port", port)
}

// Create TLS config if necessary
var tlsConfig *tls.Config
if usesTLS(url.Scheme) {
tlsConfig = &tls.Config{
ServerName: url.Hostname(),
Renegotiation: tls.RenegotiateOnceAsClient,
}
}

dialer := &net.Dialer{}
address := fmt.Sprintf("%s:%s", host, port)
// logger.Debug("dial backend tcp", "address", address)
ltf.Println("dial backend tcp", "address", address)

conn, err := dialer.DialContext(ctx, "tcp", address)
if err != nil {
defer tunnelConn.Close()

if isHTTP(tunnelConn.Proto()) {
_ = writeHTTPError(tunnelConn, err)
}
return nil, err
}

if usesTLS(url.Scheme) && !tunnelConn.PassthroughTLS() {
// logger.Debug("establishing TLS connection with backend")
ltf.Println("establishing TLS connection with backend")

return tls.Client(conn, tlsConfig), nil
}

return conn, nil
}

func usesTLS(scheme string) bool {
switch strings.ToLower(scheme) {
case "https", "tls":
return true
default:
return false
}
}

func isHTTP(scheme string) bool {
switch strings.ToLower(scheme) {
case "https", "http":
return true
default:
return false
}
}

func writeHTTPError(w io.Writer, err error) error {
resp := &http.Response{}
resp.StatusCode = http.StatusBadGateway
resp.Body = io.NopCloser(bytes.NewBufferString(fmt.Sprintf("failed to connect to backend: %s", err.Error())))
return resp.Write(w)
}

type forwarder struct {
ngrok.Tunnel
mainGroup *errgroup.Group
}

func (fwd *forwarder) Wait() error {
return fwd.mainGroup.Wait()
}

type BindExtra struct {
Token string
IPPolicyRef string
Metadata string
}

type tunnelConfigPrivate interface {
ForwardsTo() string
Extra() BindExtra
Proto() string
Opts() any
Labels() map[string]string
WithForwardsTo(string)
}

// %GOMODCACHE%\golang.ngrok.com\ngrok@v1.5.1\session.go
// c:\Users\kga\go\pkg\mod\golang.ngrok.com\ngrok@v1.5.1\session.go
// C:\Users\user\go\pkg\mod\golang.ngrok.com\ngrok@v1.5.1\session.go
func ListenAndForwardSession(ctx context.Context, url *url.URL, cfg config.Tunnel, s ngrok.Session) (ngrok.Forwarder, error) {
tunnelCfg, ok := cfg.(tunnelConfigPrivate)
if !ok {
return nil, errors.New("invalid tunnel config")
}

// Set 'Forwards To'
if tunnelCfg.ForwardsTo() == "" {
tunnelCfg.WithForwardsTo(url.Host)
}

tun, err := s.Listen(ctx, cfg)
if err != nil {
return nil, err
}

return forwardTunnel(ctx, tun, url), nil
}

// %GOMODCACHE%\golang.ngrok.com\ngrok@v1.5.1\forward.go
// c:\Users\kga\go\pkg\mod\golang.ngrok.com\ngrok@v1.5.1\forward.go
// C:\Users\user\go\pkg\mod\golang.ngrok.com\ngrok@v1.5.1\forward.go
func join_(ctx context.Context, left, right net.Conn) {
g := &sync.WaitGroup{}
g.Add(2)
go func() {
_, _ = io.Copy(left, right)
left.Close()
g.Done()
}()
go func() {
_, _ = io.Copy(right, left)
right.Close()
g.Done()
}()
g.Wait()
}

func join(ctx context.Context, left, right net.Conn) {
g, _ := errgroup.WithContext(ctx) // when ctx is canceled (on WithStopHandler or WithDisconnectHandler ) interrupts both io.Copy
g.Go(func() error {
_, err := io.Copy(left, right)
left.Close() // on left disconnection interrupts io.Copy(right, left)
return err
})
g.Go(func() error {
_, err := io.Copy(right, left)
right.Close() // on right disconnection interrupts io.Copy(left, right)
return err
})
g.Wait()

}
23 changes: 11 additions & 12 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,29 @@ module github.com/abakum/ngrokVNC

go 1.20

require github.com/ngrok/ngrok-api-go/v5 v5.0.0
require github.com/ngrok/ngrok-api-go/v5 v5.1.0

require (
github.com/go-stack/stack v1.8.1 // indirect
github.com/inconshreveable/log15 v3.0.0-testing.3+incompatible // indirect
github.com/inconshreveable/log15/v3 v3.0.0-testing.5 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/stretchr/testify v1.8.4 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.ngrok.com/muxado v1.0.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/term v0.8.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
golang.ngrok.com/muxado/v2 v2.0.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/term v0.13.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
)

require (
github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5
github.com/abakum/go-netstat v0.0.0-20231106075911-001f10558dcf
github.com/inconshreveable/log15 v3.0.0-testing.5+incompatible
github.com/mitchellh/go-ps v1.0.0
github.com/xlab/closer v1.1.0
github.com/zzl/go-win32api/v2 v2.1.0
golang.ngrok.com/ngrok v1.3.0
golang.org/x/sync v0.3.0
golang.org/x/sys v0.8.0
golang.ngrok.com/ngrok v1.5.1
golang.org/x/sync v0.5.0
golang.org/x/sys v0.14.0
gopkg.in/ini.v1 v1.67.0
)
50 changes: 25 additions & 25 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,55 +1,55 @@
github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5 h1:BjkPE3785EwPhhyuFkbINB+2a1xATwk8SNDWnJiD41g=
github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5/go.mod h1:jtAfVaU/2cu1+wdSRPWE2c1N2qeAA3K4RH9pYgqwets=
github.com/abakum/go-netstat v0.0.0-20231106075911-001f10558dcf h1:U6bESAU3E0/IMcThHn2Z+tAEgsDVn2K/+6eT6dd2l8k=
github.com/abakum/go-netstat v0.0.0-20231106075911-001f10558dcf/go.mod h1:VDmPFexMx7WdPUz+8/esyAIL4VIT13CSUmbQQZV420c=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/inconshreveable/log15 v3.0.0-testing.3+incompatible h1:zaX5fYT98jX5j4UhO/WbfY8T1HkgVrydiDMC9PWqGCo=
github.com/inconshreveable/log15 v3.0.0-testing.3+incompatible/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
github.com/inconshreveable/log15 v3.0.0-testing.5+incompatible h1:VryeOTiaZfAzwx8xBcID1KlJCeoWSIpsNbSk+/D2LNk=
github.com/inconshreveable/log15 v3.0.0-testing.5+incompatible/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
github.com/inconshreveable/log15/v3 v3.0.0-testing.5 h1:h4e0f3kjgg+RJBlKOabrohjHe47D3bbAB9BgMrc3DYA=
github.com/inconshreveable/log15/v3 v3.0.0-testing.5/go.mod h1:3GQg1SVrLoWGfRv/kAZMsdyU5cp8eFc1P3cw+Wwku94=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
github.com/ngrok/ngrok-api-go/v5 v5.0.0 h1:eksowVztKNQU0JBaYS2hXGiC/xtGXj8LAx8lAuzYlsw=
github.com/ngrok/ngrok-api-go/v5 v5.0.0/go.mod h1:cxMRsWuE0EwK/JB/5prvHK0LEWB3KP16iwvIMqvDVP0=
github.com/ngrok/ngrok-api-go/v5 v5.1.0 h1:GUYNFB/uvxYACNwnhWEi8mZcUCGuSsZ8/gB1yWPqHlY=
github.com/ngrok/ngrok-api-go/v5 v5.1.0/go.mod h1:UVTaHI5B4gEsfHCOZTlRg8WkT6+KBijIkVtjpDqCyIU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/xlab/closer v1.1.0 h1:yrDiOXjd/B7pZ3lZkl/EZ1gWrR2M2N5XpBnixynm4mc=
github.com/xlab/closer v1.1.0/go.mod h1:Ff8YcUPbn5jju6nClrMCmJHQABM0S/obEK0za/1yVMk=
github.com/zzl/go-win32api/v2 v2.1.0 h1:BwXsEl3iycRhG4lpe3JsnNACyQH/U7RTBhJwyCaIB/g=
github.com/zzl/go-win32api/v2 v2.1.0/go.mod h1:doi6ewHPdh9tDmqe837Ro7IwqtB9yE+1fC8suK/Ssj0=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.ngrok.com/muxado v1.0.0 h1:r4BOkKR/7B6JpPvWzsUJ7E7OEPDSwNku/NW3jFuU0QQ=
golang.ngrok.com/muxado v1.0.0/go.mod h1:ttzTgo3kIxNRpoJdle7jgSvPo7mqY+RtuZKM3iObkF0=
golang.ngrok.com/ngrok v1.3.0 h1:oQ7EvMOAgjTHiyksfZ0efbVcFtaP9/pGTS7LMLeqAsM=
golang.ngrok.com/ngrok v1.3.0/go.mod h1:kzmBMe8oFB3x6mmJDfT6QYpair9bRDgAdiaNU468ehQ=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.ngrok.com/muxado/v2 v2.0.0 h1:bu9eIDhRdYNtIXNnqat/HyMeHYOAbUH55ebD7gTvW6c=
golang.ngrok.com/muxado/v2 v2.0.0/go.mod h1:wzxJYX4xiAtmwumzL+QsukVwFRXmPNv86vB8RPpOxyM=
golang.ngrok.com/ngrok v1.5.1 h1:L4HTk81UIzkXatVdslkfW11XdeureKRHcg97hGUWahU=
golang.ngrok.com/ngrok v1.5.1/go.mod h1:ruVcXZ7Rre5O9oeqqa8uZCB3Xtkt2PoyjF3eW9b7t6A=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
Loading

0 comments on commit bef23a6

Please sign in to comment.