| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- package browser_dialer
- import (
- "bytes"
- "context"
- _ "embed"
- "encoding/base64"
- "encoding/json"
- "net/http"
- "time"
- "github.com/gorilla/websocket"
- "github.com/xtls/xray-core/common/errors"
- "github.com/xtls/xray-core/common/platform"
- "github.com/xtls/xray-core/common/uuid"
- )
- //go:embed dialer.html
- var webpage []byte
- type task struct {
- Method string `json:"method"`
- URL string `json:"url"`
- Extra any `json:"extra,omitempty"`
- }
- var conns chan *websocket.Conn
- var upgrader = &websocket.Upgrader{
- ReadBufferSize: 0,
- WriteBufferSize: 0,
- HandshakeTimeout: time.Second * 4,
- CheckOrigin: func(r *http.Request) bool {
- return true
- },
- }
- 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)
- }
- }))
- }
- }
- func HasBrowserDialer() bool {
- return conns != nil
- }
- type webSocketExtra struct {
- Protocol string `json:"protocol,omitempty"`
- }
- func DialWS(uri string, ed []byte) (*websocket.Conn, error) {
- task := task{
- Method: "WS",
- URL: uri,
- }
- if ed != nil {
- task.Extra = webSocketExtra{
- Protocol: base64.RawURLEncoding.EncodeToString(ed),
- }
- }
- return dialTask(task)
- }
- type httpExtra struct {
- Referrer string `json:"referrer,omitempty"`
- Headers map[string]string `json:"headers,omitempty"`
- }
- func httpExtraFromHeaders(headers http.Header) *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)
- }
- }
- return &extra
- }
- func DialGet(uri string, headers http.Header) (*websocket.Conn, error) {
- task := task{
- Method: "GET",
- URL: uri,
- Extra: httpExtraFromHeaders(headers),
- }
- return dialTask(task)
- }
- func DialPost(uri string, headers http.Header, payload []byte) error {
- task := task{
- Method: "POST",
- URL: uri,
- Extra: httpExtraFromHeaders(headers),
- }
- conn, err := dialTask(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 dialTask(task task) (*websocket.Conn, error) {
- data, err := json.Marshal(task)
- if err != nil {
- return nil, err
- }
- 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
- }
|