1
0
Эх сурвалжийг харах

hysteria2: Add more masquerade options

世界 10 сар өмнө
parent
commit
11a448b52d

+ 7 - 0
constant/hysteria2.go

@@ -0,0 +1,7 @@
+package constant
+
+const (
+	Hysterai2MasqueradeTypeFile   = "file"
+	Hysterai2MasqueradeTypeProxy  = "proxy"
+	Hysterai2MasqueradeTypeString = "string"
+)

+ 53 - 5
docs/configuration/inbound/hysteria2.md

@@ -1,11 +1,19 @@
+---
+icon: material/alert-decagram
+---
+
+!!! quote "Changes in sing-box 1.11.0"
+
+    :material-alert: [masquerade](#masquerade)
+
 ### Structure
 
 ```json
 {
   "type": "hysteria2",
   "tag": "hy2-in",
-  ...
-  // Listen Fields
+  
+  ... // Listen Fields
 
   "up_mbps": 100,
   "down_mbps": 100,
@@ -21,7 +29,7 @@
   ],
   "ignore_client_bandwidth": false,
   "tls": {},
-  "masquerade": "",
+  "masquerade": "", // or {}
   "brutal_debug": false
 }
 ```
@@ -79,14 +87,54 @@ TLS configuration, see [TLS](/configuration/shared/tls/#inbound).
 
 #### masquerade
 
-HTTP3 server behavior when authentication fails.
+HTTP3 server behavior (URL string configuration) when authentication fails.
 
 | Scheme       | Example                 | Description        |
 |--------------|-------------------------|--------------------|
 | `file`       | `file:///var/www`       | As a file server   |
 | `http/https` | `http://127.0.0.1:8080` | As a reverse proxy |
 
-A 404 page will be returned if empty.
+Conflict with `masquerade.type`.
+
+A 404 page will be returned if masquerade is not configured.
+
+#### masquerade.type
+
+HTTP3 server behavior (Object configuration) when authentication fails.
+
+| Type     | Description                 | Fields                              |
+|----------|-----------------------------|-------------------------------------|
+| `file`   | As a file server            | `directory`                         |
+| `proxy`  | As a reverse proxy          | `url`, `rewrite_host`               |
+| `string` | Reply with a fixed response | `status_code`, `headers`, `content` |
+
+Conflict with `masquerade`.
+
+A 404 page will be returned if masquerade is not configured.
+
+#### masquerade.directory
+
+File server root directory.
+
+#### masquerade.url
+
+Reverse proxy target URL.
+
+#### masquerade.rewrite_host
+
+Rewrite the `Host` header to the target URL.
+
+#### masquerade.status_code
+
+Fixed response status code.
+
+#### masquerade.headers
+
+Fixed response headers.
+
+#### masquerade.content
+
+Fixed response content.
 
 #### brutal_debug
 

+ 53 - 5
docs/configuration/inbound/hysteria2.zh.md

@@ -1,11 +1,19 @@
+---
+icon: material/alert-decagram
+---
+
+!!! quote "sing-box 1.11.0 中的更改"
+
+    :material-alert: [masquerade](#masquerade)
+
 ### 结构
 
 ```json
 {
   "type": "hysteria2",
   "tag": "hy2-in",
-  ...
-  // 监听字段
+  
+  ... // 监听字段
 
   "up_mbps": 100,
   "down_mbps": 100,
@@ -21,7 +29,7 @@
   ],
   "ignore_client_bandwidth": false,
   "tls": {},
-  "masquerade": "",
+  "masquerade": "", // 或 {}
   "brutal_debug": false
 }
 ```
@@ -76,14 +84,54 @@ TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
 
 #### masquerade
 
-HTTP3 服务器认证失败时的行为。
+HTTP3 服务器认证失败时的行为 (URL 字符串配置)
 
 | Scheme       | 示例                      | 描述      |
 |--------------|-------------------------|---------|
 | `file`       | `file:///var/www`       | 作为文件服务器 |
 | `http/https` | `http://127.0.0.1:8080` | 作为反向代理  |
 
-如果为空,则返回 404 页。
+如果 masquerade 未配置,则返回 404 页。
+
+与 `masquerade.type` 冲突。
+
+#### masquerade.type
+
+HTTP3 服务器认证失败时的行为 (对象配置)。
+
+| Type     | 描述      | 字段                                  |
+|----------|---------|-------------------------------------|
+| `file`   | 作为文件服务器 | `directory`                         |
+| `proxy`  | 作为反向代理  | `url`, `rewrite_host`               |
+| `string` | 返回固定响应  | `status_code`, `headers`, `content` |
+
+如果 masquerade 未配置,则返回 404 页。
+
+与 `masquerade` 冲突。
+
+#### masquerade.directory
+
+文件服务器根目录。
+
+#### masquerade.url
+
+反向代理目标 URL。
+
+#### masquerade.rewrite_host
+
+重写请求头中的 Host 字段到目标 URL。
+
+#### masquerade.status_code
+
+固定响应状态码。
+
+#### masquerade.headers
+
+固定响应头。
+
+#### masquerade.content
+
+固定响应内容。
 
 #### brutal_debug
 

+ 2 - 2
docs/configuration/outbound/direct.md

@@ -4,8 +4,8 @@ icon: material/alert-decagram
 
 !!! quote "Changes in sing-box 1.11.0"
 
-    :material-alert-decagram: [override_address](#override_address)  
-    :material-alert-decagram: [override_port](#override_port)
+    :material-delete-clock: [override_address](#override_address)  
+    :material-delete-clock: [override_port](#override_port)
 
 `direct` outbound send requests directly.
 

+ 88 - 2
option/hysteria2.go

@@ -1,5 +1,15 @@
 package option
 
+import (
+	"net/url"
+
+	C "github.com/sagernet/sing-box/constant"
+	E "github.com/sagernet/sing/common/exceptions"
+	"github.com/sagernet/sing/common/json"
+	"github.com/sagernet/sing/common/json/badjson"
+	"github.com/sagernet/sing/common/json/badoption"
+)
+
 type Hysteria2InboundOptions struct {
 	ListenOptions
 	UpMbps                int             `json:"up_mbps,omitempty"`
@@ -8,8 +18,8 @@ type Hysteria2InboundOptions struct {
 	Users                 []Hysteria2User `json:"users,omitempty"`
 	IgnoreClientBandwidth bool            `json:"ignore_client_bandwidth,omitempty"`
 	InboundTLSOptionsContainer
-	Masquerade  string `json:"masquerade,omitempty"`
-	BrutalDebug bool   `json:"brutal_debug,omitempty"`
+	Masquerade  *Hysteria2Masquerade `json:"masquerade,omitempty"`
+	BrutalDebug bool                 `json:"brutal_debug,omitempty"`
 }
 
 type Hysteria2Obfs struct {
@@ -22,6 +32,82 @@ type Hysteria2User struct {
 	Password string `json:"password,omitempty"`
 }
 
+type _Hysteria2Masquerade struct {
+	Type          string                    `json:"type,omitempty"`
+	FileOptions   Hysteria2MasqueradeFile   `json:"-"`
+	ProxyOptions  Hysteria2MasqueradeProxy  `json:"-"`
+	StringOptions Hysteria2MasqueradeString `json:"-"`
+}
+
+type Hysteria2Masquerade _Hysteria2Masquerade
+
+func (m Hysteria2Masquerade) MarshalJSON() ([]byte, error) {
+	var v any
+	switch m.Type {
+	case C.Hysterai2MasqueradeTypeFile:
+		v = m.FileOptions
+	case C.Hysterai2MasqueradeTypeProxy:
+		v = m.ProxyOptions
+	case C.Hysterai2MasqueradeTypeString:
+		v = m.StringOptions
+	default:
+		return nil, E.New("unknown masquerade type: ", m.Type)
+	}
+	return badjson.MarshallObjects((_Hysteria2Masquerade)(m), v)
+}
+
+func (m *Hysteria2Masquerade) UnmarshalJSON(bytes []byte) error {
+	var urlString string
+	err := json.Unmarshal(bytes, &urlString)
+	if err == nil {
+		masqueradeURL, err := url.Parse(urlString)
+		if err != nil {
+			return E.Cause(err, "invalid masquerade URL")
+		}
+		switch masqueradeURL.Scheme {
+		case "file":
+			m.Type = C.Hysterai2MasqueradeTypeFile
+			m.FileOptions.Directory = masqueradeURL.Path
+		case "http", "https":
+			m.Type = C.Hysterai2MasqueradeTypeProxy
+			m.ProxyOptions.URL = urlString
+		default:
+			return E.New("unknown masquerade URL scheme: ", masqueradeURL.Scheme)
+		}
+	}
+	err = json.Unmarshal(bytes, (*_Hysteria2Masquerade)(m))
+	if err != nil {
+		return err
+	}
+	var v any
+	switch m.Type {
+	case C.Hysterai2MasqueradeTypeFile:
+		v = &m.FileOptions
+	case C.Hysterai2MasqueradeTypeProxy:
+		v = &m.ProxyOptions
+	case C.Hysterai2MasqueradeTypeString:
+		v = &m.StringOptions
+	default:
+		return E.New("unknown masquerade type: ", m.Type)
+	}
+	return badjson.UnmarshallExcluded(bytes, (*_Hysteria2Masquerade)(m), v)
+}
+
+type Hysteria2MasqueradeFile struct {
+	Directory string `json:"directory"`
+}
+
+type Hysteria2MasqueradeProxy struct {
+	URL         string `json:"url"`
+	RewriteHost bool   `json:"rewrite_host,omitempty"`
+}
+
+type Hysteria2MasqueradeString struct {
+	StatusCode int                  `json:"status_code,omitempty"`
+	Headers    badoption.HTTPHeader `json:"headers,omitempty"`
+	Content    string               `json:"content"`
+}
+
 type Hysteria2OutboundOptions struct {
 	DialerOptions
 	ServerOptions

+ 25 - 11
protocol/hysteria2/inbound.go

@@ -60,26 +60,40 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
 		}
 	}
 	var masqueradeHandler http.Handler
-	if options.Masquerade != "" {
-		masqueradeURL, err := url.Parse(options.Masquerade)
-		if err != nil {
-			return nil, E.Cause(err, "parse masquerade URL")
-		}
-		switch masqueradeURL.Scheme {
-		case "file":
-			masqueradeHandler = http.FileServer(http.Dir(masqueradeURL.Path))
-		case "http", "https":
+	if options.Masquerade != nil && options.Masquerade.Type != "" {
+		switch options.Masquerade.Type {
+		case C.Hysterai2MasqueradeTypeFile:
+			masqueradeHandler = http.FileServer(http.Dir(options.Masquerade.FileOptions.Directory))
+		case C.Hysterai2MasqueradeTypeProxy:
+			masqueradeURL, err := url.Parse(options.Masquerade.ProxyOptions.URL)
+			if err != nil {
+				return nil, E.Cause(err, "parse masquerade URL")
+			}
 			masqueradeHandler = &httputil.ReverseProxy{
 				Rewrite: func(r *httputil.ProxyRequest) {
 					r.SetURL(masqueradeURL)
-					r.Out.Host = r.In.Host
+					if !options.Masquerade.ProxyOptions.RewriteHost {
+						r.Out.Host = r.In.Host
+					}
 				},
 				ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
 					w.WriteHeader(http.StatusBadGateway)
 				},
 			}
+		case C.Hysterai2MasqueradeTypeString:
+			masqueradeHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+				if options.Masquerade.StringOptions.StatusCode != 0 {
+					w.WriteHeader(options.Masquerade.StringOptions.StatusCode)
+				}
+				for key, values := range options.Masquerade.StringOptions.Headers {
+					for _, value := range values {
+						w.Header().Add(key, value)
+					}
+				}
+				w.Write([]byte(options.Masquerade.StringOptions.Content))
+			})
 		default:
-			return nil, E.New("unknown masquerade URL scheme: ", masqueradeURL.Scheme)
+			return nil, E.New("unknown masquerade type: ", options.Masquerade.Type)
 		}
 	}
 	inbound := &Inbound{