Files
sing-box-extended/option/v2ray_transport.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
}