mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-12-04 20:36:37 +00:00
hysteria2: Add more masquerade options
This commit is contained in:
parent
1bb67a9ff4
commit
38aad48b20
7
constant/hysteria2.go
Normal file
7
constant/hysteria2.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package constant
|
||||||
|
|
||||||
|
const (
|
||||||
|
Hysterai2MasqueradeTypeFile = "file"
|
||||||
|
Hysterai2MasqueradeTypeProxy = "proxy"
|
||||||
|
Hysterai2MasqueradeTypeString = "string"
|
||||||
|
)
|
|
@ -1,11 +1,19 @@
|
||||||
|
---
|
||||||
|
icon: material/alert-decagram
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! quote "Changes in sing-box 1.11.0"
|
||||||
|
|
||||||
|
:material-alert: [masquerade](#masquerade)
|
||||||
|
|
||||||
### Structure
|
### Structure
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "hysteria2",
|
"type": "hysteria2",
|
||||||
"tag": "hy2-in",
|
"tag": "hy2-in",
|
||||||
...
|
|
||||||
// Listen Fields
|
... // Listen Fields
|
||||||
|
|
||||||
"up_mbps": 100,
|
"up_mbps": 100,
|
||||||
"down_mbps": 100,
|
"down_mbps": 100,
|
||||||
|
@ -21,7 +29,7 @@
|
||||||
],
|
],
|
||||||
"ignore_client_bandwidth": false,
|
"ignore_client_bandwidth": false,
|
||||||
"tls": {},
|
"tls": {},
|
||||||
"masquerade": "",
|
"masquerade": "", // or {}
|
||||||
"brutal_debug": false
|
"brutal_debug": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -79,14 +87,54 @@ TLS configuration, see [TLS](/configuration/shared/tls/#inbound).
|
||||||
|
|
||||||
#### masquerade
|
#### masquerade
|
||||||
|
|
||||||
HTTP3 server behavior when authentication fails.
|
HTTP3 server behavior (URL string configuration) when authentication fails.
|
||||||
|
|
||||||
| Scheme | Example | Description |
|
| Scheme | Example | Description |
|
||||||
|--------------|-------------------------|--------------------|
|
|--------------|-------------------------|--------------------|
|
||||||
| `file` | `file:///var/www` | As a file server |
|
| `file` | `file:///var/www` | As a file server |
|
||||||
| `http/https` | `http://127.0.0.1:8080` | As a reverse proxy |
|
| `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
|
#### brutal_debug
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,19 @@
|
||||||
|
---
|
||||||
|
icon: material/alert-decagram
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! quote "sing-box 1.11.0 中的更改"
|
||||||
|
|
||||||
|
:material-alert: [masquerade](#masquerade)
|
||||||
|
|
||||||
### 结构
|
### 结构
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "hysteria2",
|
"type": "hysteria2",
|
||||||
"tag": "hy2-in",
|
"tag": "hy2-in",
|
||||||
...
|
|
||||||
// 监听字段
|
... // 监听字段
|
||||||
|
|
||||||
"up_mbps": 100,
|
"up_mbps": 100,
|
||||||
"down_mbps": 100,
|
"down_mbps": 100,
|
||||||
|
@ -21,7 +29,7 @@
|
||||||
],
|
],
|
||||||
"ignore_client_bandwidth": false,
|
"ignore_client_bandwidth": false,
|
||||||
"tls": {},
|
"tls": {},
|
||||||
"masquerade": "",
|
"masquerade": "", // 或 {}
|
||||||
"brutal_debug": false
|
"brutal_debug": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -76,14 +84,54 @@ TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
|
||||||
|
|
||||||
#### masquerade
|
#### masquerade
|
||||||
|
|
||||||
HTTP3 服务器认证失败时的行为。
|
HTTP3 服务器认证失败时的行为 (URL 字符串配置)。
|
||||||
|
|
||||||
| Scheme | 示例 | 描述 |
|
| Scheme | 示例 | 描述 |
|
||||||
|--------------|-------------------------|---------|
|
|--------------|-------------------------|---------|
|
||||||
| `file` | `file:///var/www` | 作为文件服务器 |
|
| `file` | `file:///var/www` | 作为文件服务器 |
|
||||||
| `http/https` | `http://127.0.0.1:8080` | 作为反向代理 |
|
| `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
|
#### brutal_debug
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@ icon: material/alert-decagram
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.11.0"
|
!!! quote "Changes in sing-box 1.11.0"
|
||||||
|
|
||||||
:material-alert-decagram: [override_address](#override_address)
|
:material-delete-clock: [override_address](#override_address)
|
||||||
:material-alert-decagram: [override_port](#override_port)
|
:material-delete-clock: [override_port](#override_port)
|
||||||
|
|
||||||
`direct` outbound send requests directly.
|
`direct` outbound send requests directly.
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,15 @@
|
||||||
package option
|
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 {
|
type Hysteria2InboundOptions struct {
|
||||||
ListenOptions
|
ListenOptions
|
||||||
UpMbps int `json:"up_mbps,omitempty"`
|
UpMbps int `json:"up_mbps,omitempty"`
|
||||||
|
@ -8,8 +18,8 @@ type Hysteria2InboundOptions struct {
|
||||||
Users []Hysteria2User `json:"users,omitempty"`
|
Users []Hysteria2User `json:"users,omitempty"`
|
||||||
IgnoreClientBandwidth bool `json:"ignore_client_bandwidth,omitempty"`
|
IgnoreClientBandwidth bool `json:"ignore_client_bandwidth,omitempty"`
|
||||||
InboundTLSOptionsContainer
|
InboundTLSOptionsContainer
|
||||||
Masquerade string `json:"masquerade,omitempty"`
|
Masquerade *Hysteria2Masquerade `json:"masquerade,omitempty"`
|
||||||
BrutalDebug bool `json:"brutal_debug,omitempty"`
|
BrutalDebug bool `json:"brutal_debug,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Hysteria2Obfs struct {
|
type Hysteria2Obfs struct {
|
||||||
|
@ -22,6 +32,82 @@ type Hysteria2User struct {
|
||||||
Password string `json:"password,omitempty"`
|
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 {
|
type Hysteria2OutboundOptions struct {
|
||||||
DialerOptions
|
DialerOptions
|
||||||
ServerOptions
|
ServerOptions
|
||||||
|
|
|
@ -60,26 +60,40 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var masqueradeHandler http.Handler
|
var masqueradeHandler http.Handler
|
||||||
if options.Masquerade != "" {
|
if options.Masquerade != nil && options.Masquerade.Type != "" {
|
||||||
masqueradeURL, err := url.Parse(options.Masquerade)
|
switch options.Masquerade.Type {
|
||||||
if err != nil {
|
case C.Hysterai2MasqueradeTypeFile:
|
||||||
return nil, E.Cause(err, "parse masquerade URL")
|
masqueradeHandler = http.FileServer(http.Dir(options.Masquerade.FileOptions.Directory))
|
||||||
}
|
case C.Hysterai2MasqueradeTypeProxy:
|
||||||
switch masqueradeURL.Scheme {
|
masqueradeURL, err := url.Parse(options.Masquerade.ProxyOptions.URL)
|
||||||
case "file":
|
if err != nil {
|
||||||
masqueradeHandler = http.FileServer(http.Dir(masqueradeURL.Path))
|
return nil, E.Cause(err, "parse masquerade URL")
|
||||||
case "http", "https":
|
}
|
||||||
masqueradeHandler = &httputil.ReverseProxy{
|
masqueradeHandler = &httputil.ReverseProxy{
|
||||||
Rewrite: func(r *httputil.ProxyRequest) {
|
Rewrite: func(r *httputil.ProxyRequest) {
|
||||||
r.SetURL(masqueradeURL)
|
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) {
|
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
|
||||||
w.WriteHeader(http.StatusBadGateway)
|
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:
|
default:
|
||||||
return nil, E.New("unknown masquerade URL scheme: ", masqueradeURL.Scheme)
|
return nil, E.New("unknown masquerade type: ", options.Masquerade.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inbound := &Inbound{
|
inbound := &Inbound{
|
||||||
|
|
Loading…
Reference in a new issue