telebit/relay/relay.go

126 lines
3.2 KiB
Go
Raw Normal View History

2020-04-30 10:43:36 +00:00
package relay
import (
"context"
"crypto/tls"
2020-05-01 05:47:46 +00:00
"log"
"net"
"net/http"
2020-04-30 10:43:36 +00:00
2020-05-01 05:47:46 +00:00
"git.coolaj86.com/coolaj86/go-telebitd/relay/admin"
"git.coolaj86.com/coolaj86/go-telebitd/relay/api"
2020-05-01 06:12:16 +00:00
"git.coolaj86.com/coolaj86/go-telebitd/relay/mplexy"
2020-05-01 07:06:14 +00:00
"git.coolaj86.com/coolaj86/go-telebitd/relay/tunnel"
2020-05-01 05:47:46 +00:00
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
2020-04-30 10:43:36 +00:00
)
2020-05-01 05:47:46 +00:00
// Relay is probably a layer that doesn't need to exist
2020-04-30 10:43:36 +00:00
type Relay struct {
ctx context.Context
2020-05-01 05:47:46 +00:00
status *api.Status
2020-05-01 06:12:16 +00:00
mx *mplexy.MPlexy
2020-05-01 05:47:46 +00:00
table *api.Table
2020-04-30 10:43:36 +00:00
}
2020-05-01 05:47:46 +00:00
// New initializes and returns a relay service
2020-05-01 06:12:16 +00:00
func New(ctx context.Context, tlsConfig *tls.Config, authz mplexy.Authorizer, status *api.Status, table *api.Table) *Relay {
2020-05-01 05:47:46 +00:00
// 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{
2020-04-30 10:43:36 +00:00
ctx: ctx,
status: status,
table: table,
2020-05-01 06:12:16 +00:00
mx: mplexy.New(ctx, tlsConfig, authAdmin, authz, status), // TODO Accept
2020-04-30 10:43:36 +00:00
}
2020-05-01 05:47:46 +00:00
return r
2020-04-30 10:43:36 +00:00
}
2020-05-01 05:47:46 +00:00
// ListenAndServe sets up all of the tcp, http, https, and tunnel servers
2020-04-30 10:43:36 +00:00
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
2020-05-01 05:47:46 +00:00
go r.status.ConnectionTracking.Run(r.ctx)
2020-04-30 10:43:36 +00:00
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)
2020-05-01 05:47:46 +00:00
// 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)
2020-04-30 10:43:36 +00:00
return r.mx.Run()
}
2020-05-01 05:47:46 +00:00
2020-05-01 07:06:14 +00:00
func listenAndServeTargets(mx *mplexy.MPlexy, listener net.Listener) error {
2020-05-01 05:47:46 +00:00
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,
}
2020-05-01 07:06:14 +00:00
return s.Serve(listener)
2020-05-01 05:47:46 +00:00
}