mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-05-14 00:51:12 +03:00
319 lines
9.2 KiB
Go
319 lines
9.2 KiB
Go
package option
|
|
|
|
import (
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
|
|
Xbadoption "github.com/sagernet/sing-box/common/xray/json/badoption"
|
|
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 _V2RayTransportOptions struct {
|
|
Type string `json:"type"`
|
|
HTTPOptions V2RayHTTPOptions `json:"-"`
|
|
WebsocketOptions V2RayWebsocketOptions `json:"-"`
|
|
QUICOptions V2RayQUICOptions `json:"-"`
|
|
GRPCOptions V2RayGRPCOptions `json:"-"`
|
|
HTTPUpgradeOptions V2RayHTTPUpgradeOptions `json:"-"`
|
|
XHTTPOptions V2RayXHTTPOptions `json:"-"`
|
|
KCPOptions V2RayKCPOptions `json:"-"`
|
|
}
|
|
|
|
type V2RayTransportOptions _V2RayTransportOptions
|
|
|
|
func (o V2RayTransportOptions) MarshalJSON() ([]byte, error) {
|
|
var v any
|
|
switch o.Type {
|
|
case C.V2RayTransportTypeHTTP:
|
|
v = o.HTTPOptions
|
|
case C.V2RayTransportTypeWebsocket:
|
|
v = o.WebsocketOptions
|
|
case C.V2RayTransportTypeQUIC:
|
|
v = o.QUICOptions
|
|
case C.V2RayTransportTypeGRPC:
|
|
v = o.GRPCOptions
|
|
case C.V2RayTransportTypeHTTPUpgrade:
|
|
v = o.HTTPUpgradeOptions
|
|
case C.V2RayTransportTypeXHTTP:
|
|
v = o.XHTTPOptions
|
|
case C.V2RayTransportTypeKCP:
|
|
v = o.KCPOptions
|
|
case "":
|
|
return nil, E.New("missing transport type")
|
|
default:
|
|
return nil, E.New("unknown transport type: " + o.Type)
|
|
}
|
|
return badjson.MarshallObjects((_V2RayTransportOptions)(o), v)
|
|
}
|
|
|
|
func (o *V2RayTransportOptions) UnmarshalJSON(bytes []byte) error {
|
|
err := json.Unmarshal(bytes, (*_V2RayTransportOptions)(o))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var v any
|
|
switch o.Type {
|
|
case C.V2RayTransportTypeHTTP:
|
|
v = &o.HTTPOptions
|
|
case C.V2RayTransportTypeWebsocket:
|
|
v = &o.WebsocketOptions
|
|
case C.V2RayTransportTypeQUIC:
|
|
v = &o.QUICOptions
|
|
case C.V2RayTransportTypeGRPC:
|
|
v = &o.GRPCOptions
|
|
case C.V2RayTransportTypeHTTPUpgrade:
|
|
v = &o.HTTPUpgradeOptions
|
|
case C.V2RayTransportTypeXHTTP:
|
|
v = &o.XHTTPOptions
|
|
case C.V2RayTransportTypeKCP:
|
|
v = &o.KCPOptions
|
|
default:
|
|
return E.New("unknown transport type: " + o.Type)
|
|
}
|
|
err = badjson.UnmarshallExcluded(bytes, (*_V2RayTransportOptions)(o), v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type V2RayHTTPOptions struct {
|
|
Host badoption.Listable[string] `json:"host,omitempty"`
|
|
Path string `json:"path,omitempty"`
|
|
Method string `json:"method,omitempty"`
|
|
Headers badoption.HTTPHeader `json:"headers,omitempty"`
|
|
IdleTimeout badoption.Duration `json:"idle_timeout,omitempty"`
|
|
PingTimeout badoption.Duration `json:"ping_timeout,omitempty"`
|
|
}
|
|
|
|
type V2RayWebsocketOptions struct {
|
|
Path string `json:"path,omitempty"`
|
|
Headers badoption.HTTPHeader `json:"headers,omitempty"`
|
|
MaxEarlyData uint32 `json:"max_early_data,omitempty"`
|
|
EarlyDataHeaderName string `json:"early_data_header_name,omitempty"`
|
|
}
|
|
|
|
type V2RayQUICOptions struct{}
|
|
|
|
type V2RayGRPCOptions struct {
|
|
ServiceName string `json:"service_name,omitempty"`
|
|
IdleTimeout badoption.Duration `json:"idle_timeout,omitempty"`
|
|
PingTimeout badoption.Duration `json:"ping_timeout,omitempty"`
|
|
PermitWithoutStream bool `json:"permit_without_stream,omitempty"`
|
|
ForceLite bool `json:"-"` // for test
|
|
}
|
|
|
|
type V2RayHTTPUpgradeOptions struct {
|
|
Host string `json:"host,omitempty"`
|
|
Path string `json:"path,omitempty"`
|
|
Headers badoption.HTTPHeader `json:"headers,omitempty"`
|
|
}
|
|
|
|
type V2RayXHTTPBaseOptions struct {
|
|
Host string `json:"host,omitempty"`
|
|
Path string `json:"path,omitempty"`
|
|
Headers map[string]string `json:"headers,omitempty"`
|
|
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
|
|
XPaddingBytes Xbadoption.Range `json:"x_padding_bytes"`
|
|
NoGRPCHeader bool `json:"no_grpc_header,omitempty"`
|
|
NoSSEHeader bool `json:"no_sse_header,omitempty"`
|
|
ScMaxEachPostBytes Xbadoption.Range `json:"sc_max_each_post_bytes"`
|
|
ScMinPostsIntervalMs Xbadoption.Range `json:"sc_min_posts_interval_ms"`
|
|
ScMaxBufferedPosts int64 `json:"sc_max_buffered_posts,omitempty"`
|
|
ScStreamUpServerSecs Xbadoption.Range `json:"sc_stream_up_server_secs"`
|
|
Xmux *V2RayXHTTPXmuxOptions `json:"xmux"`
|
|
}
|
|
|
|
type V2RayXHTTPOptions struct {
|
|
Mode string `json:"mode"`
|
|
V2RayXHTTPBaseOptions
|
|
Download *V2RayXHTTPDownloadOptions `json:"download"`
|
|
}
|
|
|
|
type V2RayXHTTPDownloadOptions struct {
|
|
V2RayXHTTPBaseOptions
|
|
ServerOptions
|
|
OutboundTLSOptionsContainer
|
|
Detour string `json:"detour,omitempty"`
|
|
}
|
|
|
|
func (c *V2RayXHTTPBaseOptions) GetNormalizedPath() string {
|
|
pathAndQuery := strings.SplitN(c.Path, "?", 2)
|
|
path := pathAndQuery[0]
|
|
if path == "" || path[0] != '/' {
|
|
path = "/" + path
|
|
}
|
|
if path[len(path)-1] != '/' {
|
|
path = path + "/"
|
|
}
|
|
return path
|
|
}
|
|
|
|
func (c *V2RayXHTTPBaseOptions) GetNormalizedQuery() string {
|
|
pathAndQuery := strings.SplitN(c.Path, "?", 2)
|
|
query := ""
|
|
if len(pathAndQuery) > 1 {
|
|
query = pathAndQuery[1]
|
|
}
|
|
return query
|
|
}
|
|
|
|
func (c *V2RayXHTTPBaseOptions) GetRequestHeader(rawURL string) http.Header {
|
|
header := http.Header{}
|
|
for k, v := range c.Headers {
|
|
header.Add(k, v)
|
|
}
|
|
u, _ := url.Parse(rawURL)
|
|
// https://www.rfc-editor.org/rfc/rfc7541.html#appendix-B
|
|
// h2's HPACK Header Compression feature employs a huffman encoding using a static table.
|
|
// 'X' is assigned an 8 bit code, so HPACK compression won't change actual padding length on the wire.
|
|
// https://www.rfc-editor.org/rfc/rfc9204.html#section-4.1.2-2
|
|
// h3's similar QPACK feature uses the same huffman table.
|
|
u.RawQuery = "x_padding=" + strings.Repeat("X", int(c.GetNormalizedXPaddingBytes().Rand()))
|
|
header.Set("Referer", u.String())
|
|
return header
|
|
}
|
|
|
|
func (c *V2RayXHTTPBaseOptions) GetNormalizedXPaddingBytes() Xbadoption.Range {
|
|
if c.XPaddingBytes.To == 0 {
|
|
return Xbadoption.Range{
|
|
From: 100,
|
|
To: 1000,
|
|
}
|
|
}
|
|
return c.XPaddingBytes
|
|
}
|
|
|
|
func (c *V2RayXHTTPBaseOptions) GetNormalizedScMaxEachPostBytes() Xbadoption.Range {
|
|
if c.ScMaxEachPostBytes.To == 0 {
|
|
return Xbadoption.Range{
|
|
From: 1000000,
|
|
To: 1000000,
|
|
}
|
|
}
|
|
return c.ScMaxEachPostBytes
|
|
}
|
|
|
|
func (c *V2RayXHTTPBaseOptions) GetNormalizedScMinPostsIntervalMs() Xbadoption.Range {
|
|
if c.ScMinPostsIntervalMs.To == 0 {
|
|
return Xbadoption.Range{
|
|
From: 30,
|
|
To: 30,
|
|
}
|
|
}
|
|
return c.ScMinPostsIntervalMs
|
|
}
|
|
|
|
func (c *V2RayXHTTPBaseOptions) GetNormalizedScMaxBufferedPosts() int {
|
|
if c.ScMaxBufferedPosts == 0 {
|
|
return 30
|
|
}
|
|
|
|
return int(c.ScMaxBufferedPosts)
|
|
}
|
|
|
|
func (c *V2RayXHTTPBaseOptions) GetNormalizedScStreamUpServerSecs() Xbadoption.Range {
|
|
if c.ScStreamUpServerSecs.To == 0 {
|
|
return Xbadoption.Range{
|
|
From: 20,
|
|
To: 80,
|
|
}
|
|
}
|
|
return c.ScStreamUpServerSecs
|
|
}
|
|
|
|
type V2RayXHTTPXmuxOptions struct {
|
|
MaxConcurrency Xbadoption.Range `json:"max_concurrency"`
|
|
MaxConnections Xbadoption.Range `json:"max_connections"`
|
|
CMaxReuseTimes Xbadoption.Range `json:"c_max_reuse_times"`
|
|
HMaxRequestTimes Xbadoption.Range `json:"h_max_request_times"`
|
|
HMaxReusableSecs Xbadoption.Range `json:"h_max_reusable_secs"`
|
|
HKeepAlivePeriod int64 `json:"h_keep_alive_period"`
|
|
}
|
|
|
|
func (m *V2RayXHTTPXmuxOptions) GetNormalizedMaxConcurrency() Xbadoption.Range {
|
|
return m.MaxConcurrency
|
|
}
|
|
|
|
func (m *V2RayXHTTPXmuxOptions) GetNormalizedMaxConnections() Xbadoption.Range {
|
|
return m.MaxConnections
|
|
}
|
|
|
|
func (m *V2RayXHTTPXmuxOptions) GetNormalizedCMaxReuseTimes() Xbadoption.Range {
|
|
return m.CMaxReuseTimes
|
|
}
|
|
|
|
func (m *V2RayXHTTPXmuxOptions) GetNormalizedHMaxRequestTimes() Xbadoption.Range {
|
|
return m.HMaxRequestTimes
|
|
}
|
|
|
|
func (m *V2RayXHTTPXmuxOptions) GetNormalizedHMaxReusableSecs() Xbadoption.Range {
|
|
return m.HMaxReusableSecs
|
|
}
|
|
|
|
type V2RayKCPOptions struct {
|
|
MTU uint32 `json:"mtu,omitempty"`
|
|
TTI uint32 `json:"tti,omitempty"`
|
|
UplinkCapacity uint32 `json:"uplink_capacity,omitempty"`
|
|
DownlinkCapacity uint32 `json:"downlink_capacity,omitempty"`
|
|
Congestion bool `json:"congestion,omitempty"`
|
|
ReadBufferSize uint32 `json:"read_buffer_size,omitempty"`
|
|
WriteBufferSize uint32 `json:"write_buffer_size,omitempty"`
|
|
HeaderType string `json:"header_type,omitempty"`
|
|
Seed string `json:"seed,omitempty"`
|
|
}
|
|
|
|
func (k *V2RayKCPOptions) GetMTU() uint32 {
|
|
if k.MTU == 0 {
|
|
return 1350
|
|
}
|
|
return k.MTU
|
|
}
|
|
|
|
func (k *V2RayKCPOptions) GetTTI() uint32 {
|
|
if k.TTI == 0 {
|
|
return 50
|
|
}
|
|
return k.TTI
|
|
}
|
|
|
|
func (k *V2RayKCPOptions) GetUplinkCapacity() uint32 {
|
|
if k.UplinkCapacity == 0 {
|
|
return 12
|
|
}
|
|
return k.UplinkCapacity
|
|
}
|
|
|
|
func (k *V2RayKCPOptions) GetDownlinkCapacity() uint32 {
|
|
if k.DownlinkCapacity == 0 {
|
|
return 100
|
|
}
|
|
return k.DownlinkCapacity
|
|
}
|
|
|
|
func (k *V2RayKCPOptions) GetReadBufferSize() uint32 {
|
|
if k.ReadBufferSize == 0 {
|
|
return 1
|
|
}
|
|
return k.ReadBufferSize
|
|
}
|
|
|
|
func (k *V2RayKCPOptions) GetWriteBufferSize() uint32 {
|
|
if k.WriteBufferSize == 0 {
|
|
return 1
|
|
}
|
|
return k.WriteBufferSize
|
|
}
|
|
|
|
func (k *V2RayKCPOptions) GetHeaderType() string {
|
|
if k.HeaderType == "" {
|
|
return "none"
|
|
}
|
|
return k.HeaderType
|
|
}
|