mirror of
https://github.com/oneclickvirt/basics.git
synced 2025-10-08 10:00:29 +08:00
fix: 适配多架构和平台
This commit is contained in:
90
ipv6/ipv6_mask_common.go
Normal file
90
ipv6/ipv6_mask_common.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/imroc/req/v3"
|
||||
)
|
||||
|
||||
// 获取第一个以 eth 或 en 开头的网络接口
|
||||
func getInterface() (string, error) {
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// 优先查找以 eth 或 en 开头的接口
|
||||
for _, iface := range interfaces {
|
||||
if strings.HasPrefix(iface.Name, "eth") || strings.HasPrefix(iface.Name, "en") {
|
||||
return iface.Name, nil
|
||||
}
|
||||
}
|
||||
// 如果没有找到,返回第一个非回环且启用的接口
|
||||
for _, iface := range interfaces {
|
||||
if iface.Flags&net.FlagLoopback == 0 && iface.Flags&net.FlagUp != 0 {
|
||||
return iface.Name, nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("未找到合适的网络接口")
|
||||
}
|
||||
|
||||
// 获取当前的公网 IPv6 地址
|
||||
func getCurrentIPv6() (string, error) {
|
||||
// 创建一个新的req客户端,设置超时
|
||||
client := req.C().SetTimeout(5 * time.Second)
|
||||
client.SetTimeout(6 * time.Second)
|
||||
// 尝试多个 IPv6 检测服务
|
||||
urls := []string{
|
||||
"https://ipv6.ip.sb",
|
||||
"https://api6.ipify.org",
|
||||
"https://v6.ident.me",
|
||||
}
|
||||
var lastErr error
|
||||
for _, url := range urls {
|
||||
resp, err := client.R().Get(url)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
continue
|
||||
}
|
||||
body := resp.String()
|
||||
ipv6 := strings.TrimSpace(body)
|
||||
if net.ParseIP(ipv6) != nil && strings.Contains(ipv6, ":") {
|
||||
return ipv6, nil
|
||||
}
|
||||
}
|
||||
if lastErr != nil {
|
||||
return "", lastErr
|
||||
}
|
||||
return "", fmt.Errorf("无法获取公网IPv6地址")
|
||||
}
|
||||
|
||||
// 判断是否为非全局单播地址前缀
|
||||
func isNonGlobalPrefix(prefix [16]byte) bool {
|
||||
// 链路本地地址 fe80::/10
|
||||
if prefix[0] == 0xfe && (prefix[1]&0xc0) == 0x80 {
|
||||
return true
|
||||
}
|
||||
// 唯一本地地址 fc00::/7
|
||||
if (prefix[0] & 0xfe) == 0xfc {
|
||||
return true
|
||||
}
|
||||
// 回环地址 ::1
|
||||
if prefix[0] == 0 && prefix[1] == 0 && prefix[15] == 1 {
|
||||
return true
|
||||
}
|
||||
// 组播地址 ff00::/8
|
||||
if prefix[0] == 0xff {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 格式化返回IPv6子网掩码
|
||||
func formatIPv6Mask(prefixLen string, language string) string {
|
||||
if language == "en" {
|
||||
return fmt.Sprintf(" IPv6 Mask : /%s", prefixLen)
|
||||
}
|
||||
return fmt.Sprintf(" IPv6 子网掩码 : /%s", prefixLen)
|
||||
}
|
123
ipv6/ipv6_mask_darwin.go
Normal file
123
ipv6/ipv6_mask_darwin.go
Normal file
@@ -0,0 +1,123 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// macOS上获取前缀长度
|
||||
func getPrefixFromIfconfig(interfaceName string) (string, error) {
|
||||
cmd := exec.Command("ifconfig", interfaceName)
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 匹配 inet6 地址和前缀长度
|
||||
re := regexp.MustCompile(`inet6\s+([a-fA-F0-9:]+)%?\w*\s+prefixlen\s+(\d+)`)
|
||||
matches := re.FindAllStringSubmatch(string(output), -1)
|
||||
|
||||
for _, match := range matches {
|
||||
if len(match) < 3 {
|
||||
continue
|
||||
}
|
||||
|
||||
ipv6Addr := match[1]
|
||||
prefixLen := match[2]
|
||||
|
||||
// 排除非公网地址前缀
|
||||
if strings.HasPrefix(ipv6Addr, "fe80") || // 链路本地地址
|
||||
strings.HasPrefix(ipv6Addr, "::1") || // 回环地址
|
||||
strings.HasPrefix(ipv6Addr, "fc") || // 本地唯一地址
|
||||
strings.HasPrefix(ipv6Addr, "fd") || // 本地唯一地址
|
||||
strings.HasPrefix(ipv6Addr, "ff") { // 组播地址
|
||||
continue
|
||||
}
|
||||
|
||||
return prefixLen, nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("未找到全局IPv6地址")
|
||||
}
|
||||
|
||||
// macOS平台上使用networksetup命令获取更多信息
|
||||
func getPrefixFromNetworksetup(interfaceName string) (string, error) {
|
||||
// 首先需要获取网络服务名称
|
||||
cmd := exec.Command("networksetup", "-listallhardwareports")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 解析输出查找对应的网络服务名称
|
||||
lines := strings.Split(string(output), "\n")
|
||||
var serviceName string
|
||||
|
||||
for i := 0; i < len(lines); i++ {
|
||||
if strings.Contains(lines[i], "Device: "+interfaceName) && i > 0 {
|
||||
// 服务名称在上一行
|
||||
serviceNameLine := lines[i-1]
|
||||
if strings.HasPrefix(serviceNameLine, "Hardware Port: ") {
|
||||
serviceName = strings.TrimPrefix(serviceNameLine, "Hardware Port: ")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if serviceName == "" {
|
||||
return "", fmt.Errorf("未找到网络接口对应的服务名称")
|
||||
}
|
||||
|
||||
// 使用服务名称获取IPv6配置
|
||||
cmd = exec.Command("networksetup", "-getinfo", serviceName)
|
||||
output, err = cmd.Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 查找IPv6前缀长度
|
||||
re := regexp.MustCompile(`IPv6:\s*Automatic\s*\nIPv6\s*Address:\s*([a-fA-F0-9:]+)\s*\nIPv6\s*Prefix\s*Length:\s*(\d+)`)
|
||||
match := re.FindStringSubmatch(string(output))
|
||||
|
||||
if len(match) >= 3 {
|
||||
return match[2], nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("未找到IPv6前缀长度信息")
|
||||
}
|
||||
|
||||
// 获取 IPv6 子网掩码 - macOS 实现
|
||||
func GetIPv6Mask(language string) (string, error) {
|
||||
// 首先检查是否有公网IPv6地址
|
||||
publicIPv6, err := getCurrentIPv6()
|
||||
if err != nil || publicIPv6 == "" {
|
||||
// 没有公网IPv6,返回空字符串
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// 获取网络接口
|
||||
interfaceName, err := getInterface()
|
||||
if err != nil || interfaceName == "" {
|
||||
return "", fmt.Errorf("获取网络接口失败: %v", err)
|
||||
}
|
||||
|
||||
// 方法1:从ifconfig获取前缀长度
|
||||
prefixLen, err := getPrefixFromIfconfig(interfaceName)
|
||||
if err == nil && prefixLen != "" {
|
||||
return formatIPv6Mask(prefixLen, language), nil
|
||||
}
|
||||
|
||||
// 方法2:从networksetup获取前缀长度
|
||||
prefixLen, err = getPrefixFromNetworksetup(interfaceName)
|
||||
if err == nil && prefixLen != "" {
|
||||
return formatIPv6Mask(prefixLen, language), nil
|
||||
}
|
||||
|
||||
// 如果以上方法都失败但确实有公网IPv6,使用/128作为默认子网掩码
|
||||
return formatIPv6Mask("128", language), nil
|
||||
}
|
92
ipv6/ipv6_mask_freebsd.go
Normal file
92
ipv6/ipv6_mask_freebsd.go
Normal file
@@ -0,0 +1,92 @@
|
||||
//go:build freebsd
|
||||
// +build freebsd
|
||||
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// FreeBSD 上获取前缀长度
|
||||
func getPrefixFromIfconfig(interfaceName string) (string, error) {
|
||||
cmd := exec.Command("ifconfig", interfaceName)
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// 匹配 inet6 地址和前缀长度
|
||||
re := regexp.MustCompile(`inet6\s+([a-fA-F0-9:]+)%?\w*\s+prefixlen\s+(\d+)`)
|
||||
matches := re.FindAllStringSubmatch(string(output), -1)
|
||||
for _, match := range matches {
|
||||
if len(match) < 3 {
|
||||
continue
|
||||
}
|
||||
ipv6Addr := match[1]
|
||||
prefixLen := match[2]
|
||||
// 排除非公网地址前缀
|
||||
if strings.HasPrefix(ipv6Addr, "fe80") || // 链路本地地址
|
||||
strings.HasPrefix(ipv6Addr, "::1") || // 回环地址
|
||||
strings.HasPrefix(ipv6Addr, "fc") || // 本地唯一地址
|
||||
strings.HasPrefix(ipv6Addr, "fd") || // 本地唯一地址
|
||||
strings.HasPrefix(ipv6Addr, "ff") { // 组播地址
|
||||
continue
|
||||
}
|
||||
return prefixLen, nil
|
||||
}
|
||||
return "", fmt.Errorf("未找到全局IPv6地址")
|
||||
}
|
||||
|
||||
// FreeBSD平台专用的配置文件获取方法
|
||||
func getPrefixFromConfigFiles() (string, error) {
|
||||
// FreeBSD 的网络配置文件
|
||||
configFiles := []string{
|
||||
"/etc/rc.conf",
|
||||
"/etc/rc.conf.local",
|
||||
}
|
||||
for _, file := range configFiles {
|
||||
if _, err := os.Stat(file); err == nil {
|
||||
content, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
// 在配置文件中查找IPv6前缀长度
|
||||
re := regexp.MustCompile(`(?i)ipv6_prefix(len)?="?(\d+)"?`)
|
||||
matches := re.FindStringSubmatch(string(content))
|
||||
if len(matches) >= 3 {
|
||||
return matches[2], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("在配置文件中未找到IPv6前缀长度信息")
|
||||
}
|
||||
|
||||
// 获取 IPv6 子网掩码 - FreeBSD 实现
|
||||
func GetIPv6Mask(language string) (string, error) {
|
||||
// 首先检查是否有公网IPv6地址
|
||||
publicIPv6, err := getCurrentIPv6()
|
||||
if err != nil || publicIPv6 == "" {
|
||||
// 没有公网IPv6,返回空字符串
|
||||
return "", nil
|
||||
}
|
||||
// 获取网络接口
|
||||
interfaceName, err := getInterface()
|
||||
if err != nil || interfaceName == "" {
|
||||
return "", fmt.Errorf("获取网络接口失败: %v", err)
|
||||
}
|
||||
// 方法1:从ifconfig获取前缀长度
|
||||
prefixLen, err := getPrefixFromIfconfig(interfaceName)
|
||||
if err == nil && prefixLen != "" {
|
||||
return formatIPv6Mask(prefixLen, language), nil
|
||||
}
|
||||
// 方法2:从配置文件获取前缀长度
|
||||
prefixLen, err = getPrefixFromConfigFiles()
|
||||
if err == nil && prefixLen != "" {
|
||||
return formatIPv6Mask(prefixLen, language), nil
|
||||
}
|
||||
// 如果以上方法都失败但确实有公网IPv6,使用/128作为默认子网掩码
|
||||
return formatIPv6Mask("128", language), nil
|
||||
}
|
@@ -1,3 +1,6 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
@@ -13,61 +16,13 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// 获取第一个以 eth 或 en 开头的网络接口
|
||||
func getInterface() (string, error) {
|
||||
cmd := exec.Command("sh", "-c", "ls /sys/class/net/ | grep -E '^(eth|en)' | head -n 1")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(string(output)), nil
|
||||
}
|
||||
|
||||
// 获取当前的公网 IPv6 地址
|
||||
func getCurrentIPv6() (string, error) {
|
||||
cmd := exec.Command("curl", "-s", "-6", "-m", "5", "ipv6.ip.sb")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(string(output)), nil
|
||||
}
|
||||
|
||||
// Router Advertisement前缀选项类型
|
||||
const (
|
||||
ICMPv6RouterAdvertisement = 134
|
||||
ICMPv6RouterSolicitation = 133
|
||||
ICMPv6OptionPrefix = 3
|
||||
)
|
||||
|
||||
// ICMPv6报文头部结构
|
||||
type ICMPv6Header struct {
|
||||
Type uint8
|
||||
Code uint8
|
||||
Checksum uint16
|
||||
}
|
||||
|
||||
// Router Advertisement报文结构
|
||||
type RouterAdvertisement struct {
|
||||
CurHopLimit uint8
|
||||
Flags uint8
|
||||
RouterLifetime uint16
|
||||
ReachableTime uint32
|
||||
RetransTimer uint32
|
||||
Options []byte
|
||||
}
|
||||
|
||||
// 前缀信息选项结构
|
||||
type PrefixInfoOption struct {
|
||||
Type uint8
|
||||
Length uint8
|
||||
PrefixLength uint8
|
||||
Flags uint8
|
||||
ValidLifetime uint32
|
||||
PreferredLifetime uint32
|
||||
Reserved uint32
|
||||
Prefix [16]byte
|
||||
}
|
||||
|
||||
// 从RA报文中提取前缀长度信息
|
||||
func extractPrefixFromRAOption(data []byte) []string {
|
||||
var prefixLengths []string
|
||||
@@ -105,25 +60,41 @@ func extractPrefixFromRAOption(data []byte) []string {
|
||||
return prefixLengths
|
||||
}
|
||||
|
||||
// 判断是否为非全局单播地址前缀
|
||||
func isNonGlobalPrefix(prefix [16]byte) bool {
|
||||
// 链路本地地址 fe80::/10
|
||||
if prefix[0] == 0xfe && (prefix[1]&0xc0) == 0x80 {
|
||||
return true
|
||||
// 发送Router Solicitation消息
|
||||
func sendRouterSolicitation(fd int, interfaceName string) error {
|
||||
// 获取接口信息
|
||||
intf, err := net.InterfaceByName(interfaceName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取接口信息失败: %v", err)
|
||||
}
|
||||
// 唯一本地地址 fc00::/7
|
||||
if (prefix[0] & 0xfe) == 0xfc {
|
||||
return true
|
||||
// 构造ICMPv6 Router Solicitation消息
|
||||
// ICMPv6头部(4字节): 类型(1字节) + 代码(1字节) + 校验和(2字节)
|
||||
msg := make([]byte, 8)
|
||||
msg[0] = ICMPv6RouterSolicitation // 类型:Router Solicitation
|
||||
msg[1] = 0 // 代码:0
|
||||
// msg[2]和msg[3]是校验和字段,暂时为0,稍后计算
|
||||
// 可选:添加Source Link-Layer Address选项(MAC地址)
|
||||
// 这对一些路由器来说可能是必要的
|
||||
if len(intf.HardwareAddr) == 6 { // 确保MAC地址有效
|
||||
// 添加源链路层地址选项
|
||||
// 选项类型(1) + 长度(1) + MAC地址(6)
|
||||
msg = append(msg, 1) // 选项类型:1 (Source Link-Layer Address)
|
||||
msg = append(msg, 1) // 长度:1 (以8字节为单位,这里是8字节)
|
||||
msg = append(msg, intf.HardwareAddr...) // MAC地址
|
||||
msg = append(msg, 0, 0) // 填充到8字节对齐
|
||||
}
|
||||
// 回环地址 ::1
|
||||
if prefix[0] == 0 && prefix[1] == 0 && prefix[15] == 1 {
|
||||
return true
|
||||
// 设置ICMPv6的目标地址为All Routers组播地址
|
||||
var allRoutersAddr [16]byte
|
||||
copy(allRoutersAddr[:], net.ParseIP("ff02::2").To16())
|
||||
// 构造sockaddr_in6结构
|
||||
var addr syscall.SockaddrInet6
|
||||
addr.ZoneId = uint32(intf.Index)
|
||||
copy(addr.Addr[:], allRoutersAddr[:])
|
||||
// 发送数据包
|
||||
if err := syscall.Sendto(fd, msg, 0, &addr); err != nil {
|
||||
return fmt.Errorf("发送Router Solicitation失败: %v", err)
|
||||
}
|
||||
// 组播地址 ff00::/8
|
||||
if prefix[0] == 0xff {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
// 方法1:尝试原生实现从Router Advertisement获取前缀长度
|
||||
@@ -181,10 +152,10 @@ func getPrefixFromRA(interfaceName string) (string, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
// 发送Router Solicitation
|
||||
go func() {
|
||||
// 实际应用中可能需要构造并发送Router Solicitation消息
|
||||
// 这里为简化代码,仅依赖网络上周期性的Router Advertisement
|
||||
}()
|
||||
err = sendRouterSolicitation(fd, interfaceName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("发送Router Solicitation失败: %v", err)
|
||||
}
|
||||
// 接收Router Advertisement
|
||||
buffer := make([]byte, 1500)
|
||||
for {
|
||||
@@ -252,7 +223,7 @@ func getPrefixFromIPCommand(interfaceName string) (string, error) {
|
||||
return "", fmt.Errorf("未找到有效的IPv6前缀长度")
|
||||
}
|
||||
|
||||
// 方法3:从配置文件获取前缀长度
|
||||
// Linux平台专用的配置文件获取方法
|
||||
func getPrefixFromConfigFiles() (string, error) {
|
||||
// 尝试从常见的网络配置文件中读取
|
||||
configFiles := []string{
|
||||
@@ -278,8 +249,7 @@ func getPrefixFromConfigFiles() (string, error) {
|
||||
return "", fmt.Errorf("在配置文件中未找到IPv6前缀长度信息")
|
||||
}
|
||||
|
||||
// 获取 IPv6 子网掩码
|
||||
// 获取 IPv6 子网掩码
|
||||
// 获取 IPv6 子网掩码 - Linux 实现
|
||||
func GetIPv6Mask(language string) (string, error) {
|
||||
// 首先检查是否有公网IPv6地址
|
||||
publicIPv6, err := getCurrentIPv6()
|
||||
@@ -295,30 +265,18 @@ func GetIPv6Mask(language string) (string, error) {
|
||||
// 优先级1:尝试从RA报文获取前缀长度
|
||||
prefixLen, err := getPrefixFromRA(interfaceName)
|
||||
if err == nil && prefixLen != "" {
|
||||
if language == "en" {
|
||||
return fmt.Sprintf(" IPv6 Mask : /%s", prefixLen), nil
|
||||
}
|
||||
return fmt.Sprintf(" IPv6 子网掩码 : /%s", prefixLen), nil
|
||||
return formatIPv6Mask(prefixLen, language), nil
|
||||
}
|
||||
// 优先级2:从ip命令获取前缀长度
|
||||
prefixLen, err = getPrefixFromIPCommand(interfaceName)
|
||||
if err == nil && prefixLen != "" {
|
||||
if language == "en" {
|
||||
return fmt.Sprintf(" IPv6 Mask : /%s", prefixLen), nil
|
||||
}
|
||||
return fmt.Sprintf(" IPv6 子网掩码 : /%s", prefixLen), nil
|
||||
return formatIPv6Mask(prefixLen, language), nil
|
||||
}
|
||||
// 优先级3:从配置文件获取前缀长度
|
||||
prefixLen, err = getPrefixFromConfigFiles()
|
||||
if err == nil && prefixLen != "" {
|
||||
if language == "en" {
|
||||
return fmt.Sprintf(" IPv6 Mask : /%s", prefixLen), nil
|
||||
}
|
||||
return fmt.Sprintf(" IPv6 子网掩码 : /%s", prefixLen), nil
|
||||
return formatIPv6Mask(prefixLen, language), nil
|
||||
}
|
||||
// 如果以上方法都失败但确实有公网IPv6,使用/128作为默认子网掩码
|
||||
if language == "en" {
|
||||
return " IPv6 Mask : /128", nil
|
||||
}
|
||||
return " IPv6 子网掩码 : /128", nil
|
||||
return formatIPv6Mask("128", language), nil
|
||||
}
|
109
ipv6/ipv6_mask_windows.go
Normal file
109
ipv6/ipv6_mask_windows.go
Normal file
@@ -0,0 +1,109 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Windows上获取前缀长度
|
||||
func getPrefixFromNetsh(interfaceName string) (string, error) {
|
||||
// Windows上使用netsh命令获取IPv6配置
|
||||
cmd := exec.Command("netsh", "interface", "ipv6", "show", "addresses")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// 转换输出为字符串并按行分割
|
||||
lines := strings.Split(string(output), "\n")
|
||||
// 解析输出查找IPv6地址和前缀长度
|
||||
currentInterface := ""
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
// 查找接口名称
|
||||
if strings.HasSuffix(line, ":") {
|
||||
currentInterface = strings.TrimSuffix(line, ":")
|
||||
continue
|
||||
}
|
||||
// 只处理匹配的接口
|
||||
if currentInterface == "" || !strings.Contains(strings.ToLower(currentInterface), strings.ToLower(interfaceName)) {
|
||||
continue
|
||||
}
|
||||
// 查找地址和前缀长度
|
||||
if strings.Contains(line, "Address") && strings.Contains(line, "Parameters") {
|
||||
re := regexp.MustCompile(`([a-fA-F0-9:]+)/(\d+)`)
|
||||
match := re.FindStringSubmatch(line)
|
||||
if len(match) >= 3 {
|
||||
ipv6Addr := match[1]
|
||||
prefixLen := match[2]
|
||||
// 排除非公网地址前缀
|
||||
if strings.HasPrefix(ipv6Addr, "fe80") || // 链路本地地址
|
||||
strings.HasPrefix(ipv6Addr, "::1") || // 回环地址
|
||||
strings.HasPrefix(ipv6Addr, "fc") || // 本地唯一地址
|
||||
strings.HasPrefix(ipv6Addr, "fd") || // 本地唯一地址
|
||||
strings.HasPrefix(ipv6Addr, "ff") { // 组播地址
|
||||
continue
|
||||
}
|
||||
return prefixLen, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("未找到全局IPv6地址")
|
||||
}
|
||||
|
||||
// Windows特有的PowerShell方法获取IPv6信息
|
||||
func getPrefixFromPowerShell(interfaceName string) (string, error) {
|
||||
// 使用PowerShell获取网络适配器信息
|
||||
cmd := exec.Command("powershell", "-Command",
|
||||
"Get-NetIPAddress -AddressFamily IPv6 | Where-Object { $_.InterfaceAlias -like '*"+interfaceName+"*' -and $_.PrefixOrigin -ne 'WellKnown' } | Select-Object IPAddress, PrefixLength | ConvertTo-Json")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
jsonStr := string(output)
|
||||
rePrefixLength := regexp.MustCompile(`"PrefixLength"\s*:\s*(\d+)`)
|
||||
reIPAddress := regexp.MustCompile(`"IPAddress"\s*:\s*"([^"]+)"`)
|
||||
prefixMatches := rePrefixLength.FindAllStringSubmatch(jsonStr, -1)
|
||||
ipMatches := reIPAddress.FindAllStringSubmatch(jsonStr, -1)
|
||||
if len(prefixMatches) != len(ipMatches) {
|
||||
return "", fmt.Errorf("解析PowerShell输出失败")
|
||||
}
|
||||
for i := 0; i < len(ipMatches); i++ {
|
||||
ipv6Addr := ipMatches[i][1]
|
||||
// 排除非公网地址前缀
|
||||
if strings.HasPrefix(ipv6Addr, "fe80") || // 链路本地地址
|
||||
strings.HasPrefix(ipv6Addr, "::1") || // 回环地址
|
||||
strings.HasPrefix(ipv6Addr, "fc") || // 本地唯一地址
|
||||
strings.HasPrefix(ipv6Addr, "fd") || // 本地唯一地址
|
||||
strings.HasPrefix(ipv6Addr, "ff") { // 组播地址
|
||||
continue
|
||||
}
|
||||
return prefixMatches[i][1], nil
|
||||
}
|
||||
return "", fmt.Errorf("未找到全局IPv6地址")
|
||||
}
|
||||
|
||||
// 获取 IPv6 子网掩码 - Windows 实现
|
||||
func GetIPv6Mask(language string) (string, error) {
|
||||
publicIPv6, err := getCurrentIPv6()
|
||||
if err != nil || publicIPv6 == "" {
|
||||
return "", nil
|
||||
}
|
||||
interfaceName, err := getInterface()
|
||||
if err != nil || interfaceName == "" {
|
||||
return "", fmt.Errorf("获取网络接口失败: %v", err)
|
||||
}
|
||||
prefixLen, err := getPrefixFromNetsh(interfaceName)
|
||||
if err == nil && prefixLen != "" {
|
||||
return formatIPv6Mask(prefixLen, language), nil
|
||||
}
|
||||
prefixLen, err = getPrefixFromPowerShell(interfaceName)
|
||||
if err == nil && prefixLen != "" {
|
||||
return formatIPv6Mask(prefixLen, language), nil
|
||||
}
|
||||
return formatIPv6Mask("128", language), nil
|
||||
}
|
@@ -24,7 +24,6 @@ func FetchJsonFromURL(url, netType string, enableHeader bool, additionalHeader s
|
||||
if netType != "tcp4" && netType != "tcp6" {
|
||||
return nil, fmt.Errorf("Invalid netType: %s. Expected 'tcp4' or 'tcp6'.", netType)
|
||||
}
|
||||
|
||||
// 创建 HTTP 客户端
|
||||
client := req.C()
|
||||
client.SetTimeout(7 * time.Second).
|
||||
|
Reference in New Issue
Block a user