235 lines
5.5 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"context"
"flag"
"fmt"
"log"
"net"
"os"
"strings"
"time"
)
// 判断IPv6 地址类型
func classifyIPv6(ip net.IP) string {
if ip.IsLoopback() {
return "Loopback"
} else if ip.IsLinkLocalUnicast() {
return "Link-Local"
} else if ip.IsGlobalUnicast() {
return "Global"
}
return "Unknown"
}
// getIPv6Addresses 获取网络接口的 IPv6 地址。
// 它会跳过回环接口,并且只考虑已启用的接口。
func getIPv6Addresses() (map[string][]net.IP, error) {
// 创建一个 map 用于存储接口名称及其对应的 IPv6 地址。
addresses := make(map[string][]net.IP)
// 获取系统上的所有网络接口。
interfaces, err := net.Interfaces()
if err != nil {
return nil, err // 如果无法获取接口信息,则返回错误。
}
// 遍历所有网络接口。
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 // 如果无法获取地址,则返回错误。
}
// 遍历该接口的所有地址。
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP // 从 IPNet 类型中提取 IP。
case *net.IPAddr:
ip = v.IP // 从 IPAddr 类型中提取 IP。
}
// 检查 IP 是否为 IPv6 地址(即不是 IPv4
if ip != nil && ip.To4() == nil {
// 将 IPv6 地址添加到对应的接口名称列表中。
addresses[iface.Name] = append(addresses[iface.Name], ip)
}
}
}
return addresses, nil // 返回包含 IPv6 地址的映射。
}
// 获取指定接口的 IPv6 地址
func pppoe_interface_ipv6(_interface string) string {
addresses, err := getIPv6Addresses()
if err != nil {
log.Printf("获取 IPv6 地址失败: %v\n", 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)
if addrType == "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
},
}
// 查询域名的 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()
}
}
return "NULL"
}
// 循环获取域名解析地址和当前地址,并判断是否需要更新域名解析地址
func Loop(info INFO) {
var Domain_ipv6_addr string
var Now_ipv6_addr string
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")
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 {
}
}
} else {
log.Printf("域名解析地址: %s\n", Domain_ipv6_addr)
log.Printf("当前地址: %s\n", Now_ipv6_addr)
log.Printf("域名解析地址与现在地址相等,无需处理!!!\n")
}
}
// Sleep 函数
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
_Subdomain *string
_Key *string
}
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", "网卡")
_Subdomain := flag.String("s", "v6.aixiao.me", "子域名")
_key := flag.String("k", "NULL", "NameSilo API 密钥")
flag.Parse()
var info INFO
info._interface = _interface
info._Subdomain = _Subdomain
info._Key = _key
info._Domain = GetDaemon(_Subdomain)
if *info._Key == "NULL" {
envKey := os.Getenv("NAMESILO")
if envKey != "" {
info._Key = &envKey // 将环境变量的值赋值给 info._Key
} else {
log.Fatalf("密钥未设置,请通过 -k 参数或 NAMESILO 环境变量提供密钥")
}
}
fmt.Println("主域名:", *info._Domain)
fmt.Println("子域名:", *info._Subdomain)
fmt.Println("密钥:", *info._Key)
// 守护进程模式
if *daemon {
Daemon()
}
//TimeZone()
for {
Loop(info)
Sleep(30)
}
}