Initial commit with basic version of Ngkrok

This commit is contained in:
Piotr Biernat 2023-05-16 16:28:49 +02:00
commit f730bdbc74
7 changed files with 277 additions and 0 deletions

1
src/.env.dist Normal file
View File

@ -0,0 +1 @@
SERVER_PORT=:4040

18
src/.gitignore vendored Normal file
View File

@ -0,0 +1,18 @@
# ---> Go
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
vendor/
.env

67
src/cmd/client/main.go Normal file
View File

@ -0,0 +1,67 @@
package main
import (
"bufio"
"flag"
"fmt"
"io"
"log"
"net"
"net/http"
"net/http/httputil"
"github.com/progrium/qmux/golang/session"
)
const (
CLIENT_VERSION = "v0.2"
HEADER_CLIENT_TOKEN = "X-Client-App-Token"
HEADER_USER_AGENT = "User-Agent"
)
func main() {
flag.Parse()
srvPort, srvHost, clientToken := "18181", "hop.pbiernat.dev", "ghd7H8H*&6tg8*tg68"
targetPort := flag.Arg(0)
if targetPort == "" {
log.Fatal("Missing port!")
}
conn, err := net.Dial("tcp", net.JoinHostPort(srvHost, srvPort))
fatal("main net.Dial", err)
client := httputil.NewClientConn(conn, bufio.NewReader(conn))
req, err := http.NewRequest("GET", "/register", nil)
fatal("http.NewRequest", err)
req.Host = net.JoinHostPort(srvHost, srvPort)
req.Header.Add(HEADER_CLIENT_TOKEN, clientToken)
req.Header.Add(HEADER_USER_AGENT, "HopClient/"+CLIENT_VERSION)
client.Write(req)
resp, _ := client.Read(req)
fmt.Printf("http port %s available at:\n", targetPort)
fmt.Printf("https://%s\n", resp.Header.Get("X-Public-Host"))
c, _ := client.Hijack()
sess := session.New(c)
defer sess.Close()
for {
ch, err := sess.Accept()
fatal("Accept", err)
conn, err := net.Dial("tcp", "127.0.0.1:"+targetPort)
fatal("net.Dial", err)
go join(conn, ch)
}
}
func join(a io.ReadWriteCloser, b io.ReadWriteCloser) {
go io.Copy(b, a)
io.Copy(a, b)
a.Close()
b.Close()
}
func fatal(title string, err error) {
if err != nil {
log.Fatal(title+": ", err)
}
}

39
src/cmd/server/main.go Normal file
View File

@ -0,0 +1,39 @@
package main
import (
"fmt"
"log"
"net"
"time"
"git.pbiernat.dev/golang/ngkrok/internal/ngkrok"
vhost "github.com/inconshreveable/go-vhost"
)
func main() {
addr, host, port := "::", "hop.pbiernat.dev", "18181"
lst, err := net.Listen("tcp", net.JoinHostPort(addr, port))
if err != nil {
log.Fatal(err)
}
vmux, err := vhost.NewHTTPMuxer(lst, 3*time.Second)
if err != nil {
log.Fatal(err)
}
defer vmux.Close()
srv := ngkrok.NewServer(addr, host, port, vmux)
go srv.Serve()
log.Printf("NgKrok server [%s] ready!\n", host)
for {
conn, err := vmux.NextError()
fmt.Println("vmux error:", err)
if conn != nil {
conn.Close()
}
}
}

13
src/go.mod Normal file
View File

@ -0,0 +1,13 @@
module git.pbiernat.dev/golang/ngkrok
go 1.19
require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/inconshreveable/go-vhost v1.0.0 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/klauspost/compress v1.16.5 // indirect
github.com/progrium/qmux/golang v0.0.0-20210721211401-475935a675d8 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.46.0 // indirect
)

21
src/go.sum Normal file
View File

@ -0,0 +1,21 @@
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/inconshreveable/go-vhost v1.0.0 h1:IK4VZTlXL4l9vz2IZoiSFbYaaqUW7dXJAiPriUN5Ur8=
github.com/inconshreveable/go-vhost v1.0.0/go.mod h1:aA6DnFhALT3zH0y+A39we+zbrdMC2N0X/q21e6FI0LU=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY=
github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/progrium/qmux/golang v0.0.0-20210721211401-475935a675d8 h1:hLnTL/51vGU9IB4yFFExFSQgRlqFh0GHIJxCcOh7LT0=
github.com/progrium/qmux/golang v0.0.0-20210721211401-475935a675d8/go.mod h1:Z2EPtydgPrcZxO50GhkzTGgdWjA5PPHsZkoq6KTxPVE=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.46.0 h1:6ZRhrFg8zBXTRYY6vdzbFhqsBd7FVv123pV2m9V87U4=
github.com/valyala/fasthttp v1.46.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
golang.org/x/net v0.0.0-20210420210106-798c2154c571/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@ -0,0 +1,118 @@
package ngkrok
import (
"context"
"crypto/rand"
"io"
"log"
"net"
"net/http"
"strings"
vhost "github.com/inconshreveable/go-vhost"
"github.com/progrium/qmux/golang/session"
)
const (
SERVER_VERSION = "v0.5"
HEADER_CLIENT_TOKEN = "X-Client-App-Token"
HEADER_PUBLIC_HOST = "X-Public-Host"
)
type Server struct {
addr string
host string
port string
VMux *vhost.HTTPMuxer
}
func NewServer(addr, host, port string, vmux *vhost.HTTPMuxer) *Server {
return &Server{addr, host, port, vmux}
}
func (s *Server) Serve() {
ml, err := s.VMux.Listen(net.JoinHostPort(s.host, s.port))
if err != nil {
log.Fatal(err)
}
srv := &http.Server{}
// http.HandleFunc("/", s.mainHandler)
http.HandleFunc("/register", s.registerHandler)
srv.Serve(ml)
}
// func (s *Server) mainHandler(w http.ResponseWriter, r *http.Request) {
// log.Println("mainHandler")
// return
// }
func (s *Server) registerHandler(w http.ResponseWriter, r *http.Request) {
subdomain := newSubdomain(r) + s.host
publicHost := strings.TrimSuffix(net.JoinHostPort(subdomain, s.port), ":80")
pl, err := s.VMux.Listen(publicHost)
if err != nil {
log.Fatal(err)
}
defer pl.Close()
w.Header().Add(HEADER_PUBLIC_HOST, subdomain)
w.Header().Add("Connection", "close")
w.WriteHeader(http.StatusOK)
conn, _, _ := w.(http.Hijacker).Hijack()
sess := session.New(conn)
defer sess.Close()
log.Printf("%s: start session", subdomain)
go func() {
for {
conn, err := pl.Accept()
if err != nil {
log.Println("Accept:", err)
return
}
defer conn.Close()
ch, err := sess.Open(context.Background())
if err != nil {
log.Println("sess.Open:", err)
return
}
defer ch.Close()
go join(ch, conn)
}
}()
sess.Wait()
log.Printf("%s: end session", subdomain)
}
func join(a io.ReadWriteCloser, b io.ReadWriteCloser) {
go io.Copy(b, a)
io.Copy(a, b)
a.Close()
b.Close()
}
func newSubdomain(r *http.Request) string {
clientToken := r.Header.Get(HEADER_CLIENT_TOKEN)
if clientToken == "ghd7H8H*&6tg8*tg68" { // hardcoded token for dev tests....
return "test."
}
b := make([]byte, 10)
if _, err := rand.Read(b); err != nil {
panic(err)
}
letters := []rune("abcdefghijklmnopqrstuvwxyz1234567890")
rune := make([]rune, 10)
for i := range rune {
rune[i] = letters[int(b[i])*len(letters)/256]
}
return string(rune) + "."
}