{"record":{"id":"c31da463a4b8e59f","repo":"MHSanaei/3x-ui","slug":"host-s-has-no-ip-addresses","errorCode":null,"errorMessage":"host %s has no IP addresses","messagePattern":"host (.+?) has no IP addresses","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/web/service/url_safety.go","lineNumber":76,"sourceCode":"\tif err := rejectPrivateHost(ctx, u.Hostname()); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn clean, nil\n}\n\nfunc rejectPrivateHost(ctx context.Context, hostname string) error {\n\tif ip := net.ParseIP(hostname); ip != nil {\n\t\tif isBlockedIP(ip) {\n\t\t\treturn fmt.Errorf(\"blocked private/internal address %s\", ip.String())\n\t\t}\n\t\treturn nil\n\t}\n\tips, err := net.DefaultResolver.LookupIPAddr(ctx, hostname)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cannot resolve host %s: %w\", hostname, err)\n\t}\n\tif len(ips) == 0 {\n\t\treturn fmt.Errorf(\"host %s has no IP addresses\", hostname)\n\t}\n\tfor _, ipAddr := range ips {\n\t\tif isBlockedIP(ipAddr.IP) {\n\t\t\treturn fmt.Errorf(\"host %s resolves to blocked private/internal address %s\", hostname, ipAddr.IP.String())\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc isBlockedIP(ip net.IP) bool {\n\treturn netsafe.IsBlockedIP(ip)\n}\n","sourceCodeStart":58,"sourceCodeEnd":89,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/web/service/url_safety.go#L58-L89","documentation":"rejectPrivateHost reports this when DNS resolution succeeds with an error but returns an empty IP list — a rare resolver state distinct from NXDOMAIN (which errors). It means the name exists in some sense but the resolver handed back zero A/AAAA records, so the panel cannot even evaluate the SSRF blocklist for it.","triggerScenarios":"Hostnames with only non-address records (e.g. a name that has only an MX/TXT record and no A/AAAA), or aresolver/misconfigured DNS middleware returning empty NOERROR answers.","commonSituations":"Pointing a health-check at a mail-only or TXT-only name; DNS firewalls (Response Policy Zones) that strip answers; some split-horizon setups where the public view has no A record.","solutions":["Check what records the name actually has: 'dig <host> A <host> AAAA +short' — if empty, the name is wrong for this purpose; use the name that owns address records.","If split-horizon DNS is involved, ensure the panel's resolver serves the view with A/AAAA records.","Treat as a configuration error, not transient: an empty answer rarely self-heals; fix the name."],"exampleFix":"// before\nurl := \"https://mailonly.example.com/health\" // name has only MX/TXT records\n\n// after\nurl := \"https://www.example.com/health\" // name with A/AAAA records","handlingStrategy":"validation","validationCode":"// Ensure the configured name actually has address records before relying on it\naddrs, err := net.LookupHost(hostname)\nif err != nil || len(addrs) == 0 {\n    return fmt.Errorf(\"hostname %q has no A/AAAA records\", hostname)\n}","typeGuard":"null","tryCatchPattern":"if strings.Contains(err.Error(), \"no IP addresses\") {\n    // permanent config error: pick a name that owns A/AAAA records\n}","preventionTips":["When adding health-check targets, dig them for A/AAAA first.","Avoid names that only carry MX/TXT records."],"tags":["dns","network","ssrf"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}