{"record":{"id":"071b6c124d511881","repo":"shadow1ng/fscan","slug":"service-target-unreachable-w","errorCode":null,"errorMessage":"service_target_unreachable: %w","messagePattern":"service_target_unreachable: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/services/credential_tester.go","lineNumber":227,"sourceCode":"\t\t\tService: serviceName,\n\t\t\tError:   fmt.Errorf(\"%s\", i18n.GetText(\"service_no_test_creds\")),\n\t\t}\n\t}\n\ttestConfig = normalizeConcurrentTestConfig(testConfig)\n\n\t// TCP 预检：快速验证目标可达，避免对不可达目标浪费全部凭据尝试\n\t// 代理模式下跳过：net.DialTimeout 直连无法到达代理后的内网目标\n\tif testConfig.TargetAddr != \"\" && !testConfig.UseProxy {\n\t\tdialCtx, dialCancel := context.WithTimeout(ctx, 3*time.Second)\n\t\tdefer dialCancel()\n\n\t\tvar dialer net.Dialer\n\t\tpreConn, err := dialer.DialContext(dialCtx, \"tcp\", testConfig.TargetAddr)\n\t\tif err != nil {\n\t\t\treturn &ScanResult{\n\t\t\t\tSuccess: false,\n\t\t\t\tService: serviceName,\n\t\t\t\tError:   fmt.Errorf(i18n.Tr(\"service_target_unreachable\", \"%w\"), err),\n\t\t\t}\n\t\t}\n\t\t_ = preConn.Close()\n\t}\n\n\t// 调整并发数\n\tconcurrency := testConfig.Concurrency\n\tif concurrency > len(credentials) {\n\t\tconcurrency = len(credentials)\n\t}\n\n\t// 创建可取消的 context - 找到成功后取消其他 worker\n\tcancelCtx, cancel := context.WithCancel(ctx)\n\tdefer cancel()\n\n\t// 通道（buffer 设为 concurrency+1 避免 worker 阻塞在发送上）\n\tcredChan := make(chan Credential, len(credentials))\n\tresultChan := make(chan *ScanResult, concurrency+1)","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/shadow1ng/fscan/blob/95cc12e753bf43de7004e5aef42a9ffba3934303/plugins/services/credential_tester.go#L209-L245","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the target host and port with an independent probe (nc/telnet) and correct TargetAddr if wrong.","If the target requires a proxy, configure the proxy in testConfig so the direct precheck is skipped rather than failing.","Check the underlying wrapped error: 'connection refused' means port closed; 'i/o timeout' means filtered; 'no such host' means DNS.","Increase the precheck deadline or ensure the parent context is not canceled before the precheck runs."],"exampleFix":"// before\ntestConfig.TargetAddr = \"10.0.0.5:9200\" // unreachable directly\n// after\ntestConfig.Proxy = \"socks5://proxy.internal:1080\" // precheck skipped, dial via proxy","handlingStrategy":"retry","validationCode":"conn, err := net.DialTimeout(\"tcp\", testConfig.TargetAddr, 3*time.Second)\nif err != nil {\n    return fmt.Errorf(\"precheck: target %s unreachable: %w\", testConfig.TargetAddr, err)\n}\nconn.Close()","typeGuard":null,"tryCatchPattern":"res := TestCredentialsConcurrently(ctx, svc, creds, fn, cfg)\nvar nerr net.Error\nif res.Error != nil && (errors.As(res.Error, &nerr) || errors.Is(res.Error, syscall.ECONNREFUSED)) {\n    // unreachable: back off and retry later or mark host dead\n    time.Sleep(backoff)\n}","preventionTips":["Probe targets with a lightweight TCP ping before credential testing","Configure proxy settings when targets are only reachable via proxy","Check parent-context cancellation before launching prechecks","Classify wrapped net errors (refused vs timeout vs DNS) to choose retry vs skip"],"tags":["go","network","tcp","precheck"],"backgroundTag":"connection-refused","analyzedSha":"95cc12e753bf43de7004e5aef42a9ffba3934303","analyzedAt":"2026-09-06T17:07:30.094Z","contentChangedAt":"2026-09-06T17:07:30.094Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}