2021-10-08 12:39:31 +00:00
|
|
|
// SPDX-FileCopyrightText: 2021 The NGI Pointer Secure-Scuttlebutt Team of 2020/2021
|
|
|
|
//
|
2021-02-09 11:53:33 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
2021-01-25 12:23:03 +00:00
|
|
|
package network
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/gorilla/websocket"
|
2022-11-07 09:18:13 +00:00
|
|
|
"github.com/ssbc/go-muxrpc/v2"
|
2021-05-21 07:27:12 +00:00
|
|
|
kitlog "go.mindeco.de/log"
|
|
|
|
"go.mindeco.de/log/level"
|
2021-01-25 12:23:03 +00:00
|
|
|
)
|
|
|
|
|
2021-04-20 15:13:50 +00:00
|
|
|
// WebsockHandler returns a "middleware" like thing that is able to upgrade a
|
|
|
|
// websocket request to a muxrpc connection and authenticate using shs.
|
2021-04-05 09:33:28 +00:00
|
|
|
// It calls the next handler if it fails to upgrade the connection to websocket.
|
|
|
|
// However, it will error on the request and not call the passed handler
|
|
|
|
// if the websocket upgrade is successfull.
|
|
|
|
func (n *node) WebsockHandler(next http.Handler) http.Handler {
|
2021-01-25 12:23:03 +00:00
|
|
|
var upgrader = websocket.Upgrader{
|
|
|
|
ReadBufferSize: 1024 * 4,
|
|
|
|
WriteBufferSize: 1024 * 4,
|
2021-04-05 09:33:28 +00:00
|
|
|
|
2021-01-25 12:23:03 +00:00
|
|
|
CheckOrigin: func(_ *http.Request) bool {
|
|
|
|
return true
|
|
|
|
},
|
2021-04-05 09:33:28 +00:00
|
|
|
|
2021-04-20 15:13:50 +00:00
|
|
|
// 99% of the traffic will be ciphertext which is impossible to distinguish
|
|
|
|
// from randomness and thus also hard to compress
|
2021-01-25 12:23:03 +00:00
|
|
|
EnableCompression: false,
|
2021-04-05 09:33:28 +00:00
|
|
|
|
|
|
|
// if upgrading fails, just call the next handler and ignore the error
|
|
|
|
Error: func(w http.ResponseWriter, req *http.Request, _ int, _ error) {
|
|
|
|
next.ServeHTTP(w, req)
|
|
|
|
},
|
2021-01-25 12:23:03 +00:00
|
|
|
}
|
|
|
|
|
2021-04-05 09:33:28 +00:00
|
|
|
var wsh websocketHandelr
|
|
|
|
wsh.next = next
|
|
|
|
wsh.upgrader = &upgrader
|
|
|
|
wsh.muxnetwork = n
|
2021-01-25 12:23:03 +00:00
|
|
|
|
2021-04-05 09:33:28 +00:00
|
|
|
return wsh
|
|
|
|
}
|
2021-01-25 12:23:03 +00:00
|
|
|
|
2021-04-05 09:33:28 +00:00
|
|
|
type websocketHandelr struct {
|
|
|
|
next http.Handler
|
2021-01-25 12:23:03 +00:00
|
|
|
|
2021-04-05 09:33:28 +00:00
|
|
|
muxnetwork *node
|
2021-01-25 12:23:03 +00:00
|
|
|
|
2021-04-05 09:33:28 +00:00
|
|
|
upgrader *websocket.Upgrader
|
|
|
|
}
|
2021-01-25 12:23:03 +00:00
|
|
|
|
2021-04-05 09:33:28 +00:00
|
|
|
func (wsh websocketHandelr) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|
|
|
remoteAddrStr := req.Header.Get("X-Forwarded-For")
|
|
|
|
if remoteAddrStr == "" {
|
|
|
|
remoteAddrStr = req.RemoteAddr
|
|
|
|
}
|
2021-01-25 12:23:03 +00:00
|
|
|
|
2021-04-05 09:33:28 +00:00
|
|
|
remoteAddr, err := net.ResolveTCPAddr("tcp", remoteAddrStr)
|
|
|
|
if err != nil {
|
|
|
|
wsh.next.ServeHTTP(w, req)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
wsConn, err := wsh.upgrader.Upgrade(w, req, nil)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
errLog := level.Error(wsh.muxnetwork.log)
|
|
|
|
errLog = kitlog.With(errLog, "remote", remoteAddrStr)
|
|
|
|
|
|
|
|
var wc net.Conn
|
|
|
|
wc = NewWebsockConn(wsConn)
|
|
|
|
|
|
|
|
cw := wsh.muxnetwork.secretServer.ConnWrapper()
|
|
|
|
wc, err = cw(wc)
|
|
|
|
if err != nil {
|
|
|
|
errLog.Log("warning", "failed to authenticate", "err", err, "remote", remoteAddr)
|
2021-01-25 12:23:03 +00:00
|
|
|
wsConn.Close()
|
2021-04-05 09:33:28 +00:00
|
|
|
return
|
2021-01-25 12:23:03 +00:00
|
|
|
}
|
|
|
|
|
2021-04-05 09:33:28 +00:00
|
|
|
// debugging copy of all muxrpc frames
|
|
|
|
// can be handy for reversing applications
|
|
|
|
// feed, err := GetFeedRefFromAddr(wc.RemoteAddr())
|
|
|
|
// if err != nil {
|
|
|
|
// errLog.Log("warning", "failed to get feed after auth", "err", err, "remote", remoteAddr)
|
|
|
|
// wsConn.Close()
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
// dumpPath := filepath.Join("webmux", base64.URLEncoding.EncodeToString(feed.ID[:10]), remoteAddrStr)
|
|
|
|
// wc, err = debug.WrapDump(dumpPath, wc)
|
|
|
|
// if err != nil {
|
|
|
|
// errLog.Log("warning", "failed wrap", "err", err, "remote", remoteAddr)
|
|
|
|
// wsConn.Close()
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
|
|
|
|
pkr := muxrpc.NewPacker(wc)
|
|
|
|
|
|
|
|
h, err := wsh.muxnetwork.opts.MakeHandler(wc)
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("websocket make handler failed: %w", err)
|
|
|
|
errLog.Log("warn", err)
|
|
|
|
wsConn.Close()
|
|
|
|
return
|
|
|
|
}
|
2021-01-25 12:23:03 +00:00
|
|
|
|
2021-04-05 09:33:28 +00:00
|
|
|
edp := muxrpc.Handle(pkr, h,
|
|
|
|
muxrpc.WithContext(req.Context()),
|
|
|
|
muxrpc.WithRemoteAddr(wc.RemoteAddr()))
|
|
|
|
|
|
|
|
srv := edp.(muxrpc.Server)
|
|
|
|
if err := srv.Serve(); err != nil {
|
|
|
|
errLog.Log("conn", "serve exited", "err", err, "peer", remoteAddr)
|
|
|
|
}
|
|
|
|
wsConn.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
// WebsockConn emulates a normal net.Conn from a websocket connection
|
|
|
|
type WebsockConn struct {
|
2021-01-25 12:23:03 +00:00
|
|
|
r io.Reader
|
|
|
|
wsc *websocket.Conn
|
|
|
|
}
|
|
|
|
|
2021-04-05 09:33:28 +00:00
|
|
|
func NewWebsockConn(wsc *websocket.Conn) *WebsockConn {
|
|
|
|
return &WebsockConn{
|
|
|
|
wsc: wsc,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (conn *WebsockConn) Read(data []byte) (int, error) {
|
2021-01-25 12:23:03 +00:00
|
|
|
if conn.r == nil {
|
|
|
|
if err := conn.renewReader(); err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2021-04-05 09:33:28 +00:00
|
|
|
|
2021-01-25 12:23:03 +00:00
|
|
|
n, err := conn.r.Read(data)
|
|
|
|
if err == io.EOF {
|
|
|
|
if err := conn.renewReader(); err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
2021-04-05 09:33:28 +00:00
|
|
|
n, err = conn.Read(data)
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("wsConn: failed to read after renew(): %w", err)
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
return n, nil
|
2021-01-25 12:23:03 +00:00
|
|
|
}
|
|
|
|
|
2021-04-05 09:33:28 +00:00
|
|
|
if err != nil {
|
|
|
|
return -1, fmt.Errorf("wsConn: read failed: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return n, nil
|
2021-01-25 12:23:03 +00:00
|
|
|
}
|
|
|
|
|
2021-04-05 09:33:28 +00:00
|
|
|
func (conn *WebsockConn) renewReader() error {
|
|
|
|
mt, r, err := conn.wsc.NextReader()
|
2021-01-25 12:23:03 +00:00
|
|
|
if err != nil {
|
2021-04-05 09:33:28 +00:00
|
|
|
err = fmt.Errorf("wsConn: failed to get reader: %w", err)
|
|
|
|
return err
|
2021-01-25 12:23:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if mt != websocket.BinaryMessage {
|
|
|
|
return fmt.Errorf("wsConn: not binary message: %v", mt)
|
|
|
|
|
|
|
|
}
|
2021-04-05 09:33:28 +00:00
|
|
|
|
|
|
|
conn.r = r
|
2021-01-25 12:23:03 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-05 09:33:28 +00:00
|
|
|
func (conn *WebsockConn) Write(data []byte) (int, error) {
|
2021-01-25 12:23:03 +00:00
|
|
|
writeCloser, err := conn.wsc.NextWriter(websocket.BinaryMessage)
|
|
|
|
if err != nil {
|
|
|
|
return -1, fmt.Errorf("wsConn: failed to create Reader: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
n, err := io.Copy(writeCloser, bytes.NewReader(data))
|
|
|
|
if err != nil {
|
|
|
|
return -1, fmt.Errorf("wsConn: failed to copy data: %w", err)
|
|
|
|
}
|
2021-04-05 09:33:28 +00:00
|
|
|
|
2021-01-25 12:23:03 +00:00
|
|
|
return int(n), writeCloser.Close()
|
|
|
|
}
|
|
|
|
|
2021-04-05 09:33:28 +00:00
|
|
|
func (conn *WebsockConn) Close() error {
|
2021-01-25 12:23:03 +00:00
|
|
|
return conn.wsc.Close()
|
|
|
|
}
|
|
|
|
|
2021-04-05 09:33:28 +00:00
|
|
|
func (conn *WebsockConn) LocalAddr() net.Addr { return conn.wsc.LocalAddr() }
|
|
|
|
func (conn *WebsockConn) RemoteAddr() net.Addr { return conn.wsc.RemoteAddr() }
|
|
|
|
|
|
|
|
func (conn *WebsockConn) SetDeadline(t time.Time) error {
|
|
|
|
rErr := conn.wsc.SetReadDeadline(t)
|
|
|
|
wErr := conn.wsc.SetWriteDeadline(t)
|
|
|
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
if rErr != nil {
|
|
|
|
err = fmt.Errorf("websock conn: failed to set read deadline: %w", rErr)
|
|
|
|
}
|
|
|
|
|
|
|
|
if wErr != nil {
|
|
|
|
wErr = fmt.Errorf("websock conn: failed to set read deadline: %w", wErr)
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("both faild: %w and %s", err, wErr)
|
|
|
|
} else {
|
|
|
|
err = wErr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
2021-01-25 12:23:03 +00:00
|
|
|
}
|
2021-04-05 09:33:28 +00:00
|
|
|
func (conn *WebsockConn) SetReadDeadline(t time.Time) error {
|
|
|
|
return conn.wsc.SetReadDeadline(t)
|
2021-01-25 12:23:03 +00:00
|
|
|
}
|
2021-04-05 09:33:28 +00:00
|
|
|
func (conn *WebsockConn) SetWriteDeadline(t time.Time) error {
|
|
|
|
return conn.wsc.SetWriteDeadline(t)
|
2021-01-25 12:23:03 +00:00
|
|
|
}
|