Add SplitHTTP Browser Dialer support (#3484)

This commit is contained in:
mmmray 2024-07-11 09:56:20 +02:00 committed by GitHub
parent 308f0c64c3
commit c8f6ba9ff0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 536 additions and 294 deletions

View file

@ -15,16 +15,14 @@ var _ buf.Writer = (*connection)(nil)
// connection is a wrapper for net.Conn over WebSocket connection.
type connection struct {
conn *websocket.Conn
reader io.Reader
remoteAddr net.Addr
conn *websocket.Conn
reader io.Reader
}
func newConnection(conn *websocket.Conn, remoteAddr net.Addr, extraReader io.Reader) *connection {
func NewConnection(conn *websocket.Conn, remoteAddr net.Addr, extraReader io.Reader) *connection {
return &connection{
conn: conn,
remoteAddr: remoteAddr,
reader: extraReader,
conn: conn,
reader: extraReader,
}
}
@ -92,7 +90,7 @@ func (c *connection) LocalAddr() net.Addr {
}
func (c *connection) RemoteAddr() net.Addr {
return c.remoteAddr
return c.conn.RemoteAddr()
}
func (c *connection) SetDeadline(t time.Time) error {

View file

@ -1,54 +1,23 @@
package websocket
import (
"bytes"
"context"
_ "embed"
"encoding/base64"
"io"
gonet "net"
"net/http"
"time"
"github.com/gorilla/websocket"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/platform"
"github.com/xtls/xray-core/common/uuid"
"github.com/xtls/xray-core/transport/internet"
"github.com/xtls/xray-core/transport/internet/browser_dialer"
"github.com/xtls/xray-core/transport/internet/stat"
"github.com/xtls/xray-core/transport/internet/tls"
)
//go:embed dialer.html
var webpage []byte
var conns chan *websocket.Conn
func init() {
addr := platform.NewEnvFlag(platform.BrowserDialerAddress).GetValue(func() string { return "" })
if addr != "" {
token := uuid.New()
csrfToken := token.String()
webpage = bytes.ReplaceAll(webpage, []byte("csrfToken"), []byte(csrfToken))
conns = make(chan *websocket.Conn, 256)
go http.ListenAndServe(addr, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/websocket" {
if r.URL.Query().Get("token") == csrfToken {
if conn, err := upgrader.Upgrade(w, r, nil); err == nil {
conns <- conn
} else {
errors.LogError(context.Background(), "Browser dialer http upgrade unexpected error")
}
}
} else {
w.Write(webpage)
}
}))
}
}
// Dial dials a WebSocket connection to the given destination.
func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) {
errors.LogInfo(ctx, "creating connection to ", dest)
@ -98,18 +67,18 @@ func dialWebSocket(ctx context.Context, dest net.Destination, streamSettings *in
// Like the NetDial in the dialer
pconn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to dial to " + addr)
errors.LogErrorInner(ctx, err, "failed to dial to "+addr)
return nil, err
}
// TLS and apply the handshake
cn := tls.UClient(pconn, tlsConfig, fingerprint).(*tls.UConn)
if err := cn.WebsocketHandshakeContext(ctx); err != nil {
errors.LogErrorInner(ctx, err, "failed to dial to " + addr)
errors.LogErrorInner(ctx, err, "failed to dial to "+addr)
return nil, err
}
if !tlsConfig.InsecureSkipVerify {
if err := cn.VerifyHostname(tlsConfig.ServerName); err != nil {
errors.LogErrorInner(ctx, err, "failed to dial to " + addr)
errors.LogErrorInner(ctx, err, "failed to dial to "+addr)
return nil, err
}
}
@ -124,28 +93,13 @@ func dialWebSocket(ctx context.Context, dest net.Destination, streamSettings *in
}
uri := protocol + "://" + host + wsSettings.GetNormalizedPath()
if conns != nil {
data := []byte(uri)
if ed != nil {
data = append(data, " "+base64.RawURLEncoding.EncodeToString(ed)...)
}
var conn *websocket.Conn
for {
conn = <-conns
if conn.WriteMessage(websocket.TextMessage, data) != nil {
conn.Close()
} else {
break
}
}
if _, p, err := conn.ReadMessage(); err != nil {
conn.Close()
if browser_dialer.HasBrowserDialer() {
conn, err := browser_dialer.DialWS(uri, ed)
if err != nil {
return nil, err
} else if s := string(p); s != "ok" {
conn.Close()
return nil, errors.New(s)
}
return newConnection(conn, conn.RemoteAddr(), nil), nil
return NewConnection(conn, conn.RemoteAddr(), nil), nil
}
header := wsSettings.GetRequestHeader()
@ -163,7 +117,7 @@ func dialWebSocket(ctx context.Context, dest net.Destination, streamSettings *in
return nil, errors.New("failed to dial to (", uri, "): ", reason).Base(err)
}
return newConnection(conn, conn.RemoteAddr(), nil), nil
return NewConnection(conn, conn.RemoteAddr(), nil), nil
}
type delayDialConn struct {

View file

@ -1,59 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Browser Dialer</title>
</head>
<body>
<script>
// Copyright (c) 2021 XRAY. Mozilla Public License 2.0.
var url = "ws://" + window.location.host + "/websocket?token=csrfToken"
var count = 0
setInterval(check, 1000)
function check() {
if (count <= 0) {
count += 1
console.log("Prepare", url)
var ws = new WebSocket(url)
// arraybuffer is significantly faster in chrome than default
// blob, tested with chrome 123
ws.binaryType = "arraybuffer";
var wss = undefined
var first = true
ws.onmessage = function (event) {
if (first) {
first = false
count -= 1
var arr = event.data.split(" ")
console.log("Dial", arr[0], arr[1])
wss = new WebSocket(arr[0], arr[1])
wss.binaryType = "arraybuffer";
var opened = false
wss.onopen = function (event) {
opened = true
ws.send("ok")
}
wss.onmessage = function (event) {
ws.send(event.data)
}
wss.onclose = function (event) {
ws.close()
}
wss.onerror = function (event) {
!opened && ws.send("fail")
wss.close()
}
check()
} else wss.send(event.data)
}
ws.onclose = function (event) {
if (first) count -= 1
else wss.close()
}
ws.onerror = function (event) {
ws.close()
}
}
}
</script>
</body>
</html>

View file

@ -73,7 +73,7 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
}
}
h.ln.addConn(newConnection(conn, remoteAddr, extraReader))
h.ln.addConn(NewConnection(conn, remoteAddr, extraReader))
}
type Listener struct {