flipped-aurora/gin-vue-admin · error
非法拨号 IP: %s
Error message
非法拨号 IP: %s
What it means
The SSRF dial-control parses the resolved host as an IP; if net.ParseIP fails, the address is not a valid IP literal and the dial is rejected with '非法拨号 IP: <host>'. Since the control runs post-DNS, the resolved address should always be an IP — failure signals a malformed or unexpected resolved value.
Source
Thrown at server/service/system/sys_timed_task_http.go:34
// isDisallowedIP 内网/环回/链路本地/未指定地址判定
func isDisallowedIP(ip net.IP) bool {
return ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() || ip.IsUnspecified()
}
// ssrfControl 在拨号阶段(DNS 解析后、连接建立前)校验目标 IP:
// 每次连接都过检, 天然覆盖重定向与 DNS rebinding(TOCTOU 安全)。
func ssrfControl(allowPrivate bool) func(network, address string, c syscall.RawConn) error {
return func(_ string, address string, _ syscall.RawConn) error {
if allowPrivate {
return nil
}
host, _, err := net.SplitHostPort(address)
if err != nil {
return fmt.Errorf("解析拨号地址失败: %w", err)
}
ip := net.ParseIP(host)
if ip == nil {
return fmt.Errorf("非法拨号 IP: %s", host)
}
if isDisallowedIP(ip) {
return fmt.Errorf("%w: %s", errPrivateAddr, ip)
}
return nil
}
}
// newTimedTaskHTTPClient 定时任务专用 HTTP 客户端:
// 整体超时 + 禁用环境代理(防经代理绕过 IP 校验) + 拨号层 SSRF 防护
func newTimedTaskHTTPClient(allowPrivate bool, timeout time.Duration) *http.Client {
dialer := &net.Dialer{
Timeout: 10 * time.Second,
Control: ssrfControl(allowPrivate),
}
transport := &http.Transport{
Proxy: nil, // 显式禁用代理
DialContext: dialer.DialContext,View on GitHub (pinned to 3136500ef3)
Solutions
- Verify with nslookup/dig that the target hostname resolves to a valid IP literal
- Check /etc/hosts and resolver configuration for malformed entries
- Fix the task's httpUrl if it contains a bad literal (e.g. 'http://999.1.1.1')
- If inside a mesh/VPN that rewrites addresses, whitelist the task's network path or enable allowPrivate after a security review
Example fix
// before "httpUrl": "http://internal.host.invalid:80" // resolves to garbage via broken resolver // after "httpUrl": "http://10.0.0.5:80" // use a literal after verifying reachability
Defensive patterns
Strategy: validation
Validate before calling
host := u.Hostname()
ip := net.ParseIP(host)
if ip == nil {
if addrs, err := net.LookupHost(host); err != nil || len(addrs) == 0 {
// hostname does not resolve to a valid IP; fix before scheduling
}
} Type guard
func isIPStr(s string) bool { return net.ParseIP(s) != nil } Try / catch
resp, err := client.Do(req)
if err != nil {
if strings.Contains(err.Error(), "非法拨号 IP") {
// bad resolved address: fix DNS/hosts, don't blind-retry
return
}
return err
} Prevention
- Use IP literals or verified hostnames in task httpUrl
- Audit /etc/hosts and resolver config in the deploy environment
- Pin hostnames to IPs when DNS is unreliable
When it happens
Trigger: DNS resolution yields a non-IP host string to the Control hook (unusual resolver behavior, hosts-file oddities), or a custom address format reaches the dialer.
Common situations: Broken/patched DNS resolvers returning malformed answers; /etc/hosts entries with invalid literals; running tasks against hostnames that resolve through custom networking plugins (VPN, service mesh) that rewrite addresses.
Related errors
AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31).
Data as JSON: /api/errors/b773b91eb49d842f.
Report an issue: GitHub.