diff --git a/common/netutils/ip46.go b/common/netutils/ip46.go index f246f6e..a9498f0 100644 --- a/common/netutils/ip46.go +++ b/common/netutils/ip46.go @@ -10,6 +10,7 @@ import ( "errors" "fmt" "github.com/mzz2017/softwind/netproxy" + "github.com/sirupsen/logrus" "golang.org/x/net/dns/dnsmessage" "net/netip" "sync" @@ -21,6 +22,17 @@ type Ip46 struct { } func ResolveIp46(ctx context.Context, dialer netproxy.Dialer, dns netip.AddrPort, host string, tcp bool, race bool) (ipv46 *Ip46, err error) { + var log *logrus.Logger + if _log := ctx.Value("logger"); _log != nil { + log = _log.(*logrus.Logger) + defer func() { + if err == nil { + log.Tracef("ResolveIp46 %v using %v: A(%v) AAAA(%v)", host, systemDns, ipv46.Ip4, ipv46.Ip6) + } else { + log.Tracef("ResolveIp46 %v using %v: %v", host, systemDns, err) + } + }() + } var wg sync.WaitGroup wg.Add(2) var err4, err6 error diff --git a/component/outbound/dialer/alive_dialer_set.go b/component/outbound/dialer/alive_dialer_set.go index fbd13a5..f7285a8 100644 --- a/component/outbound/dialer/alive_dialer_set.go +++ b/component/outbound/dialer/alive_dialer_set.go @@ -7,9 +7,9 @@ package dialer import ( "fmt" + "github.com/daeuniverse/dae/common/consts" "github.com/mzz2017/softwind/pkg/fastrand" "github.com/sirupsen/logrus" - "github.com/daeuniverse/dae/common/consts" "strings" "sync" "time" @@ -138,10 +138,8 @@ func (a *AliveDialerSet) NotifyLatencyChange(dialer *Dialer, alive bool) { // Dialer: not alive -> alive. if index == -NotAlive { a.log.WithFields(logrus.Fields{ - "dialer": dialer.property.Name, - "group": a.dialerGroupName, - "network": a.CheckTyp.StringWithoutDns(), - }).Infoln("NOT ALIVE -> ALIVE:") + "group": a.dialerGroupName, + }).Infof("[NOT ALIVE --%v-> ALIVE] %v", a.CheckTyp.String(), dialer.property.Name) } a.dialerToIndex[dialer] = len(a.inorderedAliveDialerSet) a.inorderedAliveDialerSet = append(a.inorderedAliveDialerSet, dialer) @@ -151,10 +149,8 @@ func (a *AliveDialerSet) NotifyLatencyChange(dialer *Dialer, alive bool) { if index >= 0 { // Dialer: alive -> not alive. a.log.WithFields(logrus.Fields{ - "dialer": dialer.property.Name, - "group": a.dialerGroupName, - "network": a.CheckTyp.StringWithoutDns(), - }).Infoln("ALIVE -> NOT ALIVE:") + "group": a.dialerGroupName, + }).Infof("[ALIVE --%v-> NOT ALIVE] %v", a.CheckTyp.String(), dialer.property.Name) // Remove the dialer from inorderedAliveDialerSet. if index >= len(a.inorderedAliveDialerSet) { a.log.Panicf("index:%v >= len(a.inorderedAliveDialerSet):%v", index, len(a.inorderedAliveDialerSet)) diff --git a/component/outbound/dialer/connectivity_check.go b/component/outbound/dialer/connectivity_check.go index db5b244..2c373d7 100644 --- a/component/outbound/dialer/connectivity_check.go +++ b/component/outbound/dialer/connectivity_check.go @@ -167,6 +167,7 @@ func ParseCheckDnsOption(ctx context.Context, dnsHostPort string) (opt *CheckDns type TcpCheckOptionRaw struct { opt *TcpCheckOption mu sync.Mutex + Log *logrus.Logger Raw string } @@ -176,6 +177,7 @@ func (c *TcpCheckOptionRaw) Option() (opt *TcpCheckOption, err error) { if c.opt == nil { ctx, cancel := context.WithTimeout(context.TODO(), 10*time.Second) defer cancel() + ctx = context.WithValue(ctx, "logger", c.Log) tcpCheckOption, err := ParseTcpCheckOption(ctx, c.Raw) if err != nil { return nil, fmt.Errorf("failed to parse tcp_check_url: %w", err) @@ -238,6 +240,7 @@ func (d *Dialer) aliveBackground() { if !opt.Ip4.IsValid() { d.Log.WithFields(logrus.Fields{ "link": d.TcpCheckOptionRaw.Raw, + "dialer": d.property.Name, "network": typ.String(), }).Debugln("Skip check due to no DNS record.") return false, nil @@ -259,6 +262,7 @@ func (d *Dialer) aliveBackground() { if !opt.Ip6.IsValid() { d.Log.WithFields(logrus.Fields{ "link": d.TcpCheckOptionRaw.Raw, + "dialer": d.property.Name, "network": typ.String(), }).Debugln("Skip check due to no DNS record.") return false, nil @@ -280,6 +284,7 @@ func (d *Dialer) aliveBackground() { if !opt.Ip4.IsValid() { d.Log.WithFields(logrus.Fields{ "link": d.CheckDnsOptionRaw.Raw, + "dialer": d.property.Name, "network": typ.String(), }).Debugln("Skip check due to no DNS record.") return false, nil @@ -301,6 +306,7 @@ func (d *Dialer) aliveBackground() { if !opt.Ip6.IsValid() { d.Log.WithFields(logrus.Fields{ "link": d.CheckDnsOptionRaw.Raw, + "dialer": d.property.Name, "network": typ.String(), }).Debugln("Skip check due to no DNS record.") return false, nil @@ -523,9 +529,16 @@ func (d *Dialer) HttpCheck(ctx context.Context, u *netutils.URL, ip netip.Addr) defer resp.Body.Close() // Judge the status code. if page := path.Base(req.URL.Path); strings.HasPrefix(page, "generate_") { - return strconv.Itoa(resp.StatusCode) == strings.TrimPrefix(page, "generate_"), nil + if strconv.Itoa(resp.StatusCode) != strings.TrimPrefix(page, "generate_") { + return false, fmt.Errorf("unexpected status code: %v", resp.StatusCode) + } + return true, nil + } else { + if resp.StatusCode < 200 || resp.StatusCode >= 500 { + return false, fmt.Errorf("bad status code: %v", resp.StatusCode) + } + return true, nil } - return resp.StatusCode >= 200 && resp.StatusCode < 500, nil } func (d *Dialer) DnsCheck(ctx context.Context, dns netip.AddrPort, tcp bool) (ok bool, err error) { diff --git a/component/outbound/dialer_group_test.go b/component/outbound/dialer_group_test.go index 6f33e4a..e770b94 100644 --- a/component/outbound/dialer_group_test.go +++ b/component/outbound/dialer_group_test.go @@ -6,10 +6,10 @@ package outbound import ( - "github.com/mzz2017/softwind/pkg/fastrand" "github.com/daeuniverse/dae/common/consts" "github.com/daeuniverse/dae/component/outbound/dialer" "github.com/daeuniverse/dae/pkg/logger" + "github.com/mzz2017/softwind/pkg/fastrand" "testing" "time" ) @@ -43,7 +43,7 @@ func TestDialerGroup_Select_Fixed(t *testing.T) { g := NewDialerGroup(option, "test-group", dialers, DialerSelectionPolicy{ Policy: consts.DialerSelectionPolicy_Fixed, FixedIndex: fixedIndex, - }, func(alive bool, networkType *dialer.NetworkType) {}) + }, func(alive bool, networkType *dialer.NetworkType, isInit bool) {}) for i := 0; i < 10; i++ { d, _, err := g.Select(TestNetworkType) if err != nil { @@ -90,7 +90,7 @@ func TestDialerGroup_Select_MinLastLatency(t *testing.T) { } g := NewDialerGroup(option, "test-group", dialers, DialerSelectionPolicy{ Policy: consts.DialerSelectionPolicy_MinLastLatency, - }, func(alive bool, networkType *dialer.NetworkType) {}) + }, func(alive bool, networkType *dialer.NetworkType, isInit bool) {}) // Test 1000 times. for i := 0; i < 1000; i++ { @@ -155,7 +155,7 @@ func TestDialerGroup_Select_Random(t *testing.T) { } g := NewDialerGroup(option, "test-group", dialers, DialerSelectionPolicy{ Policy: consts.DialerSelectionPolicy_Random, - }, func(alive bool, networkType *dialer.NetworkType) {}) + }, func(alive bool, networkType *dialer.NetworkType, isInit bool) {}) count := make([]int, len(dialers)) for i := 0; i < 100; i++ { d, _, err := g.Select(TestNetworkType) @@ -195,7 +195,7 @@ func TestDialerGroup_SetAlive(t *testing.T) { } g := NewDialerGroup(option, "test-group", dialers, DialerSelectionPolicy{ Policy: consts.DialerSelectionPolicy_Random, - }, func(alive bool, networkType *dialer.NetworkType) {}) + }, func(alive bool, networkType *dialer.NetworkType, isInit bool) {}) zeroTarget := 3 g.MustGetAliveDialerSet(TestNetworkType).NotifyLatencyChange(dialers[zeroTarget], false) count := make([]int, len(dialers)) diff --git a/component/routing/domain_matcher/ahocorasick_slimtrie_test.go b/component/routing/domain_matcher/ahocorasick_slimtrie_test.go index 8c85045..4824da7 100644 --- a/component/routing/domain_matcher/ahocorasick_slimtrie_test.go +++ b/component/routing/domain_matcher/ahocorasick_slimtrie_test.go @@ -6,8 +6,8 @@ package domain_matcher import ( - "github.com/sirupsen/logrus" "github.com/daeuniverse/dae/common/consts" + "github.com/sirupsen/logrus" "golang.org/x/exp/slices" "math/rand" "testing" @@ -21,7 +21,7 @@ func TestAhocorasickSlimtrie(t *testing.T) { t.Fatal(err) } bf := NewBruteforce(consts.MaxMatchSetLen) - actrie := NewAhocorasickSlimtrie(consts.MaxMatchSetLen) + actrie := NewAhocorasickSlimtrie(logrus.StandardLogger(), consts.MaxMatchSetLen) for _, domains := range simulatedDomainSet { bf.AddSet(domains.RuleIndex, domains.Domains, domains.Key) actrie.AddSet(domains.RuleIndex, domains.Domains, domains.Key) diff --git a/control/control_plane.go b/control/control_plane.go index b475007..63d7ec4 100644 --- a/control/control_plane.go +++ b/control/control_plane.go @@ -220,7 +220,7 @@ func NewControlPlane( } option := &dialer.GlobalOption{ Log: log, - TcpCheckOptionRaw: dialer.TcpCheckOptionRaw{Raw: global.TcpCheckUrl}, + TcpCheckOptionRaw: dialer.TcpCheckOptionRaw{Raw: global.TcpCheckUrl, Log: log}, CheckDnsOptionRaw: dialer.CheckDnsOptionRaw{Raw: global.UdpCheckDns}, CheckInterval: global.CheckInterval, CheckTolerance: global.CheckTolerance, diff --git a/control/dns_control.go b/control/dns_control.go index ea0edc9..8f74291 100644 --- a/control/dns_control.go +++ b/control/dns_control.go @@ -312,10 +312,11 @@ func (c *DnsController) Handle_(dnsMessage *dnsmessage.Message, req *udpRequest) return nil } - // Make sure there is additional record OPT in the request to filter DNS rush-answer in the response process. - // Because rush-answer has no resp OPT. We can distinguish them from multiple responses. - // Note that additional record OPT may not be supported by home router either. - _, _ = EnsureAdditionalOpt(dnsMessage, true) + //// NOTICE: Rush-answer detector was removed because it does not always work in all districts. + //// Make sure there is additional record OPT in the request to filter DNS rush-answer in the response process. + //// Because rush-answer has no resp OPT. We can distinguish them from multiple responses. + //// Note that additional record OPT may not be supported by home router either. + //_, _ = EnsureAdditionalOpt(dnsMessage, true) // Route request. upstream, err := c.routing.RequestSelect(dnsMessage) @@ -382,13 +383,17 @@ func (c *DnsController) dialSend(req *udpRequest, data []byte, id uint16, upstre // dnsRespHandler caches dns response and check rush answers. dnsRespHandler := c.DnsRespHandlerFactory(func(from netip.AddrPort) bool { - // We only validate rush-ans when outbound is direct and pkt does not send to a home device. - // Because additional record OPT may not be supported by home router. - // So se should trust home devices even if they make rush-answer (or looks like). - return dialArgument.bestDialer.Property().Name == "direct" && - !from.Addr().IsPrivate() && - !from.Addr().IsLoopback() && - !from.Addr().IsUnspecified() + //// NOTICE: Rush-answer detector was removed because it does not always work in all districts. + //// We only validate rush-ans when outbound is direct and pkt does not send to a home device. + //// Because additional record OPT may not be supported by home router. + //// So se should trust home devices even if they make rush-answer (or looks like). + //return dialArgument.bestDialer.Property().Name == "direct" && + // !from.Addr().IsPrivate() && + // !from.Addr().IsLoopback() && + // !from.Addr().IsUnspecified() + + // Do not validate rush-answer. + return false }) // Dial and send. var respMsg *dnsmessage.Message @@ -456,12 +461,12 @@ func (c *DnsController) dialSend(req *udpRequest, data []byte, id uint16, upstre if err != nil { return fmt.Errorf("failed to read from: %v (dialer: %v): %w", dialArgument.bestTarget, dialArgument.bestDialer.Property().Name, err) } - cancelDnsReqCtx() respMsg, err = dnsRespHandler(respBuf[:n], dialArgument.bestTarget) if err != nil { return err } if respMsg != nil { + cancelDnsReqCtx() break } }