{"record":{"id":"2c2da4c8f5d5db7f","repo":"Tencent/WeKnora","slug":"dns-resolution-returned-no-addresses-for-s","errorCode":null,"errorMessage":"DNS resolution returned no addresses for %s","messagePattern":"DNS resolution returned no addresses for (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/utils/security.go","lineNumber":835,"sourceCode":"\t\t\treturn nil, fmt.Errorf(\"connection blocked: hostname %s is restricted\", host)\n\t\t}\n\t}\n\tfor _, suffix := range restrictedHostSuffixes {\n\t\tif strings.HasSuffix(hostLower, suffix) {\n\t\t\treturn nil, fmt.Errorf(\"connection blocked: hostname suffix %s is restricted\", suffix)\n\t\t}\n\t}\n\n\t// Resolve the hostname once, validate every answer, and then dial one of\n\t// those exact IPs. Dialing the original hostname here would make the\n\t// standard dialer resolve it a second time, leaving a DNS-rebinding window\n\t// between validation and connection establishment.\n\tips, err := net.DefaultResolver.LookupIPAddr(ctx, host)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"DNS resolution failed for %s: %w\", host, err)\n\t}\n\tif len(ips) == 0 {\n\t\treturn nil, fmt.Errorf(\"DNS resolution returned no addresses for %s\", host)\n\t}\n\n\t// Validate all resolved IPs\n\tfor _, ipAddr := range ips {\n\t\tif restricted, reason := isRestrictedIP(ipAddr.IP); restricted {\n\t\t\treturn nil, fmt.Errorf(\"connection blocked: %s resolves to restricted IP %s (%s)\", host, ipAddr.IP.String(), reason)\n\t\t}\n\t}\n\n\t// If we get here, all IPs are safe. Pin the connection to the validated DNS\n\t// answers; TLS still uses the request hostname for SNI/certificate checks.\n\tdialer := &net.Dialer{\n\t\tTimeout:   30 * time.Second,\n\t\tKeepAlive: 30 * time.Second,\n\t}\n\tvar lastErr error\n\tfor _, ipAddr := range ips {\n\t\tpinnedAddr := net.JoinHostPort(ipAddr.IP.String(), port)","sourceCodeStart":817,"sourceCodeEnd":853,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/utils/security.go#L817-L853","documentation":"The DNS lookup for the host succeeded but returned an empty answer set, so SSRF validation has nothing to check and the dial is refused. This is distinct from a resolver error: the resolver responded, but with zero addresses. The library treats an empty answer as a hard failure rather than falling back to the standard dialer, preserving the rebinding-free guarantee.","triggerScenarios":"LookupIPAddr returning no IPs — typically a name that exists but has no A/AAAA records (e.g. only CNAME to nothing, a NULL record, or a DNS64/filtering resolver returning an empty answer) — passed to SSRFSafeDialContext / SSRFSafeGRPCDialer / a transport DialContext.","commonSituations":"DNS entries pointing at services with missing A records; split-horizon DNS where the external view has no records; security appliances or sinkhole resolvers that return empty answers for blocked domains; recently created records not yet propagated.","solutions":["Inspect the DNS zone and add proper A/AAAA records for the hostname, or point the client at a hostname that has addresses.","Check whether a corporate/security resolver is empty-answering the domain and switch to the correct internal resolver.","Dial the intended IP literal directly (subject to isRestrictedIP validation) if you know the address.","Retry after DNS propagation if the record was just created."],"exampleFix":"// before\nconn, err := utils.SSRFSafeDialContext(ctx, \"tcp\", \"staging-api.example.com:443\") // no A record\n\n// after\nips, err := net.LookupIP(\"staging-api.example.com\")\nif err != nil || len(ips) == 0 {\n    // fix DNS or fall back to a known-good address\n    return utils.SSRFSafeDialContext(ctx, \"tcp\", \"203.0.113.10:443\")\n}\nconn, err := utils.SSRFSafeDialContext(ctx, \"tcp\", \"staging-api.example.com:443\")","handlingStrategy":"validation","validationCode":"ips, err := net.LookupIP(host)\nif err == nil && len(ips) == 0 {\n    return fmt.Errorf(\"hostname %s has no A/AAAA records; fix DNS or dial a literal IP\", host)\n}","typeGuard":null,"tryCatchPattern":"conn, err := utils.SSRFSafeDialContext(ctx, \"tcp\", addr)\nif err != nil && strings.Contains(err.Error(), \"returned no addresses\") {\n    return nil, fmt.Errorf(\"no DNS addresses for %s: verify records or dial IP directly\", host)\n}","preventionTips":["Ensure every configured hostname has A/AAAA records in the zone the resolver actually queries.","Watch for security appliances that empty-answer filtered domains.","Check split-horizon DNS views if the record exists externally but not internally.","Prefer IP literals for infrastructure endpoints that never need names."],"tags":["dns","empty-answer","network","dial"],"backgroundTag":"dns-resolution-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}