修复namesilo api接口升级错误
This commit is contained in:
4
build.sh
4
build.sh
@@ -4,8 +4,10 @@ set -x
|
||||
|
||||
BIN="ddns-namesilo"
|
||||
|
||||
|
||||
# Arm 64
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o ${BIN}-linux-arm64 -a -ldflags '-extldflags "-static"' && upx -9 ${BIN}-linux-arm64
|
||||
|
||||
# AMD 64
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${BIN}-linux-amd64 -a -ldflags '-extldflags "-static"' && upx -9 ${BIN}-linux-amd64
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
98
go.sum
98
go.sum
@@ -1,98 +0,0 @@
|
||||
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
|
||||
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-github/v50 v50.2.0/go.mod h1:VBY8FB6yPIjrtKhozXv4FQupxKLS6H4m6xFZlT43q8Q=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/weppos/publicsuffix-go v0.40.2 h1:LlnoSH0Eqbsi3ReXZWBKCK5lHyzf3sc1JEHH1cnlfho=
|
||||
github.com/weppos/publicsuffix-go v0.40.2/go.mod h1:XsLZnULC3EJ1Gvk9GVjuCTZ8QUu9ufE4TZpOizDShko=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
|
||||
185
main.go
185
main.go
@@ -18,104 +18,137 @@ func classifyIPv6(ip net.IP) string {
|
||||
} else if ip.IsLinkLocalUnicast() {
|
||||
return "Link-Local"
|
||||
} else if ip.IsGlobalUnicast() {
|
||||
// 排除 ULA (Unique Local Address, fc00::/7),通常以 fd 或 fc 开头
|
||||
if len(ip) == 16 && (ip[0]&0xfe) == 0xfc {
|
||||
return "ULA"
|
||||
}
|
||||
return "Global"
|
||||
}
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
// getIPv6Addresses 获取网络接口的 IPv6 地址。
|
||||
// 它会跳过回环接口,并且只考虑已启用的接口。
|
||||
func getIPv6Addresses() (map[string][]net.IP, error) {
|
||||
// 创建一个 map 用于存储接口名称及其对应的 IPv6 地址。
|
||||
addresses := make(map[string][]net.IP)
|
||||
|
||||
// 获取系统上的所有网络接口。
|
||||
// AutoGetBestIPv6 自动寻找最佳的 IPv6 地址和对应的接口名称
|
||||
// 优先级: 接口名含 "pppoe" > "wan" > 其他
|
||||
func AutoGetBestIPv6() (string, string) {
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil, err // 如果无法获取接口信息,则返回错误。
|
||||
log.Printf("获取接口列表失败: %v", err)
|
||||
return "", "NULL"
|
||||
}
|
||||
|
||||
// 遍历所有网络接口。
|
||||
var fallbackIP string
|
||||
var fallbackIface string
|
||||
|
||||
// 遍历所有接口
|
||||
for _, iface := range interfaces {
|
||||
// 跳过未启用的接口或回环接口。
|
||||
// 跳过未启用或回环接口
|
||||
if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// 获取与该接口关联的地址。
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
return nil, err // 如果无法获取地址,则返回错误。
|
||||
continue
|
||||
}
|
||||
|
||||
// 遍历该接口的所有地址。
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = v.IP // 从 IPNet 类型中提取 IP。
|
||||
ip = v.IP
|
||||
case *net.IPAddr:
|
||||
ip = v.IP // 从 IPAddr 类型中提取 IP。
|
||||
ip = v.IP
|
||||
}
|
||||
|
||||
// 检查 IP 是否为 IPv6 地址(即不是 IPv4)。
|
||||
if ip != nil && ip.To4() == nil {
|
||||
// 将 IPv6 地址添加到对应的接口名称列表中。
|
||||
addresses[iface.Name] = append(addresses[iface.Name], ip)
|
||||
// 必须是 IPv6 且不是 IPv4
|
||||
if ip == nil || ip.To4() != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// 获取地址类型
|
||||
addrType := classifyIPv6(ip)
|
||||
|
||||
// 只处理公网 IP (Global Unicast),忽略 Link-Local (fe80) 和 ULA (fdxx)
|
||||
if addrType == "Global" {
|
||||
ipStr := ip.String()
|
||||
ifaceName := iface.Name
|
||||
|
||||
// 策略 1: 如果接口名包含 "pppoe",这就是最完美的 WAN 口,直接返回
|
||||
if strings.Contains(ifaceName, "pppoe") {
|
||||
log.Printf("自动检测: 命中高优先级接口 (PPPoE): %s -> %s\n", ifaceName, ipStr)
|
||||
return ifaceName, ipStr
|
||||
}
|
||||
|
||||
// 策略 2: 如果接口名包含 "wan" (如 wan6, eth0.2),暂存,如果没有 pppoe 就用它
|
||||
if strings.Contains(ifaceName, "wan") {
|
||||
log.Printf("自动检测: 命中次优先级接口 (WAN): %s -> %s\n", ifaceName, ipStr)
|
||||
return ifaceName, ipStr
|
||||
}
|
||||
|
||||
// 策略 3: 其他有公网 IP 的接口 (如 br-lan),作为保底
|
||||
if fallbackIP == "" {
|
||||
fallbackIP = ipStr
|
||||
fallbackIface = ifaceName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return addresses, nil // 返回包含 IPv6 地址的映射。
|
||||
// 如果没有找到明确的 WAN 口,返回保底的接口(例如 br-lan)
|
||||
if fallbackIP != "" {
|
||||
log.Printf("自动检测: 未找到 WAN 接口,使用保底接口: %s -> %s\n", fallbackIface, fallbackIP)
|
||||
return fallbackIface, fallbackIP
|
||||
}
|
||||
|
||||
// 获取指定接口的 IPv6 地址
|
||||
func pppoe_interface_ipv6(_interface string) string {
|
||||
addresses, err := getIPv6Addresses()
|
||||
return "", "NULL"
|
||||
}
|
||||
|
||||
// getIPv6ByInterface 获取指定接口名称的 IPv6
|
||||
func getIPv6ByInterface(ifaceName string) string {
|
||||
iface, err := net.InterfaceByName(ifaceName)
|
||||
if err != nil {
|
||||
log.Printf("获取 IPv6 地址失败: %v\n", err)
|
||||
log.Printf("无法找到接口 %s: %v", ifaceName, err)
|
||||
return "NULL"
|
||||
}
|
||||
|
||||
//fmt.Println("IPv6 地址列表:")
|
||||
for iface, ips := range addresses {
|
||||
//log.Printf("Interface: %s\n", iface)
|
||||
if iface == _interface {
|
||||
for _, ip := range ips {
|
||||
addrType := classifyIPv6(ip)
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
return "NULL"
|
||||
}
|
||||
|
||||
if addrType == "Global" {
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = v.IP
|
||||
case *net.IPAddr:
|
||||
ip = v.IP
|
||||
}
|
||||
|
||||
if ip != nil && ip.To4() == nil && classifyIPv6(ip) == "Global" {
|
||||
return ip.String()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return "NULL"
|
||||
}
|
||||
|
||||
// 使用指定的 DNS 服务器解析域名的 IPv6 地址
|
||||
func check_domain_ipv6(domain string) string {
|
||||
// 自定义解析器,使用 8.8.8.8 作为 DNS 服务器
|
||||
resolver := &net.Resolver{
|
||||
PreferGo: true,
|
||||
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
d := net.Dialer{
|
||||
Timeout: time.Second * 5,
|
||||
}
|
||||
return d.DialContext(ctx, "udp", "8.8.8.8:53") // 指定 Google DNS
|
||||
return d.DialContext(ctx, "udp", "8.8.8.8:53")
|
||||
},
|
||||
}
|
||||
|
||||
// 查询域名的 IP 地址
|
||||
ips, err := resolver.LookupIP(context.Background(), "ip6", domain)
|
||||
if err != nil {
|
||||
return "NULL"
|
||||
}
|
||||
|
||||
// 遍历返回的 IP 地址,返回第一个 IPv6 地址
|
||||
for _, ip := range ips {
|
||||
if ip.To4() == nil {
|
||||
return ip.String()
|
||||
@@ -125,38 +158,44 @@ func check_domain_ipv6(domain string) string {
|
||||
return "NULL"
|
||||
}
|
||||
|
||||
// 循环获取域名解析地址和当前地址,并判断是否需要更新域名解析地址
|
||||
// 循环逻辑
|
||||
func Loop(info INFO) {
|
||||
|
||||
var Domain_ipv6_addr string
|
||||
var Now_ipv6_addr string
|
||||
var currentInterface string
|
||||
|
||||
// 判断是否自动模式
|
||||
if *info._interface == "auto" {
|
||||
currentInterface, Now_ipv6_addr = AutoGetBestIPv6()
|
||||
} else {
|
||||
currentInterface = *info._interface
|
||||
Now_ipv6_addr = getIPv6ByInterface(currentInterface)
|
||||
}
|
||||
|
||||
if Now_ipv6_addr == "NULL" {
|
||||
log.Println("警告: 无法获取本机 IPv6 地址,请检查网络连接。")
|
||||
return
|
||||
}
|
||||
|
||||
Domain_ipv6_addr = check_domain_ipv6(*info._Subdomain)
|
||||
Now_ipv6_addr = pppoe_interface_ipv6(*info._interface)
|
||||
|
||||
if Now_ipv6_addr != Domain_ipv6_addr {
|
||||
log.Printf("域名解析地址: %s\n", Domain_ipv6_addr)
|
||||
log.Printf("当前地址: %s\n", Now_ipv6_addr)
|
||||
log.Printf("域名解析地址与现在地址不相等!!!\n")
|
||||
log.Printf("检测到变动! 接口[%s] IP: %s | 域名解析 IP: %s\n", currentInterface, Now_ipv6_addr, Domain_ipv6_addr)
|
||||
|
||||
RecordID := FetchSubdomainRecord(*info._Key, *info._Domain, *info._Subdomain)
|
||||
log.Printf("RecordID:%s\n", RecordID)
|
||||
if RecordID != "NULL" {
|
||||
rrid := RecordID
|
||||
rrhost := strings.Split(*info._Subdomain, ".")[0] // 获取子域名前缀 v6
|
||||
rrvalue := Now_ipv6_addr
|
||||
|
||||
r := ProcessDNSUpdateForDomain(*info._Key, *info._Domain, rrid, rrhost, rrvalue)
|
||||
if r == 1 {
|
||||
|
||||
rrhost := strings.Split(*info._Subdomain, ".")[0]
|
||||
if strings.HasSuffix(*info._Subdomain, "."+*info._Domain) {
|
||||
rrhost = strings.TrimSuffix(*info._Subdomain, "."+*info._Domain)
|
||||
}
|
||||
|
||||
rrvalue := Now_ipv6_addr
|
||||
ProcessDNSUpdateForDomain(*info._Key, *info._Domain, rrid, rrhost, rrvalue)
|
||||
}
|
||||
} else {
|
||||
log.Printf("域名解析地址: %s\n", Domain_ipv6_addr)
|
||||
log.Printf("当前地址: %s\n", Now_ipv6_addr)
|
||||
log.Printf("域名解析地址与现在地址相等,无需处理!!!\n")
|
||||
log.Printf("IP 未变动 (接口: %s, IP: %s)。无需更新。\n", currentInterface, Now_ipv6_addr)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Sleep 函数
|
||||
@@ -164,15 +203,6 @@ func Sleep(m int) {
|
||||
time.Sleep(time.Duration(m) * time.Minute)
|
||||
}
|
||||
|
||||
func TimeZone() {
|
||||
// 设置 Go 的本地时区为 Asia/Shanghai
|
||||
loc, err := time.LoadLocation("Asia/Shanghai")
|
||||
if err != nil {
|
||||
log.Fatalf("加载时区失败: %v", err)
|
||||
}
|
||||
time.Local = loc
|
||||
}
|
||||
|
||||
type INFO struct {
|
||||
_interface *string
|
||||
_Domain *string
|
||||
@@ -181,23 +211,21 @@ type INFO struct {
|
||||
}
|
||||
|
||||
func GetDaemon(_Subdomain *string) *string {
|
||||
// 使用 strings.Split 分割域名
|
||||
parts := strings.Split(*_Subdomain, ".")
|
||||
// 检查分割后的部分数量
|
||||
if len(parts) < 2 {
|
||||
fmt.Println("无效的域名")
|
||||
return nil
|
||||
}
|
||||
// 获取主域名
|
||||
mainDomain := parts[len(parts)-2] + "." + parts[len(parts)-1]
|
||||
|
||||
return &mainDomain
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
daemon := flag.Bool("d", false, "守护进程模式")
|
||||
_interface := flag.String("i", "pppoe-wan", "网卡")
|
||||
|
||||
// 修改默认值为 "auto"
|
||||
_interface := flag.String("i", "auto", "网卡接口名称 (默认 auto 自动检测)")
|
||||
|
||||
_Subdomain := flag.String("s", "v6.aixiao.me", "子域名")
|
||||
_key := flag.String("k", "NULL", "NameSilo API 密钥")
|
||||
flag.Parse()
|
||||
@@ -211,24 +239,31 @@ func main() {
|
||||
if *info._Key == "NULL" {
|
||||
envKey := os.Getenv("NAMESILO")
|
||||
if envKey != "" {
|
||||
info._Key = &envKey // 将环境变量的值赋值给 info._Key
|
||||
info._Key = &envKey
|
||||
} else {
|
||||
log.Fatalf("密钥未设置,请通过 -k 参数或 NAMESILO 环境变量提供密钥")
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("--------------------------------")
|
||||
fmt.Println("主域名:", *info._Domain)
|
||||
fmt.Println("子域名:", *info._Subdomain)
|
||||
fmt.Println("密钥:", *info._Key)
|
||||
fmt.Println("接口模式:", *info._interface)
|
||||
fmt.Println("--------------------------------")
|
||||
|
||||
// 守护进程模式
|
||||
// 守护进程模式占位
|
||||
if *daemon {
|
||||
if Daemon != nil {
|
||||
Daemon()
|
||||
}
|
||||
}
|
||||
|
||||
//TimeZone()
|
||||
for {
|
||||
// 首次运行立即检测
|
||||
Loop(info)
|
||||
|
||||
for {
|
||||
Sleep(30)
|
||||
Loop(info)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,10 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NameSilo API 响应结构
|
||||
// NameSiloAPIResponse 严格对应你提供的 JSON 返回
|
||||
type NameSiloAPIResponse struct {
|
||||
Request struct {
|
||||
Operation string `json:"operation"`
|
||||
@@ -22,29 +23,18 @@ type NameSiloAPIResponse struct {
|
||||
Type string `json:"type"`
|
||||
Host string `json:"host"`
|
||||
Value string `json:"value"`
|
||||
TTL string `json:"ttl"`
|
||||
Distance json.RawMessage `json:"distance"` // 兼容数字和字符串
|
||||
TTL int `json:"ttl"` // JSON 中显示为数字
|
||||
Distance int `json:"distance"` // JSON 中显示为数字 0
|
||||
} `json:"resource_record"`
|
||||
} `json:"reply"`
|
||||
}
|
||||
|
||||
// 解析 distance,兼容数字和字符串
|
||||
func parseDistance(raw json.RawMessage) string {
|
||||
var str string
|
||||
if err := json.Unmarshal(raw, &str); err == nil {
|
||||
return str
|
||||
}
|
||||
var num int
|
||||
if err := json.Unmarshal(raw, &num); err == nil {
|
||||
return fmt.Sprintf("%d", num)
|
||||
}
|
||||
return "0"
|
||||
}
|
||||
|
||||
// RetrieveDNSRecords 使用 Namesilo API 获取指定域名的所有 DNS 记录
|
||||
func RetrieveDNSRecords(apiKey, domain string) (*NameSiloAPIResponse, error) {
|
||||
url := fmt.Sprintf("https://www.namesilo.com/api/dnsListRecords?version=1&type=json&key=%s&domain=%s", apiKey, domain)
|
||||
|
||||
// log.Println("Requesting URL:", url) // 调试用,如果需要隐藏 Key 请注释
|
||||
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -58,38 +48,51 @@ func RetrieveDNSRecords(apiKey, domain string) (*NameSiloAPIResponse, error) {
|
||||
|
||||
var apiResponse NameSiloAPIResponse
|
||||
if err := json.Unmarshal(body, &apiResponse); err != nil {
|
||||
log.Printf("JSON 解析错误: %v\n原始数据: %s\n", err, string(body))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &apiResponse, nil
|
||||
}
|
||||
|
||||
// FetchSubdomainRecord 获取指定子域名的 DNS 记录信息
|
||||
// FetchSubdomainRecord 获取指定子域名的 DNS 记录 ID
|
||||
// Subdomain 参数通常是完整域名,如 "v6.aixiao.me"
|
||||
func FetchSubdomainRecord(key string, domain string, Subdomain string) string {
|
||||
|
||||
// 获取 DNS 记录
|
||||
// 获取 DNS 记录列表
|
||||
response, err := RetrieveDNSRecords(key, domain)
|
||||
if err != nil {
|
||||
log.Println("获取 DNS 记录失败:", err)
|
||||
log.Println("获取 DNS 记录网络请求失败:", err)
|
||||
return "NULL"
|
||||
}
|
||||
|
||||
// 打印完整 JSON 数据
|
||||
_, err = json.MarshalIndent(response, "", " ")
|
||||
if err != nil {
|
||||
log.Println("JSON 格式化失败:", err)
|
||||
// 检查 API 返回码
|
||||
if response.Reply.Code != 300 {
|
||||
log.Printf("NameSilo API 错误: Code %d, Detail: %s\n", response.Reply.Code, response.Reply.Detail)
|
||||
return "NULL"
|
||||
}
|
||||
|
||||
// 只获取指定子域名的信息
|
||||
// 计算主机头 (Host Prefix)
|
||||
// 如果 Subdomain 是 "v6.aixiao.me",Domain 是 "aixiao.me"
|
||||
// 那么我们需要匹配的 Host 应该是 "v6"
|
||||
targetHost := Subdomain
|
||||
if strings.HasSuffix(Subdomain, "."+domain) {
|
||||
targetHost = strings.TrimSuffix(Subdomain, "."+domain)
|
||||
}
|
||||
|
||||
// 遍历查找
|
||||
for _, record := range response.Reply.ResourceRecords {
|
||||
if record.Host == Subdomain {
|
||||
log.Printf("RecordID: %s, Host: %s, Type: %s, Value: %s, TTL: %s, Distance: %s\n",
|
||||
record.RecordID, record.Host, record.Type, record.Value, record.TTL, parseDistance(record.Distance))
|
||||
|
||||
// 1. 匹配主机名 (Host)
|
||||
// 2. 匹配记录类型 (Type) 必须为 AAAA (IPv6)
|
||||
if record.Host == targetHost && record.Type == "AAAA" {
|
||||
log.Printf("匹配成功! RecordID: %s | Host: %s | Type: %s | Current Value: %s\n",
|
||||
record.RecordID, record.Host, record.Type, record.Value)
|
||||
return record.RecordID
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("未在 NameSilo 找到主机头为 [%s] 的 AAAA 记录。\n", targetHost)
|
||||
log.Println("请检查 NameSilo 后台是否已手动创建了该域名的 AAAA 记录。")
|
||||
|
||||
return "NULL"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user