shadow1ng/fscan · error

service_target_unreachable: %w

Error message

service_target_unreachable: %w

What it means

Before spending the full credential list, TestCredentialsConcurrently performs a TCP pre-check by dialing testConfig.TargetAddr with a short-lived context. If that dial fails, the target is deemed unreachable and the whole test is aborted with this wrapped error (%w wraps the underlying net error). This saves all worker goroutines from timing out one by one against a dead host.

Source

Thrown at plugins/services/credential_tester.go:227

			Service: serviceName,
			Error:   fmt.Errorf("%s", i18n.GetText("service_no_test_creds")),
		}
	}
	testConfig = normalizeConcurrentTestConfig(testConfig)

	// TCP 预检:快速验证目标可达,避免对不可达目标浪费全部凭据尝试
	// 代理模式下跳过:net.DialTimeout 直连无法到达代理后的内网目标
	if testConfig.TargetAddr != "" && !testConfig.UseProxy {
		dialCtx, dialCancel := context.WithTimeout(ctx, 3*time.Second)
		defer dialCancel()

		var dialer net.Dialer
		preConn, err := dialer.DialContext(dialCtx, "tcp", testConfig.TargetAddr)
		if err != nil {
			return &ScanResult{
				Success: false,
				Service: serviceName,
				Error:   fmt.Errorf(i18n.Tr("service_target_unreachable", "%w"), err),
			}
		}
		_ = preConn.Close()
	}

	// 调整并发数
	concurrency := testConfig.Concurrency
	if concurrency > len(credentials) {
		concurrency = len(credentials)
	}

	// 创建可取消的 context - 找到成功后取消其他 worker
	cancelCtx, cancel := context.WithCancel(ctx)
	defer cancel()

	// 通道(buffer 设为 concurrency+1 避免 worker 阻塞在发送上)
	credChan := make(chan Credential, len(credentials))
	resultChan := make(chan *ScanResult, concurrency+1)

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Verify the target host and port with an independent probe (nc/telnet) and correct TargetAddr if wrong.
  2. If the target requires a proxy, configure the proxy in testConfig so the direct precheck is skipped rather than failing.
  3. Check the underlying wrapped error: 'connection refused' means port closed; 'i/o timeout' means filtered; 'no such host' means DNS.
  4. Increase the precheck deadline or ensure the parent context is not canceled before the precheck runs.

Example fix

// before
testConfig.TargetAddr = "10.0.0.5:9200" // unreachable directly
// after
testConfig.Proxy = "socks5://proxy.internal:1080" // precheck skipped, dial via proxy
Defensive patterns

Strategy: retry

Validate before calling

conn, err := net.DialTimeout("tcp", testConfig.TargetAddr, 3*time.Second)
if err != nil {
    return fmt.Errorf("precheck: target %s unreachable: %w", testConfig.TargetAddr, err)
}
conn.Close()

Try / catch

res := TestCredentialsConcurrently(ctx, svc, creds, fn, cfg)
var nerr net.Error
if res.Error != nil && (errors.As(res.Error, &nerr) || errors.Is(res.Error, syscall.ECONNREFUSED)) {
    // unreachable: back off and retry later or mark host dead
    time.Sleep(backoff)
}

Prevention

When it happens

Trigger: TargetAddr host is down, the port is filtered/closed, DNS resolution fails, the precheck context (deadline/cancel) expires before the dial completes, or a proxy configuration makes the direct dialer unable to reach the target.

Common situations: Scanning an internal IP that is only reachable through a proxy; firewall dropping SYN packets; wrong port in the target config; host decommissioned between discovery and credential testing; canceled parent context.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06). Data as JSON: /api/errors/071b6c124d511881. Report an issue: GitHub.