Compare commits

...
Author SHA1 Message Date
Wu ShuwenandGitHub a52db8f6ed common/buf/io.go: TimeoutWrapperReader returns ErrReadTimeout when timeout (#7327)
https://github.com/XTLS/Xray-core/pull/7327#issuecomment-6634105144

Fixes https://github.com/XTLS/Xray-core/issues/7318
2026-11-04 21:45:18 +00:00
MinyandGitHub 858c44f410 UdpHop finalmask: Avoid spurious error logs on closed hop sockets (#7319)
https://github.com/XTLS/Xray-core/pull/7319#issuecomment-6628950766
2026-11-04 11:01:44 +00:00
Hossin AsaadiandGitHub fff4acac52 WireGuard proxy: Give up on the endpoint lookup at the connection's deadline (#7323)
https://github.com/XTLS/Xray-core/pull/7323#issuecomment-6623261000
2026-11-04 10:53:08 +00:00
ad98ffd42b TUN inbound: Ignore dns IPv6 setup error when neither gateway nor autoSystemRoutingTable has an IPv6 entry on Windows (#7325)
https://github.com/XTLS/Xray-core/pull/7325#issuecomment-6619158295

---------

Co-authored-by: patterniha <71074308+patterniha@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:20:31 +00:00
MeowandGitHub 63ca2e0c1d Direct/Freedom outbound's finalRules: Add logLevel; blockDelay->delay; Apply default safe policy on MASQUE as well (#7329)
https://github.com/XTLS/Xray-core/pull/7281#issuecomment-6482226042
https://github.com/XTLS/Xray-core/issues/7240#issuecomment-6601762396
https://github.com/XTLS/Xray-core/pull/7324#issuecomment-6621358849
https://github.com/XTLS/Xray-core/pull/7329#issuecomment-6622083462
https://github.com/XTLS/Xray-core/pull/7329#issuecomment-6625219097
https://github.com/XTLS/Xray-core/pull/7329#issuecomment-6625305192
2026-11-04 08:34:58 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
ab88e0118f Bump github.com/klauspost/cpuid/v2 from 2.4.1 to 2.4.2 (#7328)
Bumps [github.com/klauspost/cpuid/v2](https://github.com/klauspost/cpuid) from 2.4.1 to 2.4.2.
- [Release notes](https://github.com/klauspost/cpuid/releases)
- [Commits](https://github.com/klauspost/cpuid/compare/v2.4.1...v2.4.2)

---
updated-dependencies:
- dependency-name: github.com/klauspost/cpuid/v2
  dependency-version: 2.4.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-04 02:41:21 +00:00
19 changed files with 174 additions and 74 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ RUN cat <<EOF >/tmp/usr/local/etc/xray/00_log.json
{
"log": {
"error": "/var/log/xray/error.log",
"loglevel": "warning",
"logLevel": "warning",
"access": "none",
"dnsLog": false
}
+1 -1
View File
@@ -20,7 +20,7 @@ RUN cat <<EOF >/tmp/usr/local/etc/xray/00_log.json
{
"log": {
"error": "/var/log/xray/error.log",
"loglevel": "warning",
"logLevel": "warning",
"access": "none",
"dnsLog": false
}
+1 -1
View File
@@ -73,7 +73,7 @@ func (r *TimeoutWrapperReader) ReadMultiBufferTimeout(duration time.Duration) (M
}
return r.mb, r.err
case <-timeout:
return nil, nil
return nil, ErrReadTimeout
}
}
+1 -1
View File
@@ -134,7 +134,7 @@ func TestMarshalConfigJson(t *testing.T) {
func getConfig() string {
return `{
"log": {
"loglevel": "debug"
"logLevel": "debug"
},
"stats": {},
"policy": {
+1 -1
View File
@@ -10,7 +10,7 @@ require (
github.com/google/go-cmp v0.7.0
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3
github.com/klauspost/cpuid/v2 v2.4.1
github.com/klauspost/cpuid/v2 v2.4.2
github.com/libp2p/go-nat v1.0.1-0.20250821073202-01afc089f138
github.com/miekg/dns v1.1.73
github.com/pelletier/go-toml v1.9.5
+2 -2
View File
@@ -35,8 +35,8 @@ github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=
github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
github.com/klauspost/cpuid/v2 v2.4.1 h1:I9KxadAkvo+QjtcEE2f2tyfEvmIla/T0m+14PrZNl0U=
github.com/klauspost/cpuid/v2 v2.4.1/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU=
github.com/klauspost/cpuid/v2 v2.4.2 h1:xRw7IZEsAgVCp6trS/DgWbTAqpixhmywgVgBjYGg1bQ=
github.com/klauspost/cpuid/v2 v2.4.2/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU=
github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0=
github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+33 -3
View File
@@ -9,6 +9,7 @@ import (
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/common/log"
xnet "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/proxy/freedom"
@@ -48,6 +49,8 @@ type FreedomFinalRuleConfig struct {
Network *NetworkList `json:"network"`
Port *PortList `json:"port"`
IP *StringList `json:"ip"`
Delay *Int32Range `json:"delay"`
LogLevel string `json:"logLevel"`
BlockDelay *Int32Range `json:"blockDelay"`
}
@@ -280,10 +283,37 @@ func (c *FreedomFinalRuleConfig) Build() (*freedom.FinalRuleConfig, error) {
}
if c.BlockDelay != nil {
rule.BlockDelay = &freedom.Range{
Min: uint64(c.BlockDelay.From),
Max: uint64(c.BlockDelay.To),
errors.PrintDeprecatedFeatureWarning("finalRules.blockDelay", "finalRules.delay")
c.Delay = c.BlockDelay
}
if rule.Action != freedom.RuleAction_Block {
if c.Delay != nil || c.LogLevel != "" {
return nil, errors.New("delay and logLevel are only supported for block finalRules")
}
return rule, nil
}
if c.Delay != nil {
rule.Delay = &freedom.Range{
Min: uint64(c.Delay.From),
Max: uint64(c.Delay.To),
}
}
switch strings.ToLower(c.LogLevel) {
case "", "info":
rule.LogLevel = log.Severity_Info.Enum()
case "none":
rule.LogLevel = log.Severity_Unknown.Enum()
case "debug":
rule.LogLevel = log.Severity_Debug.Enum()
case "warning":
rule.LogLevel = log.Severity_Warning.Enum()
case "error":
rule.LogLevel = log.Severity_Error.Enum()
default:
return nil, errors.New("unknown finalRules.logLevel: ", c.LogLevel)
}
return rule, nil
+5 -5
View File
@@ -4,6 +4,7 @@ import (
"testing"
"github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/common/log"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol"
. "github.com/xtls/xray-core/infra/conf"
@@ -46,7 +47,8 @@ func TestFreedomConfig(t *testing.T) {
"network": "tcp,udp",
"port": "53,443",
"ip": ["10.0.0.0/8", "2001:db8::/32"],
"blockDelay": "30-60"
"delay": "30-60",
"logLevel": "debug"
}, {
"action": "allow",
"network": ["udp"]
@@ -86,10 +88,8 @@ func TestFreedomConfig(t *testing.T) {
},
},
},
BlockDelay: &freedom.Range{
Min: 30,
Max: 60,
},
Delay: &freedom.Range{Min: 30, Max: 60},
LogLevel: log.Severity_Debug.Enum(),
},
{
Action: freedom.RuleAction_Allow,
+1 -1
View File
@@ -18,7 +18,7 @@ func DefaultLogConfig() *log.Config {
type LogConfig struct {
AccessLog string `json:"access"`
ErrorLog string `json:"error"`
LogLevel string `json:"loglevel"`
LogLevel string `json:"logLevel"`
DNSLog bool `json:"dnsLog"`
MaskAddress string `json:"maskAddress"`
}
+2 -2
View File
@@ -18,7 +18,7 @@ func TestLoaderError(t *testing.T) {
"log": {
// abcd
0,
"loglevel": "info"
"logLevel": "info"
}
}`,
Output: "line 4 char 6",
@@ -27,7 +27,7 @@ func TestLoaderError(t *testing.T) {
Input: `{
"log": {
// abcd
"loglevel": "info",
"logLevel": "info",
}
}`,
Output: "line 5 char 5",
+1 -1
View File
@@ -42,7 +42,7 @@ func TestXrayConfig(t *testing.T) {
Input: `{
"log": {
"access": "/var/log/xray/access.log",
"loglevel": "error",
"logLevel": "error",
"error": "/var/log/xray/error.log"
},
"inbounds": [{
+34 -20
View File
@@ -8,6 +8,7 @@ package freedom
import (
geodata "github.com/xtls/xray-core/common/geodata"
log "github.com/xtls/xray-core/common/log"
net "github.com/xtls/xray-core/common/net"
protocol "github.com/xtls/xray-core/common/protocol"
internet "github.com/xtls/xray-core/transport/internet"
@@ -357,7 +358,8 @@ type FinalRuleConfig struct {
Networks []net.Network `protobuf:"varint,2,rep,packed,name=networks,proto3,enum=xray.common.net.Network" json:"networks,omitempty"`
PortList *net.PortList `protobuf:"bytes,3,opt,name=port_list,json=portList,proto3" json:"port_list,omitempty"`
Ip []*geodata.IPRule `protobuf:"bytes,4,rep,name=ip,proto3" json:"ip,omitempty"`
BlockDelay *Range `protobuf:"bytes,5,opt,name=block_delay,json=blockDelay,proto3" json:"block_delay,omitempty"`
Delay *Range `protobuf:"bytes,5,opt,name=delay,proto3" json:"delay,omitempty"`
LogLevel *log.Severity `protobuf:"varint,6,opt,name=log_level,json=logLevel,proto3,enum=xray.common.log.Severity,oneof" json:"log_level,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -420,13 +422,20 @@ func (x *FinalRuleConfig) GetIp() []*geodata.IPRule {
return nil
}
func (x *FinalRuleConfig) GetBlockDelay() *Range {
func (x *FinalRuleConfig) GetDelay() *Range {
if x != nil {
return x.BlockDelay
return x.Delay
}
return nil
}
func (x *FinalRuleConfig) GetLogLevel() log.Severity {
if x != nil && x.LogLevel != nil {
return *x.LogLevel
}
return log.Severity(0)
}
type Config struct {
state protoimpl.MessageState `protogen:"open.v1"`
DomainStrategy internet.DomainStrategy `protobuf:"varint,1,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.transport.internet.DomainStrategy" json:"domain_strategy,omitempty"`
@@ -523,7 +532,7 @@ var File_proxy_freedom_config_proto protoreflect.FileDescriptor
const file_proxy_freedom_config_proto_rawDesc = "" +
"\n" +
"\x1aproxy/freedom/config.proto\x12\x12xray.proxy.freedom\x1a!common/protocol/server_spec.proto\x1a\x1ftransport/internet/config.proto\x1a\x15common/net/port.proto\x1a\x18common/net/network.proto\x1a\x1bcommon/geodata/geodat.proto\"S\n" +
"\x1aproxy/freedom/config.proto\x12\x12xray.proxy.freedom\x1a!common/protocol/server_spec.proto\x1a\x1ftransport/internet/config.proto\x1a\x15common/net/port.proto\x1a\x18common/net/network.proto\x1a\x1bcommon/geodata/geodat.proto\x1a\x14common/log/log.proto\"S\n" +
"\x13DestinationOverride\x12<\n" +
"\x06server\x18\x01 \x01(\v2$.xray.common.protocol.ServerEndpointR\x06server\"\x98\x02\n" +
"\bFragment\x12!\n" +
@@ -549,14 +558,16 @@ const file_proxy_freedom_config_proto_rawDesc = "" +
"\bapply_to\x18\x06 \x01(\tR\aapplyTo\"+\n" +
"\x05Range\x12\x10\n" +
"\x03min\x18\x01 \x01(\x04R\x03min\x12\x10\n" +
"\x03max\x18\x02 \x01(\x04R\x03max\"\xa0\x02\n" +
"\x03max\x18\x02 \x01(\x04R\x03max\"\xe0\x02\n" +
"\x0fFinalRuleConfig\x126\n" +
"\x06action\x18\x01 \x01(\x0e2\x1e.xray.proxy.freedom.RuleActionR\x06action\x124\n" +
"\bnetworks\x18\x02 \x03(\x0e2\x18.xray.common.net.NetworkR\bnetworks\x126\n" +
"\tport_list\x18\x03 \x01(\v2\x19.xray.common.net.PortListR\bportList\x12+\n" +
"\x02ip\x18\x04 \x03(\v2\x1b.xray.common.geodata.IPRuleR\x02ip\x12:\n" +
"\vblock_delay\x18\x05 \x01(\v2\x19.xray.proxy.freedom.RangeR\n" +
"blockDelay\"\xaf\x03\n" +
"\x02ip\x18\x04 \x03(\v2\x1b.xray.common.geodata.IPRuleR\x02ip\x12/\n" +
"\x05delay\x18\x05 \x01(\v2\x19.xray.proxy.freedom.RangeR\x05delay\x12;\n" +
"\tlog_level\x18\x06 \x01(\x0e2\x19.xray.common.log.SeverityH\x00R\blogLevel\x88\x01\x01B\f\n" +
"\n" +
"_log_level\"\xaf\x03\n" +
"\x06Config\x12P\n" +
"\x0fdomain_strategy\x18\x01 \x01(\x0e2'.xray.transport.internet.DomainStrategyR\x0edomainStrategy\x12Z\n" +
"\x14destination_override\x18\x03 \x01(\v2'.xray.proxy.freedom.DestinationOverrideR\x13destinationOverride\x12\x1d\n" +
@@ -599,7 +610,8 @@ var file_proxy_freedom_config_proto_goTypes = []any{
(net.Network)(0), // 8: xray.common.net.Network
(*net.PortList)(nil), // 9: xray.common.net.PortList
(*geodata.IPRule)(nil), // 10: xray.common.geodata.IPRule
(internet.DomainStrategy)(0), // 11: xray.transport.internet.DomainStrategy
(log.Severity)(0), // 11: xray.common.log.Severity
(internet.DomainStrategy)(0), // 12: xray.transport.internet.DomainStrategy
}
var file_proxy_freedom_config_proto_depIdxs = []int32{
7, // 0: xray.proxy.freedom.DestinationOverride.server:type_name -> xray.common.protocol.ServerEndpoint
@@ -607,17 +619,18 @@ var file_proxy_freedom_config_proto_depIdxs = []int32{
8, // 2: xray.proxy.freedom.FinalRuleConfig.networks:type_name -> xray.common.net.Network
9, // 3: xray.proxy.freedom.FinalRuleConfig.port_list:type_name -> xray.common.net.PortList
10, // 4: xray.proxy.freedom.FinalRuleConfig.ip:type_name -> xray.common.geodata.IPRule
4, // 5: xray.proxy.freedom.FinalRuleConfig.block_delay:type_name -> xray.proxy.freedom.Range
11, // 6: xray.proxy.freedom.Config.domain_strategy:type_name -> xray.transport.internet.DomainStrategy
1, // 7: xray.proxy.freedom.Config.destination_override:type_name -> xray.proxy.freedom.DestinationOverride
2, // 8: xray.proxy.freedom.Config.fragment:type_name -> xray.proxy.freedom.Fragment
3, // 9: xray.proxy.freedom.Config.noises:type_name -> xray.proxy.freedom.Noise
5, // 10: xray.proxy.freedom.Config.final_rules:type_name -> xray.proxy.freedom.FinalRuleConfig
11, // [11:11] is the sub-list for method output_type
11, // [11:11] is the sub-list for method input_type
11, // [11:11] is the sub-list for extension type_name
11, // [11:11] is the sub-list for extension extendee
0, // [0:11] is the sub-list for field type_name
4, // 5: xray.proxy.freedom.FinalRuleConfig.delay:type_name -> xray.proxy.freedom.Range
11, // 6: xray.proxy.freedom.FinalRuleConfig.log_level:type_name -> xray.common.log.Severity
12, // 7: xray.proxy.freedom.Config.domain_strategy:type_name -> xray.transport.internet.DomainStrategy
1, // 8: xray.proxy.freedom.Config.destination_override:type_name -> xray.proxy.freedom.DestinationOverride
2, // 9: xray.proxy.freedom.Config.fragment:type_name -> xray.proxy.freedom.Fragment
3, // 10: xray.proxy.freedom.Config.noises:type_name -> xray.proxy.freedom.Noise
5, // 11: xray.proxy.freedom.Config.final_rules:type_name -> xray.proxy.freedom.FinalRuleConfig
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_proxy_freedom_config_proto_init() }
@@ -625,6 +638,7 @@ func file_proxy_freedom_config_proto_init() {
if File_proxy_freedom_config_proto != nil {
return
}
file_proxy_freedom_config_proto_msgTypes[4].OneofWrappers = []any{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
+3 -1
View File
@@ -11,6 +11,7 @@ import "transport/internet/config.proto";
import "common/net/port.proto";
import "common/net/network.proto";
import "common/geodata/geodat.proto";
import "common/log/log.proto";
message DestinationOverride {
xray.common.protocol.ServerEndpoint server = 1;
@@ -51,7 +52,8 @@ message FinalRuleConfig {
repeated xray.common.net.Network networks = 2;
xray.common.net.PortList port_list = 3;
repeated xray.common.geodata.IPRule ip = 4;
Range block_delay = 5;
Range delay = 5;
optional xray.common.log.Severity log_level = 6;
}
message Config {
+42 -20
View File
@@ -15,6 +15,7 @@ import (
"github.com/xtls/xray-core/common/dice"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/common/log"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/platform"
"github.com/xtls/xray-core/common/retry"
@@ -72,23 +73,26 @@ func init() {
}
defaultBlockPrivateRule = &FinalRule{
action: RuleAction_Block,
network: allNetworks,
ip: geodata.GetPrivateIPMatcher(),
action: RuleAction_Block,
network: allNetworks,
ip: geodata.GetPrivateIPMatcher(),
logLevel: log.Severity_Warning.Enum(),
}
defaultBlockAllRule = &FinalRule{
action: RuleAction_Block,
network: allNetworks,
action: RuleAction_Block,
network: allNetworks,
logLevel: log.Severity_Warning.Enum(),
}
}
type FinalRule struct {
action RuleAction
network [8]bool
port net.MemoryPortList
ip geodata.IPMatcher
blockDelay *Range
action RuleAction
network [8]bool
port net.MemoryPortList
ip geodata.IPMatcher
delay *Range
logLevel *log.Severity
}
// Handler handles Freedom connections.
@@ -102,8 +106,9 @@ type Handler struct {
func buildFinalRule(config *FinalRuleConfig) (*FinalRule, error) {
rule := &FinalRule{
action: config.GetAction(),
blockDelay: config.GetBlockDelay(),
action: config.GetAction(),
delay: config.GetDelay(),
logLevel: config.LogLevel,
}
if len(config.Networks) == 0 {
@@ -164,7 +169,7 @@ func getDefaultFinalRule(inbound *session.Inbound) *FinalRule {
switch inbound.Name {
case "vless-reverse":
return defaultBlockAllRule
case "vless", "vmess", "trojan", "hysteria", "wireguard":
case "vless", "vmess", "trojan", "hysteria", "wireguard", "masque":
return defaultBlockPrivateRule
default:
if strings.HasPrefix(inbound.Name, "shadowsocks") {
@@ -212,12 +217,12 @@ func (h *Handler) policy() policy.Session {
return p
}
func (h *Handler) blockDelay(rule *FinalRule) time.Duration {
func (r *FinalRule) blockDuration() time.Duration {
min := uint64(30)
max := uint64(90)
if rule.blockDelay != nil {
min = rule.blockDelay.Min
max = rule.blockDelay.Max
if r.delay != nil {
min = r.delay.Min
max = r.delay.Max
}
span := max - min
if max < min {
@@ -226,9 +231,26 @@ func (h *Handler) blockDelay(rule *FinalRule) time.Duration {
return time.Duration(min+uint64(dice.Roll(int(span+1)))) * time.Second
}
func (r *FinalRule) log(ctx context.Context, msg ...interface{}) {
level := log.Severity_Info
if r.logLevel != nil {
level = *r.logLevel
}
switch level {
case log.Severity_Debug:
errors.LogDebug(ctx, msg...)
case log.Severity_Info:
errors.LogInfo(ctx, msg...)
case log.Severity_Warning:
errors.LogWarning(ctx, msg...)
case log.Severity_Error:
errors.LogError(ctx, msg...)
}
}
func (h *Handler) blackhole(ctx context.Context, input buf.Reader, output buf.Writer, rule *FinalRule, dest *net.Destination) error {
delay := h.blockDelay(rule)
errors.LogInfo(ctx, "blocked target: ", *dest, ", blackholing connection for ", delay)
delay := rule.blockDuration()
rule.log(ctx, "target blocked by a finalrule: ", *dest, ", blackholing connection for ", delay)
defer common.Close(output)
done := make(chan struct{})
go func() { // a DispatchLink inbound's reader ignores Interrupt, so don't wait for this copy
@@ -240,7 +262,7 @@ func (h *Handler) blackhole(ctx context.Context, input buf.Reader, output buf.Wr
case <-time.After(delay):
common.Interrupt(input)
common.Interrupt(output)
errors.LogInfo(ctx, "closed blackholed connection to blocked target: ", *dest)
rule.log(ctx, "closed blackholed connection to blocked target: ", *dest)
}
return nil
}
+6
View File
@@ -201,6 +201,12 @@ startOver:
}
err = t.luid.SetDNS(family, dns, nil)
if err != nil {
// Skip IPv6 the config does not ask for also when setting its DNS
// servers fails, as reported on Windows 11 24H2 with IPv6 disabled
// (Access is denied).
if family == windows.AF_INET6 && !address6 && !route6 {
continue
}
firstErr = errors.New("unable to set DNS").Base(err)
if err == windows.ERROR_NOT_FOUND {
goto startOver
+6 -2
View File
@@ -17,7 +17,7 @@ import (
type bind struct {
ctx context.Context
resolveFunc func(host string) (net.IP, error)
resolveFunc func(ctx context.Context, host string) (net.IP, error)
listenFunc func(ctx context.Context) (net.PacketConn, error)
downFunc func() error
reserved []byte
@@ -143,7 +143,11 @@ func (b *bind) ParseEndpoint(s string) (conn.Endpoint, error) {
if port < 0 || port > 65535 {
return nil, errors.New("invalid port " + sport)
}
ip, err := b.resolveFunc(host)
// up sets the connection's context right before it hands over the endpoint
b.mu.Lock()
ctx := b.ctx
b.mu.Unlock()
ip, err := b.resolveFunc(ctx, host)
if err != nil {
return nil, err
}
+24 -3
View File
@@ -323,12 +323,12 @@ func (h *Handler) up(ctx context.Context) error {
return h.dev.Up()
}
func MakeResolveLocal(d dns.Client, streamSettings *internet.MemoryStreamConfig) func(host string) (net.IP, error) {
return func(host string) (net.IP, error) {
func MakeResolveLocal(d dns.Client, streamSettings *internet.MemoryStreamConfig) func(ctx context.Context, host string) (net.IP, error) {
return func(ctx context.Context, host string) (net.IP, error) {
if ip := net.ParseIP(host); ip != nil {
return ip, nil
}
ips, _, err := d.LookupIP(host, dns.IPOption{IPv4Enable: true, IPv6Enable: true})
ips, err := lookupIPContext(ctx, d, host)
if err != nil {
return nil, err
}
@@ -368,6 +368,27 @@ func MakeResolveLocal(d dns.Client, streamSettings *internet.MemoryStreamConfig)
}
}
func lookupIPContext(ctx context.Context, d dns.Client, host string) ([]net.IP, error) {
if err := ctx.Err(); err != nil {
return nil, err
}
type result struct {
ips []net.IP
err error
}
done := make(chan result, 1)
go func() {
ips, _, err := d.LookupIP(host, dns.IPOption{IPv4Enable: true, IPv6Enable: true})
done <- result{ips, err}
}()
select {
case r := <-done:
return r.ips, r.err
case <-ctx.Done():
return nil, ctx.Err()
}
}
type UDPConnClient struct {
net.PacketConn
Dest *net.UDPAddr
+8 -8
View File
@@ -661,7 +661,7 @@ func TestVlessRealityFingerprints(t *testing.T) {
wg.Wait()
}
// A blocked connection must be closed after blockDelay even while the client keeps it open and sends nothing.
// A blocked connection must close after finalRules.delay even while the client keeps it open and sends nothing.
func TestVlessBlockDelay(t *testing.T) {
tcpServer := tcp.Server{
MsgProcessor: xor,
@@ -671,7 +671,7 @@ func TestVlessBlockDelay(t *testing.T) {
defer tcpServer.Close()
for _, delay := range []uint64{0, 1} {
t.Run(fmt.Sprint("blockDelay=", delay), func(t *testing.T) {
t.Run(fmt.Sprint("delay=", delay), func(t *testing.T) {
userID := protocol.NewID(uuid.New())
serverPort := tcp.PickPort()
serverConfig := &core.Config{
@@ -697,8 +697,8 @@ func TestVlessBlockDelay(t *testing.T) {
ProxySettings: serial.ToTypedMessage(&freedom.Config{
FinalRules: []*freedom.FinalRuleConfig{
{
Action: freedom.RuleAction_Block,
BlockDelay: &freedom.Range{Min: delay, Max: delay},
Action: freedom.RuleAction_Block,
Delay: &freedom.Range{Min: delay, Max: delay},
},
},
}),
@@ -726,15 +726,15 @@ func TestVlessBlockDelay(t *testing.T) {
Port: dest.Port,
}, &encoding.Addons{}))
blockDelay := time.Duration(delay) * time.Second
common.Must(conn.SetReadDeadline(time.Now().Add(blockDelay + 5*time.Second)))
blockDuration := time.Duration(delay) * time.Second
common.Must(conn.SetReadDeadline(time.Now().Add(blockDuration + 5*time.Second)))
_, err = io.Copy(io.Discard, conn)
elapsed := time.Since(start)
if err, ok := err.(net.Error); ok && err.Timeout() {
t.Fatalf("connection is still open after %v", elapsed)
}
if elapsed < blockDelay {
t.Fatalf("connection is closed after %v, before blockDelay", elapsed)
if elapsed < blockDuration {
t.Fatalf("connection is closed after %v, before finalRules.delay", elapsed)
}
})
}
+2 -1
View File
@@ -6,6 +6,7 @@ import (
goerrors "errors"
"io"
mrand "math/rand"
stdnet "net"
"net/netip"
"sync"
"time"
@@ -164,7 +165,7 @@ func (c *udpHopConn) recv(conn net.PacketConn) {
n, addr, err := conn.ReadFrom(p)
if err != nil {
pool.Put(p[:cap(p)])
if c.closed() {
if c.closed() || goerrors.Is(err, stdnet.ErrClosed) {
return
}
var netErr net.Error