Files
Xray-core/transport/internet/browser_dialer/dialer.go
T

158 lines
3.2 KiB
Go

package browser_dialer
import (
_ "embed"
"encoding/base64"
"encoding/json"
"net/http"
"github.com/gorilla/websocket"
"github.com/xtls/xray-core/common/errors"
)
//go:embed dialer.html
var webpage []byte
type task struct {
Method string `json:"method"`
URL string `json:"url"`
Extra any `json:"extra,omitempty"`
StreamResponse bool `json:"streamResponse"`
}
type webSocketExtra struct {
Protocol string `json:"protocol,omitempty"`
}
func DialWSWithAddress(addr string, uri string, ed []byte) (*websocket.Conn, error) {
task := task{
Method: "WS",
URL: uri,
StreamResponse: true,
}
if ed != nil {
task.Extra = webSocketExtra{
Protocol: base64.RawURLEncoding.EncodeToString(ed),
}
}
return dialTaskWithAddress(addr, task)
}
type httpExtra struct {
Referrer string `json:"referrer,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
Cookies map[string]string `json:"cookies,omitempty"`
}
func httpExtraFromHeadersAndCookies(headers http.Header, cookies []*http.Cookie) *httpExtra {
if len(headers) == 0 {
return nil
}
extra := httpExtra{}
if referrer := headers.Get("Referer"); referrer != "" {
extra.Referrer = referrer
headers.Del("Referer")
}
if len(headers) > 0 {
extra.Headers = make(map[string]string)
for header := range headers {
extra.Headers[header] = headers.Get(header)
}
}
if len(cookies) > 0 {
extra.Cookies = make(map[string]string)
for _, cookie := range cookies {
extra.Cookies[cookie.Name] = cookie.Value
}
}
return &extra
}
func DialGetWithAddress(addr string, uri string, headers http.Header, cookies []*http.Cookie) (*websocket.Conn, error) {
task := task{
Method: "GET",
URL: uri,
Extra: httpExtraFromHeadersAndCookies(headers, cookies),
StreamResponse: true,
}
return dialTaskWithAddress(addr, task)
}
func DialPacketWithAddress(addr string, method string, uri string, headers http.Header, cookies []*http.Cookie, payload []byte) error {
task := task{
Method: method,
URL: uri,
Extra: httpExtraFromHeadersAndCookies(headers, cookies),
StreamResponse: false,
}
conn, err := dialTaskWithAddress(addr, task)
if err != nil {
return err
}
err = conn.WriteMessage(websocket.BinaryMessage, payload)
if err != nil {
return err
}
err = CheckOK(conn)
if err != nil {
return err
}
conn.Close()
return nil
}
func dialTaskWithAddress(addr string, task task) (*websocket.Conn, error) {
data, err := json.Marshal(task)
if err != nil {
return nil, err
}
if addr == "" {
return nil, errors.New("browser dialer is not configured; set sockopt.dialerProxy to browser://host:port/uuid")
}
dialer, err := getDialerByAddress(addr)
if err != nil {
return nil, err
}
conns := dialer.conns
var conn *websocket.Conn
for {
conn = <-conns
if conn.WriteMessage(websocket.TextMessage, data) != nil {
conn.Close()
} else {
break
}
}
err = CheckOK(conn)
if err != nil {
return nil, err
}
return conn, nil
}
func CheckOK(conn *websocket.Conn) error {
if _, p, err := conn.ReadMessage(); err != nil {
conn.Close()
return err
} else if s := string(p); s != "ok" {
conn.Close()
return errors.New(s)
}
return nil
}