Skip to content

❓ 查 192.168 报错

问题

我用 192.168.x.x 这种内网地址调用 GetIPInfo,结果返回了错误,是不是库有 bug?

简答

192.168.x.x 属于保留地址,没有地理意义,本库会返回 ErrReservedIP,这是预期行为,不是 bug。

🎨 一图抵千言

查到一个 IP 后,SDK 内部是如何判定它是不是保留地址的?顺着这张图走一遍。

详解

🔒 为什么会报错

192.168.0.0/16 是 IANA 规定的私有网络地址段,和 10.0.0.0/8172.16.0.0/12127.0.0.0/8(回环)、169.254.0.0/16(链路本地)、::1(IPv6 回环)等一样,不分配给公网设备,因此没有真实的地理归属。

ipapi.co 对这类地址返回特殊错误响应,本库在 handleError 中按 Reason == "Reserved IP Address" 映射为哨兵错误 ErrReservedIP

go
case "Reserved IP Address":
    return fmt.Errorf("%w: %s", ErrReservedIP, apiErr.IP)
📖 常见保留地址段总表

IANA 规定的保留地址段都会触发 ErrReservedIP,对照如下:

范围用途Go 判定方法
🏠 私有网络 A 类10.0.0.0/8内网ip.IsPrivate()
🏠 私有网络 B 类172.16.0.0/12内网ip.IsPrivate()
🏠 私有网络 C 类192.168.0.0/16内网(最常见)ip.IsPrivate()
🔁 回环127.0.0.0/8localhostip.IsLoopback()
🔗 链路本地169.254.0.0/16自动配置ip.IsLinkLocalUnicast()
⭕ 未指定0.0.0.0/8默认路由ip.IsUnspecified()
🟣 IPv6 回环::1/128localhost v6ip.IsLoopback()

查询前用标准库 net.IP 的这些方法可预判,省一次无谓的网络请求。

🧪 复现示例

go
package main

import (
    "context"
    "errors"
    "fmt"
    "log"

    "github.com/cyberspacesec/ipapi.co-skills/pkg/ipapi"
)

func main() {
    client := ipapi.NewClient()

    // 查询一个私有地址
    info, err := client.GetIPInfo(context.Background(), "192.168.1.1", "json")
    if err != nil {
        // 👉 命中这里:ErrReservedIP
        if errors.Is(err, ipapi.ErrReservedIP) {
            fmt.Println("192.168.1.1 是保留地址,无地理信息")
            return
        }
        log.Fatalf("其他错误: %v", err)
    }
    fmt.Printf("%+v\n", info)
}

运行输出:

text
192.168.1.1 是保留地址,无地理信息

🧩 取出错误细节

服务端返回的 APIError 会带 Reserved: trueIP 字段,可以用 errors.As 取出:

go
var apiErr *ipapi.APIError
if errors.As(err, &apiErr) {
    fmt.Printf("reason=%s ip=%s reserved=%v msg=%s\n",
        apiErr.Reason, apiErr.IP, apiErr.Reserved, apiErr.Message)
}
// reason=Reserved IP Address ip=192.168.1.1 reserved=true msg=...

🧠 排查清单

遇到保留地址报错时,对照下表确认:

你查的地址是否保留说明
192.168.x.x✅ 是私有网络
10.x.x.x✅ 是私有网络
172.16~31.x.x✅ 是私有网络
127.x.x.x✅ 是回环 localhost
169.254.x.x✅ 是链路本地
::1✅ 是IPv6 回环
8.8.8.8❌ 否公网,正常返回

💡 区分两种“无效”

  • 格式非法(如 "not.an.ip")→ ErrInvalidIP,本地校验即拦截。
  • 格式合法但是保留段(如 "192.168.1.1")→ ErrReservedIP,由服务端判定后返回。

⚠️ 不可重试

ErrReservedIP 不在 IsRetryableError 的可重试列表里(只有 ErrRateLimitedErrServerErrorErrNotFound 可重试)。重试多少次结果都一样,换一个公网地址才是正解。

✅ 正确做法

  • 想测库是否正常工作:用一个公网地址,如 8.8.8.81.1.1.1
  • 想处理内网请求:先判 errors.Is(err, ipapi.ErrReservedIP),给用户一个友好提示,不要把它当故障告警。
  • 想在查询前预判:可用标准库 net.IP.IsPrivate() 提前过滤,避免一次无谓的网络请求:
go
func isReserved(ipStr string) bool {
    ip := net.ParseIP(ipStr)
    if ip == nil {
        return false
    }
    return ip.IsPrivate() || ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsUnspecified()
}

相关

基于 MIT 许可证发布