Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c862aa8dd1 | |||
| a28e215bf0 |
@@ -68,13 +68,13 @@ jobs:
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v4
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v4
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
|
||||
- name: Build Docker image (main architectures)
|
||||
id: build_main_arches
|
||||
uses: docker/build-push-action@v7
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: .github/docker/Dockerfile
|
||||
@@ -97,7 +97,7 @@ jobs:
|
||||
|
||||
- name: Build Docker image (additional architectures)
|
||||
id: build_additional_arches
|
||||
uses: docker/build-push-action@v7
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: .github/docker/Dockerfile.usa
|
||||
|
||||
@@ -173,7 +173,7 @@ jobs:
|
||||
file_glob: true
|
||||
|
||||
- name: Upload files to Artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: Xray-${{ env.ASSET_NAME }}
|
||||
path: |
|
||||
|
||||
@@ -280,7 +280,7 @@ jobs:
|
||||
file_glob: true
|
||||
|
||||
- name: Upload files to Artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: Xray-${{ env.ASSET_NAME }}
|
||||
path: |
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
|
||||
[](https://happ.su)
|
||||
|
||||
[](https://blanc.link/VMTSDqW)
|
||||
|
||||
[**Sponsor Xray-core**](https://github.com/XTLS/Xray-core/issues/3668)
|
||||
|
||||
## Donation & NFTs
|
||||
|
||||
@@ -183,9 +183,12 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
|
||||
if p.Stats.UserOnline {
|
||||
name := "user>>>" + user.Email + ">>>online"
|
||||
if om, _ := stats.GetOrRegisterOnlineMap(d.stats, name); om != nil {
|
||||
userIP := sessionInbound.Source.Address.String()
|
||||
sessionInbounds := session.InboundFromContext(ctx)
|
||||
userIP := sessionInbounds.Source.Address.String()
|
||||
om.AddIP(userIP)
|
||||
context.AfterFunc(ctx, func() { om.RemoveIP(userIP) })
|
||||
// log Online user with ips
|
||||
// errors.LogDebug(ctx, "user>>>" + user.Email + ">>>online", om.Count(), om.List())
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,9 +225,11 @@ func WrapLink(ctx context.Context, policyManager policy.Manager, statsManager st
|
||||
if p.Stats.UserOnline {
|
||||
name := "user>>>" + user.Email + ">>>online"
|
||||
if om, _ := stats.GetOrRegisterOnlineMap(statsManager, name); om != nil {
|
||||
userIP := sessionInbound.Source.Address.String()
|
||||
sessionInbounds := session.InboundFromContext(ctx)
|
||||
userIP := sessionInbounds.Source.Address.String()
|
||||
om.AddIP(userIP)
|
||||
context.AfterFunc(ctx, func() { om.RemoveIP(userIP) })
|
||||
// log Online user with ips
|
||||
// errors.LogDebug(ctx, "user>>>" + user.Email + ">>>online", om.Count(), om.List())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
|
||||
|
||||
req.Header.Add("Accept", "application/dns-message")
|
||||
req.Header.Add("Content-Type", "application/dns-message")
|
||||
utils.TryDefaultHeadersWith(req.Header, "fetch")
|
||||
req.Header.Set("User-Agent", utils.ChromeUA)
|
||||
req.Header.Set("X-Padding", utils.H2Base62Pad(crypto.RandBetween(100, 1000)))
|
||||
|
||||
hc := s.httpClient
|
||||
|
||||
@@ -32,10 +32,6 @@ func (o *Observer) GetObservation(ctx context.Context) (proto.Message, error) {
|
||||
return &observatory.ObservationResult{Status: o.createResult()}, nil
|
||||
}
|
||||
|
||||
func (o *Observer) Check(tag []string) {
|
||||
o.hp.Check(tag)
|
||||
}
|
||||
|
||||
func (o *Observer) createResult() []*observatory.OutboundStatus {
|
||||
var result []*observatory.OutboundStatus
|
||||
o.hp.access.Lock()
|
||||
|
||||
@@ -62,7 +62,7 @@ func (s *pingClient) MeasureDelay(httpMethod string) (time.Duration, error) {
|
||||
if err != nil {
|
||||
return rttFailed, err
|
||||
}
|
||||
utils.TryDefaultHeadersWith(req.Header, "nav")
|
||||
req.Header.Set("User-Agent", utils.ChromeUA)
|
||||
|
||||
start := time.Now()
|
||||
resp, err := s.httpClient.Do(req)
|
||||
|
||||
@@ -164,7 +164,7 @@ func (o *Observer) probe(outbound string) ProbeResult {
|
||||
probeURL = o.config.ProbeUrl
|
||||
}
|
||||
req, _ := http.NewRequest(http.MethodGet, probeURL, nil)
|
||||
utils.TryDefaultHeadersWith(req.Header, "nav")
|
||||
req.Header.Set("User-Agent", utils.ChromeUA)
|
||||
response, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return errors.New("outbound failed to relay connection").Base(err)
|
||||
|
||||
@@ -18,7 +18,6 @@ type Rule struct {
|
||||
RuleTag string
|
||||
Balancer *Balancer
|
||||
Condition Condition
|
||||
Webhook *WebhookNotifier
|
||||
}
|
||||
|
||||
func (r *Rule) GetTag() (string, error) {
|
||||
|
||||
+47
-127
@@ -129,7 +129,7 @@ func (x Config_DomainStrategy) Number() protoreflect.EnumNumber {
|
||||
|
||||
// Deprecated: Use Config_DomainStrategy.Descriptor instead.
|
||||
func (Config_DomainStrategy) EnumDescriptor() ([]byte, []int) {
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{11, 0}
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{10, 0}
|
||||
}
|
||||
|
||||
// Domain for routing decision.
|
||||
@@ -483,7 +483,6 @@ type RoutingRule struct {
|
||||
LocalPortList *net.PortList `protobuf:"bytes,18,opt,name=local_port_list,json=localPortList,proto3" json:"local_port_list,omitempty"`
|
||||
VlessRouteList *net.PortList `protobuf:"bytes,20,opt,name=vless_route_list,json=vlessRouteList,proto3" json:"vless_route_list,omitempty"`
|
||||
Process []string `protobuf:"bytes,21,rep,name=process,proto3" json:"process,omitempty"`
|
||||
Webhook *WebhookConfig `protobuf:"bytes,22,opt,name=webhook,proto3" json:"webhook,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@@ -648,13 +647,6 @@ func (x *RoutingRule) GetProcess() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *RoutingRule) GetWebhook() *WebhookConfig {
|
||||
if x != nil {
|
||||
return x.Webhook
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isRoutingRule_TargetTag interface {
|
||||
isRoutingRule_TargetTag()
|
||||
}
|
||||
@@ -673,66 +665,6 @@ func (*RoutingRule_Tag) isRoutingRule_TargetTag() {}
|
||||
|
||||
func (*RoutingRule_BalancingTag) isRoutingRule_TargetTag() {}
|
||||
|
||||
type WebhookConfig struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
|
||||
Deduplication uint32 `protobuf:"varint,2,opt,name=deduplication,proto3" json:"deduplication,omitempty"`
|
||||
Headers map[string]string `protobuf:"bytes,3,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *WebhookConfig) Reset() {
|
||||
*x = WebhookConfig{}
|
||||
mi := &file_app_router_config_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *WebhookConfig) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*WebhookConfig) ProtoMessage() {}
|
||||
|
||||
func (x *WebhookConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_config_proto_msgTypes[7]
|
||||
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 WebhookConfig.ProtoReflect.Descriptor instead.
|
||||
func (*WebhookConfig) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *WebhookConfig) GetUrl() string {
|
||||
if x != nil {
|
||||
return x.Url
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *WebhookConfig) GetDeduplication() uint32 {
|
||||
if x != nil {
|
||||
return x.Deduplication
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *WebhookConfig) GetHeaders() map[string]string {
|
||||
if x != nil {
|
||||
return x.Headers
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type BalancingRule struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||
@@ -746,7 +678,7 @@ type BalancingRule struct {
|
||||
|
||||
func (x *BalancingRule) Reset() {
|
||||
*x = BalancingRule{}
|
||||
mi := &file_app_router_config_proto_msgTypes[8]
|
||||
mi := &file_app_router_config_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -758,7 +690,7 @@ func (x *BalancingRule) String() string {
|
||||
func (*BalancingRule) ProtoMessage() {}
|
||||
|
||||
func (x *BalancingRule) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_config_proto_msgTypes[8]
|
||||
mi := &file_app_router_config_proto_msgTypes[7]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -771,7 +703,7 @@ func (x *BalancingRule) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use BalancingRule.ProtoReflect.Descriptor instead.
|
||||
func (*BalancingRule) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{8}
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *BalancingRule) GetTag() string {
|
||||
@@ -820,7 +752,7 @@ type StrategyWeight struct {
|
||||
|
||||
func (x *StrategyWeight) Reset() {
|
||||
*x = StrategyWeight{}
|
||||
mi := &file_app_router_config_proto_msgTypes[9]
|
||||
mi := &file_app_router_config_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -832,7 +764,7 @@ func (x *StrategyWeight) String() string {
|
||||
func (*StrategyWeight) ProtoMessage() {}
|
||||
|
||||
func (x *StrategyWeight) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_config_proto_msgTypes[9]
|
||||
mi := &file_app_router_config_proto_msgTypes[8]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -845,7 +777,7 @@ func (x *StrategyWeight) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use StrategyWeight.ProtoReflect.Descriptor instead.
|
||||
func (*StrategyWeight) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{9}
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{8}
|
||||
}
|
||||
|
||||
func (x *StrategyWeight) GetRegexp() bool {
|
||||
@@ -887,7 +819,7 @@ type StrategyLeastLoadConfig struct {
|
||||
|
||||
func (x *StrategyLeastLoadConfig) Reset() {
|
||||
*x = StrategyLeastLoadConfig{}
|
||||
mi := &file_app_router_config_proto_msgTypes[10]
|
||||
mi := &file_app_router_config_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -899,7 +831,7 @@ func (x *StrategyLeastLoadConfig) String() string {
|
||||
func (*StrategyLeastLoadConfig) ProtoMessage() {}
|
||||
|
||||
func (x *StrategyLeastLoadConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_config_proto_msgTypes[10]
|
||||
mi := &file_app_router_config_proto_msgTypes[9]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -912,7 +844,7 @@ func (x *StrategyLeastLoadConfig) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use StrategyLeastLoadConfig.ProtoReflect.Descriptor instead.
|
||||
func (*StrategyLeastLoadConfig) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{10}
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *StrategyLeastLoadConfig) GetCosts() []*StrategyWeight {
|
||||
@@ -961,7 +893,7 @@ type Config struct {
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
mi := &file_app_router_config_proto_msgTypes[11]
|
||||
mi := &file_app_router_config_proto_msgTypes[10]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -973,7 +905,7 @@ func (x *Config) String() string {
|
||||
func (*Config) ProtoMessage() {}
|
||||
|
||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_config_proto_msgTypes[11]
|
||||
mi := &file_app_router_config_proto_msgTypes[10]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -986,7 +918,7 @@ func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{11}
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{10}
|
||||
}
|
||||
|
||||
func (x *Config) GetDomainStrategy() Config_DomainStrategy {
|
||||
@@ -1024,7 +956,7 @@ type Domain_Attribute struct {
|
||||
|
||||
func (x *Domain_Attribute) Reset() {
|
||||
*x = Domain_Attribute{}
|
||||
mi := &file_app_router_config_proto_msgTypes[12]
|
||||
mi := &file_app_router_config_proto_msgTypes[11]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1036,7 +968,7 @@ func (x *Domain_Attribute) String() string {
|
||||
func (*Domain_Attribute) ProtoMessage() {}
|
||||
|
||||
func (x *Domain_Attribute) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_config_proto_msgTypes[12]
|
||||
mi := &file_app_router_config_proto_msgTypes[11]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1134,7 +1066,7 @@ const file_app_router_config_proto_rawDesc = "" +
|
||||
"\fcountry_code\x18\x01 \x01(\tR\vcountryCode\x12/\n" +
|
||||
"\x06domain\x18\x02 \x03(\v2\x17.xray.app.router.DomainR\x06domain\"=\n" +
|
||||
"\vGeoSiteList\x12.\n" +
|
||||
"\x05entry\x18\x01 \x03(\v2\x18.xray.app.router.GeoSiteR\x05entry\"\xbc\a\n" +
|
||||
"\x05entry\x18\x01 \x03(\v2\x18.xray.app.router.GeoSiteR\x05entry\"\x82\a\n" +
|
||||
"\vRoutingRule\x12\x12\n" +
|
||||
"\x03tag\x18\x01 \x01(\tH\x00R\x03tag\x12%\n" +
|
||||
"\rbalancing_tag\x18\f \x01(\tH\x00R\fbalancingTag\x12\x19\n" +
|
||||
@@ -1158,20 +1090,12 @@ const file_app_router_config_proto_rawDesc = "" +
|
||||
"localGeoip\x12A\n" +
|
||||
"\x0flocal_port_list\x18\x12 \x01(\v2\x19.xray.common.net.PortListR\rlocalPortList\x12C\n" +
|
||||
"\x10vless_route_list\x18\x14 \x01(\v2\x19.xray.common.net.PortListR\x0evlessRouteList\x12\x18\n" +
|
||||
"\aprocess\x18\x15 \x03(\tR\aprocess\x128\n" +
|
||||
"\awebhook\x18\x16 \x01(\v2\x1e.xray.app.router.WebhookConfigR\awebhook\x1a=\n" +
|
||||
"\aprocess\x18\x15 \x03(\tR\aprocess\x1a=\n" +
|
||||
"\x0fAttributesEntry\x12\x10\n" +
|
||||
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
||||
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\f\n" +
|
||||
"\n" +
|
||||
"target_tag\"\xca\x01\n" +
|
||||
"\rWebhookConfig\x12\x10\n" +
|
||||
"\x03url\x18\x01 \x01(\tR\x03url\x12$\n" +
|
||||
"\rdeduplication\x18\x02 \x01(\rR\rdeduplication\x12E\n" +
|
||||
"\aheaders\x18\x03 \x03(\v2+.xray.app.router.WebhookConfig.HeadersEntryR\aheaders\x1a:\n" +
|
||||
"\fHeadersEntry\x12\x10\n" +
|
||||
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
||||
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xdc\x01\n" +
|
||||
"target_tag\"\xdc\x01\n" +
|
||||
"\rBalancingRule\x12\x10\n" +
|
||||
"\x03tag\x18\x01 \x01(\tR\x03tag\x12+\n" +
|
||||
"\x11outbound_selector\x18\x02 \x03(\tR\x10outboundSelector\x12\x1a\n" +
|
||||
@@ -1212,7 +1136,7 @@ func file_app_router_config_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_app_router_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
|
||||
var file_app_router_config_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
|
||||
var file_app_router_config_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
|
||||
var file_app_router_config_proto_goTypes = []any{
|
||||
(Domain_Type)(0), // 0: xray.app.router.Domain.Type
|
||||
(Config_DomainStrategy)(0), // 1: xray.app.router.Config.DomainStrategy
|
||||
@@ -1223,47 +1147,43 @@ var file_app_router_config_proto_goTypes = []any{
|
||||
(*GeoSite)(nil), // 6: xray.app.router.GeoSite
|
||||
(*GeoSiteList)(nil), // 7: xray.app.router.GeoSiteList
|
||||
(*RoutingRule)(nil), // 8: xray.app.router.RoutingRule
|
||||
(*WebhookConfig)(nil), // 9: xray.app.router.WebhookConfig
|
||||
(*BalancingRule)(nil), // 10: xray.app.router.BalancingRule
|
||||
(*StrategyWeight)(nil), // 11: xray.app.router.StrategyWeight
|
||||
(*StrategyLeastLoadConfig)(nil), // 12: xray.app.router.StrategyLeastLoadConfig
|
||||
(*Config)(nil), // 13: xray.app.router.Config
|
||||
(*Domain_Attribute)(nil), // 14: xray.app.router.Domain.Attribute
|
||||
nil, // 15: xray.app.router.RoutingRule.AttributesEntry
|
||||
nil, // 16: xray.app.router.WebhookConfig.HeadersEntry
|
||||
(*net.PortList)(nil), // 17: xray.common.net.PortList
|
||||
(net.Network)(0), // 18: xray.common.net.Network
|
||||
(*serial.TypedMessage)(nil), // 19: xray.common.serial.TypedMessage
|
||||
(*BalancingRule)(nil), // 9: xray.app.router.BalancingRule
|
||||
(*StrategyWeight)(nil), // 10: xray.app.router.StrategyWeight
|
||||
(*StrategyLeastLoadConfig)(nil), // 11: xray.app.router.StrategyLeastLoadConfig
|
||||
(*Config)(nil), // 12: xray.app.router.Config
|
||||
(*Domain_Attribute)(nil), // 13: xray.app.router.Domain.Attribute
|
||||
nil, // 14: xray.app.router.RoutingRule.AttributesEntry
|
||||
(*net.PortList)(nil), // 15: xray.common.net.PortList
|
||||
(net.Network)(0), // 16: xray.common.net.Network
|
||||
(*serial.TypedMessage)(nil), // 17: xray.common.serial.TypedMessage
|
||||
}
|
||||
var file_app_router_config_proto_depIdxs = []int32{
|
||||
0, // 0: xray.app.router.Domain.type:type_name -> xray.app.router.Domain.Type
|
||||
14, // 1: xray.app.router.Domain.attribute:type_name -> xray.app.router.Domain.Attribute
|
||||
13, // 1: xray.app.router.Domain.attribute:type_name -> xray.app.router.Domain.Attribute
|
||||
3, // 2: xray.app.router.GeoIP.cidr:type_name -> xray.app.router.CIDR
|
||||
4, // 3: xray.app.router.GeoIPList.entry:type_name -> xray.app.router.GeoIP
|
||||
2, // 4: xray.app.router.GeoSite.domain:type_name -> xray.app.router.Domain
|
||||
6, // 5: xray.app.router.GeoSiteList.entry:type_name -> xray.app.router.GeoSite
|
||||
2, // 6: xray.app.router.RoutingRule.domain:type_name -> xray.app.router.Domain
|
||||
4, // 7: xray.app.router.RoutingRule.geoip:type_name -> xray.app.router.GeoIP
|
||||
17, // 8: xray.app.router.RoutingRule.port_list:type_name -> xray.common.net.PortList
|
||||
18, // 9: xray.app.router.RoutingRule.networks:type_name -> xray.common.net.Network
|
||||
15, // 8: xray.app.router.RoutingRule.port_list:type_name -> xray.common.net.PortList
|
||||
16, // 9: xray.app.router.RoutingRule.networks:type_name -> xray.common.net.Network
|
||||
4, // 10: xray.app.router.RoutingRule.source_geoip:type_name -> xray.app.router.GeoIP
|
||||
17, // 11: xray.app.router.RoutingRule.source_port_list:type_name -> xray.common.net.PortList
|
||||
15, // 12: xray.app.router.RoutingRule.attributes:type_name -> xray.app.router.RoutingRule.AttributesEntry
|
||||
15, // 11: xray.app.router.RoutingRule.source_port_list:type_name -> xray.common.net.PortList
|
||||
14, // 12: xray.app.router.RoutingRule.attributes:type_name -> xray.app.router.RoutingRule.AttributesEntry
|
||||
4, // 13: xray.app.router.RoutingRule.local_geoip:type_name -> xray.app.router.GeoIP
|
||||
17, // 14: xray.app.router.RoutingRule.local_port_list:type_name -> xray.common.net.PortList
|
||||
17, // 15: xray.app.router.RoutingRule.vless_route_list:type_name -> xray.common.net.PortList
|
||||
9, // 16: xray.app.router.RoutingRule.webhook:type_name -> xray.app.router.WebhookConfig
|
||||
16, // 17: xray.app.router.WebhookConfig.headers:type_name -> xray.app.router.WebhookConfig.HeadersEntry
|
||||
19, // 18: xray.app.router.BalancingRule.strategy_settings:type_name -> xray.common.serial.TypedMessage
|
||||
11, // 19: xray.app.router.StrategyLeastLoadConfig.costs:type_name -> xray.app.router.StrategyWeight
|
||||
1, // 20: xray.app.router.Config.domain_strategy:type_name -> xray.app.router.Config.DomainStrategy
|
||||
8, // 21: xray.app.router.Config.rule:type_name -> xray.app.router.RoutingRule
|
||||
10, // 22: xray.app.router.Config.balancing_rule:type_name -> xray.app.router.BalancingRule
|
||||
23, // [23:23] is the sub-list for method output_type
|
||||
23, // [23:23] is the sub-list for method input_type
|
||||
23, // [23:23] is the sub-list for extension type_name
|
||||
23, // [23:23] is the sub-list for extension extendee
|
||||
0, // [0:23] is the sub-list for field type_name
|
||||
15, // 14: xray.app.router.RoutingRule.local_port_list:type_name -> xray.common.net.PortList
|
||||
15, // 15: xray.app.router.RoutingRule.vless_route_list:type_name -> xray.common.net.PortList
|
||||
17, // 16: xray.app.router.BalancingRule.strategy_settings:type_name -> xray.common.serial.TypedMessage
|
||||
10, // 17: xray.app.router.StrategyLeastLoadConfig.costs:type_name -> xray.app.router.StrategyWeight
|
||||
1, // 18: xray.app.router.Config.domain_strategy:type_name -> xray.app.router.Config.DomainStrategy
|
||||
8, // 19: xray.app.router.Config.rule:type_name -> xray.app.router.RoutingRule
|
||||
9, // 20: xray.app.router.Config.balancing_rule:type_name -> xray.app.router.BalancingRule
|
||||
21, // [21:21] is the sub-list for method output_type
|
||||
21, // [21:21] is the sub-list for method input_type
|
||||
21, // [21:21] is the sub-list for extension type_name
|
||||
21, // [21:21] is the sub-list for extension extendee
|
||||
0, // [0:21] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_app_router_config_proto_init() }
|
||||
@@ -1275,7 +1195,7 @@ func file_app_router_config_proto_init() {
|
||||
(*RoutingRule_Tag)(nil),
|
||||
(*RoutingRule_BalancingTag)(nil),
|
||||
}
|
||||
file_app_router_config_proto_msgTypes[12].OneofWrappers = []any{
|
||||
file_app_router_config_proto_msgTypes[11].OneofWrappers = []any{
|
||||
(*Domain_Attribute_BoolValue)(nil),
|
||||
(*Domain_Attribute_IntValue)(nil),
|
||||
}
|
||||
@@ -1285,7 +1205,7 @@ func file_app_router_config_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_router_config_proto_rawDesc), len(file_app_router_config_proto_rawDesc)),
|
||||
NumEnums: 2,
|
||||
NumMessages: 15,
|
||||
NumMessages: 13,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -114,13 +114,6 @@ message RoutingRule {
|
||||
|
||||
xray.common.net.PortList vless_route_list = 20;
|
||||
repeated string process = 21;
|
||||
WebhookConfig webhook = 22;
|
||||
}
|
||||
|
||||
message WebhookConfig {
|
||||
string url = 1;
|
||||
uint32 deduplication = 2;
|
||||
map<string, string> headers = 3;
|
||||
}
|
||||
|
||||
message BalancingRule {
|
||||
|
||||
@@ -57,7 +57,6 @@ func (r *Router) Init(ctx context.Context, config *Config, d dns.Client, ohm out
|
||||
for _, rule := range config.Rule {
|
||||
cond, err := rule.BuildCondition()
|
||||
if err != nil {
|
||||
r.closeWebhooks()
|
||||
return err
|
||||
}
|
||||
rr := &Rule{
|
||||
@@ -65,22 +64,10 @@ func (r *Router) Init(ctx context.Context, config *Config, d dns.Client, ohm out
|
||||
Tag: rule.GetTag(),
|
||||
RuleTag: rule.GetRuleTag(),
|
||||
}
|
||||
if wh := rule.GetWebhook(); wh != nil {
|
||||
notifier, err := NewWebhookNotifier(wh)
|
||||
if err != nil {
|
||||
r.closeWebhooks()
|
||||
return err
|
||||
}
|
||||
rr.Webhook = notifier
|
||||
}
|
||||
btag := rule.GetBalancingTag()
|
||||
if len(btag) > 0 {
|
||||
brule, found := r.balancers[btag]
|
||||
if !found {
|
||||
if rr.Webhook != nil {
|
||||
rr.Webhook.Close()
|
||||
}
|
||||
r.closeWebhooks()
|
||||
return errors.New("balancer ", btag, " not found")
|
||||
}
|
||||
rr.Balancer = brule
|
||||
@@ -93,7 +80,6 @@ func (r *Router) Init(ctx context.Context, config *Config, d dns.Client, ohm out
|
||||
|
||||
// PickRoute implements routing.Router.
|
||||
func (r *Router) PickRoute(ctx routing.Context) (routing.Route, error) {
|
||||
originalCtx := ctx
|
||||
rule, ctx, err := r.pickRouteInternal(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -102,9 +88,6 @@ func (r *Router) PickRoute(ctx routing.Context) (routing.Route, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if rule.Webhook != nil {
|
||||
rule.Webhook.Fire(originalCtx, tag)
|
||||
}
|
||||
return &Route{Context: ctx, outboundTag: tag, ruleTag: rule.RuleTag}, nil
|
||||
}
|
||||
|
||||
@@ -126,11 +109,6 @@ func (r *Router) ReloadRules(config *Config, shouldAppend bool) error {
|
||||
defer r.mu.Unlock()
|
||||
|
||||
if !shouldAppend {
|
||||
for _, rule := range r.rules {
|
||||
if rule.Webhook != nil {
|
||||
rule.Webhook.Close()
|
||||
}
|
||||
}
|
||||
r.balancers = make(map[string]*Balancer, len(config.BalancingRule))
|
||||
r.rules = make([]*Rule, 0, len(config.Rule))
|
||||
}
|
||||
@@ -147,24 +125,12 @@ func (r *Router) ReloadRules(config *Config, shouldAppend bool) error {
|
||||
r.balancers[rule.Tag] = balancer
|
||||
}
|
||||
|
||||
startIdx := len(r.rules)
|
||||
closeNewWebhooks := func() {
|
||||
for i := startIdx; i < len(r.rules); i++ {
|
||||
if r.rules[i].Webhook != nil {
|
||||
r.rules[i].Webhook.Close()
|
||||
}
|
||||
}
|
||||
r.rules = r.rules[:startIdx]
|
||||
}
|
||||
|
||||
for _, rule := range config.Rule {
|
||||
if r.RuleExists(rule.GetRuleTag()) {
|
||||
closeNewWebhooks()
|
||||
return errors.New("duplicate ruleTag ", rule.GetRuleTag())
|
||||
}
|
||||
cond, err := rule.BuildCondition()
|
||||
if err != nil {
|
||||
closeNewWebhooks()
|
||||
return err
|
||||
}
|
||||
rr := &Rule{
|
||||
@@ -172,22 +138,10 @@ func (r *Router) ReloadRules(config *Config, shouldAppend bool) error {
|
||||
Tag: rule.GetTag(),
|
||||
RuleTag: rule.GetRuleTag(),
|
||||
}
|
||||
if wh := rule.GetWebhook(); wh != nil {
|
||||
notifier, err := NewWebhookNotifier(wh)
|
||||
if err != nil {
|
||||
closeNewWebhooks()
|
||||
return err
|
||||
}
|
||||
rr.Webhook = notifier
|
||||
}
|
||||
btag := rule.GetBalancingTag()
|
||||
if len(btag) > 0 {
|
||||
brule, found := r.balancers[btag]
|
||||
if !found {
|
||||
if rr.Webhook != nil {
|
||||
rr.Webhook.Close()
|
||||
}
|
||||
closeNewWebhooks()
|
||||
return errors.New("balancer ", btag, " not found")
|
||||
}
|
||||
rr.Balancer = brule
|
||||
@@ -219,8 +173,6 @@ func (r *Router) RemoveRule(tag string) error {
|
||||
for _, rule := range r.rules {
|
||||
if rule.RuleTag != tag {
|
||||
newRules = append(newRules, rule)
|
||||
} else if rule.Webhook != nil {
|
||||
rule.Webhook.Close()
|
||||
}
|
||||
}
|
||||
r.rules = newRules
|
||||
@@ -281,20 +233,8 @@ func (r *Router) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// closeWebhooks closes all webhook notifiers in the current rule set.
|
||||
func (r *Router) closeWebhooks() {
|
||||
for _, rule := range r.rules {
|
||||
if rule.Webhook != nil {
|
||||
rule.Webhook.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close implements common.Closable.
|
||||
func (r *Router) Close() error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.closeWebhooks()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,287 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
routing_session "github.com/xtls/xray-core/features/routing/session"
|
||||
)
|
||||
|
||||
// parseURL splits a webhook URL into an HTTP URL and an optional Unix socket
|
||||
// path. For regular http/https URLs the input is returned unchanged with an
|
||||
// empty socketPath. For Unix sockets the format is:
|
||||
//
|
||||
// /path/to/socket.sock:/http/path
|
||||
// @abstract:/http/path
|
||||
// @@padded:/http/path
|
||||
//
|
||||
// The :/ separator after the socket path delimits the HTTP request path.
|
||||
// If omitted, "/" is used.
|
||||
func parseURL(raw string) (httpURL, socketPath string) {
|
||||
if len(raw) == 0 || (!filepath.IsAbs(raw) && raw[0] != '@') {
|
||||
return raw, ""
|
||||
}
|
||||
if idx := strings.Index(raw, ":/"); idx >= 0 {
|
||||
return "http://localhost" + raw[idx+1:], raw[:idx]
|
||||
}
|
||||
return "http://localhost/", raw
|
||||
}
|
||||
|
||||
// resolveSocketPath applies platform-specific transformations to a Unix
|
||||
// socket path, matching the behaviour of the listen side in
|
||||
// transport/internet/system_listener.go.
|
||||
//
|
||||
// For abstract sockets (prefix @) on Linux/Android:
|
||||
// - single @ — used as-is (lock-free abstract socket)
|
||||
// - double @@ — stripped to single @ and padded to
|
||||
// syscall.RawSockaddrUnix{}.Path length (HAProxy compat)
|
||||
func resolveSocketPath(path string) string {
|
||||
if len(path) == 0 || path[0] != '@' {
|
||||
return path
|
||||
}
|
||||
if runtime.GOOS != "linux" && runtime.GOOS != "android" {
|
||||
return path
|
||||
}
|
||||
if len(path) > 1 && path[1] == '@' {
|
||||
fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path))
|
||||
copy(fullAddr, path[1:])
|
||||
return string(fullAddr)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func ptr[T any](v T) *T { return &v }
|
||||
|
||||
type event struct {
|
||||
Email *string `json:"email"`
|
||||
Level *uint32 `json:"level"`
|
||||
Protocol *string `json:"protocol"`
|
||||
Network *string `json:"network"`
|
||||
Source *string `json:"source"`
|
||||
Destination *string `json:"destination"`
|
||||
OriginalTarget *string `json:"originalTarget"`
|
||||
RouteTarget *string `json:"routeTarget"`
|
||||
InboundTag *string `json:"inboundTag"`
|
||||
InboundName *string `json:"inboundName"`
|
||||
InboundLocal *string `json:"inboundLocal"`
|
||||
OutboundTag *string `json:"outboundTag"`
|
||||
Timestamp int64 `json:"ts"`
|
||||
}
|
||||
|
||||
type WebhookNotifier struct {
|
||||
url string
|
||||
headers map[string]string
|
||||
deduplication uint32
|
||||
client *http.Client
|
||||
seen sync.Map
|
||||
done chan struct{}
|
||||
wg sync.WaitGroup
|
||||
closeOnce sync.Once
|
||||
}
|
||||
|
||||
func NewWebhookNotifier(cfg *WebhookConfig) (*WebhookNotifier, error) {
|
||||
if cfg == nil || cfg.Url == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
httpURL, socketPath := parseURL(cfg.Url)
|
||||
h := &WebhookNotifier{
|
||||
url: httpURL,
|
||||
deduplication: cfg.Deduplication,
|
||||
client: &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
},
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
|
||||
if socketPath != "" {
|
||||
dialAddr := resolveSocketPath(socketPath)
|
||||
h.client.Transport = &http.Transport{
|
||||
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
|
||||
var d net.Dialer
|
||||
return d.DialContext(ctx, "unix", dialAddr)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if len(cfg.Headers) > 0 {
|
||||
h.headers = make(map[string]string, len(cfg.Headers))
|
||||
for k, v := range cfg.Headers {
|
||||
h.headers[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
if h.deduplication > 0 {
|
||||
h.wg.Add(1)
|
||||
go h.cleanupLoop()
|
||||
}
|
||||
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func (h *WebhookNotifier) Fire(ctx routing.Context, outboundTag string) {
|
||||
ev := buildEvent(ctx, outboundTag)
|
||||
|
||||
email := ""
|
||||
if ev.Email != nil {
|
||||
email = *ev.Email
|
||||
}
|
||||
if h.isDuplicate(email) {
|
||||
return
|
||||
}
|
||||
|
||||
h.wg.Add(1)
|
||||
select {
|
||||
case <-h.done:
|
||||
h.wg.Done()
|
||||
return
|
||||
default:
|
||||
}
|
||||
go func() {
|
||||
defer h.wg.Done()
|
||||
h.post(ev)
|
||||
}()
|
||||
}
|
||||
|
||||
func buildEvent(ctx routing.Context, outboundTag string) *event {
|
||||
ev := &event{
|
||||
Timestamp: time.Now().Unix(),
|
||||
OutboundTag: ptr(outboundTag),
|
||||
InboundTag: ptr(ctx.GetInboundTag()),
|
||||
Protocol: ptr(ctx.GetProtocol()),
|
||||
Network: ptr(ctx.GetNetwork().SystemString()),
|
||||
}
|
||||
|
||||
if user := ctx.GetUser(); user != "" {
|
||||
ev.Email = ptr(user)
|
||||
}
|
||||
|
||||
if srcIPs := ctx.GetSourceIPs(); len(srcIPs) > 0 {
|
||||
srcPort := ctx.GetSourcePort()
|
||||
ev.Source = ptr(net.JoinHostPort(srcIPs[0].String(), srcPort.String()))
|
||||
}
|
||||
|
||||
targetPort := ctx.GetTargetPort()
|
||||
if domain := ctx.GetTargetDomain(); domain != "" {
|
||||
ev.Destination = ptr(net.JoinHostPort(domain, targetPort.String()))
|
||||
} else if targetIPs := ctx.GetTargetIPs(); len(targetIPs) > 0 {
|
||||
ev.Destination = ptr(net.JoinHostPort(targetIPs[0].String(), targetPort.String()))
|
||||
}
|
||||
|
||||
if localIPs := ctx.GetLocalIPs(); len(localIPs) > 0 {
|
||||
localPort := ctx.GetLocalPort()
|
||||
ev.InboundLocal = ptr(net.JoinHostPort(localIPs[0].String(), localPort.String()))
|
||||
}
|
||||
|
||||
if sctx, ok := ctx.(*routing_session.Context); ok {
|
||||
enrichFromSession(ev, sctx)
|
||||
}
|
||||
|
||||
return ev
|
||||
}
|
||||
|
||||
func enrichFromSession(ev *event, sctx *routing_session.Context) {
|
||||
if sctx.Inbound != nil {
|
||||
ev.InboundName = ptr(sctx.Inbound.Name)
|
||||
if sctx.Inbound.User != nil {
|
||||
ev.Level = ptr(sctx.Inbound.User.Level)
|
||||
}
|
||||
}
|
||||
if sctx.Outbound != nil {
|
||||
if sctx.Outbound.OriginalTarget.Address != nil {
|
||||
ev.OriginalTarget = ptr(sctx.Outbound.OriginalTarget.String())
|
||||
}
|
||||
if sctx.Outbound.RouteTarget.Address != nil {
|
||||
ev.RouteTarget = ptr(sctx.Outbound.RouteTarget.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *WebhookNotifier) post(ev *event) {
|
||||
body, err := json.Marshal(ev)
|
||||
if err != nil {
|
||||
errors.LogWarning(context.Background(), "webhook: marshal failed: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, h.url, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
errors.LogWarning(context.Background(), "webhook: request build failed: ", err)
|
||||
return
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
for k, v := range h.headers {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
|
||||
resp, err := h.client.Do(req)
|
||||
if err != nil {
|
||||
errors.LogInfo(context.Background(), "webhook: POST failed: ", err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
io.Copy(io.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
}()
|
||||
if resp.StatusCode >= 400 {
|
||||
errors.LogWarning(context.Background(), "webhook: POST returned status ", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *WebhookNotifier) isDuplicate(email string) bool {
|
||||
if h.deduplication == 0 || email == "" {
|
||||
return false
|
||||
}
|
||||
ttl := time.Duration(h.deduplication) * time.Second
|
||||
now := time.Now()
|
||||
if v, loaded := h.seen.LoadOrStore(email, now); loaded {
|
||||
if now.Sub(v.(time.Time)) < ttl {
|
||||
return true
|
||||
}
|
||||
h.seen.Store(email, now)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *WebhookNotifier) cleanupLoop() {
|
||||
defer h.wg.Done()
|
||||
ttl := time.Duration(h.deduplication) * time.Second
|
||||
ticker := time.NewTicker(ttl)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-h.done:
|
||||
return
|
||||
case <-ticker.C:
|
||||
now := time.Now()
|
||||
h.seen.Range(func(key, value any) bool {
|
||||
if now.Sub(value.(time.Time)) >= ttl {
|
||||
h.seen.Delete(key)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *WebhookNotifier) Close() error {
|
||||
h.closeOnce.Do(func() {
|
||||
close(h.done)
|
||||
})
|
||||
h.wg.Wait()
|
||||
h.client.CloseIdleConnections()
|
||||
return nil
|
||||
}
|
||||
@@ -70,7 +70,7 @@ func (s *statsServer) GetStatsOnlineIpList(ctx context.Context, request *GetStat
|
||||
}
|
||||
|
||||
ips := make(map[string]int64)
|
||||
for ip, t := range c.IPTimeMap() {
|
||||
for ip, t := range c.IpTimeMap() {
|
||||
ips[ip] = t.Unix()
|
||||
}
|
||||
|
||||
|
||||
+58
-72
@@ -2,98 +2,84 @@ package stats
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
localhostIPv4 = "127.0.0.1"
|
||||
localhostIPv6 = "[::1]"
|
||||
)
|
||||
|
||||
type ipEntry struct {
|
||||
refCount int
|
||||
lastSeen time.Time
|
||||
}
|
||||
|
||||
// OnlineMap is a refcount-based implementation of stats.OnlineMap.
|
||||
// IPs are tracked by reference counting: AddIP increments, RemoveIP decrements.
|
||||
// An IP is removed from the map when its reference count reaches zero.
|
||||
// OnlineMap is an implementation of stats.OnlineMap.
|
||||
type OnlineMap struct {
|
||||
entries map[string]*ipEntry
|
||||
access sync.Mutex
|
||||
count atomic.Int64
|
||||
ipList map[string]time.Time
|
||||
access sync.RWMutex
|
||||
lastCleanup time.Time
|
||||
cleanupPeriod time.Duration
|
||||
}
|
||||
|
||||
// NewOnlineMap creates a new OnlineMap instance.
|
||||
// NewOnlineMap creates a new instance of OnlineMap.
|
||||
func NewOnlineMap() *OnlineMap {
|
||||
return &OnlineMap{
|
||||
entries: make(map[string]*ipEntry),
|
||||
}
|
||||
}
|
||||
|
||||
// AddIP implements stats.OnlineMap.
|
||||
func (om *OnlineMap) AddIP(ip string) {
|
||||
if ip == localhostIPv4 || ip == localhostIPv6 {
|
||||
return
|
||||
}
|
||||
|
||||
om.access.Lock()
|
||||
defer om.access.Unlock()
|
||||
|
||||
if e, ok := om.entries[ip]; ok {
|
||||
e.refCount++
|
||||
e.lastSeen = time.Now()
|
||||
} else {
|
||||
om.entries[ip] = &ipEntry{
|
||||
refCount: 1,
|
||||
lastSeen: time.Now(),
|
||||
}
|
||||
om.count.Add(1)
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveIP implements stats.OnlineMap.
|
||||
func (om *OnlineMap) RemoveIP(ip string) {
|
||||
om.access.Lock()
|
||||
defer om.access.Unlock()
|
||||
|
||||
e, ok := om.entries[ip]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
e.refCount--
|
||||
if e.refCount <= 0 {
|
||||
delete(om.entries, ip)
|
||||
om.count.Add(-1)
|
||||
ipList: make(map[string]time.Time),
|
||||
lastCleanup: time.Now(),
|
||||
cleanupPeriod: 10 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
// Count implements stats.OnlineMap.
|
||||
func (om *OnlineMap) Count() int {
|
||||
return int(om.count.Load())
|
||||
func (c *OnlineMap) Count() int {
|
||||
c.access.RLock()
|
||||
defer c.access.RUnlock()
|
||||
|
||||
return len(c.ipList)
|
||||
}
|
||||
|
||||
// List implements stats.OnlineMap.
|
||||
func (om *OnlineMap) List() []string {
|
||||
om.access.Lock()
|
||||
defer om.access.Unlock()
|
||||
func (c *OnlineMap) List() []string {
|
||||
return c.GetKeys()
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(om.entries))
|
||||
for ip := range om.entries {
|
||||
keys = append(keys, ip)
|
||||
// AddIP implements stats.OnlineMap.
|
||||
func (c *OnlineMap) AddIP(ip string) {
|
||||
if ip == "127.0.0.1" {
|
||||
return
|
||||
}
|
||||
|
||||
c.access.Lock()
|
||||
c.ipList[ip] = time.Now()
|
||||
c.access.Unlock()
|
||||
|
||||
if time.Since(c.lastCleanup) > c.cleanupPeriod {
|
||||
c.RemoveExpiredIPs()
|
||||
c.lastCleanup = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *OnlineMap) GetKeys() []string {
|
||||
c.access.RLock()
|
||||
defer c.access.RUnlock()
|
||||
|
||||
keys := []string{}
|
||||
for k := range c.ipList {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// IPTimeMap implements stats.OnlineMap.
|
||||
func (om *OnlineMap) IPTimeMap() map[string]time.Time {
|
||||
om.access.Lock()
|
||||
defer om.access.Unlock()
|
||||
func (c *OnlineMap) RemoveExpiredIPs() {
|
||||
c.access.Lock()
|
||||
defer c.access.Unlock()
|
||||
|
||||
result := make(map[string]time.Time, len(om.entries))
|
||||
for ip, e := range om.entries {
|
||||
result[ip] = e.lastSeen
|
||||
now := time.Now()
|
||||
for k, t := range c.ipList {
|
||||
diff := now.Sub(t)
|
||||
if diff.Seconds() > 20 {
|
||||
delete(c.ipList, k)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *OnlineMap) IpTimeMap() map[string]time.Time {
|
||||
if time.Since(c.lastCleanup) > c.cleanupPeriod {
|
||||
c.RemoveExpiredIPs()
|
||||
c.lastCleanup = time.Now()
|
||||
}
|
||||
|
||||
return c.ipList
|
||||
}
|
||||
|
||||
+3
-3
@@ -163,12 +163,12 @@ func (m *Manager) GetChannel(name string) stats.Channel {
|
||||
|
||||
// GetAllOnlineUsers implements stats.Manager.
|
||||
func (m *Manager) GetAllOnlineUsers() []string {
|
||||
m.access.RLock()
|
||||
defer m.access.RUnlock()
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
|
||||
usersOnline := make([]string, 0, len(m.onlineMap))
|
||||
for user, onlineMap := range m.onlineMap {
|
||||
if onlineMap.Count() > 0 {
|
||||
if len(onlineMap.IpTimeMap()) > 0 {
|
||||
usersOnline = append(usersOnline, user)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,8 @@ package crypto // import "github.com/xtls/xray-core/common/crypto"
|
||||
import (
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
)
|
||||
|
||||
// [,)
|
||||
func RandBetween(from int64, to int64) int64 {
|
||||
if from == to {
|
||||
return from
|
||||
@@ -19,20 +16,3 @@ func RandBetween(from int64, to int64) int64 {
|
||||
bigInt, _ := rand.Int(rand.Reader, big.NewInt(to-from))
|
||||
return from + bigInt.Int64()
|
||||
}
|
||||
|
||||
// [,]
|
||||
func RandBytesBetween(b []byte, from, to byte) {
|
||||
common.Must2(rand.Read(b))
|
||||
|
||||
if from > to {
|
||||
from, to = to, from
|
||||
}
|
||||
|
||||
if to-from == 255 {
|
||||
return
|
||||
}
|
||||
|
||||
for i := range b {
|
||||
b[i] = from + b[i]%(to-from+1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,46 +10,46 @@ import (
|
||||
"github.com/xtls/xray-core/common/signal/done"
|
||||
)
|
||||
|
||||
type ConnectionOption func(*Connection)
|
||||
type ConnectionOption func(*connection)
|
||||
|
||||
func ConnectionLocalAddr(a net.Addr) ConnectionOption {
|
||||
return func(c *Connection) {
|
||||
return func(c *connection) {
|
||||
c.local = a
|
||||
}
|
||||
}
|
||||
|
||||
func ConnectionRemoteAddr(a net.Addr) ConnectionOption {
|
||||
return func(c *Connection) {
|
||||
return func(c *connection) {
|
||||
c.remote = a
|
||||
}
|
||||
}
|
||||
|
||||
func ConnectionInput(writer io.Writer) ConnectionOption {
|
||||
return func(c *Connection) {
|
||||
return func(c *connection) {
|
||||
c.writer = buf.NewWriter(writer)
|
||||
}
|
||||
}
|
||||
|
||||
func ConnectionInputMulti(writer buf.Writer) ConnectionOption {
|
||||
return func(c *Connection) {
|
||||
return func(c *connection) {
|
||||
c.writer = writer
|
||||
}
|
||||
}
|
||||
|
||||
func ConnectionOutput(reader io.Reader) ConnectionOption {
|
||||
return func(c *Connection) {
|
||||
return func(c *connection) {
|
||||
c.reader = &buf.BufferedReader{Reader: buf.NewReader(reader)}
|
||||
}
|
||||
}
|
||||
|
||||
func ConnectionOutputMulti(reader buf.Reader) ConnectionOption {
|
||||
return func(c *Connection) {
|
||||
return func(c *connection) {
|
||||
c.reader = &buf.BufferedReader{Reader: reader}
|
||||
}
|
||||
}
|
||||
|
||||
func ConnectionOutputMultiUDP(reader buf.Reader) ConnectionOption {
|
||||
return func(c *Connection) {
|
||||
return func(c *connection) {
|
||||
c.reader = &buf.BufferedReader{
|
||||
Reader: reader,
|
||||
Splitter: buf.SplitFirstBytes,
|
||||
@@ -58,13 +58,13 @@ func ConnectionOutputMultiUDP(reader buf.Reader) ConnectionOption {
|
||||
}
|
||||
|
||||
func ConnectionOnClose(n io.Closer) ConnectionOption {
|
||||
return func(c *Connection) {
|
||||
return func(c *connection) {
|
||||
c.onClose = n
|
||||
}
|
||||
}
|
||||
|
||||
func NewConnection(opts ...ConnectionOption) net.Conn {
|
||||
c := &Connection{
|
||||
c := &connection{
|
||||
done: done.New(),
|
||||
local: &net.TCPAddr{
|
||||
IP: []byte{0, 0, 0, 0},
|
||||
@@ -83,7 +83,7 @@ func NewConnection(opts ...ConnectionOption) net.Conn {
|
||||
return c
|
||||
}
|
||||
|
||||
type Connection struct {
|
||||
type connection struct {
|
||||
reader *buf.BufferedReader
|
||||
writer buf.Writer
|
||||
done *done.Instance
|
||||
@@ -92,17 +92,17 @@ type Connection struct {
|
||||
remote net.Addr
|
||||
}
|
||||
|
||||
func (c *Connection) Read(b []byte) (int, error) {
|
||||
func (c *connection) Read(b []byte) (int, error) {
|
||||
return c.reader.Read(b)
|
||||
}
|
||||
|
||||
// ReadMultiBuffer implements buf.Reader.
|
||||
func (c *Connection) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||
func (c *connection) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||
return c.reader.ReadMultiBuffer()
|
||||
}
|
||||
|
||||
// Write implements net.Conn.Write().
|
||||
func (c *Connection) Write(b []byte) (int, error) {
|
||||
func (c *connection) Write(b []byte) (int, error) {
|
||||
if c.done.Done() {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
@@ -113,7 +113,7 @@ func (c *Connection) Write(b []byte) (int, error) {
|
||||
return l, c.writer.WriteMultiBuffer(mb)
|
||||
}
|
||||
|
||||
func (c *Connection) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
func (c *connection) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
if c.done.Done() {
|
||||
buf.ReleaseMulti(mb)
|
||||
return io.ErrClosedPipe
|
||||
@@ -123,7 +123,7 @@ func (c *Connection) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
}
|
||||
|
||||
// Close implements net.Conn.Close().
|
||||
func (c *Connection) Close() error {
|
||||
func (c *connection) Close() error {
|
||||
common.Must(c.done.Close())
|
||||
common.Interrupt(c.reader)
|
||||
common.Close(c.writer)
|
||||
@@ -135,26 +135,26 @@ func (c *Connection) Close() error {
|
||||
}
|
||||
|
||||
// LocalAddr implements net.Conn.LocalAddr().
|
||||
func (c *Connection) LocalAddr() net.Addr {
|
||||
func (c *connection) LocalAddr() net.Addr {
|
||||
return c.local
|
||||
}
|
||||
|
||||
// RemoteAddr implements net.Conn.RemoteAddr().
|
||||
func (c *Connection) RemoteAddr() net.Addr {
|
||||
func (c *connection) RemoteAddr() net.Addr {
|
||||
return c.remote
|
||||
}
|
||||
|
||||
// SetDeadline implements net.Conn.SetDeadline().
|
||||
func (c *Connection) SetDeadline(t time.Time) error {
|
||||
func (c *connection) SetDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetReadDeadline implements net.Conn.SetReadDeadline().
|
||||
func (c *Connection) SetReadDeadline(t time.Time) error {
|
||||
func (c *connection) SetReadDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetWriteDeadline implements net.Conn.SetWriteDeadline().
|
||||
func (c *Connection) SetWriteDeadline(t time.Time) error {
|
||||
func (c *connection) SetWriteDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
+2
-165
@@ -4,8 +4,6 @@ import (
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"time"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/klauspost/cpuid/v2"
|
||||
)
|
||||
@@ -26,166 +24,5 @@ func ChromeVersion() int {
|
||||
return version - 1
|
||||
}
|
||||
|
||||
// The full Chromium brand GREASE implementation
|
||||
var clientHintGreaseNA = []string{" ", "(", ":", "-", ".", "/", ")", ";", "=", "?", "_"}
|
||||
var clientHintVersionNA = []string{"8", "99", "24"}
|
||||
var clientHintShuffle3 = [][3]int{{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}}
|
||||
var clientHintShuffle4 = [][4]int{
|
||||
{0, 1, 2, 3}, {0, 1, 3, 2}, {0, 2, 1, 3}, {0, 2, 3, 1}, {0, 3, 1, 2}, {0, 3, 2, 1},
|
||||
{1, 0, 2, 3}, {1, 0, 3, 2}, {1, 2, 0, 3}, {1, 2, 3, 0}, {1, 3, 0, 2}, {1, 3, 2, 0},
|
||||
{2, 0, 1, 3}, {2, 0, 3, 1}, {2, 1, 0, 3}, {2, 1, 3, 0}, {2, 3, 0, 1}, {2, 3, 1, 0},
|
||||
{3, 0, 1, 2}, {3, 0, 2, 1}, {3, 1, 0, 2}, {3, 1, 2, 0}, {3, 2, 0, 1}, {3, 2, 1, 0}}
|
||||
func getGreasedChInvalidBrand(seed int) string {
|
||||
return "\"Not" + clientHintGreaseNA[seed % len(clientHintGreaseNA)] + "A" + clientHintGreaseNA[(seed + 1) % len(clientHintGreaseNA)] + "Brand\";v=\"" + clientHintVersionNA[seed % len(clientHintVersionNA)] + "\"";
|
||||
}
|
||||
func getGreasedChOrder(brandLength int, seed int) []int {
|
||||
switch brandLength {
|
||||
case 1:
|
||||
return []int{0}
|
||||
case 2:
|
||||
return []int{seed % brandLength, (seed + 1) % brandLength}
|
||||
case 3:
|
||||
return clientHintShuffle3[seed % len(clientHintShuffle3)][:]
|
||||
default:
|
||||
return clientHintShuffle4[seed % len(clientHintShuffle4)][:]
|
||||
}
|
||||
return []int{}
|
||||
}
|
||||
func getUngreasedChUa(majorVersion int, forkName string) []string {
|
||||
// Set the capacity to 4, the maximum allowed brand size, so Go will never allocate memory twice
|
||||
baseChUa := make([]string, 0, 4)
|
||||
baseChUa = append(baseChUa, getGreasedChInvalidBrand(majorVersion),
|
||||
"\"Chromium\";v=\"" + strconv.Itoa(majorVersion) + "\"")
|
||||
switch forkName {
|
||||
case "chrome":
|
||||
baseChUa = append(baseChUa, "\"Google Chrome\";v=\"" + strconv.Itoa(majorVersion) + "\"")
|
||||
case "edge":
|
||||
baseChUa = append(baseChUa, "\"Microsoft Edge\";v=\"" + strconv.Itoa(majorVersion) + "\"")
|
||||
}
|
||||
return baseChUa
|
||||
}
|
||||
func getGreasedChUa(majorVersion int, forkName string) string {
|
||||
ungreasedCh := getUngreasedChUa(majorVersion, forkName)
|
||||
shuffleMap := getGreasedChOrder(len(ungreasedCh), majorVersion)
|
||||
shuffledCh := make([]string, len(ungreasedCh))
|
||||
for i, e := range shuffleMap {
|
||||
shuffledCh[e] = ungreasedCh[i]
|
||||
}
|
||||
return strings.Join(shuffledCh, ", ")
|
||||
}
|
||||
|
||||
// It's better to pin on Firefox ESR releases, and there could be a Firefox ESR version generator later.
|
||||
// However, if the Firefox fingerprint in uTLS doesn't have its update cadence match that of Firefox ESR, then it's better to update the Firefox version manually instead every time a new major ESR release is available.
|
||||
var FirefoxUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0"
|
||||
|
||||
// The code below provides a coherent default browser user agent string based on a CPU-seeded PRNG.
|
||||
var AnchoredChromeVersion = ChromeVersion()
|
||||
var ChromeUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" + strconv.Itoa(AnchoredChromeVersion) + ".0.0.0 Safari/537.36"
|
||||
var ChromeUACH = getGreasedChUa(AnchoredChromeVersion, "chrome")
|
||||
var MSEdgeUA = ChromeUA + "Edg/" + strconv.Itoa(AnchoredChromeVersion) + ".0.0.0"
|
||||
var MSEdgeUACH = getGreasedChUa(AnchoredChromeVersion, "edge")
|
||||
|
||||
func applyMasqueradedHeaders(header http.Header, browser string, variant string) {
|
||||
// Browser-specific.
|
||||
switch browser {
|
||||
case "chrome":
|
||||
header["Sec-CH-UA"] = []string{ChromeUACH}
|
||||
header["Sec-CH-UA-Mobile"] = []string{"?0"}
|
||||
header["Sec-CH-UA-Platform"] = []string{"\"Windows\""}
|
||||
header["DNT"] = []string{"1"}
|
||||
header.Set("User-Agent", ChromeUA)
|
||||
header.Set("Accept-Language", "en-US,en;q=0.9")
|
||||
case "edge":
|
||||
header["Sec-CH-UA"] = []string{MSEdgeUACH}
|
||||
header["Sec-CH-UA-Mobile"] = []string{"?0"}
|
||||
header["Sec-CH-UA-Platform"] = []string{"\"Windows\""}
|
||||
header["DNT"] = []string{"1"}
|
||||
header.Set("User-Agent", MSEdgeUA)
|
||||
header.Set("Accept-Language", "en-US,en;q=0.9")
|
||||
case "firefox":
|
||||
header.Set("User-Agent", FirefoxUA)
|
||||
header["DNT"] = []string{"1"}
|
||||
header.Set("Accept-Language", "en-US,en;q=0.5")
|
||||
case "golang":
|
||||
// Expose the default net/http header.
|
||||
header.Del("User-Agent")
|
||||
return
|
||||
}
|
||||
// Context-specific.
|
||||
switch variant {
|
||||
case "nav":
|
||||
if header.Get("Cache-Control") == "" {
|
||||
switch browser {
|
||||
case "chrome", "edge":
|
||||
header.Set("Cache-Control", "max-age=0")
|
||||
}
|
||||
}
|
||||
header.Set("Upgrade-Insecure-Requests", "1")
|
||||
if header.Get("Accept") == "" {
|
||||
switch browser {
|
||||
case "chrome", "edge":
|
||||
header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/jxl,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
|
||||
case "firefox":
|
||||
header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
|
||||
}
|
||||
}
|
||||
header.Set("Sec-Fetch-Site", "none")
|
||||
header.Set("Sec-Fetch-Mode", "navigate")
|
||||
header.Set("Sec-Fetch-User", "?1")
|
||||
header.Set("Sec-Fetch-Dest", "document")
|
||||
header.Set("Priority", "u=0, i")
|
||||
case "ws":
|
||||
header.Set("Sec-Fetch-Mode", "websocket")
|
||||
header.Set("Sec-Fetch-Dest", "empty")
|
||||
header.Set("Sec-Fetch-Site", "same-origin")
|
||||
if header.Get("Cache-Control") == "" {
|
||||
header.Set("Cache-Control", "no-cache")
|
||||
}
|
||||
if header.Get("Pragma") == "" {
|
||||
header.Set("Pragma", "no-cache")
|
||||
}
|
||||
if header.Get("Accept") == "" {
|
||||
header.Set("Accept", "*/*")
|
||||
}
|
||||
case "fetch":
|
||||
header.Set("Sec-Fetch-Mode", "cors")
|
||||
header.Set("Sec-Fetch-Dest", "empty")
|
||||
header.Set("Sec-Fetch-Site", "same-origin")
|
||||
if header.Get("Priority") == "" {
|
||||
switch browser {
|
||||
case "chrome", "edge":
|
||||
header.Set("Priority", "u=1, i")
|
||||
case "firefox":
|
||||
header.Set("Priority", "u=4")
|
||||
}
|
||||
}
|
||||
if header.Get("Cache-Control") == "" {
|
||||
header.Set("Cache-Control", "no-cache")
|
||||
}
|
||||
if header.Get("Pragma") == "" {
|
||||
header.Set("Pragma", "no-cache")
|
||||
}
|
||||
if header.Get("Accept") == "" {
|
||||
header.Set("Accept", "*/*")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TryDefaultHeadersWith(header http.Header, variant string) {
|
||||
// The global UA special value handler for transports. Used to be called HandleTransportUASettings.
|
||||
// Just a FYI to whoever needing to fix this piece of code after some spontaneous event, I tried to make the two methods separate to let the code be cleaner and more organized.
|
||||
if len(header.Values("User-Agent")) < 1 {
|
||||
applyMasqueradedHeaders(header, "chrome", variant)
|
||||
} else {
|
||||
switch header.Get("User-Agent") {
|
||||
case "chrome":
|
||||
applyMasqueradedHeaders(header, "chrome", variant)
|
||||
case "firefox":
|
||||
applyMasqueradedHeaders(header, "firefox", variant)
|
||||
case "edge":
|
||||
applyMasqueradedHeaders(header, "edge", variant)
|
||||
case "golang":
|
||||
applyMasqueradedHeaders(header, "golang", variant)
|
||||
}
|
||||
}
|
||||
}
|
||||
// ChromeUA provides default browser User-Agent based on CPU-seeded PRNG.
|
||||
var ChromeUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" + strconv.Itoa(ChromeVersion()) + ".0.0.0 Safari/537.36"
|
||||
|
||||
@@ -13,11 +13,6 @@ type Observatory interface {
|
||||
GetObservation(ctx context.Context) (proto.Message, error)
|
||||
}
|
||||
|
||||
type BurstObservatory interface {
|
||||
Observatory
|
||||
Check(tag []string)
|
||||
}
|
||||
|
||||
func ObservatoryType() interface{} {
|
||||
return (*Observatory)(nil)
|
||||
}
|
||||
|
||||
@@ -25,16 +25,14 @@ type Counter interface {
|
||||
//
|
||||
// xray:api:stable
|
||||
type OnlineMap interface {
|
||||
// Count returns the number of unique online IPs.
|
||||
// Count is the current value of the OnlineMap.
|
||||
Count() int
|
||||
// AddIP increments the reference count for the given IP.
|
||||
// AddIP adds a ip to the current OnlineMap.
|
||||
AddIP(string)
|
||||
// RemoveIP decrements the reference count for the given IP. Deletes at zero.
|
||||
RemoveIP(string)
|
||||
// List returns all currently online IPs.
|
||||
// List is the current OnlineMap ip list.
|
||||
List() []string
|
||||
// IPTimeMap returns a snapshot copy of IPs to their last-seen times.
|
||||
IPTimeMap() map[string]time.Time
|
||||
// IpTimeMap return client ips and their last access time.
|
||||
IpTimeMap() map[string]time.Time
|
||||
}
|
||||
|
||||
// Channel is the interface for stats channel.
|
||||
|
||||
@@ -3,7 +3,7 @@ module github.com/xtls/xray-core
|
||||
go 1.26
|
||||
|
||||
require (
|
||||
github.com/apernet/quic-go v0.59.1-0.20260217092621-db4786c77a22
|
||||
github.com/apernet/quic-go v0.57.2-0.20260111184307-eec823306178
|
||||
github.com/cloudflare/circl v1.6.3
|
||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344
|
||||
github.com/golang/mock v1.7.0-rc.1
|
||||
@@ -13,21 +13,21 @@ require (
|
||||
github.com/miekg/dns v1.1.72
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/pires/go-proxyproto v0.11.0
|
||||
github.com/refraction-networking/utls v1.8.3-0.20260301010127-aa6edf4b11af
|
||||
github.com/refraction-networking/utls v1.8.2
|
||||
github.com/sagernet/sing v0.5.1
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/vishvananda/netlink v1.3.1
|
||||
github.com/xtls/reality v0.0.0-20251116175510-cd53f7d50237
|
||||
github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
golang.org/x/crypto v0.49.0
|
||||
golang.org/x/crypto v0.48.0
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
||||
golang.org/x/net v0.52.0
|
||||
golang.org/x/sync v0.20.0
|
||||
golang.org/x/sys v0.42.0
|
||||
golang.org/x/net v0.50.0
|
||||
golang.org/x/sync v0.19.0
|
||||
golang.org/x/sys v0.41.0
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
||||
google.golang.org/grpc v1.79.3
|
||||
google.golang.org/grpc v1.79.1
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0
|
||||
h12.io/socks v1.0.3
|
||||
@@ -44,11 +44,13 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/vishvananda/netns v0.0.5 // indirect
|
||||
golang.org/x/mod v0.33.0 // indirect
|
||||
golang.org/x/text v0.35.0 // indirect
|
||||
golang.org/x/mod v0.32.0 // indirect
|
||||
golang.org/x/text v0.34.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.42.0 // indirect
|
||||
golang.org/x/tools v0.41.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
replace github.com/refraction-networking/utls => github.com/fangliding/utls v0.0.0-20260223095720-1ba92b0f6acd
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
||||
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/apernet/quic-go v0.59.1-0.20260217092621-db4786c77a22 h1:00ziBGnLWQEcR9LThDwvxOznJJquJ9bYUdmBFnawLMU=
|
||||
github.com/apernet/quic-go v0.59.1-0.20260217092621-db4786c77a22/go.mod h1:Npbg8qBtAZlsAB3FWmqwlVh5jtVG6a4DlYsOylUpvzA=
|
||||
github.com/apernet/quic-go v0.57.2-0.20260111184307-eec823306178 h1:bSq8n+gX4oO/qnM3MKf4kroW75n+phO9Qp6nigJKZ1E=
|
||||
github.com/apernet/quic-go v0.57.2-0.20260111184307-eec823306178/go.mod h1:N1WIjPphkqs4efXWuyDNQ6OjjIK04vM3h+bEgwV+eVU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
|
||||
@@ -9,6 +9,8 @@ github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJ
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fangliding/utls v0.0.0-20260223095720-1ba92b0f6acd h1:gOZhm1IpArbRLXkLzT5m11HU/HLJmp1aI6/WQLZ85+Y=
|
||||
github.com/fangliding/utls v0.0.0-20260223095720-1ba92b0f6acd/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM=
|
||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4=
|
||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
@@ -51,8 +53,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/refraction-networking/utls v1.8.3-0.20260301010127-aa6edf4b11af h1:er2acxbi3N1nvEq6HXHUAR1nTWEJmQfqiGR8EVT9rfs=
|
||||
github.com/refraction-networking/utls v1.8.3-0.20260301010127-aa6edf4b11af/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/sagernet/sing v0.5.1 h1:mhL/MZVq0TjuvHcpYcFtmSD1BFOxZ/+8ofbNZcg1k1Y=
|
||||
@@ -65,8 +65,8 @@ github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW
|
||||
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
|
||||
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
||||
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
github.com/xtls/reality v0.0.0-20251116175510-cd53f7d50237 h1:UXjrmniKlY+ZbIqpN91lejB3pszQQQRVu1vqH/p/aGM=
|
||||
github.com/xtls/reality v0.0.0-20251116175510-cd53f7d50237/go.mod h1:vbHCV/3VWUvy1oKvTxxWJRPEWSeR1sYgQHIh6u/JiZQ=
|
||||
github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535 h1:nwobseOLLRtdbP6z7Z2aVI97u8ZptTgD1ofovhAKmeU=
|
||||
github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535/go.mod h1:vbHCV/3VWUvy1oKvTxxWJRPEWSeR1sYgQHIh6u/JiZQ=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
@@ -86,22 +86,22 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
|
||||
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -109,21 +109,21 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
||||
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -135,8 +135,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
v2net "github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/proxy/freedom"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
)
|
||||
|
||||
type FreedomConfig struct {
|
||||
|
||||
+16
-31
@@ -522,32 +522,25 @@ func ToCidrList(ips StringList) ([]*router.GeoIP, error) {
|
||||
return geoipList, nil
|
||||
}
|
||||
|
||||
type WebhookRuleConfig struct {
|
||||
URL string `json:"url"`
|
||||
Deduplication uint32 `json:"deduplication"`
|
||||
Headers map[string]string `json:"headers"`
|
||||
}
|
||||
|
||||
func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
|
||||
type RawFieldRule struct {
|
||||
RouterRule
|
||||
Domain *StringList `json:"domain"`
|
||||
Domains *StringList `json:"domains"`
|
||||
IP *StringList `json:"ip"`
|
||||
Port *PortList `json:"port"`
|
||||
Network *NetworkList `json:"network"`
|
||||
SourceIP *StringList `json:"sourceIP"`
|
||||
Source *StringList `json:"source"`
|
||||
SourcePort *PortList `json:"sourcePort"`
|
||||
User *StringList `json:"user"`
|
||||
VlessRoute *PortList `json:"vlessRoute"`
|
||||
InboundTag *StringList `json:"inboundTag"`
|
||||
Protocols *StringList `json:"protocol"`
|
||||
Attributes map[string]string `json:"attrs"`
|
||||
LocalIP *StringList `json:"localIP"`
|
||||
LocalPort *PortList `json:"localPort"`
|
||||
Process *StringList `json:"process"`
|
||||
Webhook *WebhookRuleConfig `json:"webhook"`
|
||||
Domain *StringList `json:"domain"`
|
||||
Domains *StringList `json:"domains"`
|
||||
IP *StringList `json:"ip"`
|
||||
Port *PortList `json:"port"`
|
||||
Network *NetworkList `json:"network"`
|
||||
SourceIP *StringList `json:"sourceIP"`
|
||||
Source *StringList `json:"source"`
|
||||
SourcePort *PortList `json:"sourcePort"`
|
||||
User *StringList `json:"user"`
|
||||
VlessRoute *PortList `json:"vlessRoute"`
|
||||
InboundTag *StringList `json:"inboundTag"`
|
||||
Protocols *StringList `json:"protocol"`
|
||||
Attributes map[string]string `json:"attrs"`
|
||||
LocalIP *StringList `json:"localIP"`
|
||||
LocalPort *PortList `json:"localPort"`
|
||||
Process *StringList `json:"process"`
|
||||
}
|
||||
rawFieldRule := new(RawFieldRule)
|
||||
err := json.Unmarshal(msg, rawFieldRule)
|
||||
@@ -664,14 +657,6 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
|
||||
rule.Process = *rawFieldRule.Process
|
||||
}
|
||||
|
||||
if rawFieldRule.Webhook != nil && rawFieldRule.Webhook.URL != "" {
|
||||
rule.Webhook = &router.WebhookConfig{
|
||||
Url: rawFieldRule.Webhook.URL,
|
||||
Deduplication: rawFieldRule.Webhook.Deduplication,
|
||||
Headers: rawFieldRule.Webhook.Headers,
|
||||
}
|
||||
}
|
||||
|
||||
return rule, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -44,34 +44,6 @@ func (v *AuthenticatorRequest) Build() (*http.RequestConfig, error) {
|
||||
Name: "User-Agent",
|
||||
Value: []string{utils.ChromeUA},
|
||||
},
|
||||
{
|
||||
Name: "Sec-CH-UA",
|
||||
Value: []string{utils.ChromeUACH},
|
||||
},
|
||||
{
|
||||
Name: "Sec-CH-UA-Mobile",
|
||||
Value: []string{"?0"},
|
||||
},
|
||||
{
|
||||
Name: "Sec-CH-UA-Platform",
|
||||
Value: []string{"Windows"},
|
||||
},
|
||||
{
|
||||
Name: "Sec-Fetch-Mode",
|
||||
Value: []string{"no-cors", "cors", "same-origin"},
|
||||
},
|
||||
{
|
||||
Name: "Sec-Fetch-Dest",
|
||||
Value: []string{"empty"},
|
||||
},
|
||||
{
|
||||
Name: "Sec-Fetch-Site",
|
||||
Value: []string{"none"},
|
||||
},
|
||||
{
|
||||
Name: "Sec-Fetch-User",
|
||||
Value: []string{"?1"},
|
||||
},
|
||||
{
|
||||
Name: "Accept-Encoding",
|
||||
Value: []string{"gzip, deflate"},
|
||||
|
||||
+129
-506
@@ -7,7 +7,6 @@ import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"net/url"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -19,8 +18,6 @@ import (
|
||||
"github.com/xtls/xray-core/common/platform/filesystem"
|
||||
"github.com/xtls/xray-core/common/serial"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/fragment"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/header/custom"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/header/dns"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/header/dtls"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/header/srtp"
|
||||
@@ -29,9 +26,7 @@ import (
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/header/wireguard"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/mkcp/aes128gcm"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/mkcp/original"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/noise"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/salamander"
|
||||
finalsudoku "github.com/xtls/xray-core/transport/internet/finalmask/sudoku"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/xdns"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/xicmp"
|
||||
"github.com/xtls/xray-core/transport/internet/httpupgrade"
|
||||
@@ -77,7 +72,7 @@ func (c *KCPConfig) Build() (proto.Message, error) {
|
||||
}
|
||||
if c.Tti != nil {
|
||||
tti := *c.Tti
|
||||
if tti < 10 || tti > 5000 {
|
||||
if tti < 10 || tti > 100 {
|
||||
return nil, errors.New("invalid mKCP TTI: ", tti).AtError()
|
||||
}
|
||||
config.Tti = &kcp.TTI{Value: tti}
|
||||
@@ -235,14 +230,13 @@ type SplitHTTPConfig struct {
|
||||
SeqKey string `json:"seqKey"`
|
||||
UplinkDataPlacement string `json:"uplinkDataPlacement"`
|
||||
UplinkDataKey string `json:"uplinkDataKey"`
|
||||
UplinkChunkSize Int32Range `json:"uplinkChunkSize"`
|
||||
UplinkChunkSize uint32 `json:"uplinkChunkSize"`
|
||||
NoGRPCHeader bool `json:"noGRPCHeader"`
|
||||
NoSSEHeader bool `json:"noSSEHeader"`
|
||||
ScMaxEachPostBytes Int32Range `json:"scMaxEachPostBytes"`
|
||||
ScMinPostsIntervalMs Int32Range `json:"scMinPostsIntervalMs"`
|
||||
ScMaxBufferedPosts int64 `json:"scMaxBufferedPosts"`
|
||||
ScStreamUpServerSecs Int32Range `json:"scStreamUpServerSecs"`
|
||||
ServerMaxHeaderBytes int32 `json:"serverMaxHeaderBytes"`
|
||||
Xmux XmuxConfig `json:"xmux"`
|
||||
DownloadSettings *StreamConfig `json:"downloadSettings"`
|
||||
Extra json.RawMessage `json:"extra"`
|
||||
@@ -322,9 +316,9 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
|
||||
|
||||
switch c.UplinkDataPlacement {
|
||||
case "":
|
||||
c.UplinkDataPlacement = splithttp.PlacementAuto
|
||||
case splithttp.PlacementAuto, splithttp.PlacementBody:
|
||||
case splithttp.PlacementCookie, splithttp.PlacementHeader:
|
||||
c.UplinkDataPlacement = "body"
|
||||
case "body":
|
||||
case "cookie", "header":
|
||||
if c.Mode != "packet-up" {
|
||||
return nil, errors.New("UplinkDataPlacement can be " + c.UplinkDataPlacement + " only in packet-up mode")
|
||||
}
|
||||
@@ -353,6 +347,9 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
|
||||
case "":
|
||||
c.SeqPlacement = "path"
|
||||
case "path", "cookie", "header", "query":
|
||||
if c.SessionPlacement == "path" {
|
||||
return nil, errors.New("SeqPlacement must be path when SessionPlacement is path")
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("unsupported seq placement: " + c.SeqPlacement)
|
||||
}
|
||||
@@ -375,17 +372,24 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if c.UplinkDataPlacement != splithttp.PlacementBody && c.UplinkDataKey == "" {
|
||||
if c.UplinkDataPlacement != "body" && c.UplinkDataKey == "" {
|
||||
switch c.UplinkDataPlacement {
|
||||
case splithttp.PlacementCookie:
|
||||
case "cookie":
|
||||
c.UplinkDataKey = "x_data"
|
||||
case splithttp.PlacementAuto, splithttp.PlacementHeader:
|
||||
case "header":
|
||||
c.UplinkDataKey = "X-Data"
|
||||
}
|
||||
}
|
||||
|
||||
if c.ServerMaxHeaderBytes < 0 {
|
||||
return nil, errors.New("invalid negative value of maxHeaderBytes")
|
||||
if c.UplinkChunkSize == 0 {
|
||||
switch c.UplinkDataPlacement {
|
||||
case "cookie":
|
||||
c.UplinkChunkSize = 3 * 1024 // 3KB
|
||||
case "header":
|
||||
c.UplinkChunkSize = 4 * 1024 // 4KB
|
||||
}
|
||||
} else if c.UplinkChunkSize < 64 {
|
||||
c.UplinkChunkSize = 64
|
||||
}
|
||||
|
||||
if c.Xmux.MaxConnections.To > 0 && c.Xmux.MaxConcurrency.To > 0 {
|
||||
@@ -418,14 +422,13 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
|
||||
SeqKey: c.SeqKey,
|
||||
UplinkDataPlacement: c.UplinkDataPlacement,
|
||||
UplinkDataKey: c.UplinkDataKey,
|
||||
UplinkChunkSize: newRangeConfig(c.UplinkChunkSize),
|
||||
UplinkChunkSize: c.UplinkChunkSize,
|
||||
NoGRPCHeader: c.NoGRPCHeader,
|
||||
NoSSEHeader: c.NoSSEHeader,
|
||||
ScMaxEachPostBytes: newRangeConfig(c.ScMaxEachPostBytes),
|
||||
ScMinPostsIntervalMs: newRangeConfig(c.ScMinPostsIntervalMs),
|
||||
ScMaxBufferedPosts: c.ScMaxBufferedPosts,
|
||||
ScStreamUpServerSecs: newRangeConfig(c.ScStreamUpServerSecs),
|
||||
ServerMaxHeaderBytes: c.ServerMaxHeaderBytes,
|
||||
Xmux: &splithttp.XmuxConfig{
|
||||
MaxConcurrency: newRangeConfig(c.Xmux.MaxConcurrency),
|
||||
MaxConnections: newRangeConfig(c.Xmux.MaxConnections),
|
||||
@@ -501,7 +504,7 @@ func (b Bandwidth) Bps() (uint64, error) {
|
||||
}
|
||||
|
||||
type UdpHop struct {
|
||||
PortList json.RawMessage `json:"ports"`
|
||||
PortList json.RawMessage `json:"port"`
|
||||
Interval *Int32Range `json:"interval"`
|
||||
}
|
||||
|
||||
@@ -520,13 +523,21 @@ type Masquerade struct {
|
||||
}
|
||||
|
||||
type HysteriaConfig struct {
|
||||
Version int32 `json:"version"`
|
||||
Auth string `json:"auth"`
|
||||
Version int32 `json:"version"`
|
||||
Auth string `json:"auth"`
|
||||
Congestion string `json:"congestion"`
|
||||
Up Bandwidth `json:"up"`
|
||||
Down Bandwidth `json:"down"`
|
||||
UdpHop UdpHop `json:"udphop"`
|
||||
|
||||
Congestion *string `json:"congestion"`
|
||||
Up *Bandwidth `json:"up"`
|
||||
Down *Bandwidth `json:"down"`
|
||||
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"`
|
||||
|
||||
UdpIdleTimeout int64 `json:"udpIdleTimeout"`
|
||||
Masquerade Masquerade `json:"masquerade"`
|
||||
@@ -537,10 +548,62 @@ func (c *HysteriaConfig) Build() (proto.Message, error) {
|
||||
return nil, errors.New("version != 2")
|
||||
}
|
||||
|
||||
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")
|
||||
up, err := c.Up.Bps()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
down, err := c.Down.Bps()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
@@ -548,6 +611,20 @@ 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
|
||||
@@ -558,6 +635,27 @@ 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
|
||||
}
|
||||
@@ -627,22 +725,6 @@ func (c *TLSCertConfig) Build() (*tls.Certificate, error) {
|
||||
return certificate, nil
|
||||
}
|
||||
|
||||
type QuicParamsConfig struct {
|
||||
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 {
|
||||
AllowInsecure bool `json:"allowInsecure"`
|
||||
Certs []*TLSCertConfig `json:"certificates"`
|
||||
@@ -909,12 +991,6 @@ func (c *REALITYConfig) Build() (proto.Message, error) {
|
||||
}
|
||||
}
|
||||
|
||||
for _, sn := range config.ServerNames {
|
||||
if strings.Contains(sn, "apple") || strings.Contains(sn, "icloud") {
|
||||
errors.LogWarning(context.Background(), `REALITY: Choosing apple, icloud, etc. as the target may get your IP blocked by the GFW`)
|
||||
}
|
||||
}
|
||||
|
||||
config.LimitFallbackUpload = new(reality.LimitFallback)
|
||||
config.LimitFallbackUpload.AfterBytes = c.LimitFallbackUpload.AfterBytes
|
||||
config.LimitFallbackUpload.BytesPerSec = c.LimitFallbackUpload.BytesPerSec
|
||||
@@ -946,7 +1022,7 @@ func (c *REALITYConfig) Build() (proto.Message, error) {
|
||||
if len(c.ShortIds) != 0 {
|
||||
return nil, errors.New(`non-empty "shortIds", please use "shortId" instead`)
|
||||
}
|
||||
if len(c.ShortId) > 16 {
|
||||
if len(c.ShortIds) > 16 {
|
||||
return nil, errors.New(`too long "shortId": `, c.ShortId)
|
||||
}
|
||||
config.ShortId = make([]byte, 8)
|
||||
@@ -1200,49 +1276,8 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func PraseByteSlice(data json.RawMessage, typ string) ([]byte, error) {
|
||||
switch strings.ToLower(typ) {
|
||||
case "", "array":
|
||||
if len(data) == 0 {
|
||||
return data, nil
|
||||
}
|
||||
var packet []byte
|
||||
if err := json.Unmarshal(data, &packet); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return packet, nil
|
||||
case "str":
|
||||
var str string
|
||||
if err := json.Unmarshal(data, &str); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(str), nil
|
||||
case "hex":
|
||||
var str string
|
||||
if err := json.Unmarshal(data, &str); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return hex.DecodeString(str)
|
||||
case "base64":
|
||||
var str string
|
||||
if err := json.Unmarshal(data, &str); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return base64.StdEncoding.DecodeString(str)
|
||||
default:
|
||||
return nil, errors.New("unknown type")
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
tcpmaskLoader = NewJSONConfigLoader(ConfigCreatorCache{
|
||||
"header-custom": func() interface{} { return new(HeaderCustomTCP) },
|
||||
"fragment": func() interface{} { return new(FragmentMask) },
|
||||
"sudoku": func() interface{} { return new(Sudoku) },
|
||||
}, "type", "settings")
|
||||
|
||||
udpmaskLoader = NewJSONConfigLoader(ConfigCreatorCache{
|
||||
"header-custom": func() interface{} { return new(HeaderCustomUDP) },
|
||||
"header-dns": func() interface{} { return new(Dns) },
|
||||
"header-dtls": func() interface{} { return new(Dtls) },
|
||||
"header-srtp": func() interface{} { return new(Srtp) },
|
||||
@@ -1251,290 +1286,12 @@ var (
|
||||
"header-wireguard": func() interface{} { return new(Wireguard) },
|
||||
"mkcp-original": func() interface{} { return new(Original) },
|
||||
"mkcp-aes128gcm": func() interface{} { return new(Aes128Gcm) },
|
||||
"noise": func() interface{} { return new(NoiseMask) },
|
||||
"salamander": func() interface{} { return new(Salamander) },
|
||||
"sudoku": func() interface{} { return new(Sudoku) },
|
||||
"xdns": func() interface{} { return new(Xdns) },
|
||||
"xicmp": func() interface{} { return new(Xicmp) },
|
||||
}, "type", "settings")
|
||||
)
|
||||
|
||||
type TCPItem struct {
|
||||
Delay Int32Range `json:"delay"`
|
||||
Rand int32 `json:"rand"`
|
||||
RandRange *Int32Range `json:"randRange"`
|
||||
Type string `json:"type"`
|
||||
Packet json.RawMessage `json:"packet"`
|
||||
}
|
||||
|
||||
type HeaderCustomTCP struct {
|
||||
Clients [][]TCPItem `json:"clients"`
|
||||
Servers [][]TCPItem `json:"servers"`
|
||||
Errors [][]TCPItem `json:"errors"`
|
||||
}
|
||||
|
||||
func (c *HeaderCustomTCP) Build() (proto.Message, error) {
|
||||
for _, value := range c.Clients {
|
||||
for _, item := range value {
|
||||
if len(item.Packet) > 0 && item.Rand > 0 {
|
||||
return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0")
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, value := range c.Servers {
|
||||
for _, item := range value {
|
||||
if len(item.Packet) > 0 && item.Rand > 0 {
|
||||
return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0")
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, value := range c.Errors {
|
||||
for _, item := range value {
|
||||
if len(item.Packet) > 0 && item.Rand > 0 {
|
||||
return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
errInvalidRange := errors.New("invalid randRange")
|
||||
|
||||
clients := make([]*custom.TCPSequence, len(c.Clients))
|
||||
for i, value := range c.Clients {
|
||||
clients[i] = &custom.TCPSequence{}
|
||||
for _, item := range value {
|
||||
if item.RandRange == nil {
|
||||
item.RandRange = &Int32Range{From: 0, To: 255}
|
||||
}
|
||||
if item.RandRange.From < 0 || item.RandRange.To > 255 {
|
||||
return nil, errInvalidRange
|
||||
}
|
||||
var err error
|
||||
if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clients[i].Sequence = append(clients[i].Sequence, &custom.TCPItem{
|
||||
DelayMin: int64(item.Delay.From),
|
||||
DelayMax: int64(item.Delay.To),
|
||||
Rand: item.Rand,
|
||||
RandMin: item.RandRange.From,
|
||||
RandMax: item.RandRange.To,
|
||||
Packet: item.Packet,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
servers := make([]*custom.TCPSequence, len(c.Servers))
|
||||
for i, value := range c.Servers {
|
||||
servers[i] = &custom.TCPSequence{}
|
||||
for _, item := range value {
|
||||
if item.RandRange == nil {
|
||||
item.RandRange = &Int32Range{From: 0, To: 255}
|
||||
}
|
||||
if item.RandRange.From < 0 || item.RandRange.To > 255 {
|
||||
return nil, errInvalidRange
|
||||
}
|
||||
var err error
|
||||
if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
servers[i].Sequence = append(servers[i].Sequence, &custom.TCPItem{
|
||||
DelayMin: int64(item.Delay.From),
|
||||
DelayMax: int64(item.Delay.To),
|
||||
Rand: item.Rand,
|
||||
RandMin: item.RandRange.From,
|
||||
RandMax: item.RandRange.To,
|
||||
Packet: item.Packet,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
errors := make([]*custom.TCPSequence, len(c.Errors))
|
||||
for i, value := range c.Errors {
|
||||
errors[i] = &custom.TCPSequence{}
|
||||
for _, item := range value {
|
||||
if item.RandRange == nil {
|
||||
item.RandRange = &Int32Range{From: 0, To: 255}
|
||||
}
|
||||
if item.RandRange.From < 0 || item.RandRange.To > 255 {
|
||||
return nil, errInvalidRange
|
||||
}
|
||||
var err error
|
||||
if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
errors[i].Sequence = append(errors[i].Sequence, &custom.TCPItem{
|
||||
DelayMin: int64(item.Delay.From),
|
||||
DelayMax: int64(item.Delay.To),
|
||||
Rand: item.Rand,
|
||||
RandMin: item.RandRange.From,
|
||||
RandMax: item.RandRange.To,
|
||||
Packet: item.Packet,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return &custom.TCPConfig{
|
||||
Clients: clients,
|
||||
Servers: servers,
|
||||
Errors: errors,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type FragmentMask struct {
|
||||
Packets string `json:"packets"`
|
||||
Length Int32Range `json:"length"`
|
||||
Delay Int32Range `json:"delay"`
|
||||
MaxSplit Int32Range `json:"maxSplit"`
|
||||
}
|
||||
|
||||
func (c *FragmentMask) Build() (proto.Message, error) {
|
||||
config := &fragment.Config{}
|
||||
|
||||
switch strings.ToLower(c.Packets) {
|
||||
case "tlshello":
|
||||
config.PacketsFrom = 0
|
||||
config.PacketsTo = 1
|
||||
case "":
|
||||
config.PacketsFrom = 0
|
||||
config.PacketsTo = 0
|
||||
default:
|
||||
from, to, err := ParseRangeString(c.Packets)
|
||||
if err != nil {
|
||||
return nil, errors.New("Invalid PacketsFrom").Base(err)
|
||||
}
|
||||
config.PacketsFrom = int64(from)
|
||||
config.PacketsTo = int64(to)
|
||||
if config.PacketsFrom == 0 {
|
||||
return nil, errors.New("PacketsFrom can't be 0")
|
||||
}
|
||||
}
|
||||
|
||||
config.LengthMin = int64(c.Length.From)
|
||||
config.LengthMax = int64(c.Length.To)
|
||||
if config.LengthMin == 0 {
|
||||
return nil, errors.New("LengthMin can't be 0")
|
||||
}
|
||||
|
||||
config.DelayMin = int64(c.Delay.From)
|
||||
config.DelayMax = int64(c.Delay.To)
|
||||
|
||||
config.MaxSplitMin = int64(c.MaxSplit.From)
|
||||
config.MaxSplitMax = int64(c.MaxSplit.To)
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
type NoiseItem struct {
|
||||
Rand Int32Range `json:"rand"`
|
||||
Type string `json:"type"`
|
||||
Packet json.RawMessage `json:"packet"`
|
||||
Delay Int32Range `json:"delay"`
|
||||
}
|
||||
|
||||
type NoiseMask struct {
|
||||
Reset Int32Range `json:"reset"`
|
||||
Noise []NoiseItem `json:"noise"`
|
||||
}
|
||||
|
||||
func (c *NoiseMask) Build() (proto.Message, error) {
|
||||
for _, item := range c.Noise {
|
||||
if len(item.Packet) > 0 && item.Rand.To > 0 {
|
||||
return nil, errors.New("len(item.Packet) > 0 && item.Rand.To > 0")
|
||||
}
|
||||
}
|
||||
|
||||
noiseSlice := make([]*noise.Item, 0, len(c.Noise))
|
||||
for _, item := range c.Noise {
|
||||
var err error
|
||||
if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
noiseSlice = append(noiseSlice, &noise.Item{
|
||||
RandMin: int64(item.Rand.From),
|
||||
RandMax: int64(item.Rand.To),
|
||||
Packet: item.Packet,
|
||||
DelayMin: int64(item.Delay.From),
|
||||
DelayMax: int64(item.Delay.To),
|
||||
})
|
||||
}
|
||||
|
||||
return &noise.Config{
|
||||
ResetMin: int64(c.Reset.From),
|
||||
ResetMax: int64(c.Reset.To),
|
||||
Items: noiseSlice,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type UDPItem struct {
|
||||
Rand int32 `json:"rand"`
|
||||
RandRange *Int32Range `json:"randRange"`
|
||||
Type string `json:"type"`
|
||||
Packet json.RawMessage `json:"packet"`
|
||||
}
|
||||
|
||||
type HeaderCustomUDP struct {
|
||||
Client []UDPItem `json:"client"`
|
||||
Server []UDPItem `json:"server"`
|
||||
}
|
||||
|
||||
func (c *HeaderCustomUDP) Build() (proto.Message, error) {
|
||||
for _, item := range c.Client {
|
||||
if len(item.Packet) > 0 && item.Rand > 0 {
|
||||
return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0")
|
||||
}
|
||||
}
|
||||
for _, item := range c.Server {
|
||||
if len(item.Packet) > 0 && item.Rand > 0 {
|
||||
return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0")
|
||||
}
|
||||
}
|
||||
|
||||
client := make([]*custom.UDPItem, 0, len(c.Client))
|
||||
for _, item := range c.Client {
|
||||
if item.RandRange == nil {
|
||||
item.RandRange = &Int32Range{From: 0, To: 255}
|
||||
}
|
||||
if item.RandRange.From < 0 || item.RandRange.To > 255 {
|
||||
return nil, errors.New("invalid randRange")
|
||||
}
|
||||
var err error
|
||||
if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client = append(client, &custom.UDPItem{
|
||||
Rand: item.Rand,
|
||||
RandMin: item.RandRange.From,
|
||||
RandMax: item.RandRange.To,
|
||||
Packet: item.Packet,
|
||||
})
|
||||
}
|
||||
|
||||
server := make([]*custom.UDPItem, 0, len(c.Server))
|
||||
for _, item := range c.Server {
|
||||
if item.RandRange == nil {
|
||||
item.RandRange = &Int32Range{From: 0, To: 255}
|
||||
}
|
||||
if item.RandRange.From < 0 || item.RandRange.To > 255 {
|
||||
return nil, errors.New("invalid randRange")
|
||||
}
|
||||
var err error
|
||||
if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server = append(server, &custom.UDPItem{
|
||||
Rand: item.Rand,
|
||||
RandMin: item.RandRange.From,
|
||||
RandMax: item.RandRange.To,
|
||||
Packet: item.Packet,
|
||||
})
|
||||
}
|
||||
|
||||
return &custom.UDPConfig{
|
||||
Client: client,
|
||||
Server: server,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type Dns struct {
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
@@ -1606,50 +1363,6 @@ func (c *Salamander) Build() (proto.Message, error) {
|
||||
return config, nil
|
||||
}
|
||||
|
||||
type Sudoku struct {
|
||||
Password string `json:"password"`
|
||||
ASCII string `json:"ascii"`
|
||||
|
||||
CustomTable string `json:"customTable"`
|
||||
LegacyCustomTable string `json:"custom_table"`
|
||||
CustomTables []string `json:"customTables"`
|
||||
LegacyCustomSets []string `json:"custom_tables"`
|
||||
|
||||
PaddingMin uint32 `json:"paddingMin"`
|
||||
LegacyPaddingMin uint32 `json:"padding_min"`
|
||||
PaddingMax uint32 `json:"paddingMax"`
|
||||
LegacyPaddingMax uint32 `json:"padding_max"`
|
||||
}
|
||||
|
||||
func (c *Sudoku) Build() (proto.Message, error) {
|
||||
customTable := c.CustomTable
|
||||
if customTable == "" {
|
||||
customTable = c.LegacyCustomTable
|
||||
}
|
||||
customTables := c.CustomTables
|
||||
if len(customTables) == 0 {
|
||||
customTables = c.LegacyCustomSets
|
||||
}
|
||||
|
||||
paddingMin := c.PaddingMin
|
||||
if paddingMin == 0 {
|
||||
paddingMin = c.LegacyPaddingMin
|
||||
}
|
||||
paddingMax := c.PaddingMax
|
||||
if paddingMax == 0 {
|
||||
paddingMax = c.LegacyPaddingMax
|
||||
}
|
||||
|
||||
return &finalsudoku.Config{
|
||||
Password: c.Password,
|
||||
Ascii: c.ASCII,
|
||||
CustomTable: customTable,
|
||||
CustomTables: customTables,
|
||||
PaddingMin: paddingMin,
|
||||
PaddingMax: paddingMax,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type Xdns struct {
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
@@ -1690,7 +1403,7 @@ type Mask struct {
|
||||
func (c *Mask) Build(tcp bool) (proto.Message, error) {
|
||||
loader := udpmaskLoader
|
||||
if tcp {
|
||||
loader = tcpmaskLoader
|
||||
return nil, errors.New("")
|
||||
}
|
||||
|
||||
settings := []byte("{}")
|
||||
@@ -1709,9 +1422,8 @@ func (c *Mask) Build(tcp bool) (proto.Message, error) {
|
||||
}
|
||||
|
||||
type FinalMask struct {
|
||||
Tcp []Mask `json:"tcp"`
|
||||
Udp []Mask `json:"udp"`
|
||||
QuicParams *QuicParamsConfig `json:"quicParams"`
|
||||
Tcp []Mask `json:"tcp"`
|
||||
Udp []Mask `json:"udp"`
|
||||
}
|
||||
|
||||
type StreamConfig struct {
|
||||
@@ -1884,95 +1596,6 @@ 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.BrutalUp.Bps()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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 "", "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: 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,
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return config, nil
|
||||
|
||||
+5
-1
@@ -42,7 +42,7 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
||||
config := new(inbound.Config)
|
||||
config.Clients = make([]*protocol.User, len(c.Clients))
|
||||
switch c.Flow {
|
||||
case vless.XRV, "":
|
||||
case "", vless.XRV:
|
||||
default:
|
||||
return nil, errors.New(`VLESS "settings.flow" doesn't support "` + c.Flow + `" in this version`)
|
||||
}
|
||||
@@ -69,6 +69,9 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
||||
default:
|
||||
return nil, errors.New(`VLESS clients: "flow" doesn't support "` + account.Flow + `" in this version`)
|
||||
}
|
||||
if account.Flow == "" {
|
||||
errors.PrintNonRemovalDeprecatedFeatureWarning("VLESS (with no Flow, etc.)", "VLESS with Flow & Seed")
|
||||
}
|
||||
|
||||
if len(account.Testseed) < 4 {
|
||||
account.Testseed = c.Testseed
|
||||
@@ -277,6 +280,7 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
|
||||
|
||||
switch account.Flow {
|
||||
case "":
|
||||
errors.PrintNonRemovalDeprecatedFeatureWarning("VLESS (with no Flow, etc.)", "VLESS with Flow & Seed")
|
||||
case vless.XRV, vless.XRV + "-udp443":
|
||||
default:
|
||||
return nil, errors.New(`VLESS users: "flow" doesn't support "` + account.Flow + `" in this version`)
|
||||
|
||||
@@ -174,10 +174,6 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) {
|
||||
return nil, err
|
||||
}
|
||||
receiverSettings.StreamSettings = ss
|
||||
if strings.Contains(ss.SecurityType, "reality") && (receiverSettings.PortList == nil ||
|
||||
len(receiverSettings.PortList.Ports()) != 1 || receiverSettings.PortList.Ports()[0] != 443) {
|
||||
errors.LogWarning(context.Background(), `REALITY: Listening on non-443 ports may get your IP blocked by the GFW`)
|
||||
}
|
||||
}
|
||||
if c.SniffingConfig != nil {
|
||||
s, err := c.SniffingConfig.Build()
|
||||
|
||||
@@ -18,8 +18,6 @@ var cmdAddRules = &base.Command{
|
||||
Add routing rules to Xray.
|
||||
|
||||
Arguments:
|
||||
<c1.json> [c2.json]...
|
||||
The configs with the rules to be added. Must be in the xray config format and must have the "routing" field
|
||||
|
||||
-s, -server <server:port>
|
||||
The API server address. Default 127.0.0.1:8080
|
||||
@@ -65,11 +63,6 @@ func executeAddRules(cmd *base.Command, args []string) {
|
||||
if err != nil {
|
||||
base.Fatalf("failed to decode %s: %s", arg, err)
|
||||
}
|
||||
|
||||
if conf.RouterConfig == nil {
|
||||
base.Fatalf("failed to add routing rule: config did not have \"routing\" field")
|
||||
}
|
||||
|
||||
rcs = append(rcs, *conf.RouterConfig)
|
||||
}
|
||||
if len(rcs) == 0 {
|
||||
|
||||
@@ -30,7 +30,7 @@ func Curve25519Genkey(StdEncoding bool, input_base64 string) {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("PrivateKey: %v\nPassword (PublicKey): %v\nHash32: %v\n",
|
||||
fmt.Printf("PrivateKey: %v\nPassword: %v\nHash32: %v\n",
|
||||
encoding.EncodeToString(privateKey),
|
||||
encoding.EncodeToString(password),
|
||||
encoding.EncodeToString(hash32[:]))
|
||||
|
||||
@@ -220,7 +220,9 @@ func setUpHTTPTunnel(ctx context.Context, dest net.Destination, target string, u
|
||||
for _, h := range header {
|
||||
req.Header.Set(h.Key, h.Value)
|
||||
}
|
||||
utils.TryDefaultHeadersWith(req.Header, "nav")
|
||||
if req.Header.Get("User-Agent") == "" {
|
||||
req.Header.Set("User-Agent", utils.ChromeUA)
|
||||
}
|
||||
|
||||
connectHTTP1 := func(rawConn net.Conn) (net.Conn, error) {
|
||||
req.Header.Set("Proxy-Connection", "Keep-Alive")
|
||||
|
||||
+1
-7
@@ -28,7 +28,6 @@ import (
|
||||
"github.com/xtls/xray-core/proxy/vless/encryption"
|
||||
"github.com/xtls/xray-core/transport"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
"github.com/xtls/xray-core/transport/internet/reality"
|
||||
"github.com/xtls/xray-core/transport/internet/stat"
|
||||
"github.com/xtls/xray-core/transport/internet/tls"
|
||||
@@ -660,7 +659,7 @@ func XtlsFilterTls(buffer buf.MultiBuffer, trafficState *TrafficState, ctx conte
|
||||
}
|
||||
}
|
||||
|
||||
// UnwrapRawConn support unwrap encryption, stats, mask wrappers, tls, utls, reality, proxyproto, uds-wrapper conn and get raw tcp/uds conn from it
|
||||
// UnwrapRawConn support unwrap encryption, stats, tls, utls, reality, proxyproto, uds-wrapper conn and get raw tcp/uds conn from it
|
||||
func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) {
|
||||
var readCounter, writerCounter stats.Counter
|
||||
if conn != nil {
|
||||
@@ -677,7 +676,6 @@ func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) {
|
||||
readCounter = statConn.ReadCounter
|
||||
writerCounter = statConn.WriteCounter
|
||||
}
|
||||
|
||||
if !isEncryption { // avoids double penetration
|
||||
if xc, ok := conn.(*tls.Conn); ok {
|
||||
conn = xc.NetConn()
|
||||
@@ -689,9 +687,6 @@ func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) {
|
||||
conn = realityUConn.NetConn()
|
||||
}
|
||||
}
|
||||
|
||||
conn = finalmask.UnwrapTcpMask(conn)
|
||||
|
||||
if pc, ok := conn.(*proxyproto.Conn); ok {
|
||||
conn = pc.Raw()
|
||||
// 8192 > 4096, there is no need to process pc's bufReader
|
||||
@@ -793,7 +788,6 @@ func readV(ctx context.Context, reader buf.Reader, writer buf.Writer, timer sign
|
||||
|
||||
func IsRAWTransportWithoutSecurity(conn stat.Connection) bool {
|
||||
iConn := stat.TryUnwrapStatsConn(conn)
|
||||
iConn = finalmask.UnwrapTcpMask(iConn)
|
||||
_, ok1 := iConn.(*proxyproto.Conn)
|
||||
_, ok2 := iConn.(*net.TCPConn)
|
||||
_, ok3 := iConn.(*internet.UnixConnWrapper)
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
"unsafe"
|
||||
_ "unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.zx2c4.com/wintun"
|
||||
@@ -67,9 +66,7 @@ func NewTun(options TunOptions) (Tun, error) {
|
||||
}
|
||||
|
||||
func open(name string) (*wintun.Adapter, error) {
|
||||
// generate a deterministic GUID from the adapter name
|
||||
id := md5.Sum([]byte(name))
|
||||
guid := (*windows.GUID)(unsafe.Pointer(&id[0]))
|
||||
var guid *windows.GUID
|
||||
// try to open existing adapter by name
|
||||
adapter, err := wintun.OpenAdapter(name)
|
||||
if err == nil {
|
||||
|
||||
@@ -107,7 +107,7 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
||||
if _, err := io.ReadFull(c.Conn, peerHeader[:]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
l, err := DecodeHeader(peerHeader[:]) // l: 17~16640
|
||||
l, err := DecodeHeader(peerHeader[:]) // l: 17~17000
|
||||
if err != nil {
|
||||
if c.Client != nil && strings.Contains(err.Error(), "invalid header: ") { // client's 0-RTT
|
||||
c.Client.RWLock.Lock()
|
||||
@@ -214,7 +214,7 @@ func DecodeHeader(h []byte) (l int, err error) {
|
||||
if h[0] != 23 || h[1] != 3 || h[2] != 3 {
|
||||
l = 0
|
||||
}
|
||||
if l < 17 || l > 16640 { // TLS 1.3 max record: 16384 + 256 (RFC 8446 §5.2)
|
||||
if l < 17 || l > 17000 { // TODO: TLSv1.3 max length
|
||||
err = errors.New("invalid header: " + fmt.Sprintf("%v", h[:5])) // DO NOT CHANGE: relied by client's Read()
|
||||
}
|
||||
return
|
||||
|
||||
@@ -27,9 +27,7 @@ import (
|
||||
"github.com/xtls/xray-core/common/signal"
|
||||
"github.com/xtls/xray-core/common/task"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/features"
|
||||
"github.com/xtls/xray-core/features/dns"
|
||||
"github.com/xtls/xray-core/features/extension"
|
||||
feature_inbound "github.com/xtls/xray-core/features/inbound"
|
||||
"github.com/xtls/xray-core/features/outbound"
|
||||
"github.com/xtls/xray-core/features/policy"
|
||||
@@ -80,7 +78,6 @@ type Handler struct {
|
||||
validator vless.Validator
|
||||
decryption *encryption.ServerInstance
|
||||
outboundHandlerManager outbound.Manager
|
||||
observer features.Feature
|
||||
defaultDispatcher routing.Dispatcher
|
||||
ctx context.Context
|
||||
fallbacks map[string]map[string]map[string]*Fallback // or nil
|
||||
@@ -96,7 +93,6 @@ func New(ctx context.Context, config *Config, dc dns.Client, validator vless.Val
|
||||
stats: v.GetFeature(stats.ManagerType()).(stats.Manager),
|
||||
validator: validator,
|
||||
outboundHandlerManager: v.GetFeature(outbound.ManagerType()).(outbound.Manager),
|
||||
observer: v.GetFeature(extension.ObservatoryType()),
|
||||
defaultDispatcher: v.GetFeature(routing.DispatcherType()).(routing.Dispatcher),
|
||||
ctx: ctx,
|
||||
}
|
||||
@@ -627,7 +623,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return r.NewMux(ctx, dispatcher.WrapLink(ctx, h.policyManager, h.stats, &transport.Link{Reader: clientReader, Writer: clientWriter}), h.observer)
|
||||
return r.NewMux(ctx, dispatcher.WrapLink(ctx, h.policyManager, h.stats, &transport.Link{Reader: clientReader, Writer: clientWriter}))
|
||||
}
|
||||
|
||||
if err := dispatch.DispatchLink(ctx, request.Destination(), &transport.Link{
|
||||
@@ -649,7 +645,7 @@ func (r *Reverse) Tag() string {
|
||||
return r.tag
|
||||
}
|
||||
|
||||
func (r *Reverse) NewMux(ctx context.Context, link *transport.Link, observer features.Feature) error {
|
||||
func (r *Reverse) NewMux(ctx context.Context, link *transport.Link) error {
|
||||
muxClient, err := mux.NewClientWorker(*link, mux.ClientStrategy{})
|
||||
if err != nil {
|
||||
return errors.New("failed to create mux client worker").Base(err).AtWarning()
|
||||
@@ -659,9 +655,6 @@ func (r *Reverse) NewMux(ctx context.Context, link *transport.Link, observer fea
|
||||
return errors.New("failed to create portal worker").Base(err).AtWarning()
|
||||
}
|
||||
r.picker.AddWorker(worker)
|
||||
if burstObs, ok := observer.(extension.BurstObservatory); ok {
|
||||
go burstObs.Check([]string{r.Tag()})
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-muxClient.WaitClosed():
|
||||
|
||||
@@ -3,7 +3,6 @@ package scenarios
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -498,152 +497,3 @@ func TestVlessXtlsVisionReality(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// This testing test all known utls fingerprint in tls.PresetFingerprints that support reality (expect unsafe and random*)
|
||||
// Beacuse figerprint support may be broken after utls/reality update
|
||||
// Known broken fingerprint: android, 360
|
||||
func TestVlessRealityFingerprints(t *testing.T) {
|
||||
TestFingerprint := func(fingerprint string) error {
|
||||
tcpServer := tcp.Server{
|
||||
MsgProcessor: xor,
|
||||
}
|
||||
dest, err := tcpServer.Start()
|
||||
common.Must(err)
|
||||
defer tcpServer.Close()
|
||||
|
||||
userID := protocol.NewID(uuid.New())
|
||||
serverPort := tcp.PickPort()
|
||||
privateKey, _ := base64.RawURLEncoding.DecodeString("aGSYystUbf59_9_6LKRxD27rmSW_-2_nyd9YG_Gwbks")
|
||||
publicKey, _ := base64.RawURLEncoding.DecodeString("E59WjnvZcQMu7tR7_BgyhycuEdBS-CtKxfImRCdAvFM")
|
||||
shortIds := make([][]byte, 1)
|
||||
shortIds[0] = make([]byte, 8)
|
||||
hex.Decode(shortIds[0], []byte("0123456789abcdef"))
|
||||
serverConfig := &core.Config{
|
||||
App: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&log.Config{
|
||||
ErrorLogType: log.LogType_None,
|
||||
}),
|
||||
},
|
||||
Inbound: []*core.InboundHandlerConfig{
|
||||
{
|
||||
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||
PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}},
|
||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||
StreamSettings: &internet.StreamConfig{
|
||||
ProtocolName: "tcp",
|
||||
SecurityType: serial.GetMessageType(&reality.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&reality.Config{
|
||||
Show: false,
|
||||
Dest: "www.google.com:443", // use google for now, may fail in some region
|
||||
ServerNames: []string{"www.google.com"},
|
||||
PrivateKey: privateKey,
|
||||
ShortIds: shortIds,
|
||||
Type: "tcp",
|
||||
}),
|
||||
},
|
||||
},
|
||||
}),
|
||||
ProxySettings: serial.ToTypedMessage(&inbound.Config{
|
||||
Clients: []*protocol.User{
|
||||
{
|
||||
Account: serial.ToTypedMessage(&vless.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
Outbound: []*core.OutboundHandlerConfig{
|
||||
{
|
||||
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
clientPort := tcp.PickPort()
|
||||
clientConfig := &core.Config{
|
||||
App: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&log.Config{
|
||||
ErrorLogType: log.LogType_None,
|
||||
}),
|
||||
},
|
||||
Inbound: []*core.InboundHandlerConfig{
|
||||
{
|
||||
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||
PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}},
|
||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||
}),
|
||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||
Address: net.NewIPOrDomain(dest.Address),
|
||||
Port: uint32(dest.Port),
|
||||
Networks: []net.Network{net.Network_TCP},
|
||||
}),
|
||||
},
|
||||
},
|
||||
Outbound: []*core.OutboundHandlerConfig{
|
||||
{
|
||||
ProxySettings: serial.ToTypedMessage(&outbound.Config{
|
||||
Vnext: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vless.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
}),
|
||||
SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{
|
||||
StreamSettings: &internet.StreamConfig{
|
||||
ProtocolName: "tcp",
|
||||
TransportSettings: []*internet.TransportConfig{
|
||||
{
|
||||
ProtocolName: "tcp",
|
||||
Settings: serial.ToTypedMessage(&transtcp.Config{}),
|
||||
},
|
||||
},
|
||||
SecurityType: serial.GetMessageType(&reality.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&reality.Config{
|
||||
Show: false,
|
||||
Fingerprint: fingerprint,
|
||||
ServerName: "www.google.com",
|
||||
PublicKey: publicKey,
|
||||
ShortId: shortIds[0],
|
||||
SpiderX: "/",
|
||||
}),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
servers, err := InitializeServerConfigs(serverConfig, clientConfig)
|
||||
common.Must(err)
|
||||
defer CloseAllServers(servers)
|
||||
|
||||
err = testTCPConn(clientPort, 1024*1024, time.Second*15)()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
fingerPrints := []string{"chrome", "firefox", "safari", "ios", "edge", "qq"}
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(len(fingerPrints))
|
||||
for _, fp := range fingerPrints {
|
||||
go func() {
|
||||
err := TestFingerprint(fp)
|
||||
if err != nil {
|
||||
t.Errorf("Fingerprint %s test failed: %v", fp, err)
|
||||
} else {
|
||||
t.Logf("Fingerprint %s test passed", fp)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ type task struct {
|
||||
Method string `json:"method"`
|
||||
URL string `json:"url"`
|
||||
Extra any `json:"extra,omitempty"`
|
||||
StreamResponse bool `json:"streamResponse"`
|
||||
}
|
||||
|
||||
var conns chan *websocket.Conn
|
||||
@@ -53,7 +52,6 @@ func init() {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*");
|
||||
w.Write(webpage)
|
||||
}
|
||||
}))
|
||||
@@ -72,7 +70,6 @@ func DialWS(uri string, ed []byte) (*websocket.Conn, error) {
|
||||
task := task{
|
||||
Method: "WS",
|
||||
URL: uri,
|
||||
StreamResponse: true,
|
||||
}
|
||||
|
||||
if ed != nil {
|
||||
@@ -87,10 +84,9 @@ func DialWS(uri string, ed []byte) (*websocket.Conn, error) {
|
||||
type httpExtra struct {
|
||||
Referrer string `json:"referrer,omitempty"`
|
||||
Headers map[string]string `json:"headers,omitempty"`
|
||||
Cookies map[string]string `json:"cookies,omitempty"`
|
||||
}
|
||||
|
||||
func httpExtraFromHeadersAndCookies(headers http.Header, cookies []*http.Cookie) *httpExtra {
|
||||
func httpExtraFromHeaders(headers http.Header) *httpExtra {
|
||||
if len(headers) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -108,37 +104,24 @@ func httpExtraFromHeadersAndCookies(headers http.Header, cookies []*http.Cookie)
|
||||
}
|
||||
}
|
||||
|
||||
if len(cookies) > 0 {
|
||||
extra.Cookies = make(map[string]string)
|
||||
for _, cookie := range cookies {
|
||||
extra.Cookies[cookie.Name] = cookie.Value
|
||||
}
|
||||
}
|
||||
|
||||
return &extra
|
||||
}
|
||||
|
||||
func DialGet(uri string, headers http.Header, cookies []*http.Cookie) (*websocket.Conn, error) {
|
||||
func DialGet(uri string, headers http.Header) (*websocket.Conn, error) {
|
||||
task := task{
|
||||
Method: "GET",
|
||||
URL: uri,
|
||||
Extra: httpExtraFromHeadersAndCookies(headers, cookies),
|
||||
StreamResponse: true,
|
||||
Extra: httpExtraFromHeaders(headers),
|
||||
}
|
||||
|
||||
return dialTask(task)
|
||||
}
|
||||
|
||||
func DialPacket(method string, uri string, headers http.Header, cookies []*http.Cookie, payload []byte) error {
|
||||
return dialWithBody(method, uri, headers, cookies, payload)
|
||||
}
|
||||
|
||||
func dialWithBody(method string, uri string, headers http.Header, cookies []*http.Cookie, payload []byte) error {
|
||||
func DialPost(uri string, headers http.Header, payload []byte) error {
|
||||
task := task{
|
||||
Method: method,
|
||||
Method: "POST",
|
||||
URL: uri,
|
||||
Extra: httpExtraFromHeadersAndCookies(headers, cookies),
|
||||
StreamResponse: false,
|
||||
Extra: httpExtraFromHeaders(headers),
|
||||
}
|
||||
|
||||
conn, err := dialTask(task)
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Browser Dialer</title>
|
||||
<link rel="icon" href="data:">
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
@@ -30,37 +29,9 @@
|
||||
requestInit.headers = extra.headers;
|
||||
}
|
||||
|
||||
if (extra.cookies) {
|
||||
requestInit.credentials = 'include';
|
||||
}
|
||||
|
||||
return requestInit;
|
||||
}
|
||||
|
||||
function setCookiesFromTask(task) {
|
||||
if (!task.extra.cookies) {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = new URL(task.url);
|
||||
|
||||
for (const [name, value] of Object.entries(task.extra.cookies)) {
|
||||
document.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value) + '; path=' + url.pathname;
|
||||
}
|
||||
}
|
||||
|
||||
function clearCookiesFromTask(task) {
|
||||
if (!task.extra.cookies) {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = new URL(task.url);
|
||||
|
||||
for (const [name, value] of Object.entries(task.extra.cookies)) {
|
||||
document.cookie = encodeURIComponent(name) + '=; path=' + url.pathname + '; Max-Age=0';
|
||||
}
|
||||
}
|
||||
|
||||
let check = function () {
|
||||
if (clientIdleCount > 0) {
|
||||
return;
|
||||
@@ -77,121 +48,116 @@
|
||||
ws.onmessage = function (event) {
|
||||
clientIdleCount -= 1;
|
||||
let task = JSON.parse(event.data);
|
||||
if (task.method == "WS") {
|
||||
upstreamWsCount += 1;
|
||||
console.log("Dial WS", task.url, task.extra.protocol);
|
||||
const wss = new WebSocket(task.url, task.extra.protocol);
|
||||
wss.binaryType = "arraybuffer";
|
||||
let opened = false;
|
||||
ws.onmessage = function (event) {
|
||||
wss.send(event.data)
|
||||
};
|
||||
wss.onopen = function (event) {
|
||||
opened = true;
|
||||
ws.send("ok")
|
||||
};
|
||||
wss.onmessage = function (event) {
|
||||
ws.send(event.data)
|
||||
};
|
||||
wss.onclose = function (event) {
|
||||
upstreamWsCount -= 1;
|
||||
console.log("Dial WS DONE, remaining: ", upstreamWsCount);
|
||||
ws.close()
|
||||
};
|
||||
wss.onerror = function (event) {
|
||||
!opened && ws.send("fail")
|
||||
wss.close()
|
||||
};
|
||||
ws.onclose = function (event) {
|
||||
wss.close()
|
||||
};
|
||||
}
|
||||
else if (task.method == "GET" && task.streamResponse) {
|
||||
(async () => {
|
||||
const requestInit = prepareRequestInit(task.extra);
|
||||
|
||||
console.log("Dial GET", task.url);
|
||||
ws.send("ok");
|
||||
const controller = new AbortController();
|
||||
|
||||
/*
|
||||
Aborting a streaming response in JavaScript
|
||||
requires two levers to be pulled:
|
||||
|
||||
First, the streaming read itself has to be cancelled using
|
||||
reader.cancel(), only then controller.abort() will actually work.
|
||||
|
||||
If controller.abort() alone is called while a
|
||||
reader.read() is ongoing, it will block until the server closes the
|
||||
response, the page is refreshed or the network connection is lost.
|
||||
*/
|
||||
|
||||
let reader = null;
|
||||
ws.onclose = (event) => {
|
||||
try {
|
||||
reader && reader.cancel();
|
||||
} catch(e) {}
|
||||
|
||||
try {
|
||||
controller.abort();
|
||||
} catch(e) {}
|
||||
switch (task.method) {
|
||||
case "WS": {
|
||||
upstreamWsCount += 1;
|
||||
console.log("Dial WS", task.url, task.extra.protocol);
|
||||
const wss = new WebSocket(task.url, task.extra.protocol);
|
||||
wss.binaryType = "arraybuffer";
|
||||
let opened = false;
|
||||
ws.onmessage = function (event) {
|
||||
wss.send(event.data)
|
||||
};
|
||||
wss.onopen = function (event) {
|
||||
opened = true;
|
||||
ws.send("ok")
|
||||
};
|
||||
wss.onmessage = function (event) {
|
||||
ws.send(event.data)
|
||||
};
|
||||
wss.onclose = function (event) {
|
||||
upstreamWsCount -= 1;
|
||||
console.log("Dial WS DONE, remaining: ", upstreamWsCount);
|
||||
ws.close()
|
||||
};
|
||||
wss.onerror = function (event) {
|
||||
!opened && ws.send("fail")
|
||||
wss.close()
|
||||
};
|
||||
ws.onclose = function (event) {
|
||||
wss.close()
|
||||
};
|
||||
break;
|
||||
}
|
||||
case "GET": {
|
||||
(async () => {
|
||||
const requestInit = prepareRequestInit(task.extra);
|
||||
|
||||
try {
|
||||
upstreamGetCount += 1;
|
||||
console.log("Dial GET", task.url);
|
||||
ws.send("ok");
|
||||
const controller = new AbortController();
|
||||
|
||||
requestInit.signal = controller.signal;
|
||||
setCookiesFromTask(task);
|
||||
const response = await fetch(task.url, requestInit);
|
||||
clearCookiesFromTask(task);
|
||||
/*
|
||||
Aborting a streaming response in JavaScript
|
||||
requires two levers to be pulled:
|
||||
|
||||
const body = await response.body;
|
||||
reader = body.getReader();
|
||||
First, the streaming read itself has to be cancelled using
|
||||
reader.cancel(), only then controller.abort() will actually work.
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (value) ws.send(value); // don't send back "undefined" string when received nothing
|
||||
if (done) break;
|
||||
If controller.abort() alone is called while a
|
||||
reader.read() is ongoing, it will block until the server closes the
|
||||
response, the page is refreshed or the network connection is lost.
|
||||
*/
|
||||
|
||||
let reader = null;
|
||||
ws.onclose = (event) => {
|
||||
try {
|
||||
reader && reader.cancel();
|
||||
} catch(e) {}
|
||||
|
||||
try {
|
||||
controller.abort();
|
||||
} catch(e) {}
|
||||
};
|
||||
|
||||
try {
|
||||
upstreamGetCount += 1;
|
||||
|
||||
requestInit.signal = controller.signal;
|
||||
const response = await fetch(task.url, requestInit);
|
||||
|
||||
const body = await response.body;
|
||||
reader = body.getReader();
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (value) ws.send(value); // don't send back "undefined" string when received nothing
|
||||
if (done) break;
|
||||
}
|
||||
} finally {
|
||||
upstreamGetCount -= 1;
|
||||
console.log("Dial GET DONE, remaining: ", upstreamGetCount);
|
||||
ws.close();
|
||||
}
|
||||
} finally {
|
||||
upstreamGetCount -= 1;
|
||||
console.log("Dial GET DONE, remaining: ", upstreamGetCount);
|
||||
ws.close();
|
||||
}
|
||||
})();
|
||||
}
|
||||
else if (!task.streamResponse) {
|
||||
upstreamPostCount += 1;
|
||||
})();
|
||||
break;
|
||||
}
|
||||
case "POST": {
|
||||
upstreamPostCount += 1;
|
||||
|
||||
const requestInit = prepareRequestInit(task.extra);
|
||||
requestInit.method = task.method;
|
||||
const requestInit = prepareRequestInit(task.extra);
|
||||
requestInit.method = "POST";
|
||||
|
||||
console.log("Dial", task.method, task.url);
|
||||
ws.send("ok");
|
||||
ws.onmessage = async (event) => {
|
||||
try {
|
||||
if (event.data.byteLength > 0) {
|
||||
console.log("Dial POST", task.url);
|
||||
ws.send("ok");
|
||||
ws.onmessage = async (event) => {
|
||||
try {
|
||||
requestInit.body = event.data;
|
||||
const response = await fetch(task.url, requestInit);
|
||||
if (response.ok) {
|
||||
ws.send("ok");
|
||||
} else {
|
||||
console.error("bad status code");
|
||||
ws.send("fail");
|
||||
}
|
||||
} finally {
|
||||
upstreamPostCount -= 1;
|
||||
console.log("Dial POST DONE, remaining: ", upstreamPostCount);
|
||||
ws.close();
|
||||
}
|
||||
setCookiesFromTask(task);
|
||||
const response = await fetch(task.url, requestInit);
|
||||
clearCookiesFromTask(task);
|
||||
if (response.ok) {
|
||||
ws.send("ok");
|
||||
} else {
|
||||
console.error("bad status code");
|
||||
ws.send("fail");
|
||||
}
|
||||
} finally {
|
||||
upstreamPostCount -= 1;
|
||||
console.log("Dial", task.method, "packet DONE, remaining: ", upstreamPostCount);
|
||||
ws.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
console.error(`Incorrect task method=${task.method} streamResponse=${task.streamResponse}.`);
|
||||
ws.close();
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
check();
|
||||
|
||||
+40
-268
@@ -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{6, 0}
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{4, 0}
|
||||
}
|
||||
|
||||
type TransportConfig struct {
|
||||
@@ -276,7 +276,6 @@ type StreamConfig struct {
|
||||
SecuritySettings []*serial.TypedMessage `protobuf:"bytes,4,rep,name=security_settings,json=securitySettings,proto3" json:"security_settings,omitempty"`
|
||||
Udpmasks []*serial.TypedMessage `protobuf:"bytes,10,rep,name=udpmasks,proto3" json:"udpmasks,omitempty"`
|
||||
Tcpmasks []*serial.TypedMessage `protobuf:"bytes,11,rep,name=tcpmasks,proto3" json:"tcpmasks,omitempty"`
|
||||
QuicParams *QuicParams `protobuf:"bytes,12,opt,name=quic_params,json=quicParams,proto3" json:"quic_params,omitempty"`
|
||||
SocketSettings *SocketConfig `protobuf:"bytes,6,opt,name=socket_settings,json=socketSettings,proto3" json:"socket_settings,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -368,13 +367,6 @@ func (x *StreamConfig) GetTcpmasks() []*serial.TypedMessage {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *StreamConfig) GetQuicParams() *QuicParams {
|
||||
if x != nil {
|
||||
return x.QuicParams
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *StreamConfig) GetSocketSettings() *SocketConfig {
|
||||
if x != nil {
|
||||
return x.SocketSettings
|
||||
@@ -382,198 +374,6 @@ func (x *StreamConfig) GetSocketSettings() *SocketConfig {
|
||||
return nil
|
||||
}
|
||||
|
||||
type UdpHop struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
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[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *QuicParams) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*QuicParams) ProtoMessage() {}
|
||||
|
||||
func (x *QuicParams) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_config_proto_msgTypes[3]
|
||||
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 QuicParams.ProtoReflect.Descriptor instead.
|
||||
func (*QuicParams) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *QuicParams) GetCongestion() string {
|
||||
if x != nil {
|
||||
return x.Congestion
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *QuicParams) GetBrutalUp() uint64 {
|
||||
if x != nil {
|
||||
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
|
||||
}
|
||||
|
||||
type ProxyConfig struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||
@@ -584,7 +384,7 @@ type ProxyConfig struct {
|
||||
|
||||
func (x *ProxyConfig) Reset() {
|
||||
*x = ProxyConfig{}
|
||||
mi := &file_transport_internet_config_proto_msgTypes[4]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -596,7 +396,7 @@ func (x *ProxyConfig) String() string {
|
||||
func (*ProxyConfig) ProtoMessage() {}
|
||||
|
||||
func (x *ProxyConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_config_proto_msgTypes[4]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -609,7 +409,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{4}
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *ProxyConfig) GetTag() string {
|
||||
@@ -640,7 +440,7 @@ type CustomSockopt struct {
|
||||
|
||||
func (x *CustomSockopt) Reset() {
|
||||
*x = CustomSockopt{}
|
||||
mi := &file_transport_internet_config_proto_msgTypes[5]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -652,7 +452,7 @@ func (x *CustomSockopt) String() string {
|
||||
func (*CustomSockopt) ProtoMessage() {}
|
||||
|
||||
func (x *CustomSockopt) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_config_proto_msgTypes[5]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -665,7 +465,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{5}
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *CustomSockopt) GetSystem() string {
|
||||
@@ -747,7 +547,7 @@ type SocketConfig struct {
|
||||
|
||||
func (x *SocketConfig) Reset() {
|
||||
*x = SocketConfig{}
|
||||
mi := &file_transport_internet_config_proto_msgTypes[6]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -759,7 +559,7 @@ func (x *SocketConfig) String() string {
|
||||
func (*SocketConfig) ProtoMessage() {}
|
||||
|
||||
func (x *SocketConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_config_proto_msgTypes[6]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[4]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -772,7 +572,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{6}
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *SocketConfig) GetMark() int32 {
|
||||
@@ -948,7 +748,7 @@ type HappyEyeballsConfig struct {
|
||||
|
||||
func (x *HappyEyeballsConfig) Reset() {
|
||||
*x = HappyEyeballsConfig{}
|
||||
mi := &file_transport_internet_config_proto_msgTypes[7]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -960,7 +760,7 @@ func (x *HappyEyeballsConfig) String() string {
|
||||
func (*HappyEyeballsConfig) ProtoMessage() {}
|
||||
|
||||
func (x *HappyEyeballsConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_config_proto_msgTypes[7]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[5]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -973,7 +773,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{7}
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *HappyEyeballsConfig) GetPrioritizeIpv6() bool {
|
||||
@@ -1011,7 +811,7 @@ const file_transport_internet_config_proto_rawDesc = "" +
|
||||
"\x1ftransport/internet/config.proto\x12\x17xray.transport.internet\x1a!common/serial/typed_message.proto\x1a\x18common/net/address.proto\"t\n" +
|
||||
"\x0fTransportConfig\x12#\n" +
|
||||
"\rprotocol_name\x18\x03 \x01(\tR\fprotocolName\x12<\n" +
|
||||
"\bsettings\x18\x02 \x01(\v2 .xray.common.serial.TypedMessageR\bsettings\"\xdd\x04\n" +
|
||||
"\bsettings\x18\x02 \x01(\v2 .xray.common.serial.TypedMessageR\bsettings\"\x97\x04\n" +
|
||||
"\fStreamConfig\x125\n" +
|
||||
"\aaddress\x18\b \x01(\v2\x1b.xray.common.net.IPOrDomainR\aaddress\x12\x12\n" +
|
||||
"\x04port\x18\t \x01(\rR\x04port\x12#\n" +
|
||||
@@ -1021,32 +821,8 @@ const file_transport_internet_config_proto_rawDesc = "" +
|
||||
"\x11security_settings\x18\x04 \x03(\v2 .xray.common.serial.TypedMessageR\x10securitySettings\x12<\n" +
|
||||
"\budpmasks\x18\n" +
|
||||
" \x03(\v2 .xray.common.serial.TypedMessageR\budpmasks\x12<\n" +
|
||||
"\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\"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\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" +
|
||||
"\btcpmasks\x18\v \x03(\v2 .xray.common.serial.TypedMessageR\btcpmasks\x12N\n" +
|
||||
"\x0fsocket_settings\x18\x06 \x01(\v2%.xray.transport.internet.SocketConfigR\x0esocketSettings\"Q\n" +
|
||||
"\vProxyConfig\x12\x10\n" +
|
||||
"\x03tag\x18\x01 \x01(\tR\x03tag\x120\n" +
|
||||
"\x13transportLayerProxy\x18\x02 \x01(\bR\x13transportLayerProxy\"\x93\x01\n" +
|
||||
@@ -1135,42 +911,38 @@ 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, 8)
|
||||
var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
||||
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
|
||||
(*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
|
||||
(*ProxyConfig)(nil), // 5: xray.transport.internet.ProxyConfig
|
||||
(*CustomSockopt)(nil), // 6: xray.transport.internet.CustomSockopt
|
||||
(*SocketConfig)(nil), // 7: xray.transport.internet.SocketConfig
|
||||
(*HappyEyeballsConfig)(nil), // 8: xray.transport.internet.HappyEyeballsConfig
|
||||
(*serial.TypedMessage)(nil), // 9: xray.common.serial.TypedMessage
|
||||
(*net.IPOrDomain)(nil), // 10: xray.common.net.IPOrDomain
|
||||
}
|
||||
var file_transport_internet_config_proto_depIdxs = []int32{
|
||||
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
|
||||
9, // 0: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage
|
||||
10, // 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
|
||||
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
|
||||
9, // 3: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage
|
||||
9, // 4: xray.transport.internet.StreamConfig.udpmasks:type_name -> xray.common.serial.TypedMessage
|
||||
9, // 5: xray.transport.internet.StreamConfig.tcpmasks:type_name -> xray.common.serial.TypedMessage
|
||||
7, // 6: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
|
||||
2, // 7: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
|
||||
0, // 8: xray.transport.internet.SocketConfig.domain_strategy:type_name -> xray.transport.internet.DomainStrategy
|
||||
6, // 9: xray.transport.internet.SocketConfig.customSockopt:type_name -> xray.transport.internet.CustomSockopt
|
||||
1, // 10: xray.transport.internet.SocketConfig.address_port_strategy:type_name -> xray.transport.internet.AddressPortStrategy
|
||||
8, // 11: xray.transport.internet.SocketConfig.happy_eyeballs:type_name -> xray.transport.internet.HappyEyeballsConfig
|
||||
12, // [12:12] is the sub-list for method output_type
|
||||
12, // [12:12] is the sub-list for method input_type
|
||||
12, // [12:12] is the sub-list for extension type_name
|
||||
12, // [12:12] is the sub-list for extension extendee
|
||||
0, // [0:12] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_transport_internet_config_proto_init() }
|
||||
@@ -1184,7 +956,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: 8,
|
||||
NumMessages: 6,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -59,32 +59,9 @@ message StreamConfig {
|
||||
repeated xray.common.serial.TypedMessage udpmasks = 10;
|
||||
repeated xray.common.serial.TypedMessage tcpmasks = 11;
|
||||
|
||||
QuicParams quic_params = 12;
|
||||
|
||||
SocketConfig socket_settings = 6;
|
||||
}
|
||||
|
||||
message UdpHop {
|
||||
repeated uint32 ports = 1;
|
||||
int64 interval_min = 2;
|
||||
int64 interval_max = 3;
|
||||
}
|
||||
|
||||
message QuicParams {
|
||||
string congestion = 1;
|
||||
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 {
|
||||
string tag = 1;
|
||||
bool transportLayerProxy = 2;
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
package finalmask
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
)
|
||||
|
||||
type ConnSize interface {
|
||||
Size() int32
|
||||
}
|
||||
|
||||
type Udpmask interface {
|
||||
UDP()
|
||||
|
||||
WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error)
|
||||
WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error)
|
||||
WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error)
|
||||
WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error)
|
||||
}
|
||||
|
||||
type UdpmaskManager struct {
|
||||
@@ -26,165 +26,31 @@ func NewUdpmaskManager(udpmasks []Udpmask) *UdpmaskManager {
|
||||
}
|
||||
|
||||
func (m *UdpmaskManager) WrapPacketConnClient(raw net.PacketConn) (net.PacketConn, error) {
|
||||
var sizes []int
|
||||
var conns []net.PacketConn
|
||||
leaveSize := int32(0)
|
||||
var err error
|
||||
for i, mask := range m.udpmasks {
|
||||
if _, ok := mask.(headerConn); ok {
|
||||
conn, err := mask.WrapPacketConnClient(nil, i, len(m.udpmasks)-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sizes = append(sizes, conn.(headerSize).Size())
|
||||
conns = append(conns, conn)
|
||||
} else {
|
||||
if len(conns) > 0 {
|
||||
raw = &headerManagerConn{sizes: sizes, conns: conns, PacketConn: raw}
|
||||
sizes = nil
|
||||
conns = nil
|
||||
}
|
||||
var err error
|
||||
raw, err = mask.WrapPacketConnClient(raw, i, len(m.udpmasks)-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw, err = mask.WrapPacketConnClient(raw, i == len(m.udpmasks)-1, leaveSize, i == 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(conns) > 0 {
|
||||
raw = &headerManagerConn{sizes: sizes, conns: conns, PacketConn: raw}
|
||||
sizes = nil
|
||||
conns = nil
|
||||
leaveSize += raw.(ConnSize).Size()
|
||||
}
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
func (m *UdpmaskManager) WrapPacketConnServer(raw net.PacketConn) (net.PacketConn, error) {
|
||||
var sizes []int
|
||||
var conns []net.PacketConn
|
||||
leaveSize := int32(0)
|
||||
var err error
|
||||
for i, mask := range m.udpmasks {
|
||||
if _, ok := mask.(headerConn); ok {
|
||||
conn, err := mask.WrapPacketConnServer(nil, i, len(m.udpmasks)-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sizes = append(sizes, conn.(headerSize).Size())
|
||||
conns = append(conns, conn)
|
||||
} else {
|
||||
if len(conns) > 0 {
|
||||
raw = &headerManagerConn{sizes: sizes, conns: conns, PacketConn: raw}
|
||||
sizes = nil
|
||||
conns = nil
|
||||
}
|
||||
var err error
|
||||
raw, err = mask.WrapPacketConnServer(raw, i, len(m.udpmasks)-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw, err = mask.WrapPacketConnServer(raw, i == len(m.udpmasks)-1, leaveSize, i == 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(conns) > 0 {
|
||||
raw = &headerManagerConn{sizes: sizes, conns: conns, PacketConn: raw}
|
||||
sizes = nil
|
||||
conns = nil
|
||||
leaveSize += raw.(ConnSize).Size()
|
||||
}
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
const (
|
||||
UDPSize = 4096
|
||||
)
|
||||
|
||||
type headerConn interface {
|
||||
HeaderConn()
|
||||
}
|
||||
|
||||
type headerSize interface {
|
||||
Size() int
|
||||
}
|
||||
|
||||
type headerManagerConn struct {
|
||||
sizes []int
|
||||
conns []net.PacketConn
|
||||
net.PacketConn
|
||||
m sync.Mutex
|
||||
writeBuf [UDPSize]byte
|
||||
}
|
||||
|
||||
func (c *headerManagerConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
buf := p
|
||||
if len(buf) < UDPSize {
|
||||
buf = make([]byte, UDPSize)
|
||||
}
|
||||
|
||||
n, addr, err = c.PacketConn.ReadFrom(buf)
|
||||
if n == 0 || err != nil {
|
||||
return 0, addr, err
|
||||
}
|
||||
newBuf := buf[:n]
|
||||
|
||||
sum := 0
|
||||
for _, size := range c.sizes {
|
||||
sum += size
|
||||
}
|
||||
|
||||
if n < sum {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err short length")
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
for i := range c.conns {
|
||||
n, _, err = c.conns[i].ReadFrom(newBuf)
|
||||
if n == 0 || err != nil {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err ", err)
|
||||
return 0, addr, nil
|
||||
}
|
||||
newBuf = newBuf[c.sizes[i] : n+c.sizes[i]]
|
||||
}
|
||||
|
||||
if len(p) < n {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err short buffer")
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
copy(p, buf[sum:sum+n])
|
||||
|
||||
return n, addr, nil
|
||||
}
|
||||
|
||||
func (c *headerManagerConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
c.m.Lock()
|
||||
defer c.m.Unlock()
|
||||
|
||||
sum := 0
|
||||
for _, size := range c.sizes {
|
||||
sum += size
|
||||
}
|
||||
|
||||
if sum+len(p) > UDPSize {
|
||||
errors.LogDebug(context.Background(), addr, " mask write err short write")
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
n = copy(c.writeBuf[sum:], p)
|
||||
|
||||
for i := len(c.conns) - 1; i >= 0; i-- {
|
||||
n, err = c.conns[i].WriteTo(c.writeBuf[sum-c.sizes[i]:n+sum], nil)
|
||||
if n == 0 || err != nil {
|
||||
errors.LogDebug(context.Background(), addr, " mask write err ", err)
|
||||
return 0, nil
|
||||
}
|
||||
sum -= c.sizes[i]
|
||||
}
|
||||
|
||||
n, err = c.PacketConn.WriteTo(c.writeBuf[:n], addr)
|
||||
if n == 0 || err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
type Tcpmask interface {
|
||||
TCP()
|
||||
|
||||
@@ -223,54 +89,3 @@ func (m *TcpmaskManager) WrapConnServer(raw net.Conn) (net.Conn, error) {
|
||||
}
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
func (m *TcpmaskManager) WrapListener(l net.Listener) (net.Listener, error) {
|
||||
return NewTcpListener(m, l)
|
||||
}
|
||||
|
||||
type tcpListener struct {
|
||||
m *TcpmaskManager
|
||||
net.Listener
|
||||
}
|
||||
|
||||
func NewTcpListener(m *TcpmaskManager, l net.Listener) (net.Listener, error) {
|
||||
return &tcpListener{
|
||||
m: m,
|
||||
Listener: l,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *tcpListener) Accept() (net.Conn, error) {
|
||||
conn, err := l.Listener.Accept()
|
||||
if err != nil {
|
||||
return conn, err
|
||||
}
|
||||
|
||||
newConn, err := l.m.WrapConnServer(conn)
|
||||
if err != nil {
|
||||
errors.LogDebugInner(context.Background(), err, "mask err")
|
||||
// conn.Close()
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
return newConn, nil
|
||||
}
|
||||
|
||||
type TcpMaskConn interface {
|
||||
TcpMaskConn()
|
||||
RawConn() net.Conn
|
||||
Splice() bool
|
||||
}
|
||||
|
||||
func UnwrapTcpMask(conn net.Conn) net.Conn {
|
||||
for {
|
||||
if v, ok := conn.(TcpMaskConn); ok {
|
||||
if !v.Splice() {
|
||||
return conn
|
||||
}
|
||||
conn = v.RawConn()
|
||||
} else {
|
||||
return conn
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package fragment
|
||||
|
||||
import "net"
|
||||
|
||||
func (c *Config) TCP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapConnClient(raw net.Conn) (net.Conn, error) {
|
||||
return NewConnClient(c, raw, false)
|
||||
}
|
||||
|
||||
func (c *Config) WrapConnServer(raw net.Conn) (net.Conn, error) {
|
||||
return NewConnServer(c, raw, true)
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v6.33.5
|
||||
// source: transport/internet/finalmask/fragment/config.proto
|
||||
|
||||
package fragment
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
PacketsFrom int64 `protobuf:"varint,1,opt,name=packets_from,json=packetsFrom,proto3" json:"packets_from,omitempty"`
|
||||
PacketsTo int64 `protobuf:"varint,2,opt,name=packets_to,json=packetsTo,proto3" json:"packets_to,omitempty"`
|
||||
LengthMin int64 `protobuf:"varint,3,opt,name=length_min,json=lengthMin,proto3" json:"length_min,omitempty"`
|
||||
LengthMax int64 `protobuf:"varint,4,opt,name=length_max,json=lengthMax,proto3" json:"length_max,omitempty"`
|
||||
DelayMin int64 `protobuf:"varint,5,opt,name=delay_min,json=delayMin,proto3" json:"delay_min,omitempty"`
|
||||
DelayMax int64 `protobuf:"varint,6,opt,name=delay_max,json=delayMax,proto3" json:"delay_max,omitempty"`
|
||||
MaxSplitMin int64 `protobuf:"varint,7,opt,name=max_split_min,json=maxSplitMin,proto3" json:"max_split_min,omitempty"`
|
||||
MaxSplitMax int64 `protobuf:"varint,8,opt,name=max_split_max,json=maxSplitMax,proto3" json:"max_split_max,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
mi := &file_transport_internet_finalmask_fragment_config_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Config) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Config) ProtoMessage() {}
|
||||
|
||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_finalmask_fragment_config_proto_msgTypes[0]
|
||||
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 Config.ProtoReflect.Descriptor instead.
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_finalmask_fragment_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Config) GetPacketsFrom() int64 {
|
||||
if x != nil {
|
||||
return x.PacketsFrom
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetPacketsTo() int64 {
|
||||
if x != nil {
|
||||
return x.PacketsTo
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetLengthMin() int64 {
|
||||
if x != nil {
|
||||
return x.LengthMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetLengthMax() int64 {
|
||||
if x != nil {
|
||||
return x.LengthMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetDelayMin() int64 {
|
||||
if x != nil {
|
||||
return x.DelayMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetDelayMax() int64 {
|
||||
if x != nil {
|
||||
return x.DelayMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetMaxSplitMin() int64 {
|
||||
if x != nil {
|
||||
return x.MaxSplitMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetMaxSplitMax() int64 {
|
||||
if x != nil {
|
||||
return x.MaxSplitMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_transport_internet_finalmask_fragment_config_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_transport_internet_finalmask_fragment_config_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"2transport/internet/finalmask/fragment/config.proto\x12*xray.transport.internet.finalmask.fragment\"\x8a\x02\n" +
|
||||
"\x06Config\x12!\n" +
|
||||
"\fpackets_from\x18\x01 \x01(\x03R\vpacketsFrom\x12\x1d\n" +
|
||||
"\n" +
|
||||
"packets_to\x18\x02 \x01(\x03R\tpacketsTo\x12\x1d\n" +
|
||||
"\n" +
|
||||
"length_min\x18\x03 \x01(\x03R\tlengthMin\x12\x1d\n" +
|
||||
"\n" +
|
||||
"length_max\x18\x04 \x01(\x03R\tlengthMax\x12\x1b\n" +
|
||||
"\tdelay_min\x18\x05 \x01(\x03R\bdelayMin\x12\x1b\n" +
|
||||
"\tdelay_max\x18\x06 \x01(\x03R\bdelayMax\x12\"\n" +
|
||||
"\rmax_split_min\x18\a \x01(\x03R\vmaxSplitMin\x12\"\n" +
|
||||
"\rmax_split_max\x18\b \x01(\x03R\vmaxSplitMaxB\xa0\x01\n" +
|
||||
".com.xray.transport.internet.finalmask.fragmentP\x01Z?github.com/xtls/xray-core/transport/internet/finalmask/fragment\xaa\x02*Xray.Transport.Internet.Finalmask.Fragmentb\x06proto3"
|
||||
|
||||
var (
|
||||
file_transport_internet_finalmask_fragment_config_proto_rawDescOnce sync.Once
|
||||
file_transport_internet_finalmask_fragment_config_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_transport_internet_finalmask_fragment_config_proto_rawDescGZIP() []byte {
|
||||
file_transport_internet_finalmask_fragment_config_proto_rawDescOnce.Do(func() {
|
||||
file_transport_internet_finalmask_fragment_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_fragment_config_proto_rawDesc), len(file_transport_internet_finalmask_fragment_config_proto_rawDesc)))
|
||||
})
|
||||
return file_transport_internet_finalmask_fragment_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_transport_internet_finalmask_fragment_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_transport_internet_finalmask_fragment_config_proto_goTypes = []any{
|
||||
(*Config)(nil), // 0: xray.transport.internet.finalmask.fragment.Config
|
||||
}
|
||||
var file_transport_internet_finalmask_fragment_config_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_transport_internet_finalmask_fragment_config_proto_init() }
|
||||
func file_transport_internet_finalmask_fragment_config_proto_init() {
|
||||
if File_transport_internet_finalmask_fragment_config_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_fragment_config_proto_rawDesc), len(file_transport_internet_finalmask_fragment_config_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_transport_internet_finalmask_fragment_config_proto_goTypes,
|
||||
DependencyIndexes: file_transport_internet_finalmask_fragment_config_proto_depIdxs,
|
||||
MessageInfos: file_transport_internet_finalmask_fragment_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_transport_internet_finalmask_fragment_config_proto = out.File
|
||||
file_transport_internet_finalmask_fragment_config_proto_goTypes = nil
|
||||
file_transport_internet_finalmask_fragment_config_proto_depIdxs = nil
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package xray.transport.internet.finalmask.fragment;
|
||||
option csharp_namespace = "Xray.Transport.Internet.Finalmask.Fragment";
|
||||
option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/fragment";
|
||||
option java_package = "com.xray.transport.internet.finalmask.fragment";
|
||||
option java_multiple_files = true;
|
||||
|
||||
message Config {
|
||||
int64 packets_from = 1;
|
||||
int64 packets_to = 2;
|
||||
int64 length_min = 3;
|
||||
int64 length_max = 4;
|
||||
int64 delay_min = 5;
|
||||
int64 delay_max = 6;
|
||||
int64 max_split_min = 7;
|
||||
int64 max_split_max = 8;
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
package fragment
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
)
|
||||
|
||||
type fragmentConn struct {
|
||||
net.Conn
|
||||
config *Config
|
||||
count uint64
|
||||
|
||||
server bool
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.Conn, server bool) (net.Conn, error) {
|
||||
conn := &fragmentConn{
|
||||
Conn: raw,
|
||||
config: c,
|
||||
|
||||
server: server,
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.Conn, server bool) (net.Conn, error) {
|
||||
return NewConnClient(c, raw, server)
|
||||
}
|
||||
|
||||
func (c *fragmentConn) TcpMaskConn() {}
|
||||
|
||||
func (c *fragmentConn) RawConn() net.Conn {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
func (c *fragmentConn) Splice() bool {
|
||||
if c.server {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *fragmentConn) Write(p []byte) (n int, err error) {
|
||||
c.count++
|
||||
|
||||
if c.config.PacketsFrom == 0 && c.config.PacketsTo == 1 {
|
||||
if c.count != 1 || len(p) <= 5 || p[0] != 22 {
|
||||
return c.Conn.Write(p)
|
||||
}
|
||||
recordLen := 5 + ((int(p[3]) << 8) | int(p[4]))
|
||||
if len(p) < recordLen {
|
||||
return c.Conn.Write(p)
|
||||
}
|
||||
data := p[5:recordLen]
|
||||
buff := make([]byte, 2048)
|
||||
var hello []byte
|
||||
maxSplit := crypto.RandBetween(c.config.MaxSplitMin, c.config.MaxSplitMax)
|
||||
var splitNum int64
|
||||
for from := 0; ; {
|
||||
to := from + int(crypto.RandBetween(c.config.LengthMin, c.config.LengthMax))
|
||||
splitNum++
|
||||
if to > len(data) || (maxSplit > 0 && splitNum >= maxSplit) {
|
||||
to = len(data)
|
||||
}
|
||||
l := to - from
|
||||
if 5+l > len(buff) {
|
||||
buff = make([]byte, 5+l)
|
||||
}
|
||||
copy(buff[:3], p)
|
||||
copy(buff[5:], data[from:to])
|
||||
from = to
|
||||
buff[3] = byte(l >> 8)
|
||||
buff[4] = byte(l)
|
||||
if c.config.DelayMax == 0 {
|
||||
hello = append(hello, buff[:5+l]...)
|
||||
} else {
|
||||
_, err := c.Conn.Write(buff[:5+l])
|
||||
time.Sleep(time.Duration(crypto.RandBetween(c.config.DelayMin, c.config.DelayMax)) * time.Millisecond)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
if from == len(data) {
|
||||
if len(hello) > 0 {
|
||||
_, err := c.Conn.Write(hello)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
if len(p) > recordLen {
|
||||
n, err := c.Conn.Write(p[recordLen:])
|
||||
if err != nil {
|
||||
return recordLen + n, err
|
||||
}
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if c.config.PacketsFrom != 0 && (c.count < uint64(c.config.PacketsFrom) || c.count > uint64(c.config.PacketsTo)) {
|
||||
return c.Conn.Write(p)
|
||||
}
|
||||
maxSplit := crypto.RandBetween(c.config.MaxSplitMin, c.config.MaxSplitMax)
|
||||
var splitNum int64
|
||||
for from := 0; ; {
|
||||
to := from + int(crypto.RandBetween(c.config.LengthMin, c.config.LengthMax))
|
||||
splitNum++
|
||||
if to > len(p) || (maxSplit > 0 && splitNum >= maxSplit) {
|
||||
to = len(p)
|
||||
}
|
||||
n, err := c.Conn.Write(p[from:to])
|
||||
from += n
|
||||
if err != nil {
|
||||
return from, err
|
||||
}
|
||||
time.Sleep(time.Duration(crypto.RandBetween(c.config.DelayMin, c.config.DelayMax)) * time.Millisecond)
|
||||
if from >= len(p) {
|
||||
return from, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package custom
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func (c *TCPConfig) TCP() {
|
||||
}
|
||||
|
||||
func (c *TCPConfig) WrapConnClient(raw net.Conn) (net.Conn, error) {
|
||||
return NewConnClientTCP(c, raw)
|
||||
}
|
||||
|
||||
func (c *TCPConfig) WrapConnServer(raw net.Conn) (net.Conn, error) {
|
||||
return NewConnServerTCP(c, raw)
|
||||
}
|
||||
|
||||
func (c *UDPConfig) UDP() {
|
||||
}
|
||||
|
||||
func (c *UDPConfig) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClientUDP(c, raw)
|
||||
}
|
||||
|
||||
func (c *UDPConfig) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServerUDP(c, raw)
|
||||
}
|
||||
|
||||
func (c *UDPConfig) HeaderConn() {
|
||||
}
|
||||
@@ -1,416 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v6.33.5
|
||||
// source: transport/internet/finalmask/header/custom/config.proto
|
||||
|
||||
package custom
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type TCPItem struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
DelayMin int64 `protobuf:"varint,1,opt,name=delay_min,json=delayMin,proto3" json:"delay_min,omitempty"`
|
||||
DelayMax int64 `protobuf:"varint,2,opt,name=delay_max,json=delayMax,proto3" json:"delay_max,omitempty"`
|
||||
Rand int32 `protobuf:"varint,3,opt,name=rand,proto3" json:"rand,omitempty"`
|
||||
RandMin int32 `protobuf:"varint,4,opt,name=rand_min,json=randMin,proto3" json:"rand_min,omitempty"`
|
||||
RandMax int32 `protobuf:"varint,5,opt,name=rand_max,json=randMax,proto3" json:"rand_max,omitempty"`
|
||||
Packet []byte `protobuf:"bytes,6,opt,name=packet,proto3" json:"packet,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *TCPItem) Reset() {
|
||||
*x = TCPItem{}
|
||||
mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *TCPItem) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TCPItem) ProtoMessage() {}
|
||||
|
||||
func (x *TCPItem) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[0]
|
||||
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 TCPItem.ProtoReflect.Descriptor instead.
|
||||
func (*TCPItem) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *TCPItem) GetDelayMin() int64 {
|
||||
if x != nil {
|
||||
return x.DelayMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TCPItem) GetDelayMax() int64 {
|
||||
if x != nil {
|
||||
return x.DelayMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TCPItem) GetRand() int32 {
|
||||
if x != nil {
|
||||
return x.Rand
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TCPItem) GetRandMin() int32 {
|
||||
if x != nil {
|
||||
return x.RandMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TCPItem) GetRandMax() int32 {
|
||||
if x != nil {
|
||||
return x.RandMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TCPItem) GetPacket() []byte {
|
||||
if x != nil {
|
||||
return x.Packet
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TCPSequence struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Sequence []*TCPItem `protobuf:"bytes,1,rep,name=sequence,proto3" json:"sequence,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *TCPSequence) Reset() {
|
||||
*x = TCPSequence{}
|
||||
mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *TCPSequence) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TCPSequence) ProtoMessage() {}
|
||||
|
||||
func (x *TCPSequence) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[1]
|
||||
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 TCPSequence.ProtoReflect.Descriptor instead.
|
||||
func (*TCPSequence) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *TCPSequence) GetSequence() []*TCPItem {
|
||||
if x != nil {
|
||||
return x.Sequence
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TCPConfig struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Clients []*TCPSequence `protobuf:"bytes,1,rep,name=clients,proto3" json:"clients,omitempty"`
|
||||
Servers []*TCPSequence `protobuf:"bytes,2,rep,name=servers,proto3" json:"servers,omitempty"`
|
||||
Errors []*TCPSequence `protobuf:"bytes,3,rep,name=errors,proto3" json:"errors,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *TCPConfig) Reset() {
|
||||
*x = TCPConfig{}
|
||||
mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *TCPConfig) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TCPConfig) ProtoMessage() {}
|
||||
|
||||
func (x *TCPConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_finalmask_header_custom_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 TCPConfig.ProtoReflect.Descriptor instead.
|
||||
func (*TCPConfig) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *TCPConfig) GetClients() []*TCPSequence {
|
||||
if x != nil {
|
||||
return x.Clients
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TCPConfig) GetServers() []*TCPSequence {
|
||||
if x != nil {
|
||||
return x.Servers
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TCPConfig) GetErrors() []*TCPSequence {
|
||||
if x != nil {
|
||||
return x.Errors
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type UDPItem struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Rand int32 `protobuf:"varint,1,opt,name=rand,proto3" json:"rand,omitempty"`
|
||||
RandMin int32 `protobuf:"varint,2,opt,name=rand_min,json=randMin,proto3" json:"rand_min,omitempty"`
|
||||
RandMax int32 `protobuf:"varint,3,opt,name=rand_max,json=randMax,proto3" json:"rand_max,omitempty"`
|
||||
Packet []byte `protobuf:"bytes,4,opt,name=packet,proto3" json:"packet,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *UDPItem) Reset() {
|
||||
*x = UDPItem{}
|
||||
mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *UDPItem) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*UDPItem) ProtoMessage() {}
|
||||
|
||||
func (x *UDPItem) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[3]
|
||||
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 UDPItem.ProtoReflect.Descriptor instead.
|
||||
func (*UDPItem) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *UDPItem) GetRand() int32 {
|
||||
if x != nil {
|
||||
return x.Rand
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *UDPItem) GetRandMin() int32 {
|
||||
if x != nil {
|
||||
return x.RandMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *UDPItem) GetRandMax() int32 {
|
||||
if x != nil {
|
||||
return x.RandMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *UDPItem) GetPacket() []byte {
|
||||
if x != nil {
|
||||
return x.Packet
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type UDPConfig struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Client []*UDPItem `protobuf:"bytes,1,rep,name=client,proto3" json:"client,omitempty"`
|
||||
Server []*UDPItem `protobuf:"bytes,2,rep,name=server,proto3" json:"server,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *UDPConfig) Reset() {
|
||||
*x = UDPConfig{}
|
||||
mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *UDPConfig) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*UDPConfig) ProtoMessage() {}
|
||||
|
||||
func (x *UDPConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[4]
|
||||
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 UDPConfig.ProtoReflect.Descriptor instead.
|
||||
func (*UDPConfig) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *UDPConfig) GetClient() []*UDPItem {
|
||||
if x != nil {
|
||||
return x.Client
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *UDPConfig) GetServer() []*UDPItem {
|
||||
if x != nil {
|
||||
return x.Server
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_transport_internet_finalmask_header_custom_config_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_transport_internet_finalmask_header_custom_config_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"7transport/internet/finalmask/header/custom/config.proto\x12/xray.transport.internet.finalmask.header.custom\"\xa5\x01\n" +
|
||||
"\aTCPItem\x12\x1b\n" +
|
||||
"\tdelay_min\x18\x01 \x01(\x03R\bdelayMin\x12\x1b\n" +
|
||||
"\tdelay_max\x18\x02 \x01(\x03R\bdelayMax\x12\x12\n" +
|
||||
"\x04rand\x18\x03 \x01(\x05R\x04rand\x12\x19\n" +
|
||||
"\brand_min\x18\x04 \x01(\x05R\arandMin\x12\x19\n" +
|
||||
"\brand_max\x18\x05 \x01(\x05R\arandMax\x12\x16\n" +
|
||||
"\x06packet\x18\x06 \x01(\fR\x06packet\"c\n" +
|
||||
"\vTCPSequence\x12T\n" +
|
||||
"\bsequence\x18\x01 \x03(\v28.xray.transport.internet.finalmask.header.custom.TCPItemR\bsequence\"\x91\x02\n" +
|
||||
"\tTCPConfig\x12V\n" +
|
||||
"\aclients\x18\x01 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\aclients\x12V\n" +
|
||||
"\aservers\x18\x02 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\aservers\x12T\n" +
|
||||
"\x06errors\x18\x03 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\x06errors\"k\n" +
|
||||
"\aUDPItem\x12\x12\n" +
|
||||
"\x04rand\x18\x01 \x01(\x05R\x04rand\x12\x19\n" +
|
||||
"\brand_min\x18\x02 \x01(\x05R\arandMin\x12\x19\n" +
|
||||
"\brand_max\x18\x03 \x01(\x05R\arandMax\x12\x16\n" +
|
||||
"\x06packet\x18\x04 \x01(\fR\x06packet\"\xaf\x01\n" +
|
||||
"\tUDPConfig\x12P\n" +
|
||||
"\x06client\x18\x01 \x03(\v28.xray.transport.internet.finalmask.header.custom.UDPItemR\x06client\x12P\n" +
|
||||
"\x06server\x18\x02 \x03(\v28.xray.transport.internet.finalmask.header.custom.UDPItemR\x06serverB\xaf\x01\n" +
|
||||
"3com.xray.transport.internet.finalmask.header.customP\x01ZDgithub.com/xtls/xray-core/transport/internet/finalmask/header/custom\xaa\x02/Xray.Transport.Internet.Finalmask.Header.Customb\x06proto3"
|
||||
|
||||
var (
|
||||
file_transport_internet_finalmask_header_custom_config_proto_rawDescOnce sync.Once
|
||||
file_transport_internet_finalmask_header_custom_config_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP() []byte {
|
||||
file_transport_internet_finalmask_header_custom_config_proto_rawDescOnce.Do(func() {
|
||||
file_transport_internet_finalmask_header_custom_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_custom_config_proto_rawDesc), len(file_transport_internet_finalmask_header_custom_config_proto_rawDesc)))
|
||||
})
|
||||
return file_transport_internet_finalmask_header_custom_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_transport_internet_finalmask_header_custom_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
||||
var file_transport_internet_finalmask_header_custom_config_proto_goTypes = []any{
|
||||
(*TCPItem)(nil), // 0: xray.transport.internet.finalmask.header.custom.TCPItem
|
||||
(*TCPSequence)(nil), // 1: xray.transport.internet.finalmask.header.custom.TCPSequence
|
||||
(*TCPConfig)(nil), // 2: xray.transport.internet.finalmask.header.custom.TCPConfig
|
||||
(*UDPItem)(nil), // 3: xray.transport.internet.finalmask.header.custom.UDPItem
|
||||
(*UDPConfig)(nil), // 4: xray.transport.internet.finalmask.header.custom.UDPConfig
|
||||
}
|
||||
var file_transport_internet_finalmask_header_custom_config_proto_depIdxs = []int32{
|
||||
0, // 0: xray.transport.internet.finalmask.header.custom.TCPSequence.sequence:type_name -> xray.transport.internet.finalmask.header.custom.TCPItem
|
||||
1, // 1: xray.transport.internet.finalmask.header.custom.TCPConfig.clients:type_name -> xray.transport.internet.finalmask.header.custom.TCPSequence
|
||||
1, // 2: xray.transport.internet.finalmask.header.custom.TCPConfig.servers:type_name -> xray.transport.internet.finalmask.header.custom.TCPSequence
|
||||
1, // 3: xray.transport.internet.finalmask.header.custom.TCPConfig.errors:type_name -> xray.transport.internet.finalmask.header.custom.TCPSequence
|
||||
3, // 4: xray.transport.internet.finalmask.header.custom.UDPConfig.client:type_name -> xray.transport.internet.finalmask.header.custom.UDPItem
|
||||
3, // 5: xray.transport.internet.finalmask.header.custom.UDPConfig.server:type_name -> xray.transport.internet.finalmask.header.custom.UDPItem
|
||||
6, // [6:6] is the sub-list for method output_type
|
||||
6, // [6:6] is the sub-list for method input_type
|
||||
6, // [6:6] is the sub-list for extension type_name
|
||||
6, // [6:6] is the sub-list for extension extendee
|
||||
0, // [0:6] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_transport_internet_finalmask_header_custom_config_proto_init() }
|
||||
func file_transport_internet_finalmask_header_custom_config_proto_init() {
|
||||
if File_transport_internet_finalmask_header_custom_config_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_custom_config_proto_rawDesc), len(file_transport_internet_finalmask_header_custom_config_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 5,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_transport_internet_finalmask_header_custom_config_proto_goTypes,
|
||||
DependencyIndexes: file_transport_internet_finalmask_header_custom_config_proto_depIdxs,
|
||||
MessageInfos: file_transport_internet_finalmask_header_custom_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_transport_internet_finalmask_header_custom_config_proto = out.File
|
||||
file_transport_internet_finalmask_header_custom_config_proto_goTypes = nil
|
||||
file_transport_internet_finalmask_header_custom_config_proto_depIdxs = nil
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package xray.transport.internet.finalmask.header.custom;
|
||||
option csharp_namespace = "Xray.Transport.Internet.Finalmask.Header.Custom";
|
||||
option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/header/custom";
|
||||
option java_package = "com.xray.transport.internet.finalmask.header.custom";
|
||||
option java_multiple_files = true;
|
||||
|
||||
message TCPItem {
|
||||
int64 delay_min = 1;
|
||||
int64 delay_max = 2;
|
||||
int32 rand = 3;
|
||||
int32 rand_min = 4;
|
||||
int32 rand_max = 5;
|
||||
bytes packet = 6;
|
||||
}
|
||||
|
||||
message TCPSequence {
|
||||
repeated TCPItem sequence = 1;
|
||||
}
|
||||
|
||||
message TCPConfig {
|
||||
repeated TCPSequence clients = 1;
|
||||
repeated TCPSequence servers = 2;
|
||||
repeated TCPSequence errors = 3;
|
||||
}
|
||||
|
||||
message UDPItem {
|
||||
int32 rand = 1;
|
||||
int32 rand_min = 2;
|
||||
int32 rand_max = 3;
|
||||
bytes packet = 4;
|
||||
}
|
||||
|
||||
message UDPConfig {
|
||||
repeated UDPItem client = 1;
|
||||
repeated UDPItem server = 2;
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
package custom
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
)
|
||||
|
||||
type tcpCustomClient struct {
|
||||
clients []*TCPSequence
|
||||
servers []*TCPSequence
|
||||
}
|
||||
|
||||
type tcpCustomClientConn struct {
|
||||
net.Conn
|
||||
header *tcpCustomClient
|
||||
|
||||
auth bool
|
||||
wg sync.WaitGroup
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
func NewConnClientTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) {
|
||||
conn := &tcpCustomClientConn{
|
||||
Conn: raw,
|
||||
header: &tcpCustomClient{
|
||||
clients: c.Clients,
|
||||
servers: c.Servers,
|
||||
},
|
||||
}
|
||||
|
||||
conn.wg.Add(1)
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *tcpCustomClientConn) TcpMaskConn() {}
|
||||
|
||||
func (c *tcpCustomClientConn) RawConn() net.Conn {
|
||||
// c.wg.Wait()
|
||||
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
func (c *tcpCustomClientConn) Splice() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *tcpCustomClientConn) Read(p []byte) (n int, err error) {
|
||||
c.wg.Wait()
|
||||
|
||||
if !c.auth {
|
||||
return 0, errors.New("header auth failed")
|
||||
}
|
||||
|
||||
return c.Conn.Read(p)
|
||||
}
|
||||
|
||||
func (c *tcpCustomClientConn) Write(p []byte) (n int, err error) {
|
||||
c.once.Do(func() {
|
||||
i := 0
|
||||
j := 0
|
||||
for i = range c.header.clients {
|
||||
if !writeSequence(c.Conn, c.header.clients[i]) {
|
||||
c.wg.Done()
|
||||
return
|
||||
}
|
||||
|
||||
if j < len(c.header.servers) {
|
||||
if !readSequence(c.Conn, c.header.servers[j]) {
|
||||
c.wg.Done()
|
||||
return
|
||||
}
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
for j < len(c.header.servers) {
|
||||
if !readSequence(c.Conn, c.header.servers[j]) {
|
||||
c.wg.Done()
|
||||
return
|
||||
}
|
||||
j++
|
||||
}
|
||||
|
||||
c.auth = true
|
||||
c.wg.Done()
|
||||
})
|
||||
|
||||
c.wg.Wait()
|
||||
|
||||
if !c.auth {
|
||||
return 0, errors.New("header auth failed")
|
||||
}
|
||||
|
||||
return c.Conn.Write(p)
|
||||
}
|
||||
|
||||
type tcpCustomServer struct {
|
||||
clients []*TCPSequence
|
||||
servers []*TCPSequence
|
||||
errors []*TCPSequence
|
||||
}
|
||||
|
||||
type tcpCustomServerConn struct {
|
||||
net.Conn
|
||||
header *tcpCustomServer
|
||||
|
||||
auth bool
|
||||
wg sync.WaitGroup
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
func NewConnServerTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) {
|
||||
conn := &tcpCustomServerConn{
|
||||
Conn: raw,
|
||||
header: &tcpCustomServer{
|
||||
clients: c.Clients,
|
||||
servers: c.Servers,
|
||||
errors: c.Errors,
|
||||
},
|
||||
}
|
||||
|
||||
conn.wg.Add(1)
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *tcpCustomServerConn) TcpMaskConn() {}
|
||||
|
||||
func (c *tcpCustomServerConn) RawConn() net.Conn {
|
||||
// c.wg.Wait()
|
||||
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
func (c *tcpCustomServerConn) Splice() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) {
|
||||
c.once.Do(func() {
|
||||
i := 0
|
||||
j := 0
|
||||
for i = range c.header.clients {
|
||||
if !readSequence(c.Conn, c.header.clients[i]) {
|
||||
if i < len(c.header.errors) {
|
||||
writeSequence(c.Conn, c.header.errors[i])
|
||||
}
|
||||
c.wg.Done()
|
||||
return
|
||||
}
|
||||
|
||||
if j < len(c.header.servers) {
|
||||
if !writeSequence(c.Conn, c.header.servers[j]) {
|
||||
c.wg.Done()
|
||||
return
|
||||
}
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
for j < len(c.header.servers) {
|
||||
if !writeSequence(c.Conn, c.header.servers[j]) {
|
||||
c.wg.Done()
|
||||
return
|
||||
}
|
||||
j++
|
||||
}
|
||||
|
||||
c.auth = true
|
||||
c.wg.Done()
|
||||
})
|
||||
|
||||
c.wg.Wait()
|
||||
|
||||
if !c.auth {
|
||||
return 0, errors.New("header auth failed")
|
||||
}
|
||||
|
||||
return c.Conn.Read(p)
|
||||
}
|
||||
|
||||
func (c *tcpCustomServerConn) Write(p []byte) (n int, err error) {
|
||||
c.wg.Wait()
|
||||
|
||||
if !c.auth {
|
||||
return 0, errors.New("header auth failed")
|
||||
}
|
||||
|
||||
return c.Conn.Write(p)
|
||||
}
|
||||
|
||||
func readSequence(r io.Reader, sequence *TCPSequence) bool {
|
||||
for _, item := range sequence.Sequence {
|
||||
length := max(int(item.Rand), len(item.Packet))
|
||||
buf := make([]byte, length)
|
||||
n, err := io.ReadFull(r, buf)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if item.Rand > 0 && n != length {
|
||||
return false
|
||||
}
|
||||
if len(item.Packet) > 0 && !bytes.Equal(item.Packet, buf[:n]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func writeSequence(w io.Writer, sequence *TCPSequence) bool {
|
||||
var merged []byte
|
||||
for _, item := range sequence.Sequence {
|
||||
if item.DelayMax > 0 {
|
||||
if len(merged) > 0 {
|
||||
_, err := w.Write(merged)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
merged = nil
|
||||
}
|
||||
time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond)
|
||||
}
|
||||
if item.Rand > 0 {
|
||||
buf := make([]byte, item.Rand)
|
||||
crypto.RandBytesBetween(buf, byte(item.RandMin), byte(item.RandMax))
|
||||
merged = append(merged, buf...)
|
||||
} else {
|
||||
merged = append(merged, item.Packet...)
|
||||
}
|
||||
}
|
||||
if len(merged) > 0 {
|
||||
_, err := w.Write(merged)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
merged = nil
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
package custom
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
)
|
||||
|
||||
type udpCustomClient struct {
|
||||
client []*UDPItem
|
||||
server []*UDPItem
|
||||
merged []byte
|
||||
}
|
||||
|
||||
func (h *udpCustomClient) Serialize(b []byte) {
|
||||
index := 0
|
||||
for _, item := range h.client {
|
||||
if item.Rand > 0 {
|
||||
crypto.RandBytesBetween(h.merged[index:index+int(item.Rand)], byte(item.RandMin), byte(item.RandMax))
|
||||
index += int(item.Rand)
|
||||
} else {
|
||||
index += len(item.Packet)
|
||||
}
|
||||
}
|
||||
copy(b, h.merged)
|
||||
}
|
||||
|
||||
func (h *udpCustomClient) Match(b []byte) bool {
|
||||
if len(b) < len(h.merged) {
|
||||
return false
|
||||
}
|
||||
|
||||
data := b
|
||||
match := true
|
||||
|
||||
for _, item := range h.server {
|
||||
length := max(int(item.Rand), len(item.Packet))
|
||||
|
||||
if len(item.Packet) > 0 && !bytes.Equal(item.Packet, data[:length]) {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
|
||||
data = data[length:]
|
||||
}
|
||||
|
||||
return match
|
||||
}
|
||||
|
||||
type udpCustomClientConn struct {
|
||||
net.PacketConn
|
||||
header *udpCustomClient
|
||||
}
|
||||
|
||||
func NewConnClientUDP(c *UDPConfig, raw net.PacketConn) (net.PacketConn, error) {
|
||||
conn := &udpCustomClientConn{
|
||||
PacketConn: raw,
|
||||
header: &udpCustomClient{
|
||||
client: c.Client,
|
||||
server: c.Server,
|
||||
},
|
||||
}
|
||||
|
||||
index := 0
|
||||
for _, item := range conn.header.client {
|
||||
if item.Rand > 0 {
|
||||
conn.header.merged = append(conn.header.merged, make([]byte, item.Rand)...)
|
||||
index += int(item.Rand)
|
||||
} else {
|
||||
conn.header.merged = append(conn.header.merged, item.Packet...)
|
||||
index += len(item.Packet)
|
||||
}
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *udpCustomClientConn) Size() int {
|
||||
return len(c.header.merged)
|
||||
}
|
||||
|
||||
func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
if !c.header.Match(p) {
|
||||
return 0, addr, errors.New("header mismatch")
|
||||
}
|
||||
|
||||
return len(p) - len(c.header.merged), addr, nil
|
||||
}
|
||||
|
||||
func (c *udpCustomClientConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
c.header.Serialize(p)
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
type udpCustomServer struct {
|
||||
client []*UDPItem
|
||||
server []*UDPItem
|
||||
merged []byte
|
||||
}
|
||||
|
||||
func (h *udpCustomServer) Serialize(b []byte) {
|
||||
index := 0
|
||||
for _, item := range h.server {
|
||||
if item.Rand > 0 {
|
||||
crypto.RandBytesBetween(h.merged[index:index+int(item.Rand)], byte(item.RandMin), byte(item.RandMax))
|
||||
index += int(item.Rand)
|
||||
} else {
|
||||
index += len(item.Packet)
|
||||
}
|
||||
}
|
||||
copy(b, h.merged)
|
||||
}
|
||||
|
||||
func (h *udpCustomServer) Match(b []byte) bool {
|
||||
if len(b) < len(h.merged) {
|
||||
return false
|
||||
}
|
||||
|
||||
data := b
|
||||
match := true
|
||||
|
||||
for _, item := range h.client {
|
||||
length := max(int(item.Rand), len(item.Packet))
|
||||
|
||||
if len(item.Packet) > 0 && !bytes.Equal(item.Packet, data[:length]) {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
|
||||
data = data[length:]
|
||||
}
|
||||
|
||||
return match
|
||||
}
|
||||
|
||||
type udpCustomServerConn struct {
|
||||
net.PacketConn
|
||||
header *udpCustomServer
|
||||
}
|
||||
|
||||
func NewConnServerUDP(c *UDPConfig, raw net.PacketConn) (net.PacketConn, error) {
|
||||
conn := &udpCustomServerConn{
|
||||
PacketConn: raw,
|
||||
header: &udpCustomServer{
|
||||
client: c.Client,
|
||||
server: c.Server,
|
||||
},
|
||||
}
|
||||
|
||||
index := 0
|
||||
for _, item := range conn.header.server {
|
||||
if item.Rand > 0 {
|
||||
conn.header.merged = append(conn.header.merged, make([]byte, item.Rand)...)
|
||||
index += int(item.Rand)
|
||||
} else {
|
||||
conn.header.merged = append(conn.header.merged, item.Packet...)
|
||||
index += len(item.Packet)
|
||||
}
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *udpCustomServerConn) Size() int {
|
||||
return len(c.header.merged)
|
||||
}
|
||||
|
||||
func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
if !c.header.Match(p) {
|
||||
return 0, addr, errors.New("header mismatch")
|
||||
}
|
||||
|
||||
return len(p) - len(c.header.merged), addr, nil
|
||||
}
|
||||
|
||||
func (c *udpCustomServerConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
c.header.Serialize(p)
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
@@ -7,13 +7,10 @@ import (
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw)
|
||||
}
|
||||
|
||||
func (c *Config) HeaderConn() {
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,10 @@ package dns
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
sync "sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/dice"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
@@ -78,8 +81,8 @@ type dns struct {
|
||||
header []byte
|
||||
}
|
||||
|
||||
func (h *dns) Size() int {
|
||||
return len(h.header)
|
||||
func (h *dns) Size() int32 {
|
||||
return int32(len(h.header))
|
||||
}
|
||||
|
||||
func (h *dns) Serialize(b []byte) {
|
||||
@@ -88,11 +91,19 @@ func (h *dns) Serialize(b []byte) {
|
||||
}
|
||||
|
||||
type dnsConn struct {
|
||||
net.PacketConn
|
||||
first bool
|
||||
leaveSize int32
|
||||
|
||||
conn net.PacketConn
|
||||
header *dns
|
||||
|
||||
readBuf []byte
|
||||
readMutex sync.Mutex
|
||||
writeBuf []byte
|
||||
writeMutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
var header []byte
|
||||
header = binary.BigEndian.AppendUint16(header, 0x0000) // Transaction ID
|
||||
header = binary.BigEndian.AppendUint16(header, 0x0100) // Flags: Standard query
|
||||
@@ -110,29 +121,121 @@ func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
header = binary.BigEndian.AppendUint16(header, 0x0001) // Class: IN
|
||||
|
||||
conn := &dnsConn{
|
||||
PacketConn: raw,
|
||||
first: first,
|
||||
leaveSize: leaveSize,
|
||||
|
||||
conn: raw,
|
||||
header: &dns{
|
||||
header: header,
|
||||
},
|
||||
}
|
||||
|
||||
if first {
|
||||
conn.readBuf = make([]byte, 8192)
|
||||
conn.writeBuf = make([]byte, 8192)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *dnsConn) Size() int {
|
||||
func (c *dnsConn) Size() int32 {
|
||||
return c.header.Size()
|
||||
}
|
||||
|
||||
func (c *dnsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
return len(p) - c.header.Size(), addr, nil
|
||||
if c.first {
|
||||
c.readMutex.Lock()
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
if len(p) < n-int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
copy(p, c.readBuf[c.Size():n])
|
||||
|
||||
c.readMutex.Unlock()
|
||||
return n - int(c.Size()), addr, err
|
||||
}
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(p)
|
||||
if err != nil {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
copy(p, p[c.Size():n])
|
||||
|
||||
return n - int(c.Size()), addr, err
|
||||
}
|
||||
|
||||
func (c *dnsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
c.header.Serialize(p)
|
||||
if c.first {
|
||||
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||
return 0, errors.New("too many masks")
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
c.writeMutex.Lock()
|
||||
|
||||
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||
n += int(c.leaveSize) + int(c.Size())
|
||||
|
||||
c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()])
|
||||
|
||||
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||
|
||||
if err != nil {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if nn != n {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, errors.New("nn != n")
|
||||
}
|
||||
|
||||
c.writeMutex.Unlock()
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()])
|
||||
|
||||
return c.conn.WriteTo(p, addr)
|
||||
}
|
||||
|
||||
func (c *dnsConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *dnsConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *dnsConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *dnsConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *dnsConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
@@ -7,13 +7,10 @@ import (
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw)
|
||||
}
|
||||
|
||||
func (c *Config) HeaderConn() {
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package dtls
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
sync "sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/dice"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
)
|
||||
|
||||
type dtls struct {
|
||||
@@ -12,7 +16,7 @@ type dtls struct {
|
||||
sequence uint32
|
||||
}
|
||||
|
||||
func (*dtls) Size() int {
|
||||
func (*dtls) Size() int32 {
|
||||
return 1 + 2 + 2 + 6 + 2
|
||||
}
|
||||
|
||||
@@ -38,13 +42,24 @@ func (h *dtls) Serialize(b []byte) {
|
||||
}
|
||||
|
||||
type dtlsConn struct {
|
||||
net.PacketConn
|
||||
first bool
|
||||
leaveSize int32
|
||||
|
||||
conn net.PacketConn
|
||||
header *dtls
|
||||
|
||||
readBuf []byte
|
||||
readMutex sync.Mutex
|
||||
writeBuf []byte
|
||||
writeMutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
conn := &dtlsConn{
|
||||
PacketConn: raw,
|
||||
first: first,
|
||||
leaveSize: leaveSize,
|
||||
|
||||
conn: raw,
|
||||
header: &dtls{
|
||||
epoch: dice.RollUint16(),
|
||||
sequence: 0,
|
||||
@@ -52,23 +67,112 @@ func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
},
|
||||
}
|
||||
|
||||
if first {
|
||||
conn.readBuf = make([]byte, 8192)
|
||||
conn.writeBuf = make([]byte, 8192)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *dtlsConn) Size() int {
|
||||
func (c *dtlsConn) Size() int32 {
|
||||
return c.header.Size()
|
||||
}
|
||||
|
||||
func (c *dtlsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
return len(p) - c.header.Size(), addr, nil
|
||||
if c.first {
|
||||
c.readMutex.Lock()
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
if len(p) < n-int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
copy(p, c.readBuf[c.Size():n])
|
||||
|
||||
c.readMutex.Unlock()
|
||||
return n - int(c.Size()), addr, err
|
||||
}
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(p)
|
||||
if err != nil {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
copy(p, p[c.Size():n])
|
||||
|
||||
return n - int(c.Size()), addr, err
|
||||
}
|
||||
|
||||
func (c *dtlsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
c.header.Serialize(p)
|
||||
if c.first {
|
||||
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||
return 0, errors.New("too many masks")
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
c.writeMutex.Lock()
|
||||
|
||||
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||
n += int(c.leaveSize) + int(c.Size())
|
||||
|
||||
c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()])
|
||||
|
||||
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||
|
||||
if err != nil {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if nn != n {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, errors.New("nn != n")
|
||||
}
|
||||
|
||||
c.writeMutex.Unlock()
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()])
|
||||
|
||||
return c.conn.WriteTo(p, addr)
|
||||
}
|
||||
|
||||
func (c *dtlsConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *dtlsConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *dtlsConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *dtlsConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *dtlsConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
@@ -7,13 +7,10 @@ import (
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw)
|
||||
}
|
||||
|
||||
func (c *Config) HeaderConn() {
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
@@ -2,9 +2,13 @@ package srtp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
sync "sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/dice"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
)
|
||||
|
||||
type srtp struct {
|
||||
@@ -12,7 +16,7 @@ type srtp struct {
|
||||
number uint16
|
||||
}
|
||||
|
||||
func (*srtp) Size() int {
|
||||
func (*srtp) Size() int32 {
|
||||
return 4
|
||||
}
|
||||
|
||||
@@ -23,36 +27,136 @@ func (h *srtp) Serialize(b []byte) {
|
||||
}
|
||||
|
||||
type srtpConn struct {
|
||||
net.PacketConn
|
||||
first bool
|
||||
leaveSize int32
|
||||
|
||||
conn net.PacketConn
|
||||
header *srtp
|
||||
|
||||
readBuf []byte
|
||||
readMutex sync.Mutex
|
||||
writeBuf []byte
|
||||
writeMutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
conn := &srtpConn{
|
||||
PacketConn: raw,
|
||||
first: first,
|
||||
leaveSize: leaveSize,
|
||||
|
||||
conn: raw,
|
||||
header: &srtp{
|
||||
header: 0xB5E8,
|
||||
number: dice.RollUint16(),
|
||||
},
|
||||
}
|
||||
|
||||
if first {
|
||||
conn.readBuf = make([]byte, 8192)
|
||||
conn.writeBuf = make([]byte, 8192)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *srtpConn) Size() int {
|
||||
func (c *srtpConn) Size() int32 {
|
||||
return c.header.Size()
|
||||
}
|
||||
|
||||
func (c *srtpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
return len(p) - c.header.Size(), addr, nil
|
||||
if c.first {
|
||||
c.readMutex.Lock()
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
if len(p) < n-int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
copy(p, c.readBuf[c.Size():n])
|
||||
|
||||
c.readMutex.Unlock()
|
||||
return n - int(c.Size()), addr, err
|
||||
}
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(p)
|
||||
if err != nil {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
copy(p, p[c.Size():n])
|
||||
|
||||
return n - int(c.Size()), addr, err
|
||||
}
|
||||
|
||||
func (c *srtpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
c.header.Serialize(p)
|
||||
if c.first {
|
||||
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||
return 0, errors.New("too many masks")
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
c.writeMutex.Lock()
|
||||
|
||||
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||
n += int(c.leaveSize) + int(c.Size())
|
||||
|
||||
c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()])
|
||||
|
||||
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||
|
||||
if err != nil {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if nn != n {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, errors.New("nn != n")
|
||||
}
|
||||
|
||||
c.writeMutex.Unlock()
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()])
|
||||
|
||||
return c.conn.WriteTo(p, addr)
|
||||
}
|
||||
|
||||
func (c *srtpConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *srtpConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *srtpConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *srtpConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *srtpConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
@@ -7,13 +7,10 @@ import (
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw)
|
||||
}
|
||||
|
||||
func (c *Config) HeaderConn() {
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
@@ -2,9 +2,13 @@ package utp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
sync "sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/dice"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
)
|
||||
|
||||
type utp struct {
|
||||
@@ -13,7 +17,7 @@ type utp struct {
|
||||
connectionID uint16
|
||||
}
|
||||
|
||||
func (*utp) Size() int {
|
||||
func (*utp) Size() int32 {
|
||||
return 4
|
||||
}
|
||||
|
||||
@@ -24,13 +28,24 @@ func (h *utp) Serialize(b []byte) {
|
||||
}
|
||||
|
||||
type utpConn struct {
|
||||
net.PacketConn
|
||||
first bool
|
||||
leaveSize int32
|
||||
|
||||
conn net.PacketConn
|
||||
header *utp
|
||||
|
||||
readBuf []byte
|
||||
readMutex sync.Mutex
|
||||
writeBuf []byte
|
||||
writeMutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
conn := &utpConn{
|
||||
PacketConn: raw,
|
||||
first: first,
|
||||
leaveSize: leaveSize,
|
||||
|
||||
conn: raw,
|
||||
header: &utp{
|
||||
header: 1,
|
||||
extension: 0,
|
||||
@@ -38,23 +53,112 @@ func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
},
|
||||
}
|
||||
|
||||
if first {
|
||||
conn.readBuf = make([]byte, 8192)
|
||||
conn.writeBuf = make([]byte, 8192)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *utpConn) Size() int {
|
||||
func (c *utpConn) Size() int32 {
|
||||
return c.header.Size()
|
||||
}
|
||||
|
||||
func (c *utpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
return len(p) - c.header.Size(), addr, nil
|
||||
if c.first {
|
||||
c.readMutex.Lock()
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
if len(p) < n-int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
copy(p, c.readBuf[c.Size():n])
|
||||
|
||||
c.readMutex.Unlock()
|
||||
return n - int(c.Size()), addr, err
|
||||
}
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(p)
|
||||
if err != nil {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
copy(p, p[c.Size():n])
|
||||
|
||||
return n - int(c.Size()), addr, err
|
||||
}
|
||||
|
||||
func (c *utpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
c.header.Serialize(p)
|
||||
if c.first {
|
||||
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||
return 0, errors.New("too many masks")
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
c.writeMutex.Lock()
|
||||
|
||||
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||
n += int(c.leaveSize) + int(c.Size())
|
||||
|
||||
c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()])
|
||||
|
||||
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||
|
||||
if err != nil {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if nn != n {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, errors.New("nn != n")
|
||||
}
|
||||
|
||||
c.writeMutex.Unlock()
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()])
|
||||
|
||||
return c.conn.WriteTo(p, addr)
|
||||
}
|
||||
|
||||
func (c *utpConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *utpConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *utpConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *utpConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *utpConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
@@ -7,13 +7,10 @@ import (
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw)
|
||||
}
|
||||
|
||||
func (c *Config) HeaderConn() {
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
@@ -2,16 +2,20 @@ package wechat
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
sync "sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/dice"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
)
|
||||
|
||||
type wechat struct {
|
||||
sn uint32
|
||||
}
|
||||
|
||||
func (*wechat) Size() int {
|
||||
func (*wechat) Size() int32 {
|
||||
return 13
|
||||
}
|
||||
|
||||
@@ -30,35 +34,135 @@ func (h *wechat) Serialize(b []byte) {
|
||||
}
|
||||
|
||||
type wechatConn struct {
|
||||
net.PacketConn
|
||||
first bool
|
||||
leaveSize int32
|
||||
|
||||
conn net.PacketConn
|
||||
header *wechat
|
||||
|
||||
readBuf []byte
|
||||
readMutex sync.Mutex
|
||||
writeBuf []byte
|
||||
writeMutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
conn := &wechatConn{
|
||||
PacketConn: raw,
|
||||
first: first,
|
||||
leaveSize: leaveSize,
|
||||
|
||||
conn: raw,
|
||||
header: &wechat{
|
||||
sn: uint32(dice.RollUint16()),
|
||||
},
|
||||
}
|
||||
|
||||
if first {
|
||||
conn.readBuf = make([]byte, 8192)
|
||||
conn.writeBuf = make([]byte, 8192)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *wechatConn) Size() int {
|
||||
func (c *wechatConn) Size() int32 {
|
||||
return c.header.Size()
|
||||
}
|
||||
|
||||
func (c *wechatConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
return len(p) - c.header.Size(), addr, nil
|
||||
if c.first {
|
||||
c.readMutex.Lock()
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
if len(p) < n-int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
copy(p, c.readBuf[c.Size():n])
|
||||
|
||||
c.readMutex.Unlock()
|
||||
return n - int(c.Size()), addr, err
|
||||
}
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(p)
|
||||
if err != nil {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
copy(p, p[c.Size():n])
|
||||
|
||||
return n - int(c.Size()), addr, err
|
||||
}
|
||||
|
||||
func (c *wechatConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
c.header.Serialize(p)
|
||||
if c.first {
|
||||
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||
return 0, errors.New("too many masks")
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
c.writeMutex.Lock()
|
||||
|
||||
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||
n += int(c.leaveSize) + int(c.Size())
|
||||
|
||||
c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()])
|
||||
|
||||
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||
|
||||
if err != nil {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if nn != n {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, errors.New("nn != n")
|
||||
}
|
||||
|
||||
c.writeMutex.Unlock()
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()])
|
||||
|
||||
return c.conn.WriteTo(p, addr)
|
||||
}
|
||||
|
||||
func (c *wechatConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *wechatConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *wechatConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *wechatConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *wechatConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
@@ -7,13 +7,10 @@ import (
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw)
|
||||
}
|
||||
|
||||
func (c *Config) HeaderConn() {
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
package wireguard
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
sync "sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
)
|
||||
|
||||
type wireguare struct{}
|
||||
|
||||
func (*wireguare) Size() int {
|
||||
func (*wireguare) Size() int32 {
|
||||
return 4
|
||||
}
|
||||
|
||||
@@ -18,33 +23,133 @@ func (h *wireguare) Serialize(b []byte) {
|
||||
}
|
||||
|
||||
type wireguareConn struct {
|
||||
net.PacketConn
|
||||
first bool
|
||||
leaveSize int32
|
||||
|
||||
conn net.PacketConn
|
||||
header *wireguare
|
||||
|
||||
readBuf []byte
|
||||
readMutex sync.Mutex
|
||||
writeBuf []byte
|
||||
writeMutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
conn := &wireguareConn{
|
||||
PacketConn: raw,
|
||||
header: &wireguare{},
|
||||
first: first,
|
||||
leaveSize: leaveSize,
|
||||
|
||||
conn: raw,
|
||||
header: &wireguare{},
|
||||
}
|
||||
|
||||
if first {
|
||||
conn.readBuf = make([]byte, 8192)
|
||||
conn.writeBuf = make([]byte, 8192)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *wireguareConn) Size() int {
|
||||
func (c *wireguareConn) Size() int32 {
|
||||
return c.header.Size()
|
||||
}
|
||||
|
||||
func (c *wireguareConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
return len(p) - c.header.Size(), addr, nil
|
||||
if c.first {
|
||||
c.readMutex.Lock()
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
if len(p) < n-int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
copy(p, c.readBuf[c.Size():n])
|
||||
|
||||
c.readMutex.Unlock()
|
||||
return n - int(c.Size()), addr, err
|
||||
}
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(p)
|
||||
if err != nil {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
copy(p, p[c.Size():n])
|
||||
|
||||
return n - int(c.Size()), addr, err
|
||||
}
|
||||
|
||||
func (c *wireguareConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
c.header.Serialize(p)
|
||||
if c.first {
|
||||
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||
return 0, errors.New("too many masks")
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
c.writeMutex.Lock()
|
||||
|
||||
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||
n += int(c.leaveSize) + int(c.Size())
|
||||
|
||||
c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()])
|
||||
|
||||
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||
|
||||
if err != nil {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if nn != n {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, errors.New("nn != n")
|
||||
}
|
||||
|
||||
c.writeMutex.Unlock()
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()])
|
||||
|
||||
return c.conn.WriteTo(p, addr)
|
||||
}
|
||||
|
||||
func (c *wireguareConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *wireguareConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *wireguareConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *wireguareConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *wireguareConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
@@ -7,13 +7,10 @@ import (
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw)
|
||||
}
|
||||
|
||||
func (c *Config) HeaderConn() {
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
@@ -4,64 +4,171 @@ import (
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"io"
|
||||
"net"
|
||||
sync "sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
)
|
||||
|
||||
type aes128gcmConn struct {
|
||||
net.PacketConn
|
||||
first bool
|
||||
leaveSize int32
|
||||
|
||||
conn net.PacketConn
|
||||
aead cipher.AEAD
|
||||
|
||||
readBuf []byte
|
||||
readMutex sync.Mutex
|
||||
writeBuf []byte
|
||||
writeMutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
hashedPsk := sha256.Sum256([]byte(c.Password))
|
||||
|
||||
conn := &aes128gcmConn{
|
||||
PacketConn: raw,
|
||||
aead: crypto.NewAesGcm(hashedPsk[:16]),
|
||||
first: first,
|
||||
leaveSize: leaveSize,
|
||||
|
||||
conn: raw,
|
||||
aead: crypto.NewAesGcm(hashedPsk[:16]),
|
||||
}
|
||||
|
||||
if first {
|
||||
conn.readBuf = make([]byte, 8192)
|
||||
conn.writeBuf = make([]byte, 8192)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *aes128gcmConn) Size() int {
|
||||
return c.aead.NonceSize()
|
||||
func (c *aes128gcmConn) Size() int32 {
|
||||
return int32(c.aead.NonceSize()) + int32(c.aead.Overhead())
|
||||
}
|
||||
|
||||
func (c *aes128gcmConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
if len(p) < c.aead.NonceSize()+c.aead.Overhead() {
|
||||
return 0, addr, errors.New("aead short lenth")
|
||||
if c.first {
|
||||
c.readMutex.Lock()
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("aead").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
if len(p) < n-int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("aead").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
nonceSize := c.aead.NonceSize()
|
||||
nonce := c.readBuf[:nonceSize]
|
||||
ciphertext := c.readBuf[nonceSize:n]
|
||||
_, err = c.aead.Open(p[:0], nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("aead open").Base(err)
|
||||
}
|
||||
|
||||
c.readMutex.Unlock()
|
||||
return n - int(c.Size()), addr, nil
|
||||
}
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(p)
|
||||
if err != nil {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
return 0, addr, errors.New("aead").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
nonceSize := c.aead.NonceSize()
|
||||
nonce := p[:nonceSize]
|
||||
ciphertext := p[nonceSize:]
|
||||
ciphertext := p[nonceSize:n]
|
||||
_, err = c.aead.Open(ciphertext[:0], nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
return 0, addr, errors.New("aead open").Base(err)
|
||||
}
|
||||
copy(p, p[nonceSize:n-c.aead.Overhead()])
|
||||
|
||||
return len(p) - c.aead.NonceSize() - c.aead.Overhead(), addr, nil
|
||||
return n - int(c.Size()), addr, nil
|
||||
}
|
||||
|
||||
func (c *aes128gcmConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
if c.aead.Overhead()+len(p) > finalmask.UDPSize {
|
||||
return 0, errors.New("aead short write")
|
||||
if c.first {
|
||||
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||
return 0, errors.New("too many masks")
|
||||
}
|
||||
|
||||
c.writeMutex.Lock()
|
||||
|
||||
n = copy(c.writeBuf[c.leaveSize+int32(c.aead.NonceSize()):], p)
|
||||
// n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||
n += int(c.leaveSize) + int(c.Size())
|
||||
|
||||
nonceSize := c.aead.NonceSize()
|
||||
nonce := c.writeBuf[c.leaveSize : c.leaveSize+int32(nonceSize)]
|
||||
common.Must2(rand.Read(nonce))
|
||||
// copy(c.writeBuf[c.leaveSize+int32(nonceSize):], c.writeBuf[c.leaveSize+c.Size():n])
|
||||
plaintext := c.writeBuf[c.leaveSize+int32(nonceSize) : n-c.aead.Overhead()]
|
||||
_ = c.aead.Seal(plaintext[:0], nonce, plaintext, nil)
|
||||
|
||||
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||
|
||||
if err != nil {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if nn != n {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, errors.New("nn != n")
|
||||
}
|
||||
|
||||
c.writeMutex.Unlock()
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
nonceSize := c.aead.NonceSize()
|
||||
nonce := p[:nonceSize]
|
||||
nonce := p[c.leaveSize : c.leaveSize+int32(nonceSize)]
|
||||
common.Must2(rand.Read(nonce))
|
||||
plaintext := p[nonceSize:]
|
||||
copy(p[c.leaveSize+int32(nonceSize):], p[c.leaveSize+c.Size():])
|
||||
plaintext := p[c.leaveSize+int32(nonceSize) : len(p)-c.aead.Overhead()]
|
||||
_ = c.aead.Seal(plaintext[:0], nonce, plaintext, nil)
|
||||
|
||||
return len(p) + c.aead.Overhead(), nil
|
||||
return c.conn.WriteTo(p, addr)
|
||||
}
|
||||
|
||||
func (c *aes128gcmConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *aes128gcmConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *aes128gcmConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *aes128gcmConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *aes128gcmConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
@@ -7,13 +7,10 @@ import (
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw)
|
||||
}
|
||||
|
||||
func (c *Config) HeaderConn() {
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@ import (
|
||||
"crypto/cipher"
|
||||
"encoding/binary"
|
||||
"hash/fnv"
|
||||
"io"
|
||||
"net"
|
||||
sync "sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
@@ -72,38 +75,151 @@ func (a *simple) Open(dst, nonce, cipherText, extra []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
type simpleConn struct {
|
||||
net.PacketConn
|
||||
first bool
|
||||
leaveSize int32
|
||||
|
||||
conn net.PacketConn
|
||||
aead cipher.AEAD
|
||||
|
||||
readBuf []byte
|
||||
readMutex sync.Mutex
|
||||
writeBuf []byte
|
||||
writeMutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
conn := &simpleConn{
|
||||
PacketConn: raw,
|
||||
aead: &simple{},
|
||||
first: first,
|
||||
leaveSize: leaveSize,
|
||||
|
||||
conn: raw,
|
||||
aead: &simple{},
|
||||
}
|
||||
|
||||
if first {
|
||||
conn.readBuf = make([]byte, 8192)
|
||||
conn.writeBuf = make([]byte, 8192)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *simpleConn) Size() int {
|
||||
return c.aead.Overhead()
|
||||
func (c *simpleConn) Size() int32 {
|
||||
return int32(c.aead.NonceSize()) + int32(c.aead.Overhead())
|
||||
}
|
||||
|
||||
func (c *simpleConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
_, err = c.aead.Open(p[:0], nil, p, nil)
|
||||
if c.first {
|
||||
c.readMutex.Lock()
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("aead").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
if len(p) < n-int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("aead").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
ciphertext := c.readBuf[:n]
|
||||
opened, err := c.aead.Open(nil, nil, ciphertext, nil)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("aead open").Base(err)
|
||||
}
|
||||
|
||||
copy(p, opened)
|
||||
|
||||
c.readMutex.Unlock()
|
||||
return n - int(c.Size()), addr, nil
|
||||
}
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(p)
|
||||
if err != nil {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
return 0, addr, errors.New("aead").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
ciphertext := p[:n]
|
||||
opened, err := c.aead.Open(nil, nil, ciphertext, nil)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("aead open").Base(err)
|
||||
}
|
||||
|
||||
return len(p) - c.aead.Overhead(), addr, nil
|
||||
copy(p, opened)
|
||||
|
||||
return n - int(c.Size()), addr, nil
|
||||
}
|
||||
|
||||
func (c *simpleConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
_ = c.aead.Seal(p[:0], nil, p[c.aead.Overhead():], nil)
|
||||
if c.first {
|
||||
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||
return 0, errors.New("too many masks")
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
c.writeMutex.Lock()
|
||||
|
||||
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||
n += int(c.leaveSize) + int(c.Size())
|
||||
|
||||
plaintext := c.writeBuf[c.leaveSize+c.Size() : n]
|
||||
sealed := c.aead.Seal(nil, nil, plaintext, nil)
|
||||
copy(c.writeBuf[c.leaveSize:], sealed)
|
||||
|
||||
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||
|
||||
if err != nil {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if nn != n {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, errors.New("nn != n")
|
||||
}
|
||||
|
||||
c.writeMutex.Unlock()
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
plaintext := p[c.leaveSize+c.Size():]
|
||||
sealed := c.aead.Seal(nil, nil, plaintext, nil)
|
||||
copy(p[c.leaveSize:], sealed)
|
||||
|
||||
return c.conn.WriteTo(p, addr)
|
||||
}
|
||||
|
||||
func (c *simpleConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *simpleConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *simpleConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *simpleConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *simpleConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
@@ -8,22 +8,6 @@ import (
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/mkcp/original"
|
||||
)
|
||||
|
||||
func TestSimpleSealInPlace(t *testing.T) {
|
||||
aead := original.NewSimple()
|
||||
|
||||
text := []byte("0123456789012")
|
||||
buf := make([]byte, 8192)
|
||||
|
||||
copy(buf[aead.Overhead():], text)
|
||||
plaintext := buf[aead.Overhead() : aead.Overhead()+len(text)]
|
||||
|
||||
sealed := aead.Seal(nil, nil, plaintext, nil)
|
||||
|
||||
_ = aead.Seal(buf[:0], nil, plaintext, nil)
|
||||
|
||||
assert.Equal(t, sealed, buf[:aead.Overhead()+len(text)])
|
||||
}
|
||||
|
||||
func TestOriginalBounce(t *testing.T) {
|
||||
aead := original.NewSimple()
|
||||
buf := make([]byte, aead.NonceSize()+aead.Overhead())
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package noise
|
||||
|
||||
import "net"
|
||||
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw)
|
||||
}
|
||||
@@ -1,225 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v6.33.5
|
||||
// source: transport/internet/finalmask/noise/config.proto
|
||||
|
||||
package noise
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Item struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
RandMin int64 `protobuf:"varint,1,opt,name=rand_min,json=randMin,proto3" json:"rand_min,omitempty"`
|
||||
RandMax int64 `protobuf:"varint,2,opt,name=rand_max,json=randMax,proto3" json:"rand_max,omitempty"`
|
||||
Packet []byte `protobuf:"bytes,3,opt,name=packet,proto3" json:"packet,omitempty"`
|
||||
DelayMin int64 `protobuf:"varint,4,opt,name=delay_min,json=delayMin,proto3" json:"delay_min,omitempty"`
|
||||
DelayMax int64 `protobuf:"varint,5,opt,name=delay_max,json=delayMax,proto3" json:"delay_max,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Item) Reset() {
|
||||
*x = Item{}
|
||||
mi := &file_transport_internet_finalmask_noise_config_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Item) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Item) ProtoMessage() {}
|
||||
|
||||
func (x *Item) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_finalmask_noise_config_proto_msgTypes[0]
|
||||
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 Item.ProtoReflect.Descriptor instead.
|
||||
func (*Item) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_finalmask_noise_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Item) GetRandMin() int64 {
|
||||
if x != nil {
|
||||
return x.RandMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Item) GetRandMax() int64 {
|
||||
if x != nil {
|
||||
return x.RandMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Item) GetPacket() []byte {
|
||||
if x != nil {
|
||||
return x.Packet
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Item) GetDelayMin() int64 {
|
||||
if x != nil {
|
||||
return x.DelayMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Item) GetDelayMax() int64 {
|
||||
if x != nil {
|
||||
return x.DelayMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
ResetMin int64 `protobuf:"varint,1,opt,name=reset_min,json=resetMin,proto3" json:"reset_min,omitempty"`
|
||||
ResetMax int64 `protobuf:"varint,2,opt,name=reset_max,json=resetMax,proto3" json:"reset_max,omitempty"`
|
||||
Items []*Item `protobuf:"bytes,3,rep,name=items,proto3" json:"items,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
mi := &file_transport_internet_finalmask_noise_config_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Config) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Config) ProtoMessage() {}
|
||||
|
||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_finalmask_noise_config_proto_msgTypes[1]
|
||||
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 Config.ProtoReflect.Descriptor instead.
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_finalmask_noise_config_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *Config) GetResetMin() int64 {
|
||||
if x != nil {
|
||||
return x.ResetMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetResetMax() int64 {
|
||||
if x != nil {
|
||||
return x.ResetMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetItems() []*Item {
|
||||
if x != nil {
|
||||
return x.Items
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_transport_internet_finalmask_noise_config_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_transport_internet_finalmask_noise_config_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"/transport/internet/finalmask/noise/config.proto\x12'xray.transport.internet.finalmask.noise\"\x8e\x01\n" +
|
||||
"\x04Item\x12\x19\n" +
|
||||
"\brand_min\x18\x01 \x01(\x03R\arandMin\x12\x19\n" +
|
||||
"\brand_max\x18\x02 \x01(\x03R\arandMax\x12\x16\n" +
|
||||
"\x06packet\x18\x03 \x01(\fR\x06packet\x12\x1b\n" +
|
||||
"\tdelay_min\x18\x04 \x01(\x03R\bdelayMin\x12\x1b\n" +
|
||||
"\tdelay_max\x18\x05 \x01(\x03R\bdelayMax\"\x87\x01\n" +
|
||||
"\x06Config\x12\x1b\n" +
|
||||
"\treset_min\x18\x01 \x01(\x03R\bresetMin\x12\x1b\n" +
|
||||
"\treset_max\x18\x02 \x01(\x03R\bresetMax\x12C\n" +
|
||||
"\x05items\x18\x03 \x03(\v2-.xray.transport.internet.finalmask.noise.ItemR\x05itemsB\x97\x01\n" +
|
||||
"+com.xray.transport.internet.finalmask.noiseP\x01Z<github.com/xtls/xray-core/transport/internet/finalmask/noise\xaa\x02'Xray.Transport.Internet.Finalmask.Noiseb\x06proto3"
|
||||
|
||||
var (
|
||||
file_transport_internet_finalmask_noise_config_proto_rawDescOnce sync.Once
|
||||
file_transport_internet_finalmask_noise_config_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_transport_internet_finalmask_noise_config_proto_rawDescGZIP() []byte {
|
||||
file_transport_internet_finalmask_noise_config_proto_rawDescOnce.Do(func() {
|
||||
file_transport_internet_finalmask_noise_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_noise_config_proto_rawDesc), len(file_transport_internet_finalmask_noise_config_proto_rawDesc)))
|
||||
})
|
||||
return file_transport_internet_finalmask_noise_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_transport_internet_finalmask_noise_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_transport_internet_finalmask_noise_config_proto_goTypes = []any{
|
||||
(*Item)(nil), // 0: xray.transport.internet.finalmask.noise.Item
|
||||
(*Config)(nil), // 1: xray.transport.internet.finalmask.noise.Config
|
||||
}
|
||||
var file_transport_internet_finalmask_noise_config_proto_depIdxs = []int32{
|
||||
0, // 0: xray.transport.internet.finalmask.noise.Config.items:type_name -> xray.transport.internet.finalmask.noise.Item
|
||||
1, // [1:1] is the sub-list for method output_type
|
||||
1, // [1:1] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_transport_internet_finalmask_noise_config_proto_init() }
|
||||
func file_transport_internet_finalmask_noise_config_proto_init() {
|
||||
if File_transport_internet_finalmask_noise_config_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_noise_config_proto_rawDesc), len(file_transport_internet_finalmask_noise_config_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_transport_internet_finalmask_noise_config_proto_goTypes,
|
||||
DependencyIndexes: file_transport_internet_finalmask_noise_config_proto_depIdxs,
|
||||
MessageInfos: file_transport_internet_finalmask_noise_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_transport_internet_finalmask_noise_config_proto = out.File
|
||||
file_transport_internet_finalmask_noise_config_proto_goTypes = nil
|
||||
file_transport_internet_finalmask_noise_config_proto_depIdxs = nil
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package xray.transport.internet.finalmask.noise;
|
||||
option csharp_namespace = "Xray.Transport.Internet.Finalmask.Noise";
|
||||
option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/noise";
|
||||
option java_package = "com.xray.transport.internet.finalmask.noise";
|
||||
option java_multiple_files = true;
|
||||
|
||||
message Item {
|
||||
int64 rand_min = 1;
|
||||
int64 rand_max = 2;
|
||||
bytes packet = 3;
|
||||
int64 delay_min = 4;
|
||||
int64 delay_max = 5;
|
||||
}
|
||||
|
||||
message Config {
|
||||
int64 reset_min = 1;
|
||||
int64 reset_max = 2;
|
||||
repeated Item items = 3;
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
package noise
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
)
|
||||
|
||||
type noiseConn struct {
|
||||
net.PacketConn
|
||||
config *Config
|
||||
m map[string]time.Time
|
||||
stop chan struct{}
|
||||
once sync.Once
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
conn := &noiseConn{
|
||||
PacketConn: raw,
|
||||
config: c,
|
||||
m: make(map[string]time.Time),
|
||||
stop: make(chan struct{}),
|
||||
}
|
||||
|
||||
if conn.config.ResetMax > 0 {
|
||||
go conn.reset()
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *noiseConn) reset() {
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
c.mutex.RLock()
|
||||
now := time.Now()
|
||||
timeOut := make([]string, 0, len(c.m))
|
||||
for key, last := range c.m {
|
||||
if now.After(last) {
|
||||
timeOut = append(timeOut, key)
|
||||
}
|
||||
}
|
||||
c.mutex.RUnlock()
|
||||
|
||||
for _, key := range timeOut {
|
||||
c.mutex.Lock()
|
||||
delete(c.m, key)
|
||||
c.mutex.Unlock()
|
||||
}
|
||||
case <-c.stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *noiseConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
c.mutex.RLock()
|
||||
_, ready := c.m[addr.String()]
|
||||
c.mutex.RUnlock()
|
||||
|
||||
if !ready {
|
||||
c.mutex.Lock()
|
||||
_, ready = c.m[addr.String()]
|
||||
if !ready {
|
||||
for _, item := range c.config.Items {
|
||||
if item.RandMax > 0 {
|
||||
item.Packet = make([]byte, crypto.RandBetween(item.RandMin, item.RandMax))
|
||||
common.Must2(rand.Read(item.Packet))
|
||||
}
|
||||
c.PacketConn.WriteTo(item.Packet, addr)
|
||||
time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond)
|
||||
}
|
||||
c.m[addr.String()] = time.Now().Add(time.Duration(crypto.RandBetween(c.config.ResetMin, c.config.ResetMax)) * time.Second)
|
||||
}
|
||||
c.mutex.Unlock()
|
||||
}
|
||||
|
||||
return c.PacketConn.WriteTo(p, addr)
|
||||
}
|
||||
|
||||
func (c *noiseConn) Close() error {
|
||||
c.once.Do(func() {
|
||||
close(c.stop)
|
||||
})
|
||||
return c.PacketConn.Close()
|
||||
}
|
||||
@@ -7,13 +7,10 @@ import (
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw)
|
||||
}
|
||||
|
||||
func (c *Config) HeaderConn() {
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
@@ -1,46 +1,147 @@
|
||||
package salamander
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
)
|
||||
|
||||
type salamanderConn struct {
|
||||
net.PacketConn
|
||||
type obfsPacketConn struct {
|
||||
first bool
|
||||
leaveSize int32
|
||||
|
||||
conn net.PacketConn
|
||||
obfs *SalamanderObfuscator
|
||||
|
||||
readBuf []byte
|
||||
readMutex sync.Mutex
|
||||
writeBuf []byte
|
||||
writeMutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
ob, err := NewSalamanderObfuscator([]byte(c.Password))
|
||||
if err != nil {
|
||||
return nil, errors.New("salamander err").Base(err)
|
||||
}
|
||||
|
||||
conn := &salamanderConn{
|
||||
PacketConn: raw,
|
||||
obfs: ob,
|
||||
conn := &obfsPacketConn{
|
||||
first: first,
|
||||
leaveSize: leaveSize,
|
||||
|
||||
conn: raw,
|
||||
obfs: ob,
|
||||
}
|
||||
|
||||
if first {
|
||||
conn.readBuf = make([]byte, 8192)
|
||||
conn.writeBuf = make([]byte, 8192)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *salamanderConn) Size() int {
|
||||
func (c *obfsPacketConn) Size() int32 {
|
||||
return smSaltLen
|
||||
}
|
||||
|
||||
func (c *salamanderConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
c.obfs.Deobfuscate(p, p[smSaltLen:])
|
||||
func (c *obfsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
if c.first {
|
||||
c.readMutex.Lock()
|
||||
|
||||
return len(p) - smSaltLen, addr, nil
|
||||
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("salamander").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
if len(p) < n-int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("salamander").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
c.obfs.Deobfuscate(c.readBuf[:n], p)
|
||||
|
||||
c.readMutex.Unlock()
|
||||
return n - int(c.Size()), addr, err
|
||||
}
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(p)
|
||||
if err != nil {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
return 0, addr, errors.New("salamander").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
c.obfs.Deobfuscate(p[:n], p)
|
||||
|
||||
return n - int(c.Size()), addr, err
|
||||
}
|
||||
|
||||
func (c *salamanderConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
c.obfs.Obfuscate(p[smSaltLen:], p)
|
||||
func (c *obfsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
if c.first {
|
||||
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||
return 0, errors.New("too many masks")
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
c.writeMutex.Lock()
|
||||
|
||||
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||
n += int(c.leaveSize) + int(c.Size())
|
||||
|
||||
c.obfs.Obfuscate(c.writeBuf[c.leaveSize+c.Size():n], c.writeBuf[c.leaveSize:n])
|
||||
|
||||
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||
|
||||
if err != nil {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if nn != n {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, errors.New("nn != n")
|
||||
}
|
||||
|
||||
c.writeMutex.Unlock()
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
c.obfs.Obfuscate(p[c.leaveSize+c.Size():], p[c.leaveSize:])
|
||||
|
||||
return c.conn.WriteTo(p, addr)
|
||||
}
|
||||
|
||||
func (c *obfsPacketConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *obfsPacketConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *obfsPacketConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *obfsPacketConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *obfsPacketConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
package sudoku
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
var perm4 = [24][4]byte{
|
||||
{0, 1, 2, 3},
|
||||
{0, 1, 3, 2},
|
||||
{0, 2, 1, 3},
|
||||
{0, 2, 3, 1},
|
||||
{0, 3, 1, 2},
|
||||
{0, 3, 2, 1},
|
||||
{1, 0, 2, 3},
|
||||
{1, 0, 3, 2},
|
||||
{1, 2, 0, 3},
|
||||
{1, 2, 3, 0},
|
||||
{1, 3, 0, 2},
|
||||
{1, 3, 2, 0},
|
||||
{2, 0, 1, 3},
|
||||
{2, 0, 3, 1},
|
||||
{2, 1, 0, 3},
|
||||
{2, 1, 3, 0},
|
||||
{2, 3, 0, 1},
|
||||
{2, 3, 1, 0},
|
||||
{3, 0, 1, 2},
|
||||
{3, 0, 2, 1},
|
||||
{3, 1, 0, 2},
|
||||
{3, 1, 2, 0},
|
||||
{3, 2, 0, 1},
|
||||
{3, 2, 1, 0},
|
||||
}
|
||||
|
||||
type codec struct {
|
||||
tables []*table
|
||||
rng *rand.Rand
|
||||
paddingChance int
|
||||
tableIndex int
|
||||
}
|
||||
|
||||
func newCodec(tables []*table, pMin, pMax int) *codec {
|
||||
if len(tables) == 0 {
|
||||
tables = nil
|
||||
}
|
||||
rng := newSeededRand()
|
||||
return &codec{
|
||||
tables: tables,
|
||||
rng: rng,
|
||||
paddingChance: pickPaddingChance(rng, pMin, pMax),
|
||||
}
|
||||
}
|
||||
|
||||
func pickPaddingChance(rng *rand.Rand, pMin, pMax int) int {
|
||||
if pMin < 0 {
|
||||
pMin = 0
|
||||
}
|
||||
if pMax < pMin {
|
||||
pMax = pMin
|
||||
}
|
||||
if pMin > 100 {
|
||||
pMin = 100
|
||||
}
|
||||
if pMax > 100 {
|
||||
pMax = 100
|
||||
}
|
||||
if pMax == pMin {
|
||||
return pMin
|
||||
}
|
||||
return pMin + rng.Intn(pMax-pMin+1)
|
||||
}
|
||||
|
||||
func (c *codec) shouldPad() bool {
|
||||
if c.paddingChance <= 0 {
|
||||
return false
|
||||
}
|
||||
if c.paddingChance >= 100 {
|
||||
return true
|
||||
}
|
||||
return c.rng.Intn(100) < c.paddingChance
|
||||
}
|
||||
|
||||
func (c *codec) currentTable() *table {
|
||||
if len(c.tables) == 0 {
|
||||
return nil
|
||||
}
|
||||
return c.tables[c.tableIndex%len(c.tables)]
|
||||
}
|
||||
|
||||
func (c *codec) randomPadding(t *table) byte {
|
||||
pool := t.layout.paddingPool
|
||||
return pool[c.rng.Intn(len(pool))]
|
||||
}
|
||||
|
||||
func (c *codec) encode(in []byte) ([]byte, error) {
|
||||
if len(in) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
out := make([]byte, 0, len(in)*6+8)
|
||||
for _, b := range in {
|
||||
t := c.currentTable()
|
||||
if t == nil {
|
||||
return nil, fmt.Errorf("sudoku table set missing")
|
||||
}
|
||||
if c.shouldPad() {
|
||||
out = append(out, c.randomPadding(t))
|
||||
}
|
||||
|
||||
enc := t.encode[b]
|
||||
if len(enc) == 0 {
|
||||
return nil, fmt.Errorf("sudoku encode table missing for byte %d", b)
|
||||
}
|
||||
|
||||
hints := enc[c.rng.Intn(len(enc))]
|
||||
perm := perm4[c.rng.Intn(len(perm4))]
|
||||
for _, idx := range perm {
|
||||
if c.shouldPad() {
|
||||
out = append(out, c.randomPadding(t))
|
||||
}
|
||||
out = append(out, hints[idx])
|
||||
}
|
||||
c.tableIndex++
|
||||
}
|
||||
|
||||
if c.shouldPad() {
|
||||
if t := c.currentTable(); t != nil {
|
||||
out = append(out, c.randomPadding(t))
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func decodeBytes(tables []*table, tableIndex *int, in []byte, hintBuf []byte, out []byte) ([]byte, []byte, error) {
|
||||
if len(tables) == 0 {
|
||||
return hintBuf, out, fmt.Errorf("sudoku table set missing")
|
||||
}
|
||||
for _, b := range in {
|
||||
t := tables[*tableIndex%len(tables)]
|
||||
if !t.layout.isHint(b) {
|
||||
continue
|
||||
}
|
||||
|
||||
hintBuf = append(hintBuf, b)
|
||||
if len(hintBuf) < 4 {
|
||||
continue
|
||||
}
|
||||
|
||||
keyBytes := sort4([4]byte{hintBuf[0], hintBuf[1], hintBuf[2], hintBuf[3]})
|
||||
key := packKey(keyBytes)
|
||||
decoded, ok := t.decode[key]
|
||||
if !ok {
|
||||
return hintBuf[:0], out, fmt.Errorf("invalid sudoku hint tuple")
|
||||
}
|
||||
|
||||
out = append(out, decoded)
|
||||
hintBuf = hintBuf[:0]
|
||||
*tableIndex++
|
||||
}
|
||||
|
||||
return hintBuf, out, nil
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package sudoku
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
)
|
||||
|
||||
func (c *Config) TCP() {
|
||||
}
|
||||
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
// Sudoku in finalmask mode is a pure appearance transform with no standalone handshake.
|
||||
// TCP always keeps classic sudoku on uplink and uses packed downlink optimization on server writes.
|
||||
func (c *Config) WrapConnClient(raw net.Conn) (net.Conn, error) {
|
||||
return newPackedDirectionalConn(raw, c, true)
|
||||
}
|
||||
|
||||
func (c *Config) WrapConnServer(raw net.Conn) (net.Conn, error) {
|
||||
return newPackedDirectionalConn(raw, c, false)
|
||||
}
|
||||
|
||||
func newPackedDirectionalConn(raw net.Conn, config *Config, readPacked bool) (net.Conn, error) {
|
||||
pureReader, pureWriter, err := newPureReaderWriter(raw, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
packedReader, packedWriter, err := newPackedReaderWriter(raw, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reader, writer := pureReader, pureWriter
|
||||
if readPacked {
|
||||
reader = packedReader
|
||||
} else {
|
||||
writer = packedWriter
|
||||
}
|
||||
|
||||
return newWrappedConn(raw, reader, writer), nil
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
if level != levelCount {
|
||||
return nil, errors.New("sudoku udp mask must be the innermost mask in chain")
|
||||
}
|
||||
return NewUDPConn(raw, c)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
if level != levelCount {
|
||||
return nil, errors.New("sudoku udp mask must be the innermost mask in chain")
|
||||
}
|
||||
return NewUDPConn(raw, c)
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v6.33.5
|
||||
// source: transport/internet/finalmask/sudoku/config.proto
|
||||
|
||||
package sudoku
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Password string `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"`
|
||||
Ascii string `protobuf:"bytes,2,opt,name=ascii,proto3" json:"ascii,omitempty"`
|
||||
CustomTable string `protobuf:"bytes,3,opt,name=custom_table,json=customTable,proto3" json:"custom_table,omitempty"`
|
||||
PaddingMin uint32 `protobuf:"varint,4,opt,name=padding_min,json=paddingMin,proto3" json:"padding_min,omitempty"`
|
||||
PaddingMax uint32 `protobuf:"varint,5,opt,name=padding_max,json=paddingMax,proto3" json:"padding_max,omitempty"`
|
||||
CustomTables []string `protobuf:"bytes,7,rep,name=custom_tables,json=customTables,proto3" json:"custom_tables,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
mi := &file_transport_internet_finalmask_sudoku_config_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Config) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Config) ProtoMessage() {}
|
||||
|
||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_finalmask_sudoku_config_proto_msgTypes[0]
|
||||
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 Config.ProtoReflect.Descriptor instead.
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_finalmask_sudoku_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Config) GetPassword() string {
|
||||
if x != nil {
|
||||
return x.Password
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetAscii() string {
|
||||
if x != nil {
|
||||
return x.Ascii
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetCustomTable() string {
|
||||
if x != nil {
|
||||
return x.CustomTable
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetPaddingMin() uint32 {
|
||||
if x != nil {
|
||||
return x.PaddingMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetPaddingMax() uint32 {
|
||||
if x != nil {
|
||||
return x.PaddingMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetCustomTables() []string {
|
||||
if x != nil {
|
||||
return x.CustomTables
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_transport_internet_finalmask_sudoku_config_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_transport_internet_finalmask_sudoku_config_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"0transport/internet/finalmask/sudoku/config.proto\x12(xray.transport.internet.finalmask.sudoku\"\xc4\x01\n" +
|
||||
"\x06Config\x12\x1a\n" +
|
||||
"\bpassword\x18\x01 \x01(\tR\bpassword\x12\x14\n" +
|
||||
"\x05ascii\x18\x02 \x01(\tR\x05ascii\x12!\n" +
|
||||
"\fcustom_table\x18\x03 \x01(\tR\vcustomTable\x12\x1f\n" +
|
||||
"\vpadding_min\x18\x04 \x01(\rR\n" +
|
||||
"paddingMin\x12\x1f\n" +
|
||||
"\vpadding_max\x18\x05 \x01(\rR\n" +
|
||||
"paddingMax\x12#\n" +
|
||||
"\rcustom_tables\x18\a \x03(\tR\fcustomTablesB\x9a\x01\n" +
|
||||
",com.xray.transport.internet.finalmask.sudokuP\x01Z=github.com/xtls/xray-core/transport/internet/finalmask/sudoku\xaa\x02(Xray.Transport.Internet.Finalmask.Sudokub\x06proto3"
|
||||
|
||||
var (
|
||||
file_transport_internet_finalmask_sudoku_config_proto_rawDescOnce sync.Once
|
||||
file_transport_internet_finalmask_sudoku_config_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_transport_internet_finalmask_sudoku_config_proto_rawDescGZIP() []byte {
|
||||
file_transport_internet_finalmask_sudoku_config_proto_rawDescOnce.Do(func() {
|
||||
file_transport_internet_finalmask_sudoku_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_sudoku_config_proto_rawDesc), len(file_transport_internet_finalmask_sudoku_config_proto_rawDesc)))
|
||||
})
|
||||
return file_transport_internet_finalmask_sudoku_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_transport_internet_finalmask_sudoku_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_transport_internet_finalmask_sudoku_config_proto_goTypes = []any{
|
||||
(*Config)(nil), // 0: xray.transport.internet.finalmask.sudoku.Config
|
||||
}
|
||||
var file_transport_internet_finalmask_sudoku_config_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_transport_internet_finalmask_sudoku_config_proto_init() }
|
||||
func file_transport_internet_finalmask_sudoku_config_proto_init() {
|
||||
if File_transport_internet_finalmask_sudoku_config_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_sudoku_config_proto_rawDesc), len(file_transport_internet_finalmask_sudoku_config_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_transport_internet_finalmask_sudoku_config_proto_goTypes,
|
||||
DependencyIndexes: file_transport_internet_finalmask_sudoku_config_proto_depIdxs,
|
||||
MessageInfos: file_transport_internet_finalmask_sudoku_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_transport_internet_finalmask_sudoku_config_proto = out.File
|
||||
file_transport_internet_finalmask_sudoku_config_proto_goTypes = nil
|
||||
file_transport_internet_finalmask_sudoku_config_proto_depIdxs = nil
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package xray.transport.internet.finalmask.sudoku;
|
||||
option csharp_namespace = "Xray.Transport.Internet.Finalmask.Sudoku";
|
||||
option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/sudoku";
|
||||
option java_package = "com.xray.transport.internet.finalmask.sudoku";
|
||||
option java_multiple_files = true;
|
||||
|
||||
message Config {
|
||||
string password = 1;
|
||||
string ascii = 2;
|
||||
string custom_table = 3;
|
||||
uint32 padding_min = 4;
|
||||
uint32 padding_max = 5;
|
||||
repeated string custom_tables = 7;
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
package sudoku
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
)
|
||||
|
||||
const ioBufferSize = 32 * 1024
|
||||
|
||||
var _ finalmask.TcpMaskConn = (*wrappedConn)(nil)
|
||||
|
||||
type streamDecoder interface {
|
||||
decodeChunk(in []byte, pending []byte) ([]byte, error)
|
||||
reset()
|
||||
}
|
||||
|
||||
type streamReader struct {
|
||||
reader *bufio.Reader
|
||||
rawBuf []byte
|
||||
pending []byte
|
||||
decode streamDecoder
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func newStreamReader(raw net.Conn, decode streamDecoder) io.Reader {
|
||||
return &streamReader{
|
||||
reader: bufio.NewReaderSize(raw, ioBufferSize),
|
||||
rawBuf: make([]byte, ioBufferSize),
|
||||
pending: make([]byte, 0, 4096),
|
||||
decode: decode,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *streamReader) Read(p []byte) (int, error) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
if n, ok := drainPending(p, &r.pending); ok {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
for len(r.pending) == 0 {
|
||||
nr, rErr := r.reader.Read(r.rawBuf)
|
||||
if nr > 0 {
|
||||
var dErr error
|
||||
r.pending, dErr = r.decode.decodeChunk(r.rawBuf[:nr], r.pending)
|
||||
if dErr != nil {
|
||||
return 0, dErr
|
||||
}
|
||||
}
|
||||
|
||||
if rErr != nil {
|
||||
if rErr == io.EOF {
|
||||
r.decode.reset()
|
||||
if len(r.pending) > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return 0, rErr
|
||||
}
|
||||
}
|
||||
|
||||
n, _ := drainPending(p, &r.pending)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
type streamWriter struct {
|
||||
conn net.Conn
|
||||
encode func([]byte) ([]byte, error)
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func newStreamWriter(raw net.Conn, encode func([]byte) ([]byte, error)) io.Writer {
|
||||
return &streamWriter{
|
||||
conn: raw,
|
||||
encode: encode,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *streamWriter) Write(p []byte) (int, error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
encoded, err := w.encode(p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := writeAll(w.conn, encoded); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
type wrappedConn struct {
|
||||
net.Conn
|
||||
reader io.Reader
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
type closeWriteConn interface {
|
||||
CloseWrite() error
|
||||
}
|
||||
|
||||
func newWrappedConn(raw net.Conn, reader io.Reader, writer io.Writer) net.Conn {
|
||||
return &wrappedConn{
|
||||
Conn: raw,
|
||||
reader: reader,
|
||||
writer: writer,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *wrappedConn) Read(p []byte) (int, error) {
|
||||
return c.reader.Read(p)
|
||||
}
|
||||
|
||||
func (c *wrappedConn) Write(p []byte) (int, error) {
|
||||
return c.writer.Write(p)
|
||||
}
|
||||
|
||||
func (c *wrappedConn) TcpMaskConn() {}
|
||||
|
||||
func (c *wrappedConn) RawConn() net.Conn {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
func (c *wrappedConn) Splice() bool {
|
||||
// Sudoku transforms the entire stream; bypassing it would disable masking.
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *wrappedConn) CloseWrite() error {
|
||||
if raw, ok := c.Conn.(closeWriteConn); ok {
|
||||
return raw.CloseWrite()
|
||||
}
|
||||
return net.ErrClosed
|
||||
}
|
||||
|
||||
func NewTCPConn(raw net.Conn, config *Config) (net.Conn, error) {
|
||||
reader, writer, err := newPureReaderWriter(raw, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newWrappedConn(raw, reader, writer), nil
|
||||
}
|
||||
|
||||
func newPureReaderWriter(raw net.Conn, config *Config) (io.Reader, io.Writer, error) {
|
||||
tables, err := getTables(config)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pMin, pMax := normalizedPadding(config)
|
||||
c := newCodec(tables, pMin, pMax)
|
||||
return newStreamReader(raw, newHintStreamDecoder(tables)), newStreamWriter(raw, c.encode), nil
|
||||
}
|
||||
|
||||
type hintStreamDecoder struct {
|
||||
tables []*table
|
||||
tableIndex int
|
||||
hintBuf []byte
|
||||
}
|
||||
|
||||
func newHintStreamDecoder(tables []*table) *hintStreamDecoder {
|
||||
return &hintStreamDecoder{
|
||||
tables: tables,
|
||||
hintBuf: make([]byte, 0, 4),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *hintStreamDecoder) decodeChunk(in []byte, pending []byte) ([]byte, error) {
|
||||
var err error
|
||||
d.hintBuf, pending, err = decodeBytes(d.tables, &d.tableIndex, in, d.hintBuf, pending)
|
||||
return pending, err
|
||||
}
|
||||
|
||||
func (d *hintStreamDecoder) reset() {}
|
||||
|
||||
func drainPending(p []byte, pending *[]byte) (int, bool) {
|
||||
if len(*pending) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
n := copy(p, *pending)
|
||||
if n >= len(*pending) {
|
||||
*pending = (*pending)[:0]
|
||||
return n, true
|
||||
}
|
||||
|
||||
remaining := len(*pending) - n
|
||||
copy(*pending, (*pending)[n:])
|
||||
*pending = (*pending)[:remaining]
|
||||
return n, true
|
||||
}
|
||||
|
||||
func writeAll(conn net.Conn, b []byte) error {
|
||||
for len(b) > 0 {
|
||||
n, err := conn.Write(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b = b[n:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
package sudoku
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
type packedEncoder struct {
|
||||
layouts []*byteLayout
|
||||
codec *codec
|
||||
groupIndex int
|
||||
}
|
||||
|
||||
func newPackedEncoder(tables []*table, pMin, pMax int) *packedEncoder {
|
||||
layouts := make([]*byteLayout, 0, len(tables))
|
||||
for _, t := range tables {
|
||||
layouts = append(layouts, t.layout)
|
||||
}
|
||||
if len(layouts) == 0 {
|
||||
layouts = append(layouts, entropyLayout())
|
||||
}
|
||||
return &packedEncoder{
|
||||
layouts: layouts,
|
||||
codec: newCodec(nil, pMin, pMax),
|
||||
}
|
||||
}
|
||||
|
||||
func (e *packedEncoder) encode(p []byte) ([]byte, error) {
|
||||
out := make([]byte, 0, len(p)*2+8)
|
||||
var bitBuf uint64
|
||||
var bitCount uint8
|
||||
|
||||
for _, b := range p {
|
||||
bitBuf = (bitBuf << 8) | uint64(b)
|
||||
bitCount += 8
|
||||
|
||||
for bitCount >= 6 {
|
||||
bitCount -= 6
|
||||
layout := e.layouts[e.groupIndex%len(e.layouts)]
|
||||
group := byte(bitBuf >> bitCount)
|
||||
out = e.maybePad(out, layout)
|
||||
out = append(out, layout.encodeGroup(group&0x3f))
|
||||
e.groupIndex++
|
||||
if bitCount > 0 {
|
||||
bitBuf &= (uint64(1) << bitCount) - 1
|
||||
} else {
|
||||
bitBuf = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if bitCount > 0 {
|
||||
layout := e.layouts[e.groupIndex%len(e.layouts)]
|
||||
group := byte(bitBuf << (6 - bitCount))
|
||||
out = e.maybePad(out, layout)
|
||||
out = append(out, layout.encodeGroup(group&0x3f))
|
||||
e.groupIndex++
|
||||
nextLayout := e.layouts[e.groupIndex%len(e.layouts)]
|
||||
out = append(out, nextLayout.padMarker)
|
||||
}
|
||||
|
||||
out = e.maybePad(out, e.layouts[e.groupIndex%len(e.layouts)])
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (e *packedEncoder) maybePad(out []byte, layout *byteLayout) []byte {
|
||||
if !e.codec.shouldPad() {
|
||||
return out
|
||||
}
|
||||
if len(layout.paddingPool) == 1 {
|
||||
return append(out, layout.paddingPool[0])
|
||||
}
|
||||
for {
|
||||
b := layout.paddingPool[e.codec.rng.Intn(len(layout.paddingPool))]
|
||||
if b != layout.padMarker {
|
||||
return append(out, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type packedStreamDecoder struct {
|
||||
layouts []*byteLayout
|
||||
groupIndex int
|
||||
bitBuf uint64
|
||||
bitCount int
|
||||
}
|
||||
|
||||
func (d *packedStreamDecoder) decodeChunk(in []byte, pending []byte) ([]byte, error) {
|
||||
var err error
|
||||
d.bitBuf, d.bitCount, d.groupIndex, pending, err = decodePackedBytes(
|
||||
d.layouts,
|
||||
in,
|
||||
d.bitBuf,
|
||||
d.bitCount,
|
||||
d.groupIndex,
|
||||
pending,
|
||||
)
|
||||
return pending, err
|
||||
}
|
||||
|
||||
func (d *packedStreamDecoder) reset() {
|
||||
d.bitBuf = 0
|
||||
d.bitCount = 0
|
||||
}
|
||||
|
||||
func NewPackedTCPConn(raw net.Conn, config *Config) (net.Conn, error) {
|
||||
reader, writer, err := newPackedReaderWriter(raw, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newWrappedConn(raw, reader, writer), nil
|
||||
}
|
||||
|
||||
func newPackedReaderWriter(raw net.Conn, config *Config) (io.Reader, io.Writer, error) {
|
||||
tables, err := getTables(config)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pMin, pMax := normalizedPadding(config)
|
||||
encoder := newPackedEncoder(tables, pMin, pMax)
|
||||
decoder := &packedStreamDecoder{
|
||||
layouts: tablesToLayouts(tables),
|
||||
}
|
||||
return newStreamReader(raw, decoder), newStreamWriter(raw, encoder.encode), nil
|
||||
}
|
||||
|
||||
func tablesToLayouts(tables []*table) []*byteLayout {
|
||||
layouts := make([]*byteLayout, 0, len(tables))
|
||||
for _, t := range tables {
|
||||
layouts = append(layouts, t.layout)
|
||||
}
|
||||
if len(layouts) == 0 {
|
||||
layouts = append(layouts, entropyLayout())
|
||||
}
|
||||
return layouts
|
||||
}
|
||||
|
||||
func decodePackedBytes(
|
||||
layouts []*byteLayout,
|
||||
in []byte,
|
||||
bitBuf uint64,
|
||||
bitCount int,
|
||||
groupIndex int,
|
||||
out []byte,
|
||||
) (uint64, int, int, []byte, error) {
|
||||
if len(layouts) == 0 {
|
||||
return bitBuf, bitCount, groupIndex, out, fmt.Errorf("sudoku layout set missing")
|
||||
}
|
||||
for _, b := range in {
|
||||
layout := layouts[groupIndex%len(layouts)]
|
||||
if !layout.isHint(b) {
|
||||
if b == layout.padMarker {
|
||||
bitBuf = 0
|
||||
bitCount = 0
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
group, ok := layout.decodeGroup(b)
|
||||
if !ok {
|
||||
return bitBuf, bitCount, groupIndex, out, fmt.Errorf("invalid packed sudoku byte: %d", b)
|
||||
}
|
||||
groupIndex++
|
||||
|
||||
bitBuf = (bitBuf << 6) | uint64(group)
|
||||
bitCount += 6
|
||||
|
||||
for bitCount >= 8 {
|
||||
bitCount -= 8
|
||||
out = append(out, byte(bitBuf>>bitCount))
|
||||
if bitCount > 0 {
|
||||
bitBuf &= (uint64(1) << bitCount) - 1
|
||||
} else {
|
||||
bitBuf = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bitBuf, bitCount, groupIndex, out, nil
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
package sudoku
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type udpConn struct {
|
||||
conn net.PacketConn
|
||||
tables []*table
|
||||
pMin int
|
||||
pMax int
|
||||
|
||||
readBuf []byte
|
||||
|
||||
readMu sync.Mutex
|
||||
writeMu sync.Mutex
|
||||
}
|
||||
|
||||
func NewUDPConn(raw net.PacketConn, config *Config) (net.PacketConn, error) {
|
||||
tables, err := getTables(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pMin, pMax := normalizedPadding(config)
|
||||
return &udpConn{
|
||||
conn: raw,
|
||||
tables: tables,
|
||||
pMin: pMin,
|
||||
pMax: pMax,
|
||||
readBuf: make([]byte, 65535),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *udpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
c.readMu.Lock()
|
||||
defer c.readMu.Unlock()
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||
if err != nil {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
decoded := make([]byte, 0, n/4+1)
|
||||
hints := make([]byte, 0, 4)
|
||||
tableIndex := 0
|
||||
hints, decoded, err = decodeBytes(c.tables, &tableIndex, c.readBuf[:n], hints, decoded)
|
||||
if err != nil {
|
||||
return 0, addr, err
|
||||
}
|
||||
if len(hints) != 0 {
|
||||
return 0, addr, io.ErrUnexpectedEOF
|
||||
}
|
||||
if len(p) < len(decoded) {
|
||||
return 0, addr, io.ErrShortBuffer
|
||||
}
|
||||
copy(p, decoded)
|
||||
return len(decoded), addr, nil
|
||||
}
|
||||
|
||||
func (c *udpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
c.writeMu.Lock()
|
||||
defer c.writeMu.Unlock()
|
||||
|
||||
// UDP decoding restarts at table 0 for every datagram, so encoding must do the same.
|
||||
encoded, err := newCodec(c.tables, c.pMin, c.pMax).encode(p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
nn, err := c.conn.WriteTo(encoded, addr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if nn != len(encoded) {
|
||||
return 0, io.ErrShortWrite
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (c *udpConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *udpConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *udpConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *udpConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *udpConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,580 +0,0 @@
|
||||
package sudoku
|
||||
|
||||
import (
|
||||
crypto_rand "crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/bits"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type table struct {
|
||||
encode [256][][4]byte
|
||||
decode map[uint32]byte
|
||||
layout *byteLayout
|
||||
}
|
||||
|
||||
type tableCacheKey struct {
|
||||
password string
|
||||
ascii string
|
||||
customTable string
|
||||
}
|
||||
|
||||
var (
|
||||
tableCache sync.Map
|
||||
tableSetCache sync.Map
|
||||
|
||||
basePatternsOnce sync.Once
|
||||
basePatterns [][][4]byte
|
||||
basePatternsErr error
|
||||
)
|
||||
|
||||
type byteLayout struct {
|
||||
hintMask byte
|
||||
hintValue byte
|
||||
padMarker byte
|
||||
paddingPool []byte
|
||||
encodeHint func(group byte) byte
|
||||
encodeGroup func(group byte) byte
|
||||
decodeGroup func(b byte) (byte, bool)
|
||||
}
|
||||
|
||||
func (l *byteLayout) isHint(b byte) bool {
|
||||
if (b & l.hintMask) == l.hintValue {
|
||||
return true
|
||||
}
|
||||
// ASCII layout maps 0x7f to '\n' to avoid DEL on the wire.
|
||||
return l.hintMask == 0x40 && b == '\n'
|
||||
}
|
||||
|
||||
func getTable(config *Config) (*table, error) {
|
||||
tables, err := getTables(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(tables) == 0 {
|
||||
return nil, fmt.Errorf("empty sudoku table set")
|
||||
}
|
||||
return tables[0], nil
|
||||
}
|
||||
|
||||
func getTables(config *Config) ([]*table, error) {
|
||||
if config == nil {
|
||||
return nil, fmt.Errorf("nil sudoku config")
|
||||
}
|
||||
|
||||
mode, err := normalizeASCII(config.GetAscii())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
patterns, err := normalizedCustomPatterns(config, mode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cacheKey := tableCacheKey{
|
||||
password: config.GetPassword(),
|
||||
ascii: mode,
|
||||
customTable: strings.Join(patterns, "\x00"),
|
||||
}
|
||||
if cached, ok := tableSetCache.Load(cacheKey); ok {
|
||||
return cached.([]*table), nil
|
||||
}
|
||||
|
||||
tables := make([]*table, 0, len(patterns))
|
||||
for _, pattern := range patterns {
|
||||
layout, err := resolveLayout(mode, pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t, err := buildTable(config.GetPassword(), layout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tables = append(tables, t)
|
||||
}
|
||||
|
||||
actual, _ := tableSetCache.LoadOrStore(cacheKey, tables)
|
||||
return actual.([]*table), nil
|
||||
}
|
||||
|
||||
func normalizedCustomPatterns(config *Config, mode string) ([]string, error) {
|
||||
if config == nil {
|
||||
return []string{""}, nil
|
||||
}
|
||||
if mode == "prefer_ascii" {
|
||||
return []string{""}, nil
|
||||
}
|
||||
|
||||
rawPatterns := config.GetCustomTables()
|
||||
if len(rawPatterns) == 0 {
|
||||
rawPatterns = []string{config.GetCustomTable()}
|
||||
}
|
||||
|
||||
patterns := make([]string, 0, len(rawPatterns))
|
||||
seen := make(map[string]struct{}, len(rawPatterns))
|
||||
for _, raw := range rawPatterns {
|
||||
pattern := strings.TrimSpace(raw)
|
||||
if pattern != "" {
|
||||
var err error
|
||||
pattern, err = normalizeCustomTable(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if _, ok := seen[pattern]; ok {
|
||||
continue
|
||||
}
|
||||
seen[pattern] = struct{}{}
|
||||
patterns = append(patterns, pattern)
|
||||
}
|
||||
|
||||
if len(patterns) == 0 {
|
||||
return []string{""}, nil
|
||||
}
|
||||
|
||||
return patterns, nil
|
||||
}
|
||||
|
||||
func normalizedPadding(config *Config) (int, int) {
|
||||
if config == nil {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
pMin := int(config.GetPaddingMin())
|
||||
pMax := int(config.GetPaddingMax())
|
||||
|
||||
if pMin > 100 {
|
||||
pMin = 100
|
||||
}
|
||||
if pMax > 100 {
|
||||
pMax = 100
|
||||
}
|
||||
if pMax < pMin {
|
||||
pMax = pMin
|
||||
}
|
||||
return pMin, pMax
|
||||
}
|
||||
|
||||
func normalizeASCII(mode string) (string, error) {
|
||||
switch strings.ToLower(strings.TrimSpace(mode)) {
|
||||
case "", "entropy", "prefer_entropy":
|
||||
return "prefer_entropy", nil
|
||||
case "ascii", "prefer_ascii":
|
||||
return "prefer_ascii", nil
|
||||
default:
|
||||
return "", fmt.Errorf("invalid sudoku ascii mode: %s", mode)
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeCustomTable(pattern string) (string, error) {
|
||||
cleaned := strings.ToLower(strings.TrimSpace(pattern))
|
||||
cleaned = strings.ReplaceAll(cleaned, " ", "")
|
||||
if len(cleaned) != 8 {
|
||||
return "", fmt.Errorf("customTable must be 8 chars, got %d", len(cleaned))
|
||||
}
|
||||
|
||||
var xCount, pCount, vCount int
|
||||
for _, ch := range cleaned {
|
||||
switch ch {
|
||||
case 'x':
|
||||
xCount++
|
||||
case 'p':
|
||||
pCount++
|
||||
case 'v':
|
||||
vCount++
|
||||
default:
|
||||
return "", fmt.Errorf("customTable has invalid char %q", ch)
|
||||
}
|
||||
}
|
||||
if xCount != 2 || pCount != 2 || vCount != 4 {
|
||||
return "", fmt.Errorf("customTable must contain exactly 2 x, 2 p and 4 v")
|
||||
}
|
||||
return cleaned, nil
|
||||
}
|
||||
|
||||
func resolveLayout(mode, customTable string) (*byteLayout, error) {
|
||||
if mode == "prefer_ascii" {
|
||||
return asciiLayout(), nil
|
||||
}
|
||||
|
||||
if customTable != "" {
|
||||
return customLayout(customTable)
|
||||
}
|
||||
return entropyLayout(), nil
|
||||
}
|
||||
|
||||
func asciiLayout() *byteLayout {
|
||||
padding := make([]byte, 0, 32)
|
||||
for i := 0; i < 32; i++ {
|
||||
padding = append(padding, byte(0x20+i))
|
||||
}
|
||||
|
||||
encodeGroup := func(group byte) byte {
|
||||
b := byte(0x40 | (group & 0x3f))
|
||||
if b == 0x7f {
|
||||
return '\n'
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
return &byteLayout{
|
||||
hintMask: 0x40,
|
||||
hintValue: 0x40,
|
||||
padMarker: 0x3f,
|
||||
paddingPool: padding,
|
||||
encodeHint: encodeGroup,
|
||||
encodeGroup: encodeGroup,
|
||||
decodeGroup: func(b byte) (byte, bool) {
|
||||
if b == '\n' {
|
||||
return 0x3f, true
|
||||
}
|
||||
if (b & 0x40) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
return b & 0x3f, true
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func entropyLayout() *byteLayout {
|
||||
padding := make([]byte, 0, 16)
|
||||
for i := 0; i < 8; i++ {
|
||||
padding = append(padding, byte(0x80+i), byte(0x10+i))
|
||||
}
|
||||
|
||||
encodeGroup := func(group byte) byte {
|
||||
v := group & 0x3f
|
||||
return ((v & 0x30) << 1) | (v & 0x0f)
|
||||
}
|
||||
|
||||
return &byteLayout{
|
||||
hintMask: 0x90,
|
||||
hintValue: 0x00,
|
||||
padMarker: 0x80,
|
||||
paddingPool: padding,
|
||||
encodeHint: encodeGroup,
|
||||
encodeGroup: encodeGroup,
|
||||
decodeGroup: func(b byte) (byte, bool) {
|
||||
if (b & 0x90) != 0 {
|
||||
return 0, false
|
||||
}
|
||||
return ((b >> 1) & 0x30) | (b & 0x0f), true
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func customLayout(pattern string) (*byteLayout, error) {
|
||||
pattern, err := normalizeCustomTable(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var xBits, pBits, vBits []uint8
|
||||
for i, c := range pattern {
|
||||
bit := uint8(7 - i)
|
||||
switch c {
|
||||
case 'x':
|
||||
xBits = append(xBits, bit)
|
||||
case 'p':
|
||||
pBits = append(pBits, bit)
|
||||
case 'v':
|
||||
vBits = append(vBits, bit)
|
||||
}
|
||||
}
|
||||
|
||||
xMask := byte(0)
|
||||
for _, bit := range xBits {
|
||||
xMask |= 1 << bit
|
||||
}
|
||||
|
||||
encodeGroupWithDropX := func(group byte, dropX int) byte {
|
||||
out := xMask
|
||||
if dropX >= 0 {
|
||||
out &^= 1 << xBits[dropX]
|
||||
}
|
||||
|
||||
val := (group >> 4) & 0x03
|
||||
pos := group & 0x0f
|
||||
|
||||
if (val & 0x02) != 0 {
|
||||
out |= 1 << pBits[0]
|
||||
}
|
||||
if (val & 0x01) != 0 {
|
||||
out |= 1 << pBits[1]
|
||||
}
|
||||
for i, bit := range vBits {
|
||||
if (pos>>(3-uint8(i)))&0x01 == 1 {
|
||||
out |= 1 << bit
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
paddingSet := make(map[byte]struct{}, 64)
|
||||
padding := make([]byte, 0, 64)
|
||||
for drop := range xBits {
|
||||
for val := byte(0); val < 4; val++ {
|
||||
for pos := byte(0); pos < 16; pos++ {
|
||||
group := (val << 4) | pos
|
||||
b := encodeGroupWithDropX(group, drop)
|
||||
if bits.OnesCount8(b) >= 5 {
|
||||
if _, exists := paddingSet[b]; !exists {
|
||||
paddingSet[b] = struct{}{}
|
||||
padding = append(padding, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Slice(padding, func(i, j int) bool { return padding[i] < padding[j] })
|
||||
if len(padding) == 0 {
|
||||
return nil, fmt.Errorf("customTable produced empty padding pool")
|
||||
}
|
||||
|
||||
decodeGroup := func(b byte) (byte, bool) {
|
||||
if (b & xMask) != xMask {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
var val, pos byte
|
||||
if b&(1<<pBits[0]) != 0 {
|
||||
val |= 0x02
|
||||
}
|
||||
if b&(1<<pBits[1]) != 0 {
|
||||
val |= 0x01
|
||||
}
|
||||
for i, bit := range vBits {
|
||||
if b&(1<<bit) != 0 {
|
||||
pos |= 1 << (3 - uint8(i))
|
||||
}
|
||||
}
|
||||
|
||||
return ((val & 0x03) << 4) | (pos & 0x0f), true
|
||||
}
|
||||
encodeGroup := func(group byte) byte {
|
||||
return encodeGroupWithDropX(group, -1)
|
||||
}
|
||||
|
||||
return &byteLayout{
|
||||
hintMask: xMask,
|
||||
hintValue: xMask,
|
||||
padMarker: padding[0],
|
||||
paddingPool: padding,
|
||||
encodeHint: encodeGroup,
|
||||
encodeGroup: encodeGroup,
|
||||
decodeGroup: decodeGroup,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func buildTable(password string, layout *byteLayout) (*table, error) {
|
||||
patterns, err := getBasePatterns()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(patterns) < 256 {
|
||||
return nil, fmt.Errorf("not enough sudoku grids: %d", len(patterns))
|
||||
}
|
||||
|
||||
order := make([]int, len(patterns))
|
||||
for i := range order {
|
||||
order[i] = i
|
||||
}
|
||||
|
||||
hash := sha256.Sum256([]byte(password))
|
||||
seed := int64(binary.BigEndian.Uint64(hash[:8]))
|
||||
rng := rand.New(rand.NewSource(seed))
|
||||
rng.Shuffle(len(order), func(i, j int) {
|
||||
order[i], order[j] = order[j], order[i]
|
||||
})
|
||||
|
||||
t := &table{
|
||||
decode: make(map[uint32]byte, 1<<16),
|
||||
layout: layout,
|
||||
}
|
||||
for b := 0; b < 256; b++ {
|
||||
patList := patterns[order[b]]
|
||||
if len(patList) == 0 {
|
||||
return nil, fmt.Errorf("grid %d has no valid clue set", order[b])
|
||||
}
|
||||
|
||||
enc := make([][4]byte, 0, len(patList))
|
||||
for _, groups := range patList {
|
||||
hints := [4]byte{
|
||||
layout.encodeHint(groups[0]),
|
||||
layout.encodeHint(groups[1]),
|
||||
layout.encodeHint(groups[2]),
|
||||
layout.encodeHint(groups[3]),
|
||||
}
|
||||
sortedHints := sort4(hints)
|
||||
key := packKey(sortedHints)
|
||||
if old, exists := t.decode[key]; exists && old != byte(b) {
|
||||
return nil, fmt.Errorf("decode key collision for byte %d and %d", old, b)
|
||||
}
|
||||
t.decode[key] = byte(b)
|
||||
enc = append(enc, hints)
|
||||
}
|
||||
|
||||
t.encode[b] = enc
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func getBasePatterns() ([][][4]byte, error) {
|
||||
basePatternsOnce.Do(func() {
|
||||
basePatterns, basePatternsErr = buildBasePatterns()
|
||||
})
|
||||
return basePatterns, basePatternsErr
|
||||
}
|
||||
|
||||
type grid [16]byte
|
||||
|
||||
func buildBasePatterns() ([][][4]byte, error) {
|
||||
grids := generateAllGrids()
|
||||
positions := hintPositions()
|
||||
|
||||
patterns := make([][][4]byte, len(grids))
|
||||
for _, ps := range positions {
|
||||
counts := make(map[uint32]uint16, len(grids))
|
||||
keys := make([]uint32, len(grids))
|
||||
groupsByGrid := make([][4]byte, len(grids))
|
||||
|
||||
for gi, g := range grids {
|
||||
groups := [4]byte{
|
||||
clueGroup(g, ps[0]),
|
||||
clueGroup(g, ps[1]),
|
||||
clueGroup(g, ps[2]),
|
||||
clueGroup(g, ps[3]),
|
||||
}
|
||||
groups = sort4(groups)
|
||||
key := packKey(groups)
|
||||
keys[gi] = key
|
||||
groupsByGrid[gi] = groups
|
||||
counts[key]++
|
||||
}
|
||||
|
||||
for gi, key := range keys {
|
||||
if counts[key] == 1 {
|
||||
patterns[gi] = append(patterns[gi], groupsByGrid[gi])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for gi, list := range patterns {
|
||||
if len(list) == 0 {
|
||||
return nil, fmt.Errorf("grid %d has no uniquely decodable clue set", gi)
|
||||
}
|
||||
}
|
||||
|
||||
return patterns, nil
|
||||
}
|
||||
|
||||
func clueGroup(g grid, pos byte) byte {
|
||||
// 2 bits of value + 4 bits of position.
|
||||
return ((g[pos] - 1) << 4) | (pos & 0x0f)
|
||||
}
|
||||
|
||||
func generateAllGrids() []grid {
|
||||
grids := make([]grid, 0, 288)
|
||||
var g grid
|
||||
|
||||
var dfs func(idx int)
|
||||
dfs = func(idx int) {
|
||||
if idx == 16 {
|
||||
grids = append(grids, g)
|
||||
return
|
||||
}
|
||||
|
||||
row := idx / 4
|
||||
col := idx % 4
|
||||
boxRow := (row / 2) * 2
|
||||
boxCol := (col / 2) * 2
|
||||
|
||||
for num := byte(1); num <= 4; num++ {
|
||||
valid := true
|
||||
for i := 0; i < 4; i++ {
|
||||
if g[row*4+i] == num || g[i*4+col] == num {
|
||||
valid = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
continue
|
||||
}
|
||||
|
||||
for r := 0; r < 2 && valid; r++ {
|
||||
for c := 0; c < 2; c++ {
|
||||
if g[(boxRow+r)*4+(boxCol+c)] == num {
|
||||
valid = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
continue
|
||||
}
|
||||
|
||||
g[idx] = num
|
||||
dfs(idx + 1)
|
||||
g[idx] = 0
|
||||
}
|
||||
}
|
||||
|
||||
dfs(0)
|
||||
return grids
|
||||
}
|
||||
|
||||
func hintPositions() [][4]byte {
|
||||
// C(16, 4) = 1820.
|
||||
positions := make([][4]byte, 0, 1820)
|
||||
for a := 0; a < 13; a++ {
|
||||
for b := a + 1; b < 14; b++ {
|
||||
for c := b + 1; c < 15; c++ {
|
||||
for d := c + 1; d < 16; d++ {
|
||||
positions = append(positions, [4]byte{byte(a), byte(b), byte(c), byte(d)})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return positions
|
||||
}
|
||||
|
||||
func packKey(in [4]byte) uint32 {
|
||||
return uint32(in[0])<<24 | uint32(in[1])<<16 | uint32(in[2])<<8 | uint32(in[3])
|
||||
}
|
||||
|
||||
func sort4(in [4]byte) [4]byte {
|
||||
if in[0] > in[1] {
|
||||
in[0], in[1] = in[1], in[0]
|
||||
}
|
||||
if in[2] > in[3] {
|
||||
in[2], in[3] = in[3], in[2]
|
||||
}
|
||||
if in[0] > in[2] {
|
||||
in[0], in[2] = in[2], in[0]
|
||||
}
|
||||
if in[1] > in[3] {
|
||||
in[1], in[3] = in[3], in[1]
|
||||
}
|
||||
if in[1] > in[2] {
|
||||
in[1], in[2] = in[2], in[1]
|
||||
}
|
||||
return in
|
||||
}
|
||||
|
||||
func newSeededRand() *rand.Rand {
|
||||
seed := time.Now().UnixNano()
|
||||
var seedBytes [8]byte
|
||||
if _, err := crypto_rand.Read(seedBytes[:]); err == nil {
|
||||
seed = int64(binary.BigEndian.Uint64(seedBytes[:]))
|
||||
}
|
||||
return rand.New(rand.NewSource(seed))
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
package finalmask_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/header/custom"
|
||||
)
|
||||
|
||||
func mustSendRecvTcp(
|
||||
t *testing.T,
|
||||
from net.Conn,
|
||||
to net.Conn,
|
||||
msg []byte,
|
||||
) {
|
||||
t.Helper()
|
||||
|
||||
go func() {
|
||||
_, err := from.Write(msg)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
n, err := io.ReadFull(to, buf[:len(msg)])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if n != len(msg) {
|
||||
t.Fatalf("unexpected size: %d", n)
|
||||
}
|
||||
|
||||
if !bytes.Equal(buf[:n], msg) {
|
||||
t.Fatalf("unexpected data %q", buf[:n])
|
||||
}
|
||||
}
|
||||
|
||||
type layerMaskTcp struct {
|
||||
name string
|
||||
mask finalmask.Tcpmask
|
||||
}
|
||||
|
||||
func TestConnReadWrite(t *testing.T) {
|
||||
cases := []layerMaskTcp{
|
||||
{
|
||||
name: "custom",
|
||||
mask: &custom.TCPConfig{
|
||||
Clients: []*custom.TCPSequence{
|
||||
{
|
||||
Sequence: []*custom.TCPItem{
|
||||
{
|
||||
Packet: []byte{1},
|
||||
},
|
||||
{
|
||||
Rand: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Servers: []*custom.TCPSequence{
|
||||
{
|
||||
Sequence: []*custom.TCPItem{
|
||||
{
|
||||
Packet: []byte{2},
|
||||
},
|
||||
{
|
||||
Rand: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
mask := c.mask
|
||||
|
||||
maskManager := finalmask.NewTcpmaskManager([]finalmask.Tcpmask{mask})
|
||||
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
client, err := net.Dial("tcp", ln.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
client, err = maskManager.WrapConnClient(client)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
server, err := ln.Accept()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
server, err = maskManager.WrapConnServer(server)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_ = client.SetDeadline(time.Now().Add(time.Second))
|
||||
_ = server.SetDeadline(time.Now().Add(time.Second))
|
||||
|
||||
mustSendRecvTcp(t, client, server, []byte("client -> server"))
|
||||
mustSendRecvTcp(t, server, client, []byte("server -> client"))
|
||||
|
||||
mustSendRecvTcp(t, client, server, []byte{})
|
||||
mustSendRecvTcp(t, server, client, []byte{})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,11 @@ package finalmask_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/proxy"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/header/custom"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/header/dns"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/header/srtp"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/header/utp"
|
||||
@@ -19,7 +15,6 @@ import (
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/mkcp/aes128gcm"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/mkcp/original"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/salamander"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/sudoku"
|
||||
)
|
||||
|
||||
func mustSendRecv(
|
||||
@@ -53,140 +48,57 @@ func mustSendRecv(
|
||||
}
|
||||
|
||||
type layerMask struct {
|
||||
name string
|
||||
mask finalmask.Udpmask
|
||||
layers int
|
||||
}
|
||||
|
||||
type countingConn struct {
|
||||
net.Conn
|
||||
written atomic.Int64
|
||||
}
|
||||
|
||||
func (c *countingConn) Write(p []byte) (int, error) {
|
||||
n, err := c.Conn.Write(p)
|
||||
c.written.Add(int64(n))
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *countingConn) Written() int64 {
|
||||
return c.written.Load()
|
||||
name string
|
||||
mask finalmask.Udpmask
|
||||
}
|
||||
|
||||
func TestPacketConnReadWrite(t *testing.T) {
|
||||
cases := []layerMask{
|
||||
{
|
||||
name: "aes128gcm",
|
||||
mask: &aes128gcm.Config{Password: "123"},
|
||||
layers: 2,
|
||||
name: "aes128gcm",
|
||||
mask: &aes128gcm.Config{Password: "123"},
|
||||
},
|
||||
{
|
||||
name: "original",
|
||||
mask: &original.Config{},
|
||||
layers: 2,
|
||||
name: "original",
|
||||
mask: &original.Config{},
|
||||
},
|
||||
{
|
||||
name: "dns",
|
||||
mask: &dns.Config{Domain: "www.baidu.com"},
|
||||
layers: 2,
|
||||
name: "dns",
|
||||
mask: &dns.Config{Domain: "www.baidu.com"},
|
||||
},
|
||||
{
|
||||
name: "srtp",
|
||||
mask: &srtp.Config{},
|
||||
layers: 2,
|
||||
name: "srtp",
|
||||
mask: &srtp.Config{},
|
||||
},
|
||||
{
|
||||
name: "utp",
|
||||
mask: &utp.Config{},
|
||||
layers: 2,
|
||||
name: "utp",
|
||||
mask: &utp.Config{},
|
||||
},
|
||||
{
|
||||
name: "wechat",
|
||||
mask: &wechat.Config{},
|
||||
layers: 2,
|
||||
name: "wechat",
|
||||
mask: &wechat.Config{},
|
||||
},
|
||||
{
|
||||
name: "wireguard",
|
||||
mask: &wireguard.Config{},
|
||||
layers: 2,
|
||||
name: "wireguard",
|
||||
mask: &wireguard.Config{},
|
||||
},
|
||||
{
|
||||
name: "salamander",
|
||||
mask: &salamander.Config{Password: "1234"},
|
||||
layers: 2,
|
||||
},
|
||||
{
|
||||
name: "sudoku-prefer-ascii",
|
||||
mask: &sudoku.Config{
|
||||
Password: "sudoku-mask",
|
||||
Ascii: "prefer_ascii",
|
||||
},
|
||||
layers: 1,
|
||||
},
|
||||
{
|
||||
name: "sudoku-custom-table",
|
||||
mask: &sudoku.Config{
|
||||
Password: "sudoku-mask",
|
||||
Ascii: "prefer_entropy",
|
||||
CustomTable: "xpxvvpvv",
|
||||
},
|
||||
layers: 1,
|
||||
},
|
||||
{
|
||||
name: "sudoku-custom-tables",
|
||||
mask: &sudoku.Config{
|
||||
Password: "sudoku-mask",
|
||||
Ascii: "prefer_entropy",
|
||||
CustomTables: []string{"xpxvvpvv", "vxpvxvvp"},
|
||||
},
|
||||
layers: 1,
|
||||
},
|
||||
{
|
||||
name: "custom",
|
||||
mask: &custom.UDPConfig{
|
||||
Client: []*custom.UDPItem{
|
||||
{
|
||||
Packet: []byte{1},
|
||||
},
|
||||
{
|
||||
Rand: 1,
|
||||
},
|
||||
},
|
||||
Server: []*custom.UDPItem{
|
||||
{
|
||||
Packet: []byte{1},
|
||||
},
|
||||
{
|
||||
Rand: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
layers: 1,
|
||||
},
|
||||
{
|
||||
name: "salamander-single",
|
||||
mask: &salamander.Config{Password: "1234"},
|
||||
layers: 1,
|
||||
name: "salamander",
|
||||
mask: &salamander.Config{Password: "1234"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
mask := c.mask
|
||||
layers := c.layers
|
||||
if layers <= 0 {
|
||||
layers = 1
|
||||
}
|
||||
masks := make([]finalmask.Udpmask, 0, layers)
|
||||
for i := 0; i < layers; i++ {
|
||||
masks = append(masks, mask)
|
||||
}
|
||||
maskManager := finalmask.NewUdpmaskManager(masks)
|
||||
|
||||
maskManager := finalmask.NewUdpmaskManager([]finalmask.Udpmask{mask, mask})
|
||||
|
||||
client, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
client, err = maskManager.WrapPacketConnClient(client)
|
||||
if err != nil {
|
||||
@@ -197,6 +109,7 @@ func TestPacketConnReadWrite(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer server.Close()
|
||||
|
||||
server, err = maskManager.WrapPacketConnServer(server)
|
||||
if err != nil {
|
||||
@@ -214,419 +127,3 @@ func TestPacketConnReadWrite(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSudokuBDD(t *testing.T) {
|
||||
t.Run("GivenSudokuTCPMask_WhenRoundTripWithAsciiPreference_ThenPayloadMatches", func(t *testing.T) {
|
||||
cfg := &sudoku.Config{
|
||||
Password: "sudoku-tcp",
|
||||
Ascii: "prefer_ascii",
|
||||
}
|
||||
|
||||
clientRaw, serverRaw := net.Pipe()
|
||||
defer clientRaw.Close()
|
||||
defer serverRaw.Close()
|
||||
|
||||
clientConn, err := cfg.WrapConnClient(clientRaw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
serverConn, err := cfg.WrapConnServer(serverRaw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
send := bytes.Repeat([]byte("client->server"), 1024)
|
||||
recv := make([]byte, len(send))
|
||||
|
||||
writeErr := make(chan error, 1)
|
||||
go func() {
|
||||
_, wErr := clientConn.Write(send)
|
||||
writeErr <- wErr
|
||||
}()
|
||||
|
||||
if _, err := io.ReadFull(serverConn, recv); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := <-writeErr; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(send, recv) {
|
||||
t.Fatal("tcp sudoku payload mismatch")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GivenSudokuTCPMask_WhenRoundTrip_ThenBothDirectionsMatch", func(t *testing.T) {
|
||||
cfg := &sudoku.Config{
|
||||
Password: "sudoku-packed",
|
||||
Ascii: "prefer_ascii",
|
||||
PaddingMin: 0,
|
||||
PaddingMax: 0,
|
||||
}
|
||||
|
||||
clientRaw, serverRaw := net.Pipe()
|
||||
defer clientRaw.Close()
|
||||
defer serverRaw.Close()
|
||||
|
||||
clientConn, err := cfg.WrapConnClient(clientRaw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
serverConn, err := cfg.WrapConnServer(serverRaw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
clientToServer := bytes.Repeat([]byte("client-packed->server"), 257)
|
||||
serverToClient := bytes.Repeat([]byte("server-packed->client"), 263)
|
||||
|
||||
c2sRecv := make([]byte, len(clientToServer))
|
||||
c2sErr := make(chan error, 1)
|
||||
go func() {
|
||||
_, err := clientConn.Write(clientToServer)
|
||||
c2sErr <- err
|
||||
}()
|
||||
if _, err := io.ReadFull(serverConn, c2sRecv); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := <-c2sErr; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(clientToServer, c2sRecv) {
|
||||
t.Fatal("tcp client->server payload mismatch")
|
||||
}
|
||||
|
||||
s2cRecv := make([]byte, len(serverToClient))
|
||||
s2cErr := make(chan error, 1)
|
||||
go func() {
|
||||
_, err := serverConn.Write(serverToClient)
|
||||
s2cErr <- err
|
||||
}()
|
||||
if _, err := io.ReadFull(clientConn, s2cRecv); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := <-s2cErr; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(serverToClient, s2cRecv) {
|
||||
t.Fatal("tcp server->client payload mismatch")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GivenSudokuTCPMask_WhenServerWritesDownlink_ThenWireBytesAreReduced", func(t *testing.T) {
|
||||
payload := bytes.Repeat([]byte("0123456789abcdef"), 192) // 3072 bytes, divisible by 3.
|
||||
|
||||
countWireBytes := func(wrapServer func(net.Conn, *sudoku.Config) (net.Conn, error), cfg *sudoku.Config) int64 {
|
||||
t.Helper()
|
||||
|
||||
clientRaw, serverRaw := net.Pipe()
|
||||
watchedServerRaw := &countingConn{Conn: serverRaw}
|
||||
|
||||
clientConn, err := cfg.WrapConnClient(clientRaw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
serverConn, err := wrapServer(watchedServerRaw, cfg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
readErr := make(chan error, 1)
|
||||
go func() {
|
||||
_, err := io.CopyN(io.Discard, clientConn, int64(len(payload)))
|
||||
readErr <- err
|
||||
}()
|
||||
|
||||
if _, err := serverConn.Write(payload); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := <-readErr; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_ = clientConn.Close()
|
||||
_ = serverConn.Close()
|
||||
return watchedServerRaw.Written()
|
||||
}
|
||||
|
||||
pureUplinkPackedDownlink := &sudoku.Config{
|
||||
Password: "sudoku-bandwidth",
|
||||
Ascii: "prefer_entropy",
|
||||
PaddingMin: 0,
|
||||
PaddingMax: 0,
|
||||
}
|
||||
packedDownlinkBytes := countWireBytes(func(raw net.Conn, cfg *sudoku.Config) (net.Conn, error) {
|
||||
return cfg.WrapConnServer(raw)
|
||||
}, pureUplinkPackedDownlink)
|
||||
legacyPureBytes := countWireBytes(func(raw net.Conn, cfg *sudoku.Config) (net.Conn, error) {
|
||||
return sudoku.NewTCPConn(raw, cfg)
|
||||
}, pureUplinkPackedDownlink)
|
||||
|
||||
if packedDownlinkBytes >= legacyPureBytes {
|
||||
t.Fatalf("expected default packed downlink bytes < legacy pure bytes, got packed=%d pure=%d", packedDownlinkBytes, legacyPureBytes)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GivenSudokuMultiTableTCPMask_WhenRoundTrip_ThenPayloadMatches", func(t *testing.T) {
|
||||
cfg := &sudoku.Config{
|
||||
Password: "sudoku-multi-tcp",
|
||||
Ascii: "prefer_entropy",
|
||||
CustomTables: []string{"xpxvvpvv", "vxpvxvvp"},
|
||||
}
|
||||
|
||||
clientRaw, serverRaw := net.Pipe()
|
||||
defer clientRaw.Close()
|
||||
defer serverRaw.Close()
|
||||
|
||||
clientConn, err := cfg.WrapConnClient(clientRaw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
serverConn, err := cfg.WrapConnServer(serverRaw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
send := bytes.Repeat([]byte("rotate-table"), 513)
|
||||
recv := make([]byte, len(send))
|
||||
|
||||
writeErr := make(chan error, 1)
|
||||
go func() {
|
||||
_, wErr := clientConn.Write(send)
|
||||
writeErr <- wErr
|
||||
}()
|
||||
|
||||
if _, err := io.ReadFull(serverConn, recv); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := <-writeErr; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(send, recv) {
|
||||
t.Fatal("multi-table tcp sudoku payload mismatch")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GivenSudokuMultiTableTCPMask_WhenPackedDownlink_ThenPayloadMatches", func(t *testing.T) {
|
||||
cfg := &sudoku.Config{
|
||||
Password: "sudoku-multi-packed",
|
||||
Ascii: "prefer_entropy",
|
||||
CustomTables: []string{"xpxvvpvv", "vxpvxvvp"},
|
||||
PaddingMin: 0,
|
||||
PaddingMax: 0,
|
||||
}
|
||||
|
||||
clientRaw, serverRaw := net.Pipe()
|
||||
defer clientRaw.Close()
|
||||
defer serverRaw.Close()
|
||||
|
||||
clientConn, err := cfg.WrapConnClient(clientRaw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
serverConn, err := cfg.WrapConnServer(serverRaw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
send := bytes.Repeat([]byte("packed-rotate"), 257)
|
||||
recv := make([]byte, len(send))
|
||||
|
||||
writeErr := make(chan error, 1)
|
||||
go func() {
|
||||
_, wErr := clientConn.Write(send)
|
||||
writeErr <- wErr
|
||||
}()
|
||||
|
||||
if _, err := io.ReadFull(serverConn, recv); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := <-writeErr; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(send, recv) {
|
||||
t.Fatal("multi-table tcp sudoku payload mismatch")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GivenSudokuUDPMask_WhenNotInnermost_ThenWrapFails", func(t *testing.T) {
|
||||
cfg := &sudoku.Config{Password: "sudoku-udp"}
|
||||
raw, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer raw.Close()
|
||||
|
||||
if _, err := cfg.WrapPacketConnClient(raw, 0, 1); err == nil {
|
||||
t.Fatal("expected innermost check failure")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GivenSudokuMultiTableUDPMask_WhenClientSendsMultipleDatagrams_ThenPayloadMatches", func(t *testing.T) {
|
||||
cfg := &sudoku.Config{
|
||||
Password: "sudoku-udp-multi",
|
||||
Ascii: "prefer_entropy",
|
||||
CustomTables: []string{"xpxvvpvv", "vxpvxvvp"},
|
||||
PaddingMin: 0,
|
||||
PaddingMax: 0,
|
||||
}
|
||||
maskManager := finalmask.NewUdpmaskManager([]finalmask.Udpmask{cfg})
|
||||
|
||||
clientRaw, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer clientRaw.Close()
|
||||
|
||||
serverRaw, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer serverRaw.Close()
|
||||
|
||||
client, err := maskManager.WrapPacketConnClient(clientRaw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
server, err := maskManager.WrapPacketConnServer(serverRaw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_ = client.SetDeadline(time.Now().Add(2 * time.Second))
|
||||
_ = server.SetDeadline(time.Now().Add(2 * time.Second))
|
||||
|
||||
mustSendRecv(t, client, server, []byte("first-datagram"))
|
||||
mustSendRecv(t, client, server, []byte("second-datagram"))
|
||||
mustSendRecv(t, client, server, []byte("third-datagram"))
|
||||
})
|
||||
|
||||
t.Run("GivenSudokuTCPMask_WhenCloseWriteIsCalled_ThenEOFPropagates", func(t *testing.T) {
|
||||
cfg := &sudoku.Config{
|
||||
Password: "sudoku-closewrite",
|
||||
Ascii: "prefer_ascii",
|
||||
PaddingMin: 0,
|
||||
PaddingMax: 0,
|
||||
}
|
||||
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
acceptCh := make(chan net.Conn, 1)
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
acceptCh <- conn
|
||||
}()
|
||||
|
||||
clientRaw, err := net.Dial("tcp", listener.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer clientRaw.Close()
|
||||
|
||||
var serverRaw net.Conn
|
||||
select {
|
||||
case serverRaw = <-acceptCh:
|
||||
case err := <-errCh:
|
||||
t.Fatal(err)
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("accept timeout")
|
||||
}
|
||||
defer serverRaw.Close()
|
||||
|
||||
clientConn, err := cfg.WrapConnClient(clientRaw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
serverConn, err := cfg.WrapConnServer(serverRaw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
closeWriter, ok := clientConn.(interface{ CloseWrite() error })
|
||||
if !ok {
|
||||
t.Fatalf("wrapped conn does not expose CloseWrite: %T", clientConn)
|
||||
}
|
||||
|
||||
writeErr := make(chan error, 1)
|
||||
go func() {
|
||||
if _, err := clientConn.Write([]byte("closewrite")); err != nil {
|
||||
writeErr <- err
|
||||
return
|
||||
}
|
||||
writeErr <- closeWriter.CloseWrite()
|
||||
}()
|
||||
|
||||
buf := make([]byte, len("closewrite"))
|
||||
if _, err := io.ReadFull(serverConn, buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(buf, []byte("closewrite")) {
|
||||
t.Fatal("unexpected payload before closewrite")
|
||||
}
|
||||
if err := <-writeErr; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
one := make([]byte, 1)
|
||||
n, err := serverConn.Read(one)
|
||||
if n != 0 || err != io.EOF {
|
||||
t.Fatalf("expected EOF after CloseWrite, got n=%d err=%v", n, err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GivenSudokuTCPMask_WhenProxyUnwrapRawConn_ThenMaskConnIsRetained", func(t *testing.T) {
|
||||
cfg := &sudoku.Config{
|
||||
Password: "sudoku-unwrap",
|
||||
Ascii: "prefer_entropy",
|
||||
}
|
||||
|
||||
clientRaw, serverRaw := net.Pipe()
|
||||
defer clientRaw.Close()
|
||||
defer serverRaw.Close()
|
||||
|
||||
clientConn, err := cfg.WrapConnClient(clientRaw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
unwrapped, readCounter, writeCounter := proxy.UnwrapRawConn(clientConn)
|
||||
if readCounter != nil || writeCounter != nil {
|
||||
t.Fatal("unexpected stat counters while unwrapping sudoku conn")
|
||||
}
|
||||
if unwrapped != clientConn {
|
||||
t.Fatalf("expected sudoku conn to stay wrapped, got %T", unwrapped)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GivenSudokuTCPMask_WhenProxyUnwrapRawConn_AfterDownlinkOptimization_ThenMaskConnIsRetained", func(t *testing.T) {
|
||||
cfg := &sudoku.Config{
|
||||
Password: "sudoku-packed-unwrap",
|
||||
Ascii: "prefer_entropy",
|
||||
}
|
||||
|
||||
clientRaw, serverRaw := net.Pipe()
|
||||
defer clientRaw.Close()
|
||||
defer serverRaw.Close()
|
||||
|
||||
clientConn, err := cfg.WrapConnClient(clientRaw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
unwrapped, readCounter, writeCounter := proxy.UnwrapRawConn(clientConn)
|
||||
if readCounter != nil || writeCounter != nil {
|
||||
t.Fatal("unexpected stat counters while unwrapping sudoku conn")
|
||||
}
|
||||
if unwrapped != clientConn {
|
||||
t.Fatalf("expected sudoku conn to stay wrapped, got %T", unwrapped)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,15 +6,12 @@ import (
|
||||
"crypto/rand"
|
||||
"encoding/base32"
|
||||
"encoding/binary"
|
||||
go_errors "errors"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -34,7 +31,7 @@ type packet struct {
|
||||
}
|
||||
|
||||
type xdnsConnClient struct {
|
||||
net.PacketConn
|
||||
conn net.PacketConn
|
||||
|
||||
clientID []byte
|
||||
domain Name
|
||||
@@ -47,24 +44,28 @@ type xdnsConnClient struct {
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
func NewConnClient(c *Config, raw net.PacketConn, end bool) (net.PacketConn, error) {
|
||||
if !end {
|
||||
return nil, errors.New("xdns requires being at the outermost level")
|
||||
}
|
||||
|
||||
domain, err := ParseName(c.Domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn := &xdnsConnClient{
|
||||
PacketConn: raw,
|
||||
conn: raw,
|
||||
|
||||
clientID: make([]byte, 8),
|
||||
domain: domain,
|
||||
|
||||
pollChan: make(chan struct{}, pollLimit),
|
||||
readQueue: make(chan *packet, 256),
|
||||
writeQueue: make(chan *packet, 256),
|
||||
readQueue: make(chan *packet, 128),
|
||||
writeQueue: make(chan *packet, 128),
|
||||
}
|
||||
|
||||
common.Must2(rand.Read(conn.clientID))
|
||||
rand.Read(conn.clientID)
|
||||
|
||||
go conn.recvLoop()
|
||||
go conn.sendLoop()
|
||||
@@ -73,24 +74,20 @@ func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
}
|
||||
|
||||
func (c *xdnsConnClient) recvLoop() {
|
||||
var buf [finalmask.UDPSize]byte
|
||||
|
||||
for {
|
||||
if c.closed {
|
||||
break
|
||||
}
|
||||
|
||||
n, addr, err := c.PacketConn.ReadFrom(buf[:])
|
||||
if err != nil || n == 0 {
|
||||
if go_errors.Is(err, net.ErrClosed) || go_errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
var buf [4096]byte
|
||||
|
||||
n, addr, err := c.conn.ReadFrom(buf[:])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
resp, err := MessageFromWireFormat(buf[:n])
|
||||
if err != nil {
|
||||
errors.LogDebug(context.Background(), addr, " xdns from wireformat err ", err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -113,7 +110,6 @@ func (c *xdnsConnClient) recvLoop() {
|
||||
addr: addr,
|
||||
}:
|
||||
default:
|
||||
errors.LogDebug(context.Background(), addr, " mask read err queue full")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,16 +121,8 @@ func (c *xdnsConnClient) recvLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
errors.LogDebug(context.Background(), "xdns closed")
|
||||
|
||||
close(c.pollChan)
|
||||
close(c.readQueue)
|
||||
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
c.closed = true
|
||||
close(c.writeQueue)
|
||||
}
|
||||
|
||||
func (c *xdnsConnClient) sendLoop() {
|
||||
@@ -190,26 +178,25 @@ func (c *xdnsConnClient) sendLoop() {
|
||||
}
|
||||
|
||||
if p != nil {
|
||||
_, err := c.PacketConn.WriteTo(p.p, p.addr)
|
||||
if go_errors.Is(err, net.ErrClosed) || go_errors.Is(err, io.ErrClosedPipe) {
|
||||
c.closed = true
|
||||
break
|
||||
}
|
||||
_, _ = c.conn.WriteTo(p.p, p.addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *xdnsConnClient) Size() int32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *xdnsConnClient) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
packet, ok := <-c.readQueue
|
||||
if !ok {
|
||||
return 0, nil, net.ErrClosed
|
||||
return 0, nil, io.EOF
|
||||
}
|
||||
if len(p) < len(packet.p) {
|
||||
errors.LogDebug(context.Background(), packet.addr, " mask read err short buffer ", len(p), " ", len(packet.p))
|
||||
return 0, packet.addr, nil
|
||||
n = copy(p, packet.p)
|
||||
if n != len(packet.p) {
|
||||
return 0, nil, io.ErrShortBuffer
|
||||
}
|
||||
copy(p, packet.p)
|
||||
return len(packet.p), packet.addr, nil
|
||||
return n, packet.addr, nil
|
||||
}
|
||||
|
||||
func (c *xdnsConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
@@ -217,13 +204,13 @@ func (c *xdnsConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if c.closed {
|
||||
return 0, io.ErrClosedPipe
|
||||
return 0, errors.New("xdns closed")
|
||||
}
|
||||
|
||||
encoded, err := encode(p, c.clientID, c.domain)
|
||||
if err != nil {
|
||||
errors.LogDebug(context.Background(), addr, " xdns wireformat err ", err, " ", len(p))
|
||||
return 0, nil
|
||||
errors.LogDebug(context.Background(), "xdns encode err ", err)
|
||||
return 0, errors.New("xdns encode").Base(err)
|
||||
}
|
||||
|
||||
select {
|
||||
@@ -233,14 +220,38 @@ func (c *xdnsConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
}:
|
||||
return len(p), nil
|
||||
default:
|
||||
errors.LogDebug(context.Background(), addr, " mask write err queue full")
|
||||
return 0, nil
|
||||
return 0, errors.New("xdns queue full")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *xdnsConnClient) Close() error {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if c.closed {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.closed = true
|
||||
return c.PacketConn.Close()
|
||||
close(c.writeQueue)
|
||||
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *xdnsConnClient) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *xdnsConnClient) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *xdnsConnClient) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *xdnsConnClient) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
func encode(p []byte, clientID []byte, domain Name) ([]byte, error) {
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, end)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw)
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw, end)
|
||||
}
|
||||
|
||||
@@ -557,8 +557,6 @@ func TestEncodeRDataTXT(t *testing.T) {
|
||||
if len(encoded) > 256 {
|
||||
t.Errorf("EncodeRDataTXT(%d bytes) returned %d bytes", len(p), len(encoded))
|
||||
}
|
||||
|
||||
fmt.Println(EncodeRDataTXT(nil))
|
||||
}
|
||||
|
||||
func TestRDataTXTRoundTrip(t *testing.T) {
|
||||
|
||||
@@ -4,18 +4,16 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
go_errors "errors"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
)
|
||||
|
||||
const (
|
||||
idleTimeout = 10 * time.Second
|
||||
idleTimeout = 2 * time.Minute
|
||||
responseTTL = 60
|
||||
maxResponseDelay = 1 * time.Second
|
||||
)
|
||||
@@ -44,13 +42,13 @@ type record struct {
|
||||
}
|
||||
|
||||
type queue struct {
|
||||
last time.Time
|
||||
lash time.Time
|
||||
queue chan []byte
|
||||
stash chan []byte
|
||||
}
|
||||
|
||||
type xdnsConnServer struct {
|
||||
net.PacketConn
|
||||
conn net.PacketConn
|
||||
|
||||
domain Name
|
||||
|
||||
@@ -62,19 +60,23 @@ type xdnsConnServer struct {
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
func NewConnServer(c *Config, raw net.PacketConn, end bool) (net.PacketConn, error) {
|
||||
if !end {
|
||||
return nil, errors.New("xdns requires being at the outermost level")
|
||||
}
|
||||
|
||||
domain, err := ParseName(c.Domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn := &xdnsConnServer{
|
||||
PacketConn: raw,
|
||||
conn: raw,
|
||||
|
||||
domain: domain,
|
||||
|
||||
ch: make(chan *record, 500),
|
||||
readQueue: make(chan *packet, 512),
|
||||
ch: make(chan *record, 100),
|
||||
readQueue: make(chan *packet, 128),
|
||||
writeQueueMap: make(map[string]*queue),
|
||||
}
|
||||
|
||||
@@ -97,7 +99,7 @@ func (c *xdnsConnServer) clean() {
|
||||
now := time.Now()
|
||||
|
||||
for key, q := range c.writeQueueMap {
|
||||
if now.Sub(q.last) >= idleTimeout {
|
||||
if now.Sub(q.lash) >= idleTimeout {
|
||||
close(q.queue)
|
||||
close(q.stash)
|
||||
delete(c.writeQueueMap, key)
|
||||
@@ -116,6 +118,9 @@ func (c *xdnsConnServer) clean() {
|
||||
}
|
||||
|
||||
func (c *xdnsConnServer) ensureQueue(addr net.Addr) *queue {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if c.closed {
|
||||
return nil
|
||||
}
|
||||
@@ -123,12 +128,12 @@ func (c *xdnsConnServer) ensureQueue(addr net.Addr) *queue {
|
||||
q, ok := c.writeQueueMap[addr.String()]
|
||||
if !ok {
|
||||
q = &queue{
|
||||
queue: make(chan []byte, 512),
|
||||
queue: make(chan []byte, 128),
|
||||
stash: make(chan []byte, 1),
|
||||
}
|
||||
c.writeQueueMap[addr.String()] = q
|
||||
}
|
||||
q.last = time.Now()
|
||||
q.lash = time.Now()
|
||||
|
||||
return q
|
||||
}
|
||||
@@ -148,24 +153,19 @@ func (c *xdnsConnServer) stash(queue *queue, p []byte) {
|
||||
}
|
||||
|
||||
func (c *xdnsConnServer) recvLoop() {
|
||||
var buf [finalmask.UDPSize]byte
|
||||
|
||||
for {
|
||||
if c.closed {
|
||||
break
|
||||
}
|
||||
|
||||
n, addr, err := c.PacketConn.ReadFrom(buf[:])
|
||||
if err != nil || n == 0 {
|
||||
if go_errors.Is(err, net.ErrClosed) || go_errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
var buf [4096]byte
|
||||
n, addr, err := c.conn.ReadFrom(buf[:])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
query, err := MessageFromWireFormat(buf[:n])
|
||||
if err != nil {
|
||||
errors.LogDebug(context.Background(), addr, " xdns from wireformat err ", err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -190,7 +190,6 @@ func (c *xdnsConnServer) recvLoop() {
|
||||
addr: clientIDToAddr(clientID),
|
||||
}:
|
||||
default:
|
||||
errors.LogDebug(context.Background(), addr, " ", clientID, " mask read err queue full")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -203,25 +202,12 @@ func (c *xdnsConnServer) recvLoop() {
|
||||
select {
|
||||
case c.ch <- &record{resp, addr, clientIDToAddr(clientID)}:
|
||||
default:
|
||||
errors.LogDebug(context.Background(), addr, " ", clientID, " mask read err record queue full")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
errors.LogDebug(context.Background(), "xdns closed")
|
||||
|
||||
close(c.ch)
|
||||
close(c.readQueue)
|
||||
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
c.closed = true
|
||||
for key, q := range c.writeQueueMap {
|
||||
close(q.queue)
|
||||
close(q.stash)
|
||||
delete(c.writeQueueMap, key)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *xdnsConnServer) sendLoop() {
|
||||
@@ -252,28 +238,24 @@ func (c *xdnsConnServer) sendLoop() {
|
||||
var payload bytes.Buffer
|
||||
limit := maxEncodedPayload
|
||||
timer := time.NewTimer(maxResponseDelay)
|
||||
|
||||
for {
|
||||
c.mutex.Lock()
|
||||
q := c.ensureQueue(rec.ClientAddr)
|
||||
if q == nil {
|
||||
c.mutex.Unlock()
|
||||
queue := c.ensureQueue(rec.ClientAddr)
|
||||
if queue == nil {
|
||||
return
|
||||
}
|
||||
c.mutex.Unlock()
|
||||
|
||||
var p []byte
|
||||
|
||||
select {
|
||||
case p = <-q.stash:
|
||||
case p = <-queue.stash:
|
||||
default:
|
||||
select {
|
||||
case p = <-q.stash:
|
||||
case p = <-q.queue:
|
||||
case p = <-queue.stash:
|
||||
case p = <-queue.queue:
|
||||
default:
|
||||
select {
|
||||
case p = <-q.stash:
|
||||
case p = <-q.queue:
|
||||
case p = <-queue.stash:
|
||||
case p = <-queue.queue:
|
||||
case <-timer.C:
|
||||
case nextRec = <-c.ch:
|
||||
}
|
||||
@@ -287,31 +269,33 @@ func (c *xdnsConnServer) sendLoop() {
|
||||
}
|
||||
|
||||
limit -= 2 + len(p)
|
||||
if payload.Len() > 0 && limit < 0 {
|
||||
c.stash(q, p)
|
||||
if payload.Len() == 0 {
|
||||
|
||||
} else if limit < 0 {
|
||||
c.stash(queue, p)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
// if len(p) > 65535 {
|
||||
// panic(len(p))
|
||||
// }
|
||||
if int(uint16(len(p))) != len(p) {
|
||||
panic(len(p))
|
||||
}
|
||||
|
||||
_ = binary.Write(&payload, binary.BigEndian, uint16(len(p)))
|
||||
payload.Write(p)
|
||||
}
|
||||
|
||||
timer.Stop()
|
||||
|
||||
rec.Resp.Answer[0].Data = EncodeRDataTXT(payload.Bytes())
|
||||
}
|
||||
|
||||
buf, err := rec.Resp.WireFormat()
|
||||
if err != nil {
|
||||
errors.LogDebug(context.Background(), rec.Addr, " ", rec.ClientAddr, " xdns wireformat err ", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(buf) > maxUDPPayload {
|
||||
errors.LogDebug(context.Background(), rec.Addr, " ", rec.ClientAddr, " xdns truncate ", len(buf))
|
||||
errors.LogDebug(context.Background(), "xdns server truncate ", len(buf))
|
||||
buf = buf[:maxUDPPayload]
|
||||
buf[2] |= 0x02
|
||||
}
|
||||
@@ -320,39 +304,37 @@ func (c *xdnsConnServer) sendLoop() {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = c.PacketConn.WriteTo(buf, rec.Addr)
|
||||
if go_errors.Is(err, net.ErrClosed) || go_errors.Is(err, io.ErrClosedPipe) {
|
||||
c.closed = true
|
||||
break
|
||||
}
|
||||
_, _ = c.conn.WriteTo(buf, rec.Addr)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *xdnsConnServer) Size() int32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *xdnsConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
packet, ok := <-c.readQueue
|
||||
if !ok {
|
||||
return 0, nil, net.ErrClosed
|
||||
return 0, nil, io.EOF
|
||||
}
|
||||
if len(p) < len(packet.p) {
|
||||
errors.LogDebug(context.Background(), packet.addr, " mask read err short buffer ", len(p), " ", len(packet.p))
|
||||
return 0, packet.addr, nil
|
||||
n = copy(p, packet.p)
|
||||
if n != len(packet.p) {
|
||||
return 0, nil, io.ErrShortBuffer
|
||||
}
|
||||
copy(p, packet.p)
|
||||
return len(packet.p), packet.addr, nil
|
||||
return n, packet.addr, nil
|
||||
}
|
||||
|
||||
func (c *xdnsConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
if len(p)+2 > maxEncodedPayload {
|
||||
errors.LogDebug(context.Background(), addr, " mask write err short write ", len(p), "+2 > ", maxEncodedPayload)
|
||||
return 0, nil
|
||||
q := c.ensureQueue(addr)
|
||||
if q == nil {
|
||||
return 0, errors.New("xdns closed")
|
||||
}
|
||||
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
q := c.ensureQueue(addr)
|
||||
if q == nil {
|
||||
return 0, io.ErrClosedPipe
|
||||
if c.closed {
|
||||
return 0, errors.New("xdns closed")
|
||||
}
|
||||
|
||||
buf := make([]byte, len(p))
|
||||
@@ -362,14 +344,42 @@ func (c *xdnsConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
case q.queue <- buf:
|
||||
return len(p), nil
|
||||
default:
|
||||
// errors.LogDebug(context.Background(), addr, " mask write err queue full")
|
||||
return 0, nil
|
||||
return 0, errors.New("xdns queue full")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *xdnsConnServer) Close() error {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if c.closed {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.closed = true
|
||||
return c.PacketConn.Close()
|
||||
for key, q := range c.writeQueueMap {
|
||||
close(q.queue)
|
||||
close(q.stash)
|
||||
delete(c.writeQueueMap, key)
|
||||
}
|
||||
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *xdnsConnServer) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *xdnsConnServer) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *xdnsConnServer) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *xdnsConnServer) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
func nextPacketServer(r *bytes.Reader) ([]byte, error) {
|
||||
|
||||
@@ -10,9 +10,6 @@ import (
|
||||
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
"github.com/xtls/xray-core/transport/internet/hysteria/udphop"
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
@@ -54,10 +51,8 @@ type xicmpConnClient struct {
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn, level int) (net.PacketConn, error) {
|
||||
_, ok1 := raw.(*internet.FakePacketConn)
|
||||
_, ok2 := raw.(*udphop.UdpHopPacketConn)
|
||||
if level != 0 || ok1 || ok2 {
|
||||
func NewConnClient(c *Config, raw net.PacketConn, end bool) (net.PacketConn, error) {
|
||||
if !end {
|
||||
return nil, errors.New("xicmp requires being at the outermost level")
|
||||
}
|
||||
|
||||
@@ -90,8 +85,8 @@ func NewConnClient(c *Config, raw net.PacketConn, level int) (net.PacketConn, er
|
||||
seqStatus: make(map[int]*seqStatus),
|
||||
|
||||
pollChan: make(chan struct{}, pollLimit),
|
||||
readQueue: make(chan *packet, 256),
|
||||
writeQueue: make(chan *packet, 256),
|
||||
readQueue: make(chan *packet, 128),
|
||||
writeQueue: make(chan *packet, 128),
|
||||
}
|
||||
|
||||
go conn.recvLoop()
|
||||
@@ -127,8 +122,8 @@ func (c *xicmpConnClient) encode(p []byte) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(buf) > finalmask.UDPSize {
|
||||
return nil, errors.New("xicmp len(buf) > finalmask.UDPSize")
|
||||
if len(buf) > 8192 {
|
||||
return nil, errors.New("xicmp len(buf) > 8192")
|
||||
}
|
||||
|
||||
c.seqStatus[c.seq] = &seqStatus{
|
||||
@@ -149,13 +144,13 @@ func (c *xicmpConnClient) encode(p []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
func (c *xicmpConnClient) recvLoop() {
|
||||
var buf [finalmask.UDPSize]byte
|
||||
|
||||
for {
|
||||
if c.closed {
|
||||
break
|
||||
}
|
||||
|
||||
var buf [8192]byte
|
||||
|
||||
n, addr, err := c.icmpConn.ReadFrom(buf[:])
|
||||
if err != nil {
|
||||
continue
|
||||
@@ -206,7 +201,6 @@ func (c *xicmpConnClient) recvLoop() {
|
||||
addr: &net.UDPAddr{IP: addr.(*net.IPAddr).IP},
|
||||
}:
|
||||
default:
|
||||
errors.LogDebug(context.Background(), addr, " ", echo.Seq, " ", echo.ID, " mask read err queue full")
|
||||
}
|
||||
|
||||
select {
|
||||
@@ -216,16 +210,8 @@ func (c *xicmpConnClient) recvLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
errors.LogDebug(context.Background(), "xicmp closed")
|
||||
|
||||
close(c.pollChan)
|
||||
close(c.readQueue)
|
||||
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
c.closed = true
|
||||
close(c.writeQueue)
|
||||
}
|
||||
|
||||
func (c *xicmpConnClient) sendLoop() {
|
||||
@@ -283,37 +269,39 @@ func (c *xicmpConnClient) sendLoop() {
|
||||
if p != nil {
|
||||
_, err := c.icmpConn.WriteTo(p.p, p.addr)
|
||||
if err != nil {
|
||||
errors.LogDebug(context.Background(), p.addr, " xicmp writeto err ", err)
|
||||
errors.LogDebug(context.Background(), "xicmp writeto err ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *xicmpConnClient) Size() int32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *xicmpConnClient) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
packet, ok := <-c.readQueue
|
||||
if !ok {
|
||||
return 0, nil, net.ErrClosed
|
||||
return 0, nil, io.EOF
|
||||
}
|
||||
if len(p) < len(packet.p) {
|
||||
errors.LogDebug(context.Background(), packet.addr, " mask read err short buffer ", len(p), " ", len(packet.p))
|
||||
return 0, packet.addr, nil
|
||||
n = copy(p, packet.p)
|
||||
if n != len(packet.p) {
|
||||
return 0, nil, io.ErrShortBuffer
|
||||
}
|
||||
copy(p, packet.p)
|
||||
return len(packet.p), packet.addr, nil
|
||||
return n, packet.addr, nil
|
||||
}
|
||||
|
||||
func (c *xicmpConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
encoded, err := c.encode(p)
|
||||
if err != nil {
|
||||
errors.LogDebug(context.Background(), addr, " xicmp wireformat err ", err)
|
||||
return 0, nil
|
||||
return 0, errors.New("xicmp encode").Base(err)
|
||||
}
|
||||
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if c.closed {
|
||||
return 0, io.ErrClosedPipe
|
||||
return 0, errors.New("xicmp closed")
|
||||
}
|
||||
|
||||
select {
|
||||
@@ -323,13 +311,21 @@ func (c *xicmpConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
}:
|
||||
return len(p), nil
|
||||
default:
|
||||
errors.LogDebug(context.Background(), addr, " mask write err queue full")
|
||||
return 0, nil
|
||||
return 0, errors.New("xicmp queue full")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *xicmpConnClient) Close() error {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if c.closed {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.closed = true
|
||||
close(c.writeQueue)
|
||||
|
||||
_ = c.icmpConn.Close()
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, level)
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, end)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw, level)
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw, end)
|
||||
}
|
||||
|
||||
@@ -10,14 +10,13 @@ import (
|
||||
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
const (
|
||||
idleTimeout = 10 * time.Second
|
||||
idleTimeout = 2 * time.Minute
|
||||
maxResponseDelay = 1 * time.Second
|
||||
)
|
||||
|
||||
@@ -30,7 +29,7 @@ type record struct {
|
||||
}
|
||||
|
||||
type queue struct {
|
||||
last time.Time
|
||||
lash time.Time
|
||||
queue chan []byte
|
||||
}
|
||||
|
||||
@@ -50,8 +49,8 @@ type xicmpConnServer struct {
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn, level int) (net.PacketConn, error) {
|
||||
if level != 0 {
|
||||
func NewConnServer(c *Config, raw net.PacketConn, end bool) (net.PacketConn, error) {
|
||||
if !end {
|
||||
return nil, errors.New("xicmp requires being at the outermost level")
|
||||
}
|
||||
|
||||
@@ -77,8 +76,8 @@ func NewConnServer(c *Config, raw net.PacketConn, level int) (net.PacketConn, er
|
||||
proto: proto,
|
||||
config: c,
|
||||
|
||||
ch: make(chan *record, 500),
|
||||
readQueue: make(chan *packet, 512),
|
||||
ch: make(chan *record, 100),
|
||||
readQueue: make(chan *packet, 128),
|
||||
writeQueueMap: make(map[string]*queue),
|
||||
}
|
||||
|
||||
@@ -101,7 +100,7 @@ func (c *xicmpConnServer) clean() {
|
||||
now := time.Now()
|
||||
|
||||
for key, q := range c.writeQueueMap {
|
||||
if now.Sub(q.last) >= idleTimeout {
|
||||
if now.Sub(q.lash) >= idleTimeout {
|
||||
close(q.queue)
|
||||
delete(c.writeQueueMap, key)
|
||||
}
|
||||
@@ -119,6 +118,9 @@ func (c *xicmpConnServer) clean() {
|
||||
}
|
||||
|
||||
func (c *xicmpConnServer) ensureQueue(addr net.Addr) *queue {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if c.closed {
|
||||
return nil
|
||||
}
|
||||
@@ -126,11 +128,11 @@ func (c *xicmpConnServer) ensureQueue(addr net.Addr) *queue {
|
||||
q, ok := c.writeQueueMap[addr.String()]
|
||||
if !ok {
|
||||
q = &queue{
|
||||
queue: make(chan []byte, 512),
|
||||
queue: make(chan []byte, 128),
|
||||
}
|
||||
c.writeQueueMap[addr.String()] = q
|
||||
}
|
||||
q.last = time.Now()
|
||||
q.lash = time.Now()
|
||||
|
||||
return q
|
||||
}
|
||||
@@ -157,8 +159,8 @@ func (c *xicmpConnServer) encode(p []byte, id int, seq int, needSeqByte bool, se
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(buf) > finalmask.UDPSize {
|
||||
return nil, errors.New("xicmp len(buf) > finalmask.UDPSize")
|
||||
if len(buf) > 8192 {
|
||||
return nil, errors.New("xicmp len(buf) > 8192")
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
@@ -175,13 +177,13 @@ func (c *xicmpConnServer) randUntil(b1 byte) byte {
|
||||
}
|
||||
|
||||
func (c *xicmpConnServer) recvLoop() {
|
||||
var buf [finalmask.UDPSize]byte
|
||||
|
||||
for {
|
||||
if c.closed {
|
||||
break
|
||||
}
|
||||
|
||||
var buf [8192]byte
|
||||
|
||||
n, addr, err := c.icmpConn.ReadFrom(buf[:])
|
||||
if err != nil {
|
||||
continue
|
||||
@@ -223,7 +225,6 @@ func (c *xicmpConnServer) recvLoop() {
|
||||
},
|
||||
}:
|
||||
default:
|
||||
errors.LogDebug(context.Background(), addr, " ", echo.ID, " ", echo.Seq, " mask read err queue full")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,23 +240,11 @@ func (c *xicmpConnServer) recvLoop() {
|
||||
},
|
||||
}:
|
||||
default:
|
||||
errors.LogDebug(context.Background(), addr, " ", echo.ID, " ", echo.Seq, " mask read err record queue full")
|
||||
}
|
||||
}
|
||||
|
||||
errors.LogDebug(context.Background(), "xicmp closed")
|
||||
|
||||
close(c.ch)
|
||||
close(c.readQueue)
|
||||
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
c.closed = true
|
||||
for key, q := range c.writeQueueMap {
|
||||
close(q.queue)
|
||||
delete(c.writeQueueMap, key)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *xicmpConnServer) sendLoop() {
|
||||
@@ -272,23 +261,20 @@ func (c *xicmpConnServer) sendLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
c.mutex.Lock()
|
||||
q := c.ensureQueue(rec.addr)
|
||||
if q == nil {
|
||||
c.mutex.Unlock()
|
||||
queue := c.ensureQueue(rec.addr)
|
||||
if queue == nil {
|
||||
return
|
||||
}
|
||||
c.mutex.Unlock()
|
||||
|
||||
var p []byte
|
||||
|
||||
timer := time.NewTimer(maxResponseDelay)
|
||||
|
||||
select {
|
||||
case p = <-q.queue:
|
||||
case p = <-queue.queue:
|
||||
default:
|
||||
select {
|
||||
case p = <-q.queue:
|
||||
case p = <-queue.queue:
|
||||
case <-timer.C:
|
||||
case nextRec = <-c.ch:
|
||||
}
|
||||
@@ -302,7 +288,6 @@ func (c *xicmpConnServer) sendLoop() {
|
||||
|
||||
buf, err := c.encode(p, rec.id, rec.seq, rec.needSeqByte, rec.seqByte)
|
||||
if err != nil {
|
||||
errors.LogDebug(context.Background(), rec.addr, " ", rec.id, " ", rec.seq, " xicmp wireformat err ", err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -312,36 +297,38 @@ func (c *xicmpConnServer) sendLoop() {
|
||||
|
||||
_, err = c.icmpConn.WriteTo(buf, &net.IPAddr{IP: rec.addr.(*net.UDPAddr).IP})
|
||||
if err != nil {
|
||||
errors.LogDebug(context.Background(), rec.addr, " ", rec.id, " ", rec.seq, " xicmp writeto err ", err)
|
||||
errors.LogDebug(context.Background(), "xicmp writeto err ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *xicmpConnServer) Size() int32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *xicmpConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
packet, ok := <-c.readQueue
|
||||
if !ok {
|
||||
return 0, nil, net.ErrClosed
|
||||
return 0, nil, io.EOF
|
||||
}
|
||||
if len(p) < len(packet.p) {
|
||||
errors.LogDebug(context.Background(), packet.addr, " mask read err short buffer ", len(p), " ", len(packet.p))
|
||||
return 0, packet.addr, nil
|
||||
n = copy(p, packet.p)
|
||||
if n != len(packet.p) {
|
||||
return 0, nil, io.ErrShortBuffer
|
||||
}
|
||||
copy(p, packet.p)
|
||||
return len(packet.p), packet.addr, nil
|
||||
return n, packet.addr, nil
|
||||
}
|
||||
|
||||
func (c *xicmpConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
if len(p)+8+1 > finalmask.UDPSize {
|
||||
errors.LogDebug(context.Background(), addr, " mask write err short write ", len(p), "+8+1 > ", finalmask.UDPSize)
|
||||
return 0, nil
|
||||
q := c.ensureQueue(addr)
|
||||
if q == nil {
|
||||
return 0, errors.New("xicmp closed")
|
||||
}
|
||||
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
q := c.ensureQueue(addr)
|
||||
if q == nil {
|
||||
return 0, io.ErrClosedPipe
|
||||
if c.closed {
|
||||
return 0, errors.New("xicmp closed")
|
||||
}
|
||||
|
||||
buf := make([]byte, len(p))
|
||||
@@ -351,13 +338,24 @@ func (c *xicmpConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
case q.queue <- buf:
|
||||
return len(p), nil
|
||||
default:
|
||||
// errors.LogDebug(context.Background(), addr, " mask write err queue full")
|
||||
return 0, nil
|
||||
return 0, errors.New("xicmp queue full")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *xicmpConnServer) Close() error {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if c.closed {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.closed = true
|
||||
for key, q := range c.writeQueueMap {
|
||||
close(q.queue)
|
||||
delete(c.writeQueueMap, key)
|
||||
}
|
||||
|
||||
_ = c.icmpConn.Close()
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
c "github.com/xtls/xray-core/common/ctx"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/utils"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/common/utils"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/grpc/encoding"
|
||||
"github.com/xtls/xray-core/transport/internet/reality"
|
||||
@@ -126,15 +126,6 @@ func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *in
|
||||
|
||||
c, err := internet.DialSystem(gctx, net.TCPDestination(address, port), sockopt)
|
||||
if err == nil {
|
||||
if streamSettings.TcpmaskManager != nil {
|
||||
newConn, err := streamSettings.TcpmaskManager.WrapConnClient(c)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return nil, errors.New("mask err").Base(err)
|
||||
}
|
||||
c = newConn
|
||||
}
|
||||
|
||||
if tlsConfig != nil {
|
||||
config := tlsConfig.GetTLSConfig()
|
||||
if config.ServerName == "" && address.Family().IsDomain() {
|
||||
@@ -191,16 +182,8 @@ func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *in
|
||||
)
|
||||
if err == nil {
|
||||
userAgent := grpcSettings.UserAgent
|
||||
// It's NOT recommended to set the UA of gRPC connections to that of real browsers, as they are fundamentally incapable of initiating real gRPC connections.
|
||||
switch userAgent {
|
||||
case "chrome", "":
|
||||
if userAgent == "" {
|
||||
userAgent = utils.ChromeUA
|
||||
case "firefox":
|
||||
userAgent = utils.FirefoxUA
|
||||
case "edge":
|
||||
userAgent = utils.MSEdgeUA
|
||||
case "golang":
|
||||
userAgent = ""
|
||||
}
|
||||
setUserAgent(conn, userAgent)
|
||||
conn.Connect()
|
||||
|
||||
@@ -120,10 +120,6 @@ func Listen(ctx context.Context, address net.Address, port net.Port, settings *i
|
||||
}
|
||||
}
|
||||
|
||||
if settings.TcpmaskManager != nil {
|
||||
streamListener, _ = settings.TcpmaskManager.WrapListener(streamListener)
|
||||
}
|
||||
|
||||
errors.LogDebug(ctx, "gRPC listen for service name `"+grpcSettings.getServiceName()+"` tun `"+grpcSettings.getTunStreamName()+"` multi tun `"+grpcSettings.getTunMultiStreamName()+"`")
|
||||
encoding.RegisterGRPCServiceServerX(s, listener, grpcSettings.getServiceName(), grpcSettings.getTunStreamName(), grpcSettings.getTunMultiStreamName())
|
||||
|
||||
|
||||
@@ -52,15 +52,6 @@ func dialhttpUpgrade(ctx context.Context, dest net.Destination, streamSettings *
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if streamSettings.TcpmaskManager != nil {
|
||||
newConn, err := streamSettings.TcpmaskManager.WrapConnClient(pconn)
|
||||
if err != nil {
|
||||
pconn.Close()
|
||||
return nil, errors.New("mask err").Base(err)
|
||||
}
|
||||
pconn = newConn
|
||||
}
|
||||
|
||||
var conn net.Conn
|
||||
var requestURL url.URL
|
||||
tConfig := tls.ConfigFromStreamSettings(streamSettings)
|
||||
@@ -96,7 +87,9 @@ func dialhttpUpgrade(ctx context.Context, dest net.Destination, streamSettings *
|
||||
for key, value := range transportConfiguration.Header {
|
||||
AddHeader(req.Header, key, value)
|
||||
}
|
||||
utils.TryDefaultHeadersWith(req.Header, "ws")
|
||||
if req.Header.Get("User-Agent") == "" {
|
||||
req.Header.Set("User-Agent", utils.ChromeUA)
|
||||
}
|
||||
req.Header.Set("Connection", "Upgrade")
|
||||
req.Header.Set("Upgrade", "websocket")
|
||||
|
||||
|
||||
@@ -142,10 +142,6 @@ func ListenHTTPUpgrade(ctx context.Context, address net.Address, port net.Port,
|
||||
errors.LogInfo(ctx, "listening TCP(for HttpUpgrade) on ", address, ":", port)
|
||||
}
|
||||
|
||||
if streamSettings.TcpmaskManager != nil {
|
||||
listener, _ = streamSettings.TcpmaskManager.WrapListener(listener)
|
||||
}
|
||||
|
||||
if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol {
|
||||
errors.LogWarning(ctx, "accepting PROXY protocol")
|
||||
}
|
||||
|
||||
@@ -22,20 +22,34 @@ 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"`
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
@@ -82,6 +96,104 @@ 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
|
||||
@@ -149,21 +261,37 @@ 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\"\xa3\x04\n" +
|
||||
"(transport/internet/hysteria/config.proto\x12 xray.transport.internet.hysteria\"\xf0\b\n" +
|
||||
"\x06Config\x12\x18\n" +
|
||||
"\aversion\x18\x01 \x01(\x05R\aversion\x12\x12\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" +
|
||||
"\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" +
|
||||
"masqString\x12o\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" +
|
||||
"\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" +
|
||||
"\x16MasqStringHeadersEntry\x12\x10\n" +
|
||||
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
||||
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\x82\x01\n" +
|
||||
|
||||
@@ -9,14 +9,29 @@ 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;
|
||||
|
||||
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<string, string> masq_string_headers = 10;
|
||||
int32 masq_string_status_code = 11;
|
||||
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<string, string> masq_string_headers = 24;
|
||||
int32 masq_string_status_code = 25;
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/apernet/quic-go/congestion"
|
||||
"github.com/apernet/quic-go/monotime"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -104,7 +103,7 @@ type maxAckHeightTracker struct {
|
||||
// bandwidth.
|
||||
maxAckHeightFilter *WindowedFilter[extraAckedEvent, roundTripCount]
|
||||
// The time this aggregation started and the number of bytes acked during it.
|
||||
aggregationEpochStartTime monotime.Time
|
||||
aggregationEpochStartTime congestion.Time
|
||||
aggregationEpochBytes congestion.ByteCount
|
||||
// The last sent packet number before the current aggregation epoch started.
|
||||
lastSentPacketNumberBeforeEpoch congestion.PacketNumber
|
||||
@@ -134,7 +133,7 @@ func (m *maxAckHeightTracker) Update(
|
||||
roundTripCount roundTripCount,
|
||||
lastSentPacketNumber congestion.PacketNumber,
|
||||
lastAckedPacketNumber congestion.PacketNumber,
|
||||
ackTime monotime.Time,
|
||||
ackTime congestion.Time,
|
||||
bytesAcked congestion.ByteCount,
|
||||
) congestion.ByteCount {
|
||||
forceNewEpoch := false
|
||||
@@ -242,7 +241,7 @@ func (m *maxAckHeightTracker) NumAckAggregationEpochs() uint64 {
|
||||
|
||||
// AckPoint represents a point on the ack line.
|
||||
type ackPoint struct {
|
||||
ackTime monotime.Time
|
||||
ackTime congestion.Time
|
||||
totalBytesAcked congestion.ByteCount
|
||||
}
|
||||
|
||||
@@ -251,7 +250,7 @@ type recentAckPoints struct {
|
||||
ackPoints [2]ackPoint
|
||||
}
|
||||
|
||||
func (r *recentAckPoints) Update(ackTime monotime.Time, totalBytesAcked congestion.ByteCount) {
|
||||
func (r *recentAckPoints) Update(ackTime congestion.Time, totalBytesAcked congestion.ByteCount) {
|
||||
if ackTime.Before(r.ackPoints[1].ackTime) {
|
||||
r.ackPoints[1].ackTime = ackTime
|
||||
} else if ackTime.After(r.ackPoints[1].ackTime) {
|
||||
@@ -285,7 +284,7 @@ func (r *recentAckPoints) LessRecentPoint() *ackPoint {
|
||||
// that moment.
|
||||
type connectionStateOnSentPacket struct {
|
||||
// Time at which the packet is sent.
|
||||
sentTime monotime.Time
|
||||
sentTime congestion.Time
|
||||
// Size of the packet.
|
||||
size congestion.ByteCount
|
||||
// The value of |totalBytesSentAtLastAckedPacket| at the time the
|
||||
@@ -293,10 +292,10 @@ type connectionStateOnSentPacket struct {
|
||||
totalBytesSentAtLastAckedPacket congestion.ByteCount
|
||||
// The value of |lastAckedPacketSentTime| at the time the packet was
|
||||
// sent.
|
||||
lastAckedPacketSentTime monotime.Time
|
||||
lastAckedPacketSentTime congestion.Time
|
||||
// The value of |lastAckedPacketAckTime| at the time the packet was
|
||||
// sent.
|
||||
lastAckedPacketAckTime monotime.Time
|
||||
lastAckedPacketAckTime congestion.Time
|
||||
// Send time states that are returned to the congestion controller when the
|
||||
// packet is acked or lost.
|
||||
sendTimeState sendTimeState
|
||||
@@ -306,7 +305,7 @@ type connectionStateOnSentPacket struct {
|
||||
// sampler.
|
||||
// |bytes_in_flight| is the bytes in flight right after the packet is sent.
|
||||
func newConnectionStateOnSentPacket(
|
||||
sentTime monotime.Time,
|
||||
sentTime congestion.Time,
|
||||
size congestion.ByteCount,
|
||||
bytesInFlight congestion.ByteCount,
|
||||
sampler *bandwidthSampler,
|
||||
@@ -457,10 +456,10 @@ type bandwidthSampler struct {
|
||||
|
||||
// The time at which the last acknowledged packet was sent. Set to
|
||||
// QuicTime::Zero() if no valid timestamp is available.
|
||||
lastAckedPacketSentTime monotime.Time
|
||||
lastAckedPacketSentTime congestion.Time
|
||||
|
||||
// The time at which the most recent packet was acknowledged.
|
||||
lastAckedPacketAckTime monotime.Time
|
||||
lastAckedPacketAckTime congestion.Time
|
||||
|
||||
// The most recently sent packet.
|
||||
lastSentPacket congestion.PacketNumber
|
||||
@@ -552,7 +551,7 @@ func (b *bandwidthSampler) IsOverestimateAvoidanceEnabled() bool {
|
||||
}
|
||||
|
||||
func (b *bandwidthSampler) OnPacketSent(
|
||||
sentTime monotime.Time,
|
||||
sentTime congestion.Time,
|
||||
packetNumber congestion.PacketNumber,
|
||||
bytes congestion.ByteCount,
|
||||
bytesInFlight congestion.ByteCount,
|
||||
@@ -596,7 +595,7 @@ func (b *bandwidthSampler) OnPacketSent(
|
||||
}
|
||||
|
||||
func (b *bandwidthSampler) OnCongestionEvent(
|
||||
ackTime monotime.Time,
|
||||
ackTime congestion.Time,
|
||||
ackedPackets []congestion.AckedPacketInfo,
|
||||
lostPackets []congestion.LostPacketInfo,
|
||||
maxBandwidth Bandwidth,
|
||||
@@ -759,7 +758,7 @@ func (b *bandwidthSampler) chooseA0Point(totalBytesAcked congestion.ByteCount, a
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *bandwidthSampler) onPacketAcknowledged(ackTime monotime.Time, packetNumber congestion.PacketNumber) bandwidthSample {
|
||||
func (b *bandwidthSampler) onPacketAcknowledged(ackTime congestion.Time, packetNumber congestion.PacketNumber) bandwidthSample {
|
||||
sample := newBandwidthSample()
|
||||
b.lastAckedPacket = packetNumber
|
||||
sentPacketPointer := b.connectionStateMap.GetEntry(packetNumber)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user