flipped-aurora/gin-vue-admin · error
解析拨号地址失败: %w
Error message
解析拨号地址失败: %w
What it means
ssrfControl is a dialer Control function that runs after DNS resolution, before the connection is established. It splits the dial address into host and port; if net.SplitHostPort fails (malformed address reaching the dialer), the error is wrapped as '解析拨号地址失败'. This indicates the resolved address string could not be parsed, which should not normally happen with a healthy net stack.
Source
Thrown at server/service/system/sys_timed_task_http.go:30
// errPrivateAddr SSRF 防护拒绝(错误信息含"SSRF"关键字, 供日志/测试识别)
var errPrivateAddr = errors.New("目标解析为内网/环回/链路本地地址, 已被 SSRF 防护拒绝(可在任务上开启\"允许内网\"豁免)")
// 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),View on GitHub (pinned to 3136500ef3)
Solutions
- Re-check the task's httpUrl for stray characters, unbalanced brackets, or embedded spaces and fix it
- Retry the task — transient resolver glitches can produce bad resolved addresses; persistent occurrences warrant DNS diagnostics
- If allowPrivate is intended, enable the task's 'allow private' flag — though note this error precedes the allowPrivate early-return only after the flag check, so confirm the task's actual configuration
Example fix
// before "httpUrl": "http://[192.168.1.10:8080" // unbalanced bracket -> SplitHostPort error // after "httpUrl": "http://192.168.1.10:8080"
Defensive patterns
Strategy: try-catch
Validate before calling
u, err := url.Parse(taskHttpUrl)
if err != nil || u.Host == "" {
// reject before scheduling the HTTP task
}
// prefer IP literals or well-formed hostnames validated with:
if net.ParseIP(u.Hostname()) == nil && net.ParseIP(u.Port()) != nil {
// suspicious mix; review
} Type guard
func isParseableHostPort(addr string) bool {
_, _, err := net.SplitHostPort(addr)
return err == nil
} Try / catch
resp, err := client.Do(req)
if err != nil {
if strings.Contains(err.Error(), "解析拨号地址失败") {
// malformed resolved address: log httpUrl, skip retry (deterministic)
return
}
return err
} Prevention
- Validate httpUrl with url.Parse at task-creation time (the service already does)
- Avoid hand-editing URLs in config; use the admin UI validation
- Keep DNS/resolver stack healthy; test resolution in the deployment environment
When it happens
Trigger: An HTTP-executor timed task dials an address whose host:port form is invalid after DNS resolution — e.g. a malformed httpUrl that slipped past validation, or an unusual custom dialer/proxy path producing a non host:port address.
Common situations: DNS returning an unusual literal (IPv6 zone id or bracket-mismatched host); proxy or custom resolver injecting a malformed address; internal misconfiguration of the httpUrl (missing port is fine, but stray characters break SplitHostPort).
Related errors
AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31).
Data as JSON: /api/errors/b6e85d6630746d21.
Report an issue: GitHub.