Provide better error message when self-check failed

By connecting to all upstreams when self-check failed, so it's clearer
to users what causes self-check failed.
This commit is contained in:
Cuong Manh Le
2024-03-28 18:28:38 +07:00
committed by Cuong Manh Le
parent 52954b8ceb
commit 1a8c1ec73d
3 changed files with 37 additions and 10 deletions

View File

@@ -369,7 +369,7 @@ func initCLI() {
}
// If ctrld service is running but selfCheckStatus failed, it could be related
// to user's system firewall configuration, notice users about it.
if status == service.StatusRunning {
if status == service.StatusRunning && err == nil {
_, _ = mainLog.Load().Write(marker)
mainLog.Load().Write([]byte(`ctrld service was running, but a DNS query could not be sent to its listener`))
mainLog.Load().Write([]byte(`Please check your system firewall if it is configured to block/intercept/redirect DNS queries`))
@@ -1705,6 +1705,12 @@ func selfCheckStatus(s service.Service) (bool, service.Status, error) {
bo.BackOff(ctx, fmt.Errorf("ExchangeContext: %w", exErr))
}
mainLog.Load().Debug().Msgf("self-check against %q failed", domain)
// Ping all upstreams to provide better error message to users.
for name, uc := range cfg.Upstream {
if err := uc.ErrorPing(); err != nil {
mainLog.Load().Err(err).Msgf("failed to connect to upstream.%s, endpoint: %s", name, uc.Endpoint)
}
}
lc := cfg.FirstListener()
addr := net.JoinHostPort(lc.IP, strconv.Itoa(lc.Port))
marker := strings.Repeat("=", 32)

View File

@@ -962,6 +962,7 @@ func isWanClient(na net.Addr) bool {
// resolveInternalDomainTestQuery resolves internal test domain query, returning the answer to the caller.
func resolveInternalDomainTestQuery(ctx context.Context, domain string, m *dns.Msg) *dns.Msg {
ctrld.Log(ctx, mainLog.Load().Debug(), "internal domain test query")
q := m.Question[0]
answer := new(dns.Msg)
rrStr := fmt.Sprintf("%s A %s", domain, net.IPv4zero)

View File

@@ -521,35 +521,55 @@ func (uc *UpstreamConfig) newDOHTransport(addrs []string) *http.Transport {
// Ping warms up the connection to DoH/DoH3 upstream.
func (uc *UpstreamConfig) Ping() {
_ = uc.ping()
}
// ErrorPing is like Ping, but return an error if any.
func (uc *UpstreamConfig) ErrorPing() error {
return uc.ping()
}
func (uc *UpstreamConfig) ping() error {
switch uc.Type {
case ResolverTypeDOH, ResolverTypeDOH3:
default:
return
return nil
}
ping := func(t http.RoundTripper) {
ping := func(t http.RoundTripper) error {
if t == nil {
return
return nil
}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
req, _ := http.NewRequestWithContext(ctx, "HEAD", uc.Endpoint, nil)
resp, _ := t.RoundTrip(req)
if resp == nil {
return
req, err := http.NewRequestWithContext(ctx, "HEAD", uc.Endpoint, nil)
if err != nil {
return err
}
resp, err := t.RoundTrip(req)
if err != nil {
return err
}
defer resp.Body.Close()
_, _ = io.Copy(io.Discard, resp.Body)
return nil
}
for _, typ := range []uint16{dns.TypeA, dns.TypeAAAA} {
switch uc.Type {
case ResolverTypeDOH:
ping(uc.dohTransport(typ))
if err := ping(uc.dohTransport(typ)); err != nil {
return err
}
case ResolverTypeDOH3:
ping(uc.doh3Transport(typ))
if err := ping(uc.doh3Transport(typ)); err != nil {
return err
}
}
}
return nil
}
func (uc *UpstreamConfig) isControlD() bool {