siyuan-note/siyuan · error
host has no public IP: %s
Error message
host has no public IP: %s
What it means
When the host is a hostname (not a literal IP), this dialer resolves all its IPs and requires at least one public address to proceed. If every resolved IP is private/internal (or resolution returned nothing usable), it returns 'host has no public IP' to block SSRF via DNS rebinding.
Source
Thrown at kernel/util/net.go:209
ips, err := net.DefaultResolver.LookupIPAddr(ctx, host)
if err != nil {
return nil, err
}
var lastErr error
for _, ipAddr := range ips {
if isPrivateIP(ipAddr.IP) {
continue
}
conn, err := dialer.DialContext(ctx, network, net.JoinHostPort(ipAddr.IP.String(), port))
if err == nil {
return conn, nil
}
lastErr = err
}
if lastErr != nil {
return nil, lastErr
}
return nil, errors.New("host has no public IP: " + host)
}
}
// isPrivateIP 判断 IP 是否为私网地址,含内嵌私网 IPv4 的 IPv6 过渡地址(NAT64、6to4、Teredo、IPv4 兼容)。
// https://github.com/siyuan-note/siyuan/security/advisories/GHSA-qq8m-8p8v-x4xg
// https://github.com/siyuan-note/siyuan/security/advisories/GHSA-rg26-cg95-gq6p
func isPrivateIP(ip net.IP) bool {
if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() ||
ip.IsPrivate() || ip.IsUnspecified() || ip.IsMulticast() {
return true
}
// Go 标准库的分类方法不识别 IPv6 过渡地址,需按 RFC 内嵌格式提取其中的 IPv4 后再递归判断。
if ip4 := extractEmbeddedIPv4(ip); nil != ip4 && !ip4.Equal(ip) {
return isPrivateIP(ip4)
}
return false
}
View on GitHub (pinned to 8641553a1f)
Solutions
- Point the hostname at a genuinely public IP, or use the SafeMode-permitting dialer if private access is intended
- Check DNS resolution (nslookup/dig the host) and remove private-only records
- If testing locally, run without the hardened dialer or bind via a public-facing reverse proxy
Example fix
// before /etc/hosts: api.example.com 127.0.0.1 // dialer rejects // after # use the real public DNS record for api.example.com
Defensive patterns
Strategy: validation
Validate before calling
// resolve first, then decide
ips, err := net.DefaultResolver.LookupIPAddr(ctx, host)
if err != nil { return err }
for _, ip := range ips {
if !(ip.IP.IsPrivate() || ip.IP.IsLoopback()) { /* at least one public IP */ }
} Try / catch
if _, err := doFetch(url); err != nil && strings.HasPrefix(err.Error(), "host has no public IP") {
return fmt.Errorf("%s resolves only to private addresses; use a public host", url)
} Prevention
- Keep /etc/hosts free of entries mapping public domains to 127.0.0.1
- Verify DNS records with dig/nslookup when deploying internal names
- Use split-horizon aware resolvers or public endpoints for hardened fetches
When it happens
Trigger: Fetching from a hostname whose DNS resolves only to private addresses (e.g. internal DNS names, hosts-file entries pointing a domain at 127.0.0.1, split-horizon DNS).
Common situations: Developers adding /etc/hosts entries mapping public-looking domains to localhost; self-hosted services reachable only via LAN DNS names; misconfigured DNS returning private IPs.
Related errors
- host has no public IP:
- failed to resolve host:
- ip address [%s] is prohibited
- access to private/internal IP is prohibited
- access to private/internal IP is prohibited
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/446d29335e26dbc2.
Report an issue: GitHub.