dialer.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. package browser_dialer
  2. import (
  3. "bytes"
  4. "context"
  5. _ "embed"
  6. "encoding/base64"
  7. "encoding/json"
  8. "net/http"
  9. "time"
  10. "github.com/gorilla/websocket"
  11. "github.com/xtls/xray-core/common/errors"
  12. "github.com/xtls/xray-core/common/platform"
  13. "github.com/xtls/xray-core/common/uuid"
  14. )
  15. //go:embed dialer.html
  16. var webpage []byte
  17. type task struct {
  18. Method string `json:"method"`
  19. URL string `json:"url"`
  20. Extra any `json:"extra,omitempty"`
  21. }
  22. var conns chan *websocket.Conn
  23. var upgrader = &websocket.Upgrader{
  24. ReadBufferSize: 0,
  25. WriteBufferSize: 0,
  26. HandshakeTimeout: time.Second * 4,
  27. CheckOrigin: func(r *http.Request) bool {
  28. return true
  29. },
  30. }
  31. func init() {
  32. addr := platform.NewEnvFlag(platform.BrowserDialerAddress).GetValue(func() string { return "" })
  33. if addr != "" {
  34. token := uuid.New()
  35. csrfToken := token.String()
  36. webpage = bytes.ReplaceAll(webpage, []byte("csrfToken"), []byte(csrfToken))
  37. conns = make(chan *websocket.Conn, 256)
  38. go http.ListenAndServe(addr, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  39. if r.URL.Path == "/websocket" {
  40. if r.URL.Query().Get("token") == csrfToken {
  41. if conn, err := upgrader.Upgrade(w, r, nil); err == nil {
  42. conns <- conn
  43. } else {
  44. errors.LogError(context.Background(), "Browser dialer http upgrade unexpected error")
  45. }
  46. }
  47. } else {
  48. w.Write(webpage)
  49. }
  50. }))
  51. }
  52. }
  53. func HasBrowserDialer() bool {
  54. return conns != nil
  55. }
  56. type webSocketExtra struct {
  57. Protocol string `json:"protocol,omitempty"`
  58. }
  59. func DialWS(uri string, ed []byte) (*websocket.Conn, error) {
  60. task := task{
  61. Method: "WS",
  62. URL: uri,
  63. }
  64. if ed != nil {
  65. task.Extra = webSocketExtra{
  66. Protocol: base64.RawURLEncoding.EncodeToString(ed),
  67. }
  68. }
  69. return dialTask(task)
  70. }
  71. type httpExtra struct {
  72. Referrer string `json:"referrer,omitempty"`
  73. Headers map[string]string `json:"headers,omitempty"`
  74. }
  75. func httpExtraFromHeaders(headers http.Header) *httpExtra {
  76. if len(headers) == 0 {
  77. return nil
  78. }
  79. extra := httpExtra{}
  80. if referrer := headers.Get("Referer"); referrer != "" {
  81. extra.Referrer = referrer
  82. headers.Del("Referer")
  83. }
  84. if len(headers) > 0 {
  85. extra.Headers = make(map[string]string)
  86. for header := range headers {
  87. extra.Headers[header] = headers.Get(header)
  88. }
  89. }
  90. return &extra
  91. }
  92. func DialGet(uri string, headers http.Header) (*websocket.Conn, error) {
  93. task := task{
  94. Method: "GET",
  95. URL: uri,
  96. Extra: httpExtraFromHeaders(headers),
  97. }
  98. return dialTask(task)
  99. }
  100. func DialPost(uri string, headers http.Header, payload []byte) error {
  101. task := task{
  102. Method: "POST",
  103. URL: uri,
  104. Extra: httpExtraFromHeaders(headers),
  105. }
  106. conn, err := dialTask(task)
  107. if err != nil {
  108. return err
  109. }
  110. err = conn.WriteMessage(websocket.BinaryMessage, payload)
  111. if err != nil {
  112. return err
  113. }
  114. err = CheckOK(conn)
  115. if err != nil {
  116. return err
  117. }
  118. conn.Close()
  119. return nil
  120. }
  121. func dialTask(task task) (*websocket.Conn, error) {
  122. data, err := json.Marshal(task)
  123. if err != nil {
  124. return nil, err
  125. }
  126. var conn *websocket.Conn
  127. for {
  128. conn = <-conns
  129. if conn.WriteMessage(websocket.TextMessage, data) != nil {
  130. conn.Close()
  131. } else {
  132. break
  133. }
  134. }
  135. err = CheckOK(conn)
  136. if err != nil {
  137. return nil, err
  138. }
  139. return conn, nil
  140. }
  141. func CheckOK(conn *websocket.Conn) error {
  142. if _, p, err := conn.ReadMessage(); err != nil {
  143. conn.Close()
  144. return err
  145. } else if s := string(p); s != "ok" {
  146. conn.Close()
  147. return errors.New(s)
  148. }
  149. return nil
  150. }