it works
This commit is contained in:
parent
65452a4413
commit
dd69e1b432
|
@ -0,0 +1,77 @@
|
||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/yamux"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Handle(ctx context.Context, agentURL, agentToken string, h http.Handler) error {
|
||||||
|
u, err := url.Parse(agentURL)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error parsing URL: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Scheme != "iconia" {
|
||||||
|
return fmt.Errorf("wanted scheme %s, got: %s", "iconia", u.Scheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Path == "" {
|
||||||
|
return fmt.Errorf("put the domain you want to forward in the path")
|
||||||
|
}
|
||||||
|
|
||||||
|
tc := &tls.Config{
|
||||||
|
ServerName: u.Path[1:],
|
||||||
|
InsecureSkipVerify: true, // TODO(Cadey): FIX THIS OMG
|
||||||
|
NextProtos: []string{agentToken},
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("dialing", u.Host)
|
||||||
|
|
||||||
|
conn, err := tls.Dial("tcp", u.Host, tc)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error dialing remote host %s: %w", u.Host, err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
log.Println("connection established")
|
||||||
|
|
||||||
|
sesh, err := yamux.Client(conn, &yamux.Config{
|
||||||
|
AcceptBacklog: 1,
|
||||||
|
EnableKeepAlive: true,
|
||||||
|
KeepAliveInterval: time.Minute,
|
||||||
|
ConnectionWriteTimeout: 100 * time.Millisecond,
|
||||||
|
MaxStreamWindowSize: 262144 * 16,
|
||||||
|
Logger: log.New(os.Stderr, u.Path+"(yamux): ", log.LstdFlags),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error connecting to iconia: %w", err)
|
||||||
|
}
|
||||||
|
defer sesh.Close()
|
||||||
|
|
||||||
|
s := &http.Server{
|
||||||
|
Handler: h,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("listening for traffic from iconia")
|
||||||
|
|
||||||
|
err = s.Serve(sesh)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error serving http: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
s.Shutdown(context.Background())
|
||||||
|
sesh.GoAway()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"expvar"
|
||||||
|
"flag"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"tulpa.dev/cadey/iconia/agent"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
agentURL = flag.String("agent-url", "iconia://127.0.0.1:3045/test.local.cetacean.club", "url of iconia server")
|
||||||
|
agentToken = flag.String("agent-token", "95FD1C09-47E0-438E-B1AA-40CF41E1CD01", "token to use for iconia")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
hitCounter := expvar.NewInt("hits")
|
||||||
|
|
||||||
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
http.Error(w, "Hello world!", http.StatusOK)
|
||||||
|
hitCounter.Add(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
log.Fatal(agent.Handle(ctx, *agentURL, *agentToken, http.DefaultServeMux))
|
||||||
|
}
|
2
go.mod
2
go.mod
|
@ -7,10 +7,12 @@ require (
|
||||||
github.com/facebookgo/flagenv v0.0.0-20160425205200-fcd59fca7456 // indirect
|
github.com/facebookgo/flagenv v0.0.0-20160425205200-fcd59fca7456 // indirect
|
||||||
github.com/golang/protobuf v1.3.2
|
github.com/golang/protobuf v1.3.2
|
||||||
github.com/hashicorp/yamux v0.0.0-20190923154419-df201c70410d
|
github.com/hashicorp/yamux v0.0.0-20190923154419-df201c70410d
|
||||||
|
github.com/kr/pretty v0.1.0
|
||||||
github.com/smartystreets/goconvey v1.6.4 // indirect
|
github.com/smartystreets/goconvey v1.6.4 // indirect
|
||||||
github.com/twitchtv/twirp v5.8.0+incompatible
|
github.com/twitchtv/twirp v5.8.0+incompatible
|
||||||
go.chromium.org/luci v0.0.0-20191128012655-94aa8d72b78e
|
go.chromium.org/luci v0.0.0-20191128012655-94aa8d72b78e
|
||||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392
|
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392
|
||||||
within.website/confyg v0.4.0
|
within.website/confyg v0.4.0
|
||||||
|
within.website/ln v0.7.0
|
||||||
within.website/x v1.2.0
|
within.website/x v1.2.0
|
||||||
)
|
)
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -57,8 +57,10 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||||
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||||
github.com/klauspost/reedsolomon v1.9.2/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
|
github.com/klauspost/reedsolomon v1.9.2/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
host-token (
|
host-token (
|
||||||
test=95FD1C09-47E0-438E-B1AA-40CF41E1CD01
|
test.local.cetacean.club=95FD1C09-47E0-438E-B1AA-40CF41E1CD01
|
||||||
)
|
)
|
||||||
|
|
91
main.go
91
main.go
|
@ -1,16 +1,23 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"flag"
|
"flag"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/facebookarchive/flagenv"
|
"github.com/facebookarchive/flagenv"
|
||||||
"github.com/hashicorp/yamux"
|
"github.com/hashicorp/yamux"
|
||||||
"go.chromium.org/luci/common/flag/stringmapflag"
|
"go.chromium.org/luci/common/flag/stringmapflag"
|
||||||
"golang.org/x/crypto/acme/autocert"
|
"golang.org/x/crypto/acme/autocert"
|
||||||
"within.website/confyg/flagconfyg"
|
"within.website/confyg/flagconfyg"
|
||||||
|
"within.website/ln"
|
||||||
"within.website/x/localca"
|
"within.website/x/localca"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -51,16 +58,96 @@ func main() {
|
||||||
DomainSuffix: *domainSuffix,
|
DomainSuffix: *domainSuffix,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
ctx = ln.WithF(ctx, cfg.F())
|
||||||
|
|
||||||
|
certManager, err := localca.New(*certFile, *keyFile, *domainSuffix, autocert.DirCache(*certFolder))
|
||||||
|
if err != nil {
|
||||||
|
ln.FatalErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
httpsTc := &tls.Config{
|
||||||
|
GetCertificate: certManager.GetCertificate,
|
||||||
|
}
|
||||||
|
|
||||||
|
httpsListener, err := tls.Listen("tcp", ":"+*httpsPort, httpsTc)
|
||||||
|
if err != nil {
|
||||||
|
ln.FatalErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
s := &Server{
|
s := &Server{
|
||||||
Config: cfg,
|
Config: cfg,
|
||||||
|
|
||||||
clients: map[string][]*yamux.Session{},
|
clients: map[string][]*yamux.Session{},
|
||||||
clientsLock: &sync.RWMutex{},
|
clientsLock: &sync.RWMutex{},
|
||||||
|
|
||||||
certManager: localca.New(*certFile, *keyFile, *domainSuffix, autocert.DirCache(*certFolder)),
|
tokenInfo: map[string]string(*hostsToTokens),
|
||||||
|
tokensLock: &sync.Mutex{},
|
||||||
|
|
||||||
|
certManager: certManager,
|
||||||
|
|
||||||
|
tlsListener: httpsListener,
|
||||||
|
|
||||||
plainServer: &http.Server{
|
plainServer: &http.Server{
|
||||||
Addr: ":" + *httpPort,
|
Addr: ":" + *httpPort,
|
||||||
|
Handler: http.HandlerFunc(insecureRedirect),
|
||||||
|
},
|
||||||
|
|
||||||
|
statusServer: &http.Server{
|
||||||
|
Addr: ":" + *statusPort,
|
||||||
|
Handler: http.DefaultServeMux,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
_ = s
|
||||||
|
|
||||||
|
yamuxTc := &tls.Config{
|
||||||
|
GetCertificate: certManager.GetCertificate,
|
||||||
|
GetConfigForClient: s.handleYamuxClientHello,
|
||||||
|
}
|
||||||
|
|
||||||
|
yamuxListener, err := tls.Listen("tcp", ":"+*yamuxPort, yamuxTc)
|
||||||
|
if err != nil {
|
||||||
|
ln.FatalErr(ctx, err)
|
||||||
|
}
|
||||||
|
s.yamuxListener = yamuxListener
|
||||||
|
|
||||||
|
ln.Log(ctx, ln.Info("now listening for traffic"))
|
||||||
|
|
||||||
|
go func() { ln.FatalErr(ctx, s.plainServer.ListenAndServe()) }()
|
||||||
|
go func() { ln.FatalErr(ctx, s.statusServer.ListenAndServe()) }()
|
||||||
|
go func() { ln.FatalErr(ctx, s.tlsForward(httpsListener)) }()
|
||||||
|
go func() { ln.FatalErr(ctx, s.yamuxHandler(yamuxListener)) }()
|
||||||
|
|
||||||
|
sigs := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
<-sigs
|
||||||
|
ln.Log(ctx, ln.Info("got SIGINT/SIGTERM, dying"))
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
s.yamuxListener.Close()
|
||||||
|
s.tlsListener.Close()
|
||||||
|
s.plainServer.Shutdown(ctx)
|
||||||
|
s.statusServer.Shutdown(ctx)
|
||||||
|
s.goAwayClients()
|
||||||
|
|
||||||
|
time.Sleep(4 * time.Minute)
|
||||||
|
}
|
||||||
|
|
||||||
|
// insecureRedirect redirects a client to https if they connect over plain HTTP.
|
||||||
|
func insecureRedirect(w http.ResponseWriter, r *http.Request) {
|
||||||
|
switch r.Method {
|
||||||
|
case http.MethodPatch, http.MethodPut, http.MethodPost:
|
||||||
|
http.Error(w, "use https", http.StatusNotAcceptable)
|
||||||
|
ln.Log(r.Context(), ln.Action("cannot redirect (wrong method)"), ln.F{"remote": r.RemoteAddr, "host": r.Host, "path": r.URL.Path})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.URL.Host = r.Host
|
||||||
|
r.URL.Scheme = "https"
|
||||||
|
|
||||||
|
ln.Log(r.Context(), ln.Action("redirecting insecure HTTP to HTTPS"), ln.F{"remote": r.RemoteAddr, "host": r.Host, "path": r.URL.Path})
|
||||||
|
|
||||||
|
http.Redirect(w, r, r.URL.String(), http.StatusPermanentRedirect)
|
||||||
}
|
}
|
||||||
|
|
200
server.go
200
server.go
|
@ -1,23 +1,42 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/yamux"
|
"github.com/hashicorp/yamux"
|
||||||
|
"within.website/ln"
|
||||||
"within.website/x/localca"
|
"within.website/x/localca"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config uration for the server
|
// Config uration for the server.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
HTTPPort, HTTPSPort, YamuxPort, StatusPort, DomainSuffix string
|
HTTPPort, HTTPSPort, YamuxPort, StatusPort, DomainSuffix string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server is the iconia gateway server
|
// F ields for logging.
|
||||||
|
func (c Config) F() ln.F {
|
||||||
|
return ln.F{
|
||||||
|
"http-port": c.HTTPPort,
|
||||||
|
"https-port": c.HTTPSPort,
|
||||||
|
"yamux-port": c.YamuxPort,
|
||||||
|
"status-port": c.StatusPort,
|
||||||
|
"domain-suffix": c.DomainSuffix,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server is the iconia gateway server.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Config
|
Config
|
||||||
|
|
||||||
|
@ -33,13 +52,24 @@ type Server struct {
|
||||||
tokensLock *sync.Mutex
|
tokensLock *sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) goAwayClients() {
|
||||||
|
s.clientsLock.Lock()
|
||||||
|
defer s.clientsLock.Unlock()
|
||||||
|
|
||||||
|
for _, set := range s.clients {
|
||||||
|
for _, sesh := range set {
|
||||||
|
sesh.GoAway()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) handleYamuxClientHello(chi *tls.ClientHelloInfo) (*tls.Config, error) {
|
func (s *Server) handleYamuxClientHello(chi *tls.ClientHelloInfo) (*tls.Config, error) {
|
||||||
var found bool
|
var found bool
|
||||||
s.tokensLock.Lock()
|
s.tokensLock.Lock()
|
||||||
var token = s.tokenInfo[strings.Split(chi.ServerName, s.Config.DomainSuffix)[0]]
|
var token = s.tokenInfo[chi.ServerName]
|
||||||
s.tokensLock.Unlock()
|
s.tokensLock.Unlock()
|
||||||
|
|
||||||
for _, proto := range chi.NextProtos {
|
for _, proto := range chi.SupportedProtos {
|
||||||
if proto == token {
|
if proto == token {
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
|
@ -50,5 +80,163 @@ func (s *Server) handleYamuxClientHello(chi *tls.ClientHelloInfo) (*tls.Config,
|
||||||
return nil, fmt.Errorf("unknown token for domain %s", chi.ServerName)
|
return nil, fmt.Errorf("unknown token for domain %s", chi.ServerName)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
tc := &tls.Config{
|
||||||
|
GetCertificate: s.certManager.GetCertificate,
|
||||||
|
NextProtos: []string{token},
|
||||||
|
ServerName: chi.ServerName,
|
||||||
|
}
|
||||||
|
|
||||||
|
return tc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func gen502Page(host, why string) *http.Response {
|
||||||
|
template := `<html><head><title>${WHY}</title></head><body><h1>${WHY}</h1><p>Please ensure a backend is running for ${HOST}.</p></body></html>`
|
||||||
|
|
||||||
|
resbody := []byte(os.Expand(template, func(in string) string {
|
||||||
|
switch in {
|
||||||
|
case "HOST":
|
||||||
|
return host
|
||||||
|
case "WHY":
|
||||||
|
return why
|
||||||
|
}
|
||||||
|
|
||||||
|
return "<unknown>"
|
||||||
|
}))
|
||||||
|
reshdr := http.Header{}
|
||||||
|
reshdr.Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
|
||||||
|
resp := &http.Response{
|
||||||
|
Status: fmt.Sprintf("%d Bad Gateway", http.StatusBadGateway),
|
||||||
|
StatusCode: http.StatusBadGateway,
|
||||||
|
Body: ioutil.NopCloser(bytes.NewBuffer(resbody)),
|
||||||
|
|
||||||
|
Proto: "HTTP/1.1",
|
||||||
|
ProtoMajor: 1,
|
||||||
|
ProtoMinor: 1,
|
||||||
|
Header: reshdr,
|
||||||
|
ContentLength: int64(len(resbody)),
|
||||||
|
Close: true,
|
||||||
|
Request: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) yamuxHandler(l net.Listener) error {
|
||||||
|
for {
|
||||||
|
c, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go s.handleYamuxClient(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("unexpected state")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleYamuxClient(c net.Conn) {
|
||||||
|
tlsConn, ok := c.(*tls.Conn)
|
||||||
|
if !ok {
|
||||||
|
panic("no, this should really be impossible")
|
||||||
|
}
|
||||||
|
tlsConn.Handshake()
|
||||||
|
|
||||||
|
sName := tlsConn.ConnectionState().ServerName
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
ctx = ln.WithF(ctx, ln.F{
|
||||||
|
"domain-name": sName,
|
||||||
|
"remote-host": c.RemoteAddr().String(),
|
||||||
|
})
|
||||||
|
|
||||||
|
sesh, err := yamux.Server(c, &yamux.Config{
|
||||||
|
AcceptBacklog: 1,
|
||||||
|
EnableKeepAlive: true,
|
||||||
|
KeepAliveInterval: time.Minute,
|
||||||
|
ConnectionWriteTimeout: 100 * time.Millisecond,
|
||||||
|
MaxStreamWindowSize: 262144 * 16,
|
||||||
|
Logger: log.New(os.Stderr, sName+": ", log.LstdFlags),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ln.Error(ctx, err)
|
||||||
|
c.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.clientsLock.Lock()
|
||||||
|
s.clients[sName] = append(s.clients[sName], sesh)
|
||||||
|
i := len(s.clients[sName]) - 1
|
||||||
|
s.clientsLock.Unlock()
|
||||||
|
|
||||||
|
ln.Log(ctx, ln.Info("agent registered"))
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-sesh.CloseChan()
|
||||||
|
ln.Log(ctx, ln.Info("client closed"))
|
||||||
|
|
||||||
|
s.clientsLock.Lock()
|
||||||
|
s.clients[sName] = append(s.clients[sName][:i], s.clients[sName][i+1:]...)
|
||||||
|
s.clientsLock.Unlock()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) tlsForward(l net.Listener) error {
|
||||||
|
for {
|
||||||
|
c, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error accepting connection: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go s.handleTLSClient(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("unexpected state")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleTLSClient(c net.Conn) {
|
||||||
|
tlsConn, ok := c.(*tls.Conn)
|
||||||
|
if !ok {
|
||||||
|
gen502Page("unknown", "this should be impossible").Write(c)
|
||||||
|
c.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tlsConn.Handshake()
|
||||||
|
|
||||||
|
sName := tlsConn.ConnectionState().ServerName
|
||||||
|
|
||||||
|
s.clientsLock.RLock()
|
||||||
|
set, ok := s.clients[sName]
|
||||||
|
s.clientsLock.RUnlock()
|
||||||
|
|
||||||
|
if !ok || len(set) == 0 {
|
||||||
|
gen502Page(sName, "no backends connected").Write(c)
|
||||||
|
c.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
sesh *yamux.Session
|
||||||
|
stream *yamux.Stream
|
||||||
|
count int
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
retry:
|
||||||
|
sesh = set[rand.Intn(len(set))]
|
||||||
|
|
||||||
|
stream, err = sesh.OpenStream()
|
||||||
|
if err != nil {
|
||||||
|
if count > 3 {
|
||||||
|
gen502Page(sName, "no working session").Write(c)
|
||||||
|
c.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
count++
|
||||||
|
goto retry
|
||||||
|
}
|
||||||
|
|
||||||
|
go io.Copy(c, stream)
|
||||||
|
io.Copy(stream, c)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue