telebit/relay/relay.go

126 lines
3.2 KiB
Go

package relay
import (
"context"
"crypto/tls"
"log"
"net"
"net/http"
"git.coolaj86.com/coolaj86/go-telebitd/relay/admin"
"git.coolaj86.com/coolaj86/go-telebitd/relay/api"
"git.coolaj86.com/coolaj86/go-telebitd/relay/mplexy"
"git.coolaj86.com/coolaj86/go-telebitd/relay/tunnel"
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
)
// Relay is probably a layer that doesn't need to exist
type Relay struct {
ctx context.Context
status *api.Status
mx *mplexy.MPlexy
table *api.Table
}
// New initializes and returns a relay service
func New(ctx context.Context, tlsConfig *tls.Config, authz mplexy.Authorizer, status *api.Status, table *api.Table) *Relay {
// TODO do we need this already setup here? or is it just for logging?
status.ConnectionTracking = api.NewTracking()
status.ConnectionTable = table
authAdmin := authz
r := &Relay{
ctx: ctx,
status: status,
table: table,
mx: mplexy.New(ctx, tlsConfig, authAdmin, authz, status), // TODO Accept
}
return r
}
// ListenAndServe sets up all of the tcp, http, https, and tunnel servers
func (r *Relay) ListenAndServe(port int) error {
serverStatus := r.status
// Setup for GenericListenServe.
// - establish context for the generic listener
// - startup listener
// - accept with peek buffer.
// - peek at the 1st 30 bytes.
// - check for tls
// - if tls, establish, protocol peek buffer, else decrypted
// - match protocol
go r.status.ConnectionTracking.Run(r.ctx)
go serverStatus.ConnectionTable.Run(r.ctx)
//serverStatus.GenericListeners = genericListeners
// blocks until it can listen, which it can't until started
go r.mx.MultiListenAndServe(port)
// funnel target devices into WebSocket pool
tunnelListener := tunnel.NewListener()
r.mx.AcceptTargetServer = func(conn net.Conn) {
tunnelListener.Feed(conn)
}
go listenAndServeTargets(r.mx, tunnelListener)
// funnel admin clients to API
adminListener := tunnel.NewListener()
r.mx.AcceptAdminClient = func(conn net.Conn) {
adminListener.Feed(conn)
}
go admin.ListenAndServe(r.mx, adminListener)
return r.mx.Run()
}
func listenAndServeTargets(mx *mplexy.MPlexy, listener net.Listener) error {
serverStatus := mx.Status
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
log.Println("HandleFunc /")
switch url := r.URL.Path; url {
case "/":
log.Println("websocket opening ", r.RemoteAddr, " ", r.Host)
authz, err := mx.AuthorizeTarget(r)
var upgrader = websocket.Upgrader{
ReadBufferSize: 65535,
WriteBufferSize: 65535,
}
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("WebSocket upgrade failed", err)
return
}
log.Println("before connection table")
serverName := authz.Domains[0]
newRegistration := api.NewRegistration(conn, r.RemoteAddr, authz.Domains, serverStatus.ConnectionTracking, serverName)
serverStatus.WSSConnectionRegister(newRegistration)
if ok := <-newRegistration.CommCh(); !ok {
log.Println("connection registration failed ", newRegistration)
return
}
log.Println("connection registration accepted ", newRegistration)
}
})
// TODO setup for http/2
s := &http.Server{
Addr: ":80",
Handler: router,
}
return s.Serve(listener)
}