{"record":{"id":"a0c1097ce6e91e3a","repo":"siyuan-note/siyuan","slug":"failed-to-resolve-host-s","errorCode":null,"errorMessage":"failed to resolve host: %s","messagePattern":"failed to resolve host: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/httprequest.go","lineNumber":45,"sourceCode":"\t\"strings\"\n\n\t\"github.com/imroc/req/v3\"\n\t\"github.com/siyuan-note/httpclient\"\n)\n\nconst (\n\tmaxHTTPRequestBytes     = 5 * 1024 * 1024  // text/html、text/plain、application/json 等文本类响应上限\n\tmaxHTTPRequestFileBytes = 10 * 1024 * 1024 // 二进制响应落盘上限\n\tmaxHTTPRequestChars     = 50000\n)\n\n// CheckHostSSRF 校验主机名解析出的 IP 不落在内网/回环等不可达地址段，\n// 防止智能体被诱导发起 SSRF 攻击。web_fetch 与 http_request 共用此校验。\n// https://github.com/siyuan-note/siyuan/security/advisories/GHSA-rg26-cg95-gq6p\nfunc CheckHostSSRF(host string) error {\n\tips, err := net.LookupIP(host)\n\tif err != nil {\n\t\treturn errors.New(\"failed to resolve host: \" + err.Error())\n\t}\n\tfor _, ip := range ips {\n\t\t// 与 SSRFSafeDialer 共用 isPrivateIP，覆盖 NAT64、6to4、Teredo 等 IPv6 过渡地址。\n\t\tif isPrivateIP(ip) {\n\t\t\treturn errors.New(\"access to private/internal IP is prohibited\")\n\t\t}\n\t}\n\treturn nil\n}\n\n// HTTPRequest 发起一次通用 HTTP 调用，供智能体 http_request 工具使用。\n// 与 WebFetch 不同：本函数不做 HTML→Markdown 转换，文本类响应（含 JSON/XML）原样返回，\n// 便于智能体直接消费 REST API 的 JSON 输出。method 取值：GET/POST/PUT/DELETE/PATCH。\n// 返回的 text 为响应正文（文本类）或落盘后的文件路径（二进制类）。\nfunc HTTPRequest(method, rawURL string, headers map[string]string, body string) (statusCode int, contentType string, text string, err error) {\n\tu, err := url.Parse(rawURL)\n\tif err != nil || (u.Scheme != \"http\" && u.Scheme != \"https\") {\n\t\treturn 0, \"\", \"\", errors.New(\"URL must start with http:// or https://\")","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/util/httprequest.go#L27-L63","documentation":"Returned by CheckHostSSRF when net.LookupIP fails for the given host. The DNS resolution error text is appended, so the message shows the underlying resolver failure. This runs before any connection is opened and is shared by web_fetch and the http_request tool to prevent SSRF.","triggerScenarios":"Calling util.CheckHostSSRF(host) or HTTPRequest with a host that does not resolve: NXDOMAIN, SERVFAIL, DNS server unreachable, or a malformed hostname that LookupIP rejects.","commonSituations":"The agent typed a typo'd or non-existent domain; the kernel host has no working DNS (container with broken /etc/resolv.conf, offline, captive portal); a temporary DNS outage; an IPv6-only host queried with a v4-only resolver.","solutions":["Verify the hostname is correct and resolvable (dig/nslookup host from the kernel host).","Fix DNS on the kernel host: check /etc/resolv.conf, connectivity to the resolver, firewall rules blocking port 53.","Retry transient DNS failures (SERVFAIL, timeout) after the resolver recovers."],"exampleFix":"// before\nif err := util.CheckHostSSRF(host); err != nil {\n    return err\n}\n\n// after\nif _, err := net.LookupHost(host); err != nil {\n    return fmt.Errorf(\"precheck: host %q is not resolvable: %w\", host, err)\n}\nif err := util.CheckHostSSRF(host); err != nil {\n    return err\n}","handlingStrategy":"validation","validationCode":"func hostResolvable(host string) bool {\n    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)\n    defer cancel()\n    _, err := net.DefaultResolver.LookupHost(ctx, host)\n    return err == nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pre-resolve agent-supplied hostnames to give a clear 'unknown host' message.","Ensure the kernel host has working DNS (/etc/resolv.conf, port 53 reachable)."],"tags":["network","dns","ssrf","http-request"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}