diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index bf6d7a43..3bd45966 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -7,6 +7,7 @@ import ( "encoding/json" "math" "net/url" + "os" "runtime" "strconv" "strings" @@ -500,7 +501,7 @@ func (b Bandwidth) Bps() (uint64, error) { } type UdpHop struct { - PortList json.RawMessage `json:"port"` + PortList json.RawMessage `json:"ports"` Interval *Int32Range `json:"interval"` } @@ -519,21 +520,13 @@ type Masquerade struct { } type HysteriaConfig struct { - Version int32 `json:"version"` - Auth string `json:"auth"` - Congestion string `json:"congestion"` - Up Bandwidth `json:"up"` - Down Bandwidth `json:"down"` - UdpHop UdpHop `json:"udphop"` + Version int32 `json:"version"` + Auth string `json:"auth"` - InitStreamReceiveWindow uint64 `json:"initStreamReceiveWindow"` - MaxStreamReceiveWindow uint64 `json:"maxStreamReceiveWindow"` - InitConnectionReceiveWindow uint64 `json:"initConnectionReceiveWindow"` - MaxConnectionReceiveWindow uint64 `json:"maxConnectionReceiveWindow"` - MaxIdleTimeout int64 `json:"maxIdleTimeout"` - KeepAlivePeriod int64 `json:"keepAlivePeriod"` - DisablePathMTUDiscovery bool `json:"disablePathMTUDiscovery"` - MaxIncomingStreams int64 `json:"maxIncomingStreams"` + Congestion *string `json:"congestion"` + Up *Bandwidth `json:"up"` + Down *Bandwidth `json:"down"` + UdpHop *UdpHop `json:"udphop"` UdpIdleTimeout int64 `json:"udpIdleTimeout"` Masquerade Masquerade `json:"masquerade"` @@ -544,62 +537,10 @@ func (c *HysteriaConfig) Build() (proto.Message, error) { return nil, errors.New("version != 2") } - up, err := c.Up.Bps() - if err != nil { - return nil, err - } - down, err := c.Down.Bps() - if err != nil { - return nil, err + if c.Congestion != nil || c.Up != nil || c.Down != nil || c.UdpHop != nil { + errors.LogWarning(context.Background(), "congestion & up & down & udphop move to finalmask/quicParams") } - c.Congestion = strings.ToLower(c.Congestion) - if c.Congestion == "force-brutal" && up == 0 { - return nil, errors.New("force-brutal require up") - } - - var hop *PortList - if err := json.Unmarshal(c.UdpHop.PortList, &hop); err != nil { - hop = &PortList{} - } - - var inertvalMin, inertvalMax int64 - if c.UdpHop.Interval != nil { - inertvalMin = int64(c.UdpHop.Interval.From) - inertvalMax = int64(c.UdpHop.Interval.To) - } - - if up > 0 && up < 65536 { - return nil, errors.New("Up must be at least 65536 bytes per second") - } - if down > 0 && down < 65536 { - return nil, errors.New("Down must be at least 65536 bytes per second") - } - if (inertvalMin != 0 && inertvalMin < 5) || (inertvalMax != 0 && inertvalMax < 5) { - return nil, errors.New("Interval must be at least 5") - } - - if c.InitStreamReceiveWindow > 0 && c.InitStreamReceiveWindow < 16384 { - return nil, errors.New("InitStreamReceiveWindow must be at least 16384") - } - if c.MaxStreamReceiveWindow > 0 && c.MaxStreamReceiveWindow < 16384 { - return nil, errors.New("MaxStreamReceiveWindow must be at least 16384") - } - if c.InitConnectionReceiveWindow > 0 && c.InitConnectionReceiveWindow < 16384 { - return nil, errors.New("InitConnectionReceiveWindow must be at least 16384") - } - if c.MaxConnectionReceiveWindow > 0 && c.MaxConnectionReceiveWindow < 16384 { - return nil, errors.New("MaxConnectionReceiveWindow must be at least 16384") - } - if c.MaxIdleTimeout != 0 && (c.MaxIdleTimeout < 4 || c.MaxIdleTimeout > 120) { - return nil, errors.New("MaxIdleTimeout must be between 4 and 120") - } - if c.KeepAlivePeriod != 0 && (c.KeepAlivePeriod < 2 || c.KeepAlivePeriod > 60) { - return nil, errors.New("KeepAlivePeriod must be between 2 and 60") - } - if c.MaxIncomingStreams != 0 && c.MaxIncomingStreams < 8 { - return nil, errors.New("MaxIncomingStreams must be at least 8") - } if c.UdpIdleTimeout != 0 && (c.UdpIdleTimeout < 2 || c.UdpIdleTimeout > 600) { return nil, errors.New("UdpIdleTimeout must be between 2 and 600") } @@ -607,20 +548,6 @@ func (c *HysteriaConfig) Build() (proto.Message, error) { config := &hysteria.Config{} config.Version = c.Version config.Auth = c.Auth - config.Congestion = c.Congestion - config.Up = up - config.Down = down - config.Ports = hop.Build().Ports() - config.IntervalMin = inertvalMin - config.IntervalMax = inertvalMax - config.InitStreamReceiveWindow = c.InitStreamReceiveWindow - config.MaxStreamReceiveWindow = c.MaxStreamReceiveWindow - config.InitConnReceiveWindow = c.InitConnectionReceiveWindow - config.MaxConnReceiveWindow = c.MaxConnectionReceiveWindow - config.MaxIdleTimeout = c.MaxIdleTimeout - config.KeepAlivePeriod = c.KeepAlivePeriod - config.DisablePathMtuDiscovery = c.DisablePathMTUDiscovery - config.MaxIncomingStreams = c.MaxIncomingStreams config.UdpIdleTimeout = c.UdpIdleTimeout config.MasqType = c.Masquerade.Type config.MasqFile = c.Masquerade.Dir @@ -631,27 +558,6 @@ func (c *HysteriaConfig) Build() (proto.Message, error) { config.MasqStringHeaders = c.Masquerade.Headers config.MasqStringStatusCode = c.Masquerade.StatusCode - if config.InitStreamReceiveWindow == 0 { - config.InitStreamReceiveWindow = 8388608 - } - if config.MaxStreamReceiveWindow == 0 { - config.MaxStreamReceiveWindow = 8388608 - } - if config.InitConnReceiveWindow == 0 { - config.InitConnReceiveWindow = 8388608 * 5 / 2 - } - if config.MaxConnReceiveWindow == 0 { - config.MaxConnReceiveWindow = 8388608 * 5 / 2 - } - if config.MaxIdleTimeout == 0 { - config.MaxIdleTimeout = 30 - } - // if config.KeepAlivePeriod == 0 { - // config.KeepAlivePeriod = 10 - // } - if config.MaxIncomingStreams == 0 { - config.MaxIncomingStreams = 1024 - } if config.UdpIdleTimeout == 0 { config.UdpIdleTimeout = 60 } @@ -722,8 +628,19 @@ func (c *TLSCertConfig) Build() (*tls.Certificate, error) { } type QuicParamsConfig struct { - Congestion string `json:"congestion"` - Up Bandwidth `json:"up"` + Congestion string `json:"congestion"` + Debug bool `json:"debug"` + BrutalUp Bandwidth `json:"brutalUp"` + BrutalDown Bandwidth `json:"brutalDown"` + UdpHop UdpHop `json:"udpHop"` + InitStreamReceiveWindow uint64 `json:"initStreamReceiveWindow"` + MaxStreamReceiveWindow uint64 `json:"maxStreamReceiveWindow"` + InitConnectionReceiveWindow uint64 `json:"initConnectionReceiveWindow"` + MaxConnectionReceiveWindow uint64 `json:"maxConnectionReceiveWindow"` + MaxIdleTimeout int64 `json:"maxIdleTimeout"` + KeepAlivePeriod int64 `json:"keepAlivePeriod"` + DisablePathMTUDiscovery bool `json:"disablePathMTUDiscovery"` + MaxIncomingStreams int64 `json:"maxIncomingStreams"` } type TLSConfig struct { @@ -1918,25 +1835,92 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) { config.Udpmasks = append(config.Udpmasks, serial.ToTypedMessage(u)) } if c.FinalMask.QuicParams != nil { - up, err := c.FinalMask.QuicParams.Up.Bps() + up, err := c.FinalMask.QuicParams.BrutalUp.Bps() if err != nil { return nil, err } - if up > 0 && up < 65536 { - return nil, errors.New("Up must be at least 65536 bytes per second") + down, err := c.FinalMask.QuicParams.BrutalDown.Bps() + if err != nil { + return nil, err } + + if up > 0 && up < 65536 { + return nil, errors.New("BrutalUp must be at least 65536 bytes per second") + } + if down > 0 && down < 65536 { + return nil, errors.New("BrutalDown must be at least 65536 bytes per second") + } + + c.FinalMask.QuicParams.Congestion = strings.ToLower(c.FinalMask.QuicParams.Congestion) switch c.FinalMask.QuicParams.Congestion { - case "", "bbr", "reno": + case "", "brutal", "reno", "bbr": case "force-brutal": if up == 0 { return nil, errors.New("force-brutal requires up") } default: - return nil, errors.New("unknown congestion control: ", c.FinalMask.QuicParams.Congestion, ", valid values: bbr, reno, force-brutal") + return nil, errors.New("unknown congestion control: ", c.FinalMask.QuicParams.Congestion, ", valid values: reno, bbr, brutal, force-brutal") } + + var hop *PortList + if err := json.Unmarshal(c.FinalMask.QuicParams.UdpHop.PortList, &hop); err != nil { + hop = &PortList{} + } + + var inertvalMin, inertvalMax int64 + if c.FinalMask.QuicParams.UdpHop.Interval != nil { + inertvalMin = int64(c.FinalMask.QuicParams.UdpHop.Interval.From) + inertvalMax = int64(c.FinalMask.QuicParams.UdpHop.Interval.To) + } + + if (inertvalMin != 0 && inertvalMin < 5) || (inertvalMax != 0 && inertvalMax < 5) { + return nil, errors.New("Interval must be at least 5") + } + + if c.FinalMask.QuicParams.InitStreamReceiveWindow > 0 && c.FinalMask.QuicParams.InitStreamReceiveWindow < 16384 { + return nil, errors.New("InitStreamReceiveWindow must be at least 16384") + } + if c.FinalMask.QuicParams.MaxStreamReceiveWindow > 0 && c.FinalMask.QuicParams.MaxStreamReceiveWindow < 16384 { + return nil, errors.New("MaxStreamReceiveWindow must be at least 16384") + } + if c.FinalMask.QuicParams.InitConnectionReceiveWindow > 0 && c.FinalMask.QuicParams.InitConnectionReceiveWindow < 16384 { + return nil, errors.New("InitConnectionReceiveWindow must be at least 16384") + } + if c.FinalMask.QuicParams.MaxConnectionReceiveWindow > 0 && c.FinalMask.QuicParams.MaxConnectionReceiveWindow < 16384 { + return nil, errors.New("MaxConnectionReceiveWindow must be at least 16384") + } + if c.FinalMask.QuicParams.MaxIdleTimeout != 0 && (c.FinalMask.QuicParams.MaxIdleTimeout < 4 || c.FinalMask.QuicParams.MaxIdleTimeout > 120) { + return nil, errors.New("MaxIdleTimeout must be between 4 and 120") + } + if c.FinalMask.QuicParams.KeepAlivePeriod != 0 && (c.FinalMask.QuicParams.KeepAlivePeriod < 2 || c.FinalMask.QuicParams.KeepAlivePeriod > 60) { + return nil, errors.New("KeepAlivePeriod must be between 2 and 60") + } + if c.FinalMask.QuicParams.MaxIncomingStreams != 0 && c.FinalMask.QuicParams.MaxIncomingStreams < 8 { + return nil, errors.New("MaxIncomingStreams must be at least 8") + } + + if c.FinalMask.QuicParams.Debug { + os.Setenv("HYSTERIA_BBR_DEBUG", "true") + os.Setenv("HYSTERIA_BRUTAL_DEBUG", "true") + } + config.QuicParams = &internet.QuicParams{ Congestion: c.FinalMask.QuicParams.Congestion, - Up: up, + BrutalUp: up, + BrutalDown: down, + UdpHop: &internet.UdpHop{ + Ports: hop.Build().Ports(), + IntervalMin: inertvalMin, + IntervalMax: inertvalMax, + }, + InitStreamReceiveWindow: c.FinalMask.QuicParams.InitStreamReceiveWindow, + MaxStreamReceiveWindow: c.FinalMask.QuicParams.MaxStreamReceiveWindow, + InitConnReceiveWindow: c.FinalMask.QuicParams.InitConnectionReceiveWindow, + MaxConnReceiveWindow: c.FinalMask.QuicParams.MaxConnectionReceiveWindow, + MaxIdleTimeout: c.FinalMask.QuicParams.MaxIdleTimeout, + KeepAlivePeriod: c.FinalMask.QuicParams.KeepAlivePeriod, + DisablePathMtuDiscovery: c.FinalMask.QuicParams.DisablePathMTUDiscovery, + MaxIncomingStreams: c.FinalMask.QuicParams.MaxIncomingStreams, } } } diff --git a/transport/internet/config.pb.go b/transport/internet/config.pb.go index 9ab67df5..7e1da3f6 100644 --- a/transport/internet/config.pb.go +++ b/transport/internet/config.pb.go @@ -206,7 +206,7 @@ func (x SocketConfig_TProxyMode) Number() protoreflect.EnumNumber { // Deprecated: Use SocketConfig_TProxyMode.Descriptor instead. func (SocketConfig_TProxyMode) EnumDescriptor() ([]byte, []int) { - return file_transport_internet_config_proto_rawDescGZIP(), []int{5, 0} + return file_transport_internet_config_proto_rawDescGZIP(), []int{6, 0} } type TransportConfig struct { @@ -382,17 +382,87 @@ func (x *StreamConfig) GetSocketSettings() *SocketConfig { return nil } -type QuicParams struct { +type UdpHop struct { state protoimpl.MessageState `protogen:"open.v1"` - Congestion string `protobuf:"bytes,1,opt,name=congestion,proto3" json:"congestion,omitempty"` - Up uint64 `protobuf:"varint,2,opt,name=up,proto3" json:"up,omitempty"` + Ports []uint32 `protobuf:"varint,1,rep,packed,name=ports,proto3" json:"ports,omitempty"` + IntervalMin int64 `protobuf:"varint,2,opt,name=interval_min,json=intervalMin,proto3" json:"interval_min,omitempty"` + IntervalMax int64 `protobuf:"varint,3,opt,name=interval_max,json=intervalMax,proto3" json:"interval_max,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } +func (x *UdpHop) Reset() { + *x = UdpHop{} + mi := &file_transport_internet_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UdpHop) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UdpHop) ProtoMessage() {} + +func (x *UdpHop) ProtoReflect() protoreflect.Message { + mi := &file_transport_internet_config_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UdpHop.ProtoReflect.Descriptor instead. +func (*UdpHop) Descriptor() ([]byte, []int) { + return file_transport_internet_config_proto_rawDescGZIP(), []int{2} +} + +func (x *UdpHop) GetPorts() []uint32 { + if x != nil { + return x.Ports + } + return nil +} + +func (x *UdpHop) GetIntervalMin() int64 { + if x != nil { + return x.IntervalMin + } + return 0 +} + +func (x *UdpHop) GetIntervalMax() int64 { + if x != nil { + return x.IntervalMax + } + return 0 +} + +type QuicParams struct { + state protoimpl.MessageState `protogen:"open.v1"` + Congestion string `protobuf:"bytes,1,opt,name=congestion,proto3" json:"congestion,omitempty"` + BrutalUp uint64 `protobuf:"varint,2,opt,name=brutal_up,json=brutalUp,proto3" json:"brutal_up,omitempty"` + BrutalDown uint64 `protobuf:"varint,3,opt,name=brutal_down,json=brutalDown,proto3" json:"brutal_down,omitempty"` + UdpHop *UdpHop `protobuf:"bytes,4,opt,name=udp_hop,json=udpHop,proto3" json:"udp_hop,omitempty"` + InitStreamReceiveWindow uint64 `protobuf:"varint,5,opt,name=init_stream_receive_window,json=initStreamReceiveWindow,proto3" json:"init_stream_receive_window,omitempty"` + MaxStreamReceiveWindow uint64 `protobuf:"varint,6,opt,name=max_stream_receive_window,json=maxStreamReceiveWindow,proto3" json:"max_stream_receive_window,omitempty"` + InitConnReceiveWindow uint64 `protobuf:"varint,7,opt,name=init_conn_receive_window,json=initConnReceiveWindow,proto3" json:"init_conn_receive_window,omitempty"` + MaxConnReceiveWindow uint64 `protobuf:"varint,8,opt,name=max_conn_receive_window,json=maxConnReceiveWindow,proto3" json:"max_conn_receive_window,omitempty"` + MaxIdleTimeout int64 `protobuf:"varint,9,opt,name=max_idle_timeout,json=maxIdleTimeout,proto3" json:"max_idle_timeout,omitempty"` + KeepAlivePeriod int64 `protobuf:"varint,10,opt,name=keep_alive_period,json=keepAlivePeriod,proto3" json:"keep_alive_period,omitempty"` + DisablePathMtuDiscovery bool `protobuf:"varint,11,opt,name=disable_path_mtu_discovery,json=disablePathMtuDiscovery,proto3" json:"disable_path_mtu_discovery,omitempty"` + MaxIncomingStreams int64 `protobuf:"varint,12,opt,name=max_incoming_streams,json=maxIncomingStreams,proto3" json:"max_incoming_streams,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + func (x *QuicParams) Reset() { *x = QuicParams{} - mi := &file_transport_internet_config_proto_msgTypes[2] + mi := &file_transport_internet_config_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -404,7 +474,7 @@ func (x *QuicParams) String() string { func (*QuicParams) ProtoMessage() {} func (x *QuicParams) ProtoReflect() protoreflect.Message { - mi := &file_transport_internet_config_proto_msgTypes[2] + mi := &file_transport_internet_config_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -417,7 +487,7 @@ func (x *QuicParams) ProtoReflect() protoreflect.Message { // Deprecated: Use QuicParams.ProtoReflect.Descriptor instead. func (*QuicParams) Descriptor() ([]byte, []int) { - return file_transport_internet_config_proto_rawDescGZIP(), []int{2} + return file_transport_internet_config_proto_rawDescGZIP(), []int{3} } func (x *QuicParams) GetCongestion() string { @@ -427,9 +497,79 @@ func (x *QuicParams) GetCongestion() string { return "" } -func (x *QuicParams) GetUp() uint64 { +func (x *QuicParams) GetBrutalUp() uint64 { if x != nil { - return x.Up + return x.BrutalUp + } + return 0 +} + +func (x *QuicParams) GetBrutalDown() uint64 { + if x != nil { + return x.BrutalDown + } + return 0 +} + +func (x *QuicParams) GetUdpHop() *UdpHop { + if x != nil { + return x.UdpHop + } + return nil +} + +func (x *QuicParams) GetInitStreamReceiveWindow() uint64 { + if x != nil { + return x.InitStreamReceiveWindow + } + return 0 +} + +func (x *QuicParams) GetMaxStreamReceiveWindow() uint64 { + if x != nil { + return x.MaxStreamReceiveWindow + } + return 0 +} + +func (x *QuicParams) GetInitConnReceiveWindow() uint64 { + if x != nil { + return x.InitConnReceiveWindow + } + return 0 +} + +func (x *QuicParams) GetMaxConnReceiveWindow() uint64 { + if x != nil { + return x.MaxConnReceiveWindow + } + return 0 +} + +func (x *QuicParams) GetMaxIdleTimeout() int64 { + if x != nil { + return x.MaxIdleTimeout + } + return 0 +} + +func (x *QuicParams) GetKeepAlivePeriod() int64 { + if x != nil { + return x.KeepAlivePeriod + } + return 0 +} + +func (x *QuicParams) GetDisablePathMtuDiscovery() bool { + if x != nil { + return x.DisablePathMtuDiscovery + } + return false +} + +func (x *QuicParams) GetMaxIncomingStreams() int64 { + if x != nil { + return x.MaxIncomingStreams } return 0 } @@ -444,7 +584,7 @@ type ProxyConfig struct { func (x *ProxyConfig) Reset() { *x = ProxyConfig{} - mi := &file_transport_internet_config_proto_msgTypes[3] + mi := &file_transport_internet_config_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -456,7 +596,7 @@ func (x *ProxyConfig) String() string { func (*ProxyConfig) ProtoMessage() {} func (x *ProxyConfig) ProtoReflect() protoreflect.Message { - mi := &file_transport_internet_config_proto_msgTypes[3] + mi := &file_transport_internet_config_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -469,7 +609,7 @@ func (x *ProxyConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use ProxyConfig.ProtoReflect.Descriptor instead. func (*ProxyConfig) Descriptor() ([]byte, []int) { - return file_transport_internet_config_proto_rawDescGZIP(), []int{3} + return file_transport_internet_config_proto_rawDescGZIP(), []int{4} } func (x *ProxyConfig) GetTag() string { @@ -500,7 +640,7 @@ type CustomSockopt struct { func (x *CustomSockopt) Reset() { *x = CustomSockopt{} - mi := &file_transport_internet_config_proto_msgTypes[4] + mi := &file_transport_internet_config_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -512,7 +652,7 @@ func (x *CustomSockopt) String() string { func (*CustomSockopt) ProtoMessage() {} func (x *CustomSockopt) ProtoReflect() protoreflect.Message { - mi := &file_transport_internet_config_proto_msgTypes[4] + mi := &file_transport_internet_config_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -525,7 +665,7 @@ func (x *CustomSockopt) ProtoReflect() protoreflect.Message { // Deprecated: Use CustomSockopt.ProtoReflect.Descriptor instead. func (*CustomSockopt) Descriptor() ([]byte, []int) { - return file_transport_internet_config_proto_rawDescGZIP(), []int{4} + return file_transport_internet_config_proto_rawDescGZIP(), []int{5} } func (x *CustomSockopt) GetSystem() string { @@ -607,7 +747,7 @@ type SocketConfig struct { func (x *SocketConfig) Reset() { *x = SocketConfig{} - mi := &file_transport_internet_config_proto_msgTypes[5] + mi := &file_transport_internet_config_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -619,7 +759,7 @@ func (x *SocketConfig) String() string { func (*SocketConfig) ProtoMessage() {} func (x *SocketConfig) ProtoReflect() protoreflect.Message { - mi := &file_transport_internet_config_proto_msgTypes[5] + mi := &file_transport_internet_config_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -632,7 +772,7 @@ func (x *SocketConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use SocketConfig.ProtoReflect.Descriptor instead. func (*SocketConfig) Descriptor() ([]byte, []int) { - return file_transport_internet_config_proto_rawDescGZIP(), []int{5} + return file_transport_internet_config_proto_rawDescGZIP(), []int{6} } func (x *SocketConfig) GetMark() int32 { @@ -808,7 +948,7 @@ type HappyEyeballsConfig struct { func (x *HappyEyeballsConfig) Reset() { *x = HappyEyeballsConfig{} - mi := &file_transport_internet_config_proto_msgTypes[6] + mi := &file_transport_internet_config_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -820,7 +960,7 @@ func (x *HappyEyeballsConfig) String() string { func (*HappyEyeballsConfig) ProtoMessage() {} func (x *HappyEyeballsConfig) ProtoReflect() protoreflect.Message { - mi := &file_transport_internet_config_proto_msgTypes[6] + mi := &file_transport_internet_config_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -833,7 +973,7 @@ func (x *HappyEyeballsConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use HappyEyeballsConfig.ProtoReflect.Descriptor instead. func (*HappyEyeballsConfig) Descriptor() ([]byte, []int) { - return file_transport_internet_config_proto_rawDescGZIP(), []int{6} + return file_transport_internet_config_proto_rawDescGZIP(), []int{7} } func (x *HappyEyeballsConfig) GetPrioritizeIpv6() bool { @@ -884,13 +1024,29 @@ const file_transport_internet_config_proto_rawDesc = "" + "\btcpmasks\x18\v \x03(\v2 .xray.common.serial.TypedMessageR\btcpmasks\x12D\n" + "\vquic_params\x18\f \x01(\v2#.xray.transport.internet.QuicParamsR\n" + "quicParams\x12N\n" + - "\x0fsocket_settings\x18\x06 \x01(\v2%.xray.transport.internet.SocketConfigR\x0esocketSettings\"<\n" + + "\x0fsocket_settings\x18\x06 \x01(\v2%.xray.transport.internet.SocketConfigR\x0esocketSettings\"d\n" + + "\x06UdpHop\x12\x14\n" + + "\x05ports\x18\x01 \x03(\rR\x05ports\x12!\n" + + "\finterval_min\x18\x02 \x01(\x03R\vintervalMin\x12!\n" + + "\finterval_max\x18\x03 \x01(\x03R\vintervalMax\"\xd1\x04\n" + "\n" + "QuicParams\x12\x1e\n" + "\n" + "congestion\x18\x01 \x01(\tR\n" + - "congestion\x12\x0e\n" + - "\x02up\x18\x02 \x01(\x04R\x02up\"Q\n" + + "congestion\x12\x1b\n" + + "\tbrutal_up\x18\x02 \x01(\x04R\bbrutalUp\x12\x1f\n" + + "\vbrutal_down\x18\x03 \x01(\x04R\n" + + "brutalDown\x128\n" + + "\audp_hop\x18\x04 \x01(\v2\x1f.xray.transport.internet.UdpHopR\x06udpHop\x12;\n" + + "\x1ainit_stream_receive_window\x18\x05 \x01(\x04R\x17initStreamReceiveWindow\x129\n" + + "\x19max_stream_receive_window\x18\x06 \x01(\x04R\x16maxStreamReceiveWindow\x127\n" + + "\x18init_conn_receive_window\x18\a \x01(\x04R\x15initConnReceiveWindow\x125\n" + + "\x17max_conn_receive_window\x18\b \x01(\x04R\x14maxConnReceiveWindow\x12(\n" + + "\x10max_idle_timeout\x18\t \x01(\x03R\x0emaxIdleTimeout\x12*\n" + + "\x11keep_alive_period\x18\n" + + " \x01(\x03R\x0fkeepAlivePeriod\x12;\n" + + "\x1adisable_path_mtu_discovery\x18\v \x01(\bR\x17disablePathMtuDiscovery\x120\n" + + "\x14max_incoming_streams\x18\f \x01(\x03R\x12maxIncomingStreams\"Q\n" + "\vProxyConfig\x12\x10\n" + "\x03tag\x18\x01 \x01(\tR\x03tag\x120\n" + "\x13transportLayerProxy\x18\x02 \x01(\bR\x13transportLayerProxy\"\x93\x01\n" + @@ -979,40 +1135,42 @@ func file_transport_internet_config_proto_rawDescGZIP() []byte { } var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_transport_internet_config_proto_goTypes = []any{ (DomainStrategy)(0), // 0: xray.transport.internet.DomainStrategy (AddressPortStrategy)(0), // 1: xray.transport.internet.AddressPortStrategy (SocketConfig_TProxyMode)(0), // 2: xray.transport.internet.SocketConfig.TProxyMode (*TransportConfig)(nil), // 3: xray.transport.internet.TransportConfig (*StreamConfig)(nil), // 4: xray.transport.internet.StreamConfig - (*QuicParams)(nil), // 5: xray.transport.internet.QuicParams - (*ProxyConfig)(nil), // 6: xray.transport.internet.ProxyConfig - (*CustomSockopt)(nil), // 7: xray.transport.internet.CustomSockopt - (*SocketConfig)(nil), // 8: xray.transport.internet.SocketConfig - (*HappyEyeballsConfig)(nil), // 9: xray.transport.internet.HappyEyeballsConfig - (*serial.TypedMessage)(nil), // 10: xray.common.serial.TypedMessage - (*net.IPOrDomain)(nil), // 11: xray.common.net.IPOrDomain + (*UdpHop)(nil), // 5: xray.transport.internet.UdpHop + (*QuicParams)(nil), // 6: xray.transport.internet.QuicParams + (*ProxyConfig)(nil), // 7: xray.transport.internet.ProxyConfig + (*CustomSockopt)(nil), // 8: xray.transport.internet.CustomSockopt + (*SocketConfig)(nil), // 9: xray.transport.internet.SocketConfig + (*HappyEyeballsConfig)(nil), // 10: xray.transport.internet.HappyEyeballsConfig + (*serial.TypedMessage)(nil), // 11: xray.common.serial.TypedMessage + (*net.IPOrDomain)(nil), // 12: xray.common.net.IPOrDomain } var file_transport_internet_config_proto_depIdxs = []int32{ - 10, // 0: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage - 11, // 1: xray.transport.internet.StreamConfig.address:type_name -> xray.common.net.IPOrDomain + 11, // 0: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage + 12, // 1: xray.transport.internet.StreamConfig.address:type_name -> xray.common.net.IPOrDomain 3, // 2: xray.transport.internet.StreamConfig.transport_settings:type_name -> xray.transport.internet.TransportConfig - 10, // 3: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage - 10, // 4: xray.transport.internet.StreamConfig.udpmasks:type_name -> xray.common.serial.TypedMessage - 10, // 5: xray.transport.internet.StreamConfig.tcpmasks:type_name -> xray.common.serial.TypedMessage - 5, // 6: xray.transport.internet.StreamConfig.quic_params:type_name -> xray.transport.internet.QuicParams - 8, // 7: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig - 2, // 8: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode - 0, // 9: xray.transport.internet.SocketConfig.domain_strategy:type_name -> xray.transport.internet.DomainStrategy - 7, // 10: xray.transport.internet.SocketConfig.customSockopt:type_name -> xray.transport.internet.CustomSockopt - 1, // 11: xray.transport.internet.SocketConfig.address_port_strategy:type_name -> xray.transport.internet.AddressPortStrategy - 9, // 12: xray.transport.internet.SocketConfig.happy_eyeballs:type_name -> xray.transport.internet.HappyEyeballsConfig - 13, // [13:13] is the sub-list for method output_type - 13, // [13:13] is the sub-list for method input_type - 13, // [13:13] is the sub-list for extension type_name - 13, // [13:13] is the sub-list for extension extendee - 0, // [0:13] is the sub-list for field type_name + 11, // 3: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage + 11, // 4: xray.transport.internet.StreamConfig.udpmasks:type_name -> xray.common.serial.TypedMessage + 11, // 5: xray.transport.internet.StreamConfig.tcpmasks:type_name -> xray.common.serial.TypedMessage + 6, // 6: xray.transport.internet.StreamConfig.quic_params:type_name -> xray.transport.internet.QuicParams + 9, // 7: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig + 5, // 8: xray.transport.internet.QuicParams.udp_hop:type_name -> xray.transport.internet.UdpHop + 2, // 9: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode + 0, // 10: xray.transport.internet.SocketConfig.domain_strategy:type_name -> xray.transport.internet.DomainStrategy + 8, // 11: xray.transport.internet.SocketConfig.customSockopt:type_name -> xray.transport.internet.CustomSockopt + 1, // 12: xray.transport.internet.SocketConfig.address_port_strategy:type_name -> xray.transport.internet.AddressPortStrategy + 10, // 13: xray.transport.internet.SocketConfig.happy_eyeballs:type_name -> xray.transport.internet.HappyEyeballsConfig + 14, // [14:14] is the sub-list for method output_type + 14, // [14:14] is the sub-list for method input_type + 14, // [14:14] is the sub-list for extension type_name + 14, // [14:14] is the sub-list for extension extendee + 0, // [0:14] is the sub-list for field type_name } func init() { file_transport_internet_config_proto_init() } @@ -1026,7 +1184,7 @@ func file_transport_internet_config_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_config_proto_rawDesc), len(file_transport_internet_config_proto_rawDesc)), NumEnums: 3, - NumMessages: 7, + NumMessages: 8, NumExtensions: 0, NumServices: 0, }, diff --git a/transport/internet/config.proto b/transport/internet/config.proto index 5dedb1ae..653cb9aa 100644 --- a/transport/internet/config.proto +++ b/transport/internet/config.proto @@ -64,9 +64,25 @@ message StreamConfig { SocketConfig socket_settings = 6; } +message UdpHop { + repeated uint32 ports = 1; + int64 interval_min = 2; + int64 interval_max = 3; +} + message QuicParams { string congestion = 1; - uint64 up = 2; + uint64 brutal_up = 2; + uint64 brutal_down = 3; + UdpHop udp_hop = 4; + uint64 init_stream_receive_window = 5; + uint64 max_stream_receive_window = 6; + uint64 init_conn_receive_window = 7; + uint64 max_conn_receive_window = 8; + int64 max_idle_timeout = 9; + int64 keep_alive_period = 10; + bool disable_path_mtu_discovery = 11; + int64 max_incoming_streams = 12; } message ProxyConfig { diff --git a/transport/internet/finalmask/sudoku/sudoku_test.go b/transport/internet/finalmask/sudoku/sudoku_test.go index 4c22aba1..4713e4bc 100644 --- a/transport/internet/finalmask/sudoku/sudoku_test.go +++ b/transport/internet/finalmask/sudoku/sudoku_test.go @@ -372,9 +372,6 @@ func runHysteria2Case(t *testing.T, bin string, mode trafficMode, payloadSize in Settings: serial.ToTypedMessage(&hytransport.Config{ Version: 2, Auth: auth, - Congestion: "bbr", - Up: 10 * 1024 * 1024, - Down: 10 * 1024 * 1024, UdpIdleTimeout: 60, }), }, @@ -438,9 +435,6 @@ func runHysteria2Case(t *testing.T, bin string, mode trafficMode, payloadSize in Settings: serial.ToTypedMessage(&hytransport.Config{ Version: 2, Auth: auth, - Congestion: "bbr", - Up: 10 * 1024 * 1024, - Down: 10 * 1024 * 1024, UdpIdleTimeout: 60, }), }, diff --git a/transport/internet/hysteria/config.pb.go b/transport/internet/hysteria/config.pb.go index 913c26ff..7a0b02ca 100644 --- a/transport/internet/hysteria/config.pb.go +++ b/transport/internet/hysteria/config.pb.go @@ -22,34 +22,20 @@ const ( ) type Config struct { - state protoimpl.MessageState `protogen:"open.v1"` - Version int32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` - Auth string `protobuf:"bytes,2,opt,name=auth,proto3" json:"auth,omitempty"` - Congestion string `protobuf:"bytes,3,opt,name=congestion,proto3" json:"congestion,omitempty"` - Up uint64 `protobuf:"varint,4,opt,name=up,proto3" json:"up,omitempty"` - Down uint64 `protobuf:"varint,5,opt,name=down,proto3" json:"down,omitempty"` - Ports []uint32 `protobuf:"varint,6,rep,packed,name=ports,proto3" json:"ports,omitempty"` - IntervalMin int64 `protobuf:"varint,7,opt,name=interval_min,json=intervalMin,proto3" json:"interval_min,omitempty"` - IntervalMax int64 `protobuf:"varint,8,opt,name=interval_max,json=intervalMax,proto3" json:"interval_max,omitempty"` - InitStreamReceiveWindow uint64 `protobuf:"varint,9,opt,name=init_stream_receive_window,json=initStreamReceiveWindow,proto3" json:"init_stream_receive_window,omitempty"` - MaxStreamReceiveWindow uint64 `protobuf:"varint,10,opt,name=max_stream_receive_window,json=maxStreamReceiveWindow,proto3" json:"max_stream_receive_window,omitempty"` - InitConnReceiveWindow uint64 `protobuf:"varint,11,opt,name=init_conn_receive_window,json=initConnReceiveWindow,proto3" json:"init_conn_receive_window,omitempty"` - MaxConnReceiveWindow uint64 `protobuf:"varint,12,opt,name=max_conn_receive_window,json=maxConnReceiveWindow,proto3" json:"max_conn_receive_window,omitempty"` - MaxIdleTimeout int64 `protobuf:"varint,13,opt,name=max_idle_timeout,json=maxIdleTimeout,proto3" json:"max_idle_timeout,omitempty"` - KeepAlivePeriod int64 `protobuf:"varint,14,opt,name=keep_alive_period,json=keepAlivePeriod,proto3" json:"keep_alive_period,omitempty"` - DisablePathMtuDiscovery bool `protobuf:"varint,15,opt,name=disable_path_mtu_discovery,json=disablePathMtuDiscovery,proto3" json:"disable_path_mtu_discovery,omitempty"` - MaxIncomingStreams int64 `protobuf:"varint,16,opt,name=max_incoming_streams,json=maxIncomingStreams,proto3" json:"max_incoming_streams,omitempty"` - UdpIdleTimeout int64 `protobuf:"varint,17,opt,name=udp_idle_timeout,json=udpIdleTimeout,proto3" json:"udp_idle_timeout,omitempty"` - MasqType string `protobuf:"bytes,18,opt,name=masq_type,json=masqType,proto3" json:"masq_type,omitempty"` - MasqFile string `protobuf:"bytes,19,opt,name=masq_file,json=masqFile,proto3" json:"masq_file,omitempty"` - MasqUrl string `protobuf:"bytes,20,opt,name=masq_url,json=masqUrl,proto3" json:"masq_url,omitempty"` - MasqUrlRewriteHost bool `protobuf:"varint,21,opt,name=masq_url_rewrite_host,json=masqUrlRewriteHost,proto3" json:"masq_url_rewrite_host,omitempty"` - MasqUrlInsecure bool `protobuf:"varint,22,opt,name=masq_url_insecure,json=masqUrlInsecure,proto3" json:"masq_url_insecure,omitempty"` - MasqString string `protobuf:"bytes,23,opt,name=masq_string,json=masqString,proto3" json:"masq_string,omitempty"` - MasqStringHeaders map[string]string `protobuf:"bytes,24,rep,name=masq_string_headers,json=masqStringHeaders,proto3" json:"masq_string_headers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - MasqStringStatusCode int32 `protobuf:"varint,25,opt,name=masq_string_status_code,json=masqStringStatusCode,proto3" json:"masq_string_status_code,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Version int32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` + Auth string `protobuf:"bytes,2,opt,name=auth,proto3" json:"auth,omitempty"` + UdpIdleTimeout int64 `protobuf:"varint,3,opt,name=udp_idle_timeout,json=udpIdleTimeout,proto3" json:"udp_idle_timeout,omitempty"` + MasqType string `protobuf:"bytes,4,opt,name=masq_type,json=masqType,proto3" json:"masq_type,omitempty"` + MasqFile string `protobuf:"bytes,5,opt,name=masq_file,json=masqFile,proto3" json:"masq_file,omitempty"` + MasqUrl string `protobuf:"bytes,6,opt,name=masq_url,json=masqUrl,proto3" json:"masq_url,omitempty"` + MasqUrlRewriteHost bool `protobuf:"varint,7,opt,name=masq_url_rewrite_host,json=masqUrlRewriteHost,proto3" json:"masq_url_rewrite_host,omitempty"` + MasqUrlInsecure bool `protobuf:"varint,8,opt,name=masq_url_insecure,json=masqUrlInsecure,proto3" json:"masq_url_insecure,omitempty"` + MasqString string `protobuf:"bytes,9,opt,name=masq_string,json=masqString,proto3" json:"masq_string,omitempty"` + MasqStringHeaders map[string]string `protobuf:"bytes,10,rep,name=masq_string_headers,json=masqStringHeaders,proto3" json:"masq_string_headers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + MasqStringStatusCode int32 `protobuf:"varint,11,opt,name=masq_string_status_code,json=masqStringStatusCode,proto3" json:"masq_string_status_code,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *Config) Reset() { @@ -96,104 +82,6 @@ func (x *Config) GetAuth() string { return "" } -func (x *Config) GetCongestion() string { - if x != nil { - return x.Congestion - } - return "" -} - -func (x *Config) GetUp() uint64 { - if x != nil { - return x.Up - } - return 0 -} - -func (x *Config) GetDown() uint64 { - if x != nil { - return x.Down - } - return 0 -} - -func (x *Config) GetPorts() []uint32 { - if x != nil { - return x.Ports - } - return nil -} - -func (x *Config) GetIntervalMin() int64 { - if x != nil { - return x.IntervalMin - } - return 0 -} - -func (x *Config) GetIntervalMax() int64 { - if x != nil { - return x.IntervalMax - } - return 0 -} - -func (x *Config) GetInitStreamReceiveWindow() uint64 { - if x != nil { - return x.InitStreamReceiveWindow - } - return 0 -} - -func (x *Config) GetMaxStreamReceiveWindow() uint64 { - if x != nil { - return x.MaxStreamReceiveWindow - } - return 0 -} - -func (x *Config) GetInitConnReceiveWindow() uint64 { - if x != nil { - return x.InitConnReceiveWindow - } - return 0 -} - -func (x *Config) GetMaxConnReceiveWindow() uint64 { - if x != nil { - return x.MaxConnReceiveWindow - } - return 0 -} - -func (x *Config) GetMaxIdleTimeout() int64 { - if x != nil { - return x.MaxIdleTimeout - } - return 0 -} - -func (x *Config) GetKeepAlivePeriod() int64 { - if x != nil { - return x.KeepAlivePeriod - } - return 0 -} - -func (x *Config) GetDisablePathMtuDiscovery() bool { - if x != nil { - return x.DisablePathMtuDiscovery - } - return false -} - -func (x *Config) GetMaxIncomingStreams() int64 { - if x != nil { - return x.MaxIncomingStreams - } - return 0 -} - func (x *Config) GetUdpIdleTimeout() int64 { if x != nil { return x.UdpIdleTimeout @@ -261,37 +149,21 @@ var File_transport_internet_hysteria_config_proto protoreflect.FileDescriptor const file_transport_internet_hysteria_config_proto_rawDesc = "" + "\n" + - "(transport/internet/hysteria/config.proto\x12 xray.transport.internet.hysteria\"\xf0\b\n" + + "(transport/internet/hysteria/config.proto\x12 xray.transport.internet.hysteria\"\xa3\x04\n" + "\x06Config\x12\x18\n" + "\aversion\x18\x01 \x01(\x05R\aversion\x12\x12\n" + - "\x04auth\x18\x02 \x01(\tR\x04auth\x12\x1e\n" + - "\n" + - "congestion\x18\x03 \x01(\tR\n" + - "congestion\x12\x0e\n" + - "\x02up\x18\x04 \x01(\x04R\x02up\x12\x12\n" + - "\x04down\x18\x05 \x01(\x04R\x04down\x12\x14\n" + - "\x05ports\x18\x06 \x03(\rR\x05ports\x12!\n" + - "\finterval_min\x18\a \x01(\x03R\vintervalMin\x12!\n" + - "\finterval_max\x18\b \x01(\x03R\vintervalMax\x12;\n" + - "\x1ainit_stream_receive_window\x18\t \x01(\x04R\x17initStreamReceiveWindow\x129\n" + - "\x19max_stream_receive_window\x18\n" + - " \x01(\x04R\x16maxStreamReceiveWindow\x127\n" + - "\x18init_conn_receive_window\x18\v \x01(\x04R\x15initConnReceiveWindow\x125\n" + - "\x17max_conn_receive_window\x18\f \x01(\x04R\x14maxConnReceiveWindow\x12(\n" + - "\x10max_idle_timeout\x18\r \x01(\x03R\x0emaxIdleTimeout\x12*\n" + - "\x11keep_alive_period\x18\x0e \x01(\x03R\x0fkeepAlivePeriod\x12;\n" + - "\x1adisable_path_mtu_discovery\x18\x0f \x01(\bR\x17disablePathMtuDiscovery\x120\n" + - "\x14max_incoming_streams\x18\x10 \x01(\x03R\x12maxIncomingStreams\x12(\n" + - "\x10udp_idle_timeout\x18\x11 \x01(\x03R\x0eudpIdleTimeout\x12\x1b\n" + - "\tmasq_type\x18\x12 \x01(\tR\bmasqType\x12\x1b\n" + - "\tmasq_file\x18\x13 \x01(\tR\bmasqFile\x12\x19\n" + - "\bmasq_url\x18\x14 \x01(\tR\amasqUrl\x121\n" + - "\x15masq_url_rewrite_host\x18\x15 \x01(\bR\x12masqUrlRewriteHost\x12*\n" + - "\x11masq_url_insecure\x18\x16 \x01(\bR\x0fmasqUrlInsecure\x12\x1f\n" + - "\vmasq_string\x18\x17 \x01(\tR\n" + + "\x04auth\x18\x02 \x01(\tR\x04auth\x12(\n" + + "\x10udp_idle_timeout\x18\x03 \x01(\x03R\x0eudpIdleTimeout\x12\x1b\n" + + "\tmasq_type\x18\x04 \x01(\tR\bmasqType\x12\x1b\n" + + "\tmasq_file\x18\x05 \x01(\tR\bmasqFile\x12\x19\n" + + "\bmasq_url\x18\x06 \x01(\tR\amasqUrl\x121\n" + + "\x15masq_url_rewrite_host\x18\a \x01(\bR\x12masqUrlRewriteHost\x12*\n" + + "\x11masq_url_insecure\x18\b \x01(\bR\x0fmasqUrlInsecure\x12\x1f\n" + + "\vmasq_string\x18\t \x01(\tR\n" + "masqString\x12o\n" + - "\x13masq_string_headers\x18\x18 \x03(\v2?.xray.transport.internet.hysteria.Config.MasqStringHeadersEntryR\x11masqStringHeaders\x125\n" + - "\x17masq_string_status_code\x18\x19 \x01(\x05R\x14masqStringStatusCode\x1aD\n" + + "\x13masq_string_headers\x18\n" + + " \x03(\v2?.xray.transport.internet.hysteria.Config.MasqStringHeadersEntryR\x11masqStringHeaders\x125\n" + + "\x17masq_string_status_code\x18\v \x01(\x05R\x14masqStringStatusCode\x1aD\n" + "\x16MasqStringHeadersEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\x82\x01\n" + diff --git a/transport/internet/hysteria/config.proto b/transport/internet/hysteria/config.proto index fbc919cc..3d039da8 100644 --- a/transport/internet/hysteria/config.proto +++ b/transport/internet/hysteria/config.proto @@ -9,29 +9,14 @@ option java_multiple_files = true; message Config { int32 version = 1; string auth = 2; - string congestion = 3; - uint64 up = 4; - uint64 down = 5; - repeated uint32 ports = 6; - int64 interval_min = 7; - int64 interval_max = 8; - uint64 init_stream_receive_window = 9; - uint64 max_stream_receive_window = 10; - uint64 init_conn_receive_window = 11; - uint64 max_conn_receive_window = 12; - int64 max_idle_timeout = 13; - int64 keep_alive_period = 14; - bool disable_path_mtu_discovery = 15; - int64 max_incoming_streams = 16; - - int64 udp_idle_timeout = 17; - string masq_type = 18; - string masq_file = 19; - string masq_url = 20; - bool masq_url_rewrite_host = 21; - bool masq_url_insecure = 22; - string masq_string = 23; - map masq_string_headers = 24; - int32 masq_string_status_code = 25; + int64 udp_idle_timeout = 3; + string masq_type = 4; + string masq_file = 5; + string masq_url = 6; + bool masq_url_rewrite_host = 7; + bool masq_url_insecure = 8; + string masq_string = 9; + map masq_string_headers = 10; + int32 masq_string_status_code = 11; } \ No newline at end of file diff --git a/transport/internet/hysteria/dialer.go b/transport/internet/hysteria/dialer.go index 9a4c6374..d305b00d 100644 --- a/transport/internet/hysteria/dialer.go +++ b/transport/internet/hysteria/dialer.go @@ -120,8 +120,10 @@ type client struct { tlsConfig *go_tls.Config socketConfig *internet.SocketConfig udpmaskManager *finalmask.UdpmaskManager - udpSM *udpSessionManagerClient - mutex sync.Mutex + quicParams *internet.QuicParams + + udpSM *udpSessionManagerClient + mutex sync.Mutex } func (c *client) status() Status { @@ -153,10 +155,18 @@ func (c *client) dial() error { c.close() } + quicParams := c.quicParams + if quicParams == nil { + quicParams = &internet.QuicParams{} + } + if quicParams.UdpHop == nil { + quicParams.UdpHop = &internet.UdpHop{} + } + var index int - if len(c.config.Ports) > 0 { - index = rand.Intn(len(c.config.Ports)) - c.dest.Port = net.Port(c.config.Ports[index]) + if len(quicParams.UdpHop.Ports) > 0 { + index = rand.Intn(len(quicParams.UdpHop.Ports)) + c.dest.Port = net.Port(quicParams.UdpHop.Ports[index]) } raw, err := internet.DialSystem(c.ctx, c.dest, c.socketConfig) @@ -179,7 +189,7 @@ func (c *client) dial() error { pktConn = fakeConn remote = fakeConn.RemoteAddr().(*net.UDPAddr) - if len(c.config.Ports) > 0 { + if len(quicParams.UdpHop.Ports) > 0 { raw.Close() return errors.New("udphop requires being at the outermost level") } @@ -188,12 +198,12 @@ func (c *client) dial() error { return errors.New("unknown conn ", reflect.TypeOf(conn)) } - if len(c.config.Ports) > 0 { + if len(quicParams.UdpHop.Ports) > 0 { addr := &udphop.UDPHopAddr{ IP: remote.IP, - Ports: c.config.Ports, + Ports: quicParams.UdpHop.Ports, } - pktConn, err = udphop.NewUDPHopPacketConn(addr, c.config.IntervalMin, c.config.IntervalMax, c.udphopDialer, pktConn, index) + pktConn, err = udphop.NewUDPHopPacketConn(addr, index, quicParams.UdpHop.IntervalMin, quicParams.UdpHop.IntervalMax, c.udphopDialer, pktConn) if err != nil { raw.Close() return errors.New("udphop err").Base(err) @@ -208,21 +218,41 @@ func (c *client) dial() error { } } + quicConfig := &quic.Config{ + InitialStreamReceiveWindow: quicParams.InitStreamReceiveWindow, + MaxStreamReceiveWindow: quicParams.MaxStreamReceiveWindow, + InitialConnectionReceiveWindow: quicParams.InitConnReceiveWindow, + MaxConnectionReceiveWindow: quicParams.MaxConnReceiveWindow, + MaxIdleTimeout: time.Duration(quicParams.MaxIdleTimeout) * time.Second, + KeepAlivePeriod: time.Duration(quicParams.KeepAlivePeriod) * time.Second, + DisablePathMTUDiscovery: quicParams.DisablePathMtuDiscovery, + EnableDatagrams: true, + MaxDatagramFrameSize: MaxDatagramFrameSize, + DisablePathManager: true, + } + if quicParams.InitStreamReceiveWindow == 0 { + quicConfig.InitialStreamReceiveWindow = 8388608 + } + if quicParams.MaxStreamReceiveWindow == 0 { + quicConfig.MaxStreamReceiveWindow = 8388608 + } + if quicParams.InitConnReceiveWindow == 0 { + quicConfig.InitialConnectionReceiveWindow = 8388608 * 5 / 2 + } + if quicParams.MaxConnReceiveWindow == 0 { + quicConfig.MaxConnectionReceiveWindow = 8388608 * 5 / 2 + } + if quicParams.MaxIdleTimeout == 0 { + quicConfig.MaxIdleTimeout = 30 * time.Second + } + // if quicParams.KeepAlivePeriod == 0 { + // quicConfig.KeepAlivePeriod = 10 * time.Second + // } + var quicConn *quic.Conn rt := &http3.Transport{ TLSClientConfig: c.tlsConfig, - QUICConfig: &quic.Config{ - InitialStreamReceiveWindow: c.config.InitStreamReceiveWindow, - MaxStreamReceiveWindow: c.config.MaxStreamReceiveWindow, - InitialConnectionReceiveWindow: c.config.InitConnReceiveWindow, - MaxConnectionReceiveWindow: c.config.MaxConnReceiveWindow, - MaxIdleTimeout: time.Duration(c.config.MaxIdleTimeout) * time.Second, - KeepAlivePeriod: time.Duration(c.config.KeepAlivePeriod) * time.Second, - DisablePathMTUDiscovery: c.config.DisablePathMtuDiscovery, - EnableDatagrams: true, - MaxDatagramFrameSize: MaxDatagramFrameSize, - DisablePathManager: true, - }, + QUICConfig: quicConfig, Dial: func(ctx context.Context, _ string, tlsCfg *go_tls.Config, cfg *quic.Config) (*quic.Conn, error) { qc, err := quic.DialEarly(ctx, pktConn, remote, tlsCfg, cfg) if err != nil { @@ -241,7 +271,7 @@ func (c *client) dial() error { }, Header: http.Header{ RequestHeaderAuth: []string{c.config.Auth}, - CommonHeaderCCRX: []string{strconv.FormatUint(c.config.Down, 10)}, + CommonHeaderCCRX: []string{strconv.FormatUint(quicParams.BrutalDown, 10)}, CommonHeaderPadding: []string{authRequestPadding.String()}, }, } @@ -264,23 +294,23 @@ func (c *client) dial() error { serverAuto := resp.Header.Get(CommonHeaderCCRX) serverDown, _ := strconv.ParseUint(serverAuto, 10, 64) - switch c.config.Congestion { + switch quicParams.Congestion { case "reno": errors.LogDebug(c.ctx, "congestion reno") case "bbr": errors.LogDebug(c.ctx, "congestion bbr") congestion.UseBBR(quicConn) case "brutal", "": - if serverAuto == "auto" || c.config.Up == 0 || serverDown == 0 { + if serverAuto == "auto" || quicParams.BrutalUp == 0 || serverDown == 0 { errors.LogDebug(c.ctx, "congestion bbr") congestion.UseBBR(quicConn) } else { - errors.LogDebug(c.ctx, "congestion brutal bytes per second ", min(c.config.Up, serverDown)) - congestion.UseBrutal(quicConn, min(c.config.Up, serverDown)) + errors.LogDebug(c.ctx, "congestion brutal bytes per second ", min(quicParams.BrutalUp, serverDown)) + congestion.UseBrutal(quicConn, min(quicParams.BrutalUp, serverDown)) } case "force-brutal": - errors.LogDebug(c.ctx, "congestion brutal bytes per second ", c.config.Up) - congestion.UseBrutal(quicConn, c.config.Up) + errors.LogDebug(c.ctx, "congestion brutal bytes per second ", quicParams.BrutalUp) + congestion.UseBrutal(quicConn, quicParams.BrutalUp) default: errors.LogDebug(c.ctx, "congestion reno") } @@ -359,13 +389,14 @@ func (c *client) udphopDialer(addr *net.UDPAddr) (net.PacketConn, error) { defer c.mutex.Unlock() if c.status() != StatusActive { - errors.LogDebug(c.ctx, "skip hop: disconnected QUIC") + errors.LogDebug(context.Background(), "skip hop: disconnected QUIC") return nil, errors.New() } raw, err := internet.DialSystem(c.ctx, net.UDPDestination(net.IPAddress(addr.IP), net.Port(addr.Port)), c.socketConfig) if err != nil { - errors.LogDebug(c.ctx, "skip hop: failed to dial to dest") + errors.LogDebug(context.Background(), "skip hop: failed to dial to dest") + raw.Close() return nil, errors.New() } @@ -377,10 +408,12 @@ func (c *client) udphopDialer(addr *net.UDPAddr) (net.PacketConn, error) { case *net.UDPConn: pktConn = conn case *cnc.Connection: - errors.LogDebug(c.ctx, "skip hop: udphop requires being at the outermost level") + errors.LogDebug(context.Background(), "skip hop: udphop requires being at the outermost level") + raw.Close() return nil, errors.New() default: - errors.LogDebug(c.ctx, "skip hop: unknown conn ", reflect.TypeOf(conn)) + errors.LogDebug(context.Background(), "skip hop: unknown conn ", reflect.TypeOf(conn)) + raw.Close() return nil, errors.New() } @@ -424,6 +457,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me tlsConfig: tlsConfig.GetTLSConfig(), socketConfig: streamSettings.SocketSettings, udpmaskManager: streamSettings.UdpmaskManager, + quicParams: streamSettings.QuicParams, } manger.m[addr] = c } diff --git a/transport/internet/hysteria/hub.go b/transport/internet/hysteria/hub.go index d7b8b879..d68bb9f4 100644 --- a/transport/internet/hysteria/hub.go +++ b/transport/internet/hysteria/hub.go @@ -140,6 +140,7 @@ type httpHandler struct { addConn internet.ConnHandler config *Config + quicParams *internet.QuicParams validator *account.Validator masqHandler http.Handler @@ -155,7 +156,7 @@ func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if h.auth { w.Header().Set(ResponseHeaderUDPEnabled, strconv.FormatBool(hyCtx.RequireDatagramFromContext(h.ctx))) - w.Header().Set(CommonHeaderCCRX, strconv.FormatUint(h.config.Down, 10)) + w.Header().Set(CommonHeaderCCRX, strconv.FormatUint(h.quicParams.BrutalDown, 10)) w.Header().Set(CommonHeaderPadding, authResponsePadding.String()) w.WriteHeader(StatusAuthOK) return @@ -176,23 +177,23 @@ func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.auth = true h.user = user - switch h.config.Congestion { + switch h.quicParams.Congestion { case "reno": errors.LogDebug(context.Background(), h.conn.RemoteAddr(), " ", "congestion reno") case "bbr": errors.LogDebug(context.Background(), h.conn.RemoteAddr(), " ", "congestion bbr") congestion.UseBBR(h.conn) case "brutal", "": - if h.config.Up == 0 || clientDown == 0 { + if h.quicParams.BrutalUp == 0 || clientDown == 0 { errors.LogDebug(context.Background(), h.conn.RemoteAddr(), " ", "congestion bbr") congestion.UseBBR(h.conn) } else { - errors.LogDebug(context.Background(), h.conn.RemoteAddr(), " ", "congestion brutal bytes per second ", min(h.config.Up, clientDown)) - congestion.UseBrutal(h.conn, min(h.config.Up, clientDown)) + errors.LogDebug(context.Background(), h.conn.RemoteAddr(), " ", "congestion brutal bytes per second ", min(h.quicParams.BrutalUp, clientDown)) + congestion.UseBrutal(h.conn, min(h.quicParams.BrutalUp, clientDown)) } case "force-brutal": - errors.LogDebug(context.Background(), h.conn.RemoteAddr(), " ", "congestion brutal bytes per second ", h.config.Up) - congestion.UseBrutal(h.conn, h.config.Up) + errors.LogDebug(context.Background(), h.conn.RemoteAddr(), " ", "congestion brutal bytes per second ", h.quicParams.BrutalUp) + congestion.UseBrutal(h.conn, h.quicParams.BrutalUp) default: errors.LogDebug(context.Background(), h.conn.RemoteAddr(), " ", "congestion reno") } @@ -212,7 +213,7 @@ func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } w.Header().Set(ResponseHeaderUDPEnabled, strconv.FormatBool(hyCtx.RequireDatagramFromContext(h.ctx))) - w.Header().Set(CommonHeaderCCRX, strconv.FormatUint(h.config.Down, 10)) + w.Header().Set(CommonHeaderCCRX, strconv.FormatUint(h.quicParams.BrutalDown, 10)) w.Header().Set(CommonHeaderPadding, authResponsePadding.String()) w.WriteHeader(StatusAuthOK) return @@ -253,6 +254,7 @@ type Listener struct { addConn internet.ConnHandler config *Config + quicParams *internet.QuicParams validator *account.Validator masqHandler http.Handler } @@ -264,6 +266,7 @@ func (l *Listener) handleClient(conn *quic.Conn) { addConn: l.addConn, config: l.config, + quicParams: l.quicParams, validator: l.validator, masqHandler: l.masqHandler, } @@ -272,8 +275,8 @@ func (l *Listener) handleClient(conn *quic.Conn) { StreamDispatcher: handler.StreamDispatcher, } err := h3.ServeQUICConn(conn) - errors.LogDebug(context.Background(), conn.RemoteAddr(), " disconnected with err ", err) _ = conn.CloseWithError(closeErrCodeOK, "") + errors.LogDebug(context.Background(), conn.RemoteAddr(), " disconnected with err ", err) } func (l *Listener) keepAccepting() { @@ -377,18 +380,41 @@ func Listen(ctx context.Context, address net.Address, port net.Port, streamSetti } } + quicParams := streamSettings.QuicParams + if quicParams == nil { + quicParams = &internet.QuicParams{} + } + quicConfig := &quic.Config{ - InitialStreamReceiveWindow: config.InitStreamReceiveWindow, - MaxStreamReceiveWindow: config.MaxStreamReceiveWindow, - InitialConnectionReceiveWindow: config.InitConnReceiveWindow, - MaxConnectionReceiveWindow: config.MaxConnReceiveWindow, - MaxIdleTimeout: time.Duration(config.MaxIdleTimeout) * time.Second, - MaxIncomingStreams: config.MaxIncomingStreams, - DisablePathMTUDiscovery: config.DisablePathMtuDiscovery, + InitialStreamReceiveWindow: quicParams.InitStreamReceiveWindow, + MaxStreamReceiveWindow: quicParams.MaxStreamReceiveWindow, + InitialConnectionReceiveWindow: quicParams.InitConnReceiveWindow, + MaxConnectionReceiveWindow: quicParams.MaxConnReceiveWindow, + MaxIdleTimeout: time.Duration(quicParams.MaxIdleTimeout) * time.Second, + MaxIncomingStreams: quicParams.MaxIncomingStreams, + DisablePathMTUDiscovery: quicParams.DisablePathMtuDiscovery, EnableDatagrams: true, MaxDatagramFrameSize: MaxDatagramFrameSize, DisablePathManager: true, } + if quicParams.InitStreamReceiveWindow == 0 { + quicConfig.InitialStreamReceiveWindow = 8388608 + } + if quicParams.MaxStreamReceiveWindow == 0 { + quicConfig.MaxStreamReceiveWindow = 8388608 + } + if quicParams.InitConnReceiveWindow == 0 { + quicConfig.InitialConnectionReceiveWindow = 8388608 * 5 / 2 + } + if quicParams.MaxConnReceiveWindow == 0 { + quicConfig.MaxConnectionReceiveWindow = 8388608 * 5 / 2 + } + if quicParams.MaxIdleTimeout == 0 { + quicConfig.MaxIdleTimeout = 30 * time.Second + } + if quicParams.MaxIncomingStreams == 0 { + quicConfig.MaxIncomingStreams = 1024 + } qListener, err := quic.Listen(pktConn, tlsConfig.GetTLSConfig(), quicConfig) if err != nil { @@ -403,6 +429,7 @@ func Listen(ctx context.Context, address net.Address, port net.Port, streamSetti addConn: handler, config: config, + quicParams: quicParams, validator: validator, masqHandler: masqHandler, } diff --git a/transport/internet/hysteria/udphop/conn.go b/transport/internet/hysteria/udphop/conn.go index 663b27e5..50dcc36d 100644 --- a/transport/internet/hysteria/udphop/conn.go +++ b/transport/internet/hysteria/udphop/conn.go @@ -9,11 +9,12 @@ import ( "time" "github.com/xtls/xray-core/common/crypto" + "github.com/xtls/xray-core/transport/internet/finalmask" ) const ( packetQueueSize = 1024 - udpBufferSize = 2048 // QUIC packets are at most 1500 bytes long, so 2k should be more than enough + udpBufferSize = finalmask.UDPSize defaultHopInterval = 30 * time.Second ) @@ -49,7 +50,7 @@ type udpPacket struct { type ListenUDPFunc = func(*net.UDPAddr) (net.PacketConn, error) -func NewUDPHopPacketConn(addr *UDPHopAddr, intervalMin int64, intervalMax int64, listenUDPFunc ListenUDPFunc, pktConn net.PacketConn, index int) (net.PacketConn, error) { +func NewUDPHopPacketConn(addr *UDPHopAddr, index int, intervalMin int64, intervalMax int64, listenUDPFunc ListenUDPFunc, pktConn net.PacketConn) (net.PacketConn, error) { if intervalMin == 0 || intervalMax == 0 { intervalMin = int64(defaultHopInterval) intervalMax = int64(defaultHopInterval) diff --git a/transport/internet/hysteria/utils/portunion.go b/transport/internet/hysteria/utils/portunion.go deleted file mode 100644 index f76a6fd0..00000000 --- a/transport/internet/hysteria/utils/portunion.go +++ /dev/null @@ -1,107 +0,0 @@ -package utils - -import ( - "sort" - "strconv" - "strings" -) - -// PortUnion is a collection of multiple port ranges. -type PortUnion []PortRange - -// PortRange represents a range of ports. -// Start and End are inclusive. [Start, End] -type PortRange struct { - Start, End uint16 -} - -// ParsePortUnion parses a string of comma-separated port ranges (or single ports) into a PortUnion. -// Returns nil if the input is invalid. -// The returned PortUnion is guaranteed to be normalized. -func ParsePortUnion(s string) PortUnion { - if s == "all" || s == "*" { - // Wildcard special case - return PortUnion{PortRange{0, 65535}} - } - var result PortUnion - portStrs := strings.Split(s, ",") - for _, portStr := range portStrs { - if strings.Contains(portStr, "-") { - // Port range - portRange := strings.Split(portStr, "-") - if len(portRange) != 2 { - return nil - } - start, err := strconv.ParseUint(portRange[0], 10, 16) - if err != nil { - return nil - } - end, err := strconv.ParseUint(portRange[1], 10, 16) - if err != nil { - return nil - } - if start > end { - start, end = end, start - } - result = append(result, PortRange{uint16(start), uint16(end)}) - } else { - // Single port - port, err := strconv.ParseUint(portStr, 10, 16) - if err != nil { - return nil - } - result = append(result, PortRange{uint16(port), uint16(port)}) - } - } - if result == nil { - return nil - } - return result.Normalize() -} - -// Normalize normalizes a PortUnion. -// No overlapping ranges, ranges are sorted from low to high. -func (u PortUnion) Normalize() PortUnion { - if len(u) == 0 { - return u - } - sort.Slice(u, func(i, j int) bool { - if u[i].Start == u[j].Start { - return u[i].End < u[j].End - } - return u[i].Start < u[j].Start - }) - normalized := PortUnion{u[0]} - for _, current := range u[1:] { - last := &normalized[len(normalized)-1] - if uint32(current.Start) <= uint32(last.End)+1 { - if current.End > last.End { - last.End = current.End - } - } else { - normalized = append(normalized, current) - } - } - return normalized -} - -// Ports returns all ports in the PortUnion as a slice. -func (u PortUnion) Ports() []uint16 { - var ports []uint16 - for _, r := range u { - for i := uint32(r.Start); i <= uint32(r.End); i++ { - ports = append(ports, uint16(i)) - } - } - return ports -} - -// Contains returns true if the PortUnion contains the given port. -func (u PortUnion) Contains(port uint16) bool { - for _, r := range u { - if port >= r.Start && port <= r.End { - return true - } - } - return false -} diff --git a/transport/internet/hysteria/utils/portunion_test.go b/transport/internet/hysteria/utils/portunion_test.go deleted file mode 100644 index ba056a37..00000000 --- a/transport/internet/hysteria/utils/portunion_test.go +++ /dev/null @@ -1,150 +0,0 @@ -package utils - -import ( - "reflect" - "slices" - "testing" -) - -func TestParsePortUnion(t *testing.T) { - tests := []struct { - name string - s string - want PortUnion - }{ - { - name: "empty", - s: "", - want: nil, - }, - { - name: "all 1", - s: "all", - want: PortUnion{{0, 65535}}, - }, - { - name: "all 2", - s: "*", - want: PortUnion{{0, 65535}}, - }, - { - name: "single port", - s: "1234", - want: PortUnion{{1234, 1234}}, - }, - { - name: "multiple ports (unsorted)", - s: "5678,1234,9012", - want: PortUnion{{1234, 1234}, {5678, 5678}, {9012, 9012}}, - }, - { - name: "one range", - s: "1234-1240", - want: PortUnion{{1234, 1240}}, - }, - { - name: "one range (reversed)", - s: "1240-1234", - want: PortUnion{{1234, 1240}}, - }, - { - name: "multiple ports and ranges (reversed, unsorted, overlapping)", - s: "5678,1200-1236,9100-9012,1234-1240", - want: PortUnion{{1200, 1240}, {5678, 5678}, {9012, 9100}}, - }, - { - name: "multiple ports and ranges with 65535 (reversed, unsorted, overlapping)", - s: "5678,1200-1236,65531-65535,65532-65534,9100-9012,1234-1240", - want: PortUnion{{1200, 1240}, {5678, 5678}, {9012, 9100}, {65531, 65535}}, - }, - { - name: "multiple ports and ranges with 65535 (reversed, unsorted, overlapping) 2", - s: "5678,1200-1236,65532-65535,65531-65534,9100-9012,1234-1240", - want: PortUnion{{1200, 1240}, {5678, 5678}, {9012, 9100}, {65531, 65535}}, - }, - { - name: "invalid 1", - s: "1234-", - want: nil, - }, - { - name: "invalid 2", - s: "1234-ggez", - want: nil, - }, - { - name: "invalid 3", - s: "233,", - want: nil, - }, - { - name: "invalid 4", - s: "1234-1240-1250", - want: nil, - }, - { - name: "invalid 5", - s: "-,,", - want: nil, - }, - { - name: "invalid 6", - s: "http", - want: nil, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := ParsePortUnion(tt.s); !reflect.DeepEqual(got, tt.want) { - t.Errorf("ParsePortUnion() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestPortUnion_Ports(t *testing.T) { - tests := []struct { - name string - pu PortUnion - want []uint16 - }{ - { - name: "single port", - pu: PortUnion{{1234, 1234}}, - want: []uint16{1234}, - }, - { - name: "multiple ports", - pu: PortUnion{{1234, 1236}}, - want: []uint16{1234, 1235, 1236}, - }, - { - name: "multiple ports and ranges", - pu: PortUnion{{1234, 1236}, {5678, 5680}, {9000, 9002}}, - want: []uint16{1234, 1235, 1236, 5678, 5679, 5680, 9000, 9001, 9002}, - }, - { - name: "single port 65535", - pu: PortUnion{{65535, 65535}}, - want: []uint16{65535}, - }, - { - name: "port range with 65535", - pu: PortUnion{{65530, 65535}}, - want: []uint16{65530, 65531, 65532, 65533, 65534, 65535}, - }, - { - name: "multiple ports and ranges with 65535", - pu: PortUnion{{65530, 65535}, {1234, 1236}}, - want: []uint16{65530, 65531, 65532, 65533, 65534, 65535, 1234, 1235, 1236}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := tt.pu.Ports(); !slices.Equal(got, tt.want) { - t.Errorf("PortUnion.Ports() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/transport/internet/splithttp/dialer.go b/transport/internet/splithttp/dialer.go index 3471c633..71888a73 100644 --- a/transport/internet/splithttp/dialer.go +++ b/transport/internet/splithttp/dialer.go @@ -5,9 +5,11 @@ import ( gotls "crypto/tls" "fmt" "io" + "math/rand" "net/http" "net/http/httptrace" "net/url" + reflect "reflect" "strconv" "sync" "sync/atomic" @@ -24,6 +26,7 @@ import ( "github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet/browser_dialer" "github.com/xtls/xray-core/transport/internet/hysteria/congestion" + "github.com/xtls/xray-core/transport/internet/hysteria/udphop" "github.com/xtls/xray-core/transport/internet/reality" "github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/tls" @@ -153,25 +156,73 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea var transport http.RoundTripper if httpVersion == "3" { - if keepAlivePeriod == 0 { - keepAlivePeriod = net.QuicgoH3KeepAlivePeriod + quicParams := streamSettings.QuicParams + if quicParams == nil { + quicParams = &internet.QuicParams{} } - if keepAlivePeriod < 0 { - keepAlivePeriod = 0 + if quicParams.UdpHop == nil { + quicParams.UdpHop = &internet.UdpHop{} } - quicConfig := &quic.Config{ - MaxIdleTimeout: net.ConnIdleTimeout, + quicConfig := &quic.Config{ + InitialStreamReceiveWindow: quicParams.InitStreamReceiveWindow, + MaxStreamReceiveWindow: quicParams.MaxStreamReceiveWindow, + InitialConnectionReceiveWindow: quicParams.InitConnReceiveWindow, + MaxConnectionReceiveWindow: quicParams.MaxConnReceiveWindow, + MaxIdleTimeout: time.Duration(quicParams.MaxIdleTimeout) * time.Second, + KeepAlivePeriod: time.Duration(quicParams.KeepAlivePeriod) * time.Second, + MaxIncomingStreams: quicParams.MaxIncomingStreams, + DisablePathMTUDiscovery: quicParams.DisablePathMtuDiscovery, + } + if quicParams.MaxIdleTimeout == 0 { + quicConfig.MaxIdleTimeout = net.ConnIdleTimeout + } + if quicParams.KeepAlivePeriod == 0 { + if keepAlivePeriod == 0 { + quicConfig.KeepAlivePeriod = net.QuicgoH3KeepAlivePeriod + } + } + if quicParams.MaxIncomingStreams == 0 { // these two are defaults of quic-go/http3. the default of quic-go (no // http3) is different, so it is hardcoded here for clarity. // https://github.com/quic-go/quic-go/blob/b8ea5c798155950fb5bbfdd06cad1939c9355878/http3/client.go#L36-L39 - MaxIncomingStreams: -1, - KeepAlivePeriod: keepAlivePeriod, + quicConfig.MaxIncomingStreams = -1 } + transport = &http3.Transport{ QUICConfig: quicConfig, TLSClientConfig: gotlsConfig, Dial: func(ctx context.Context, addr string, tlsCfg *gotls.Config, cfg *quic.Config) (*quic.Conn, error) { + udphopDialer := func(addr *net.UDPAddr) (net.PacketConn, error) { + conn, err := internet.DialSystem(ctx, net.UDPDestination(net.IPAddress(addr.IP), net.Port(addr.Port)), streamSettings.SocketSettings) + if err != nil { + errors.LogDebug(context.Background(), "skip hop: failed to dial to dest") + conn.Close() + return nil, errors.New() + } + + var udpConn net.PacketConn + + switch c := conn.(type) { + case *internet.PacketConnWrapper: + udpConn = c.PacketConn + case *net.UDPConn: + udpConn = c + default: + errors.LogDebug(context.Background(), "skip hop: udphop requires being at the outermost level ", reflect.TypeOf(c)) + conn.Close() + return nil, errors.New() + } + + return udpConn, nil + } + + var index int + if len(quicParams.UdpHop.Ports) > 0 { + index = rand.Intn(len(quicParams.UdpHop.Ports)) + dest.Port = net.Port(quicParams.UdpHop.Ports[index]) + } + conn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings) if err != nil { return nil, err @@ -182,54 +233,69 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea switch c := conn.(type) { case *internet.PacketConnWrapper: - var ok bool - udpConn, ok = c.PacketConn.(*net.UDPConn) - if !ok { - return nil, errors.New("PacketConnWrapper does not contain a UDP connection") - } + udpConn = c.PacketConn udpAddr, err = net.ResolveUDPAddr("udp", c.Dest.String()) if err != nil { + conn.Close() return nil, err } case *net.UDPConn: udpConn = c udpAddr, err = net.ResolveUDPAddr("udp", c.RemoteAddr().String()) if err != nil { + conn.Close() return nil, err } default: udpConn = &internet.FakePacketConn{Conn: c} udpAddr, err = net.ResolveUDPAddr("udp", c.RemoteAddr().String()) if err != nil { + conn.Close() return nil, err } + + if len(quicParams.UdpHop.Ports) > 0 { + conn.Close() + return nil, errors.New("udphop requires being at the outermost level ", reflect.TypeOf(c)) + } + } + + if len(quicParams.UdpHop.Ports) > 0 { + addr := &udphop.UDPHopAddr{ + IP: udpAddr.IP, + Ports: quicParams.UdpHop.Ports, + } + udpConn, err = udphop.NewUDPHopPacketConn(addr, index, quicParams.UdpHop.IntervalMin, quicParams.UdpHop.IntervalMax, udphopDialer, udpConn) + if err != nil { + conn.Close() + return nil, errors.New("udphop err").Base(err) + } } if streamSettings.UdpmaskManager != nil { - pktConn, err := streamSettings.UdpmaskManager.WrapPacketConnClient(udpConn) + udpConn, err = streamSettings.UdpmaskManager.WrapPacketConnClient(udpConn) if err != nil { - udpConn.Close() + conn.Close() return nil, errors.New("mask err").Base(err) } - udpConn = pktConn } quicConn, err := quic.DialEarly(ctx, udpConn, udpAddr, tlsCfg, cfg) if err != nil { return nil, err } - if streamSettings.QuicParams != nil { - switch streamSettings.QuicParams.Congestion { - case "force-brutal": - congestion.UseBrutal(quicConn, streamSettings.QuicParams.Up) - case "reno": - // quic-go default, do nothing - default: - congestion.UseBBR(quicConn) - } - } else { + + switch quicParams.Congestion { + case "force-brutal": + errors.LogDebug(context.Background(), quicConn.RemoteAddr(), " ", "congestion brutal bytes per second ", quicParams.BrutalUp) + congestion.UseBrutal(quicConn, quicParams.BrutalUp) + case "reno": + errors.LogDebug(context.Background(), quicConn.RemoteAddr(), " ", "congestion reno") + default: + errors.LogDebug(context.Background(), quicConn.RemoteAddr(), " ", "congestion bbr") congestion.UseBBR(quicConn) } + return quicConn, nil }, } diff --git a/transport/internet/splithttp/hub.go b/transport/internet/splithttp/hub.go index e53ecb6a..c2ab8fbf 100644 --- a/transport/internet/splithttp/hub.go +++ b/transport/internet/splithttp/hub.go @@ -20,10 +20,10 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/transport/internet/hysteria/congestion" http_proto "github.com/xtls/xray-core/common/protocol/http" "github.com/xtls/xray-core/common/signal/done" "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/hysteria/congestion" "github.com/xtls/xray-core/transport/internet/reality" "github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/tls" @@ -254,7 +254,7 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req dataPlacement := h.config.GetNormalizedUplinkDataPlacement() var headerPayload []byte if dataPlacement == PlacementAuto || dataPlacement == PlacementHeader { - var headerPayloadChunks [] string + var headerPayloadChunks []string for i := 0; true; i++ { chunk := request.Header.Get(fmt.Sprintf("%s-%d", uplinkDataKey, i)) if chunk == "" { @@ -463,7 +463,6 @@ func ListenXH(ctx context.Context, address net.Address, port net.Port, streamSet if err != nil { return nil, errors.New("failed to listen UDP for XHTTP/3 on ", address, ":", port).Base(err) } - if streamSettings.UdpmaskManager != nil { pktConn, err := streamSettings.UdpmaskManager.WrapPacketConnServer(Conn) if err != nil { @@ -473,7 +472,22 @@ func ListenXH(ctx context.Context, address net.Address, port net.Port, streamSet Conn = pktConn } - l.h3listener, err = quic.ListenEarly(Conn, tlsConfig, nil) + quicParams := streamSettings.QuicParams + if quicParams == nil { + quicParams = &internet.QuicParams{} + } + + quicConfig := &quic.Config{ + InitialStreamReceiveWindow: quicParams.InitStreamReceiveWindow, + MaxStreamReceiveWindow: quicParams.MaxStreamReceiveWindow, + InitialConnectionReceiveWindow: quicParams.InitConnReceiveWindow, + MaxConnectionReceiveWindow: quicParams.MaxConnReceiveWindow, + MaxIdleTimeout: time.Duration(quicParams.MaxIdleTimeout) * time.Second, + MaxIncomingStreams: quicParams.MaxIncomingStreams, + DisablePathMTUDiscovery: quicParams.DisablePathMtuDiscovery, + } + + l.h3listener, err = quic.ListenEarly(Conn, tlsConfig, quicConfig) if err != nil { return nil, errors.New("failed to listen QUIC for XHTTP/3 on ", address, ":", port).Base(err) } @@ -491,22 +505,23 @@ func ListenXH(ctx context.Context, address net.Address, port net.Port, streamSet errors.LogInfoInner(ctx, err, "XHTTP/3 listener closed") return } - if streamSettings.QuicParams != nil { - switch streamSettings.QuicParams.Congestion { - case "force-brutal": - congestion.UseBrutal(conn, streamSettings.QuicParams.Up) - case "reno": - // quic-go default, do nothing - default: - congestion.UseBBR(conn) - } - } else { + + switch quicParams.Congestion { + case "force-brutal": + errors.LogDebug(context.Background(), conn.RemoteAddr(), " ", "congestion brutal bytes per second ", quicParams.BrutalUp) + congestion.UseBrutal(conn, quicParams.BrutalUp) + case "reno": + errors.LogDebug(context.Background(), conn.RemoteAddr(), " ", "congestion reno") + default: + errors.LogDebug(context.Background(), conn.RemoteAddr(), " ", "congestion bbr") congestion.UseBBR(conn) } + go func() { if err := l.h3server.ServeQUICConn(conn); err != nil { errors.LogDebugInner(ctx, err, "XHTTP/3 connection ended") } + _ = conn.CloseWithError(0, "") }() } }() @@ -573,6 +588,7 @@ func (ln *Listener) Addr() net.Addr { func (ln *Listener) Close() error { if ln.h3server != nil { if err := ln.h3server.Close(); err != nil { + _ = ln.h3listener.Close() return err } return ln.h3listener.Close()