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

  1. Re-check the task's httpUrl for stray characters, unbalanced brackets, or embedded spaces and fix it
  2. Retry the task — transient resolver glitches can produce bad resolved addresses; persistent occurrences warrant DNS diagnostics
  3. 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

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.