// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn . package utils import ( "bytes" "encoding/json" "errors" "github.com/iwind/TeaGo/lists" stringutil "github.com/iwind/TeaGo/utils/string" "net" "os" "regexp" "runtime" "sort" "strings" "time" ) // RequestKey 申请码 type RequestKey struct { MacAddresses []string `json:"macAddresses"` // MAC 排序后内容 MachineId string `json:"machineId"` // /etc/machine-id HardwareUUID string `json:"hardwareUUID"` // hardware disk uuid } // GenerateRequestKey 生成请求Key func GenerateRequestKey() (*RequestKey, error) { // mac addresses netInterfaces, err := findAllNetInterfaces() if err != nil { return nil, errors.New("could not generate request key (code: 001)") } var macAddrs = []string{} for _, netInterface := range netInterfaces { var macAddr = strings.TrimSpace(netInterface.HardwareAddr.String()) if len(macAddr) == 0 { continue } if !lists.ContainsString(macAddrs, macAddr) { macAddrs = append(macAddrs, macAddr) } } if len(macAddrs) == 0 { return nil, errors.New("could not generate request key (code: 002)") } sort.Strings(macAddrs) // machine id var machineId = "" var machineIdFile = "/etc/machine-id" stat, err := os.Stat(machineIdFile) if err == nil && !stat.IsDir() { data, err := os.ReadFile(machineIdFile) data = bytes.TrimSpace(data) if err == nil && len(data) <= 32 { machineId = string(data) } } return &RequestKey{ MacAddresses: macAddrs, MachineId: machineId, HardwareUUID: generateHardwareUUID(), }, nil } // GenerateRequestCode 生成请求Key代码 func GenerateRequestCode() (string, error) { key, err := GenerateRequestKey() if err != nil { return "", err } keyJSON, err := json.Marshal(key) if err != nil { return "", errors.New("could not generate request code (code: 001)") } return Encode(keyJSON) } // DecodeRequestCode 解析请求Key代码 func DecodeRequestCode(requestCode string) (*RequestKey, error) { requestCode = regexp.MustCompile(`\s+`).ReplaceAllString(requestCode, "") if requestCode == "*" { return &RequestKey{ MacAddresses: nil, MachineId: "", }, nil } m, err := DecodeData([]byte(requestCode)) if err != nil { return nil, err } jsonData, err := json.Marshal(m) if err != nil { return nil, err } var key = &RequestKey{} err = json.Unmarshal(jsonData, key) return key, err } // ValidateRequestCode 校验请求Key代码 func ValidateRequestCode(requestCode string) (ok bool, errorCode string) { requestCode = regexp.MustCompile(`\s+`).ReplaceAllString(requestCode, "") if requestCode == "*" { return true, "" } key, err := DecodeRequestCode(requestCode) if err != nil { return false, "001" } // check machine id if len(key.MachineId) > 0 { // machine id var machineId = "" var machineIdFile = "/etc/machine-id" stat, err := os.Stat(machineIdFile) if err == nil && !stat.IsDir() { data, err := os.ReadFile(machineIdFile) data = bytes.TrimSpace(data) if err == nil && len(data) <= 32 { machineId = string(data) } } if machineId != key.MachineId { return false, "004" } } // hardware uuid if len(key.HardwareUUID) > 0 && key.HardwareUUID == generateHardwareUUID() { return true, "" } // mac addresses netInterfaces, err := findAllNetInterfaces() if err != nil { return false, "002" } // remove net interfaces related to docker { var cmd = NewTimeoutCmd(5*time.Second, "docker", "network", "ls") cmd.WithStdout() err = cmd.Run() if err == nil { var dockerIdRegexp = regexp.MustCompile(`^[0-9a-f]{12,}$`) var spaceRegexp = regexp.MustCompile(`\s+`) var stdoutLines = strings.Split(cmd.Stdout(), "\n") var dockerInterfaceIds []string var dockerInterfaceNames []string for _, line := range stdoutLines { var pieces = spaceRegexp.Split(strings.TrimSpace(line), -1) if len(pieces) <= 3 { continue } var piece0 = strings.TrimSpace(pieces[0]) if !dockerIdRegexp.MatchString(piece0) { continue } dockerInterfaceIds = append(dockerInterfaceIds, piece0) var piece1 = strings.TrimSpace(pieces[1]) if len(piece1) > 0 { dockerInterfaceNames = append(dockerInterfaceNames, piece1) } } var newInterfaces []net.Interface for _, i := range netInterfaces { var skip bool for _, dockerInterfaceId := range dockerInterfaceIds { if strings.HasSuffix(i.Name, dockerInterfaceId) { skip = true break } } if !skip { skip = lists.ContainsString(dockerInterfaceNames, i.Name) } if skip { continue } newInterfaces = append(newInterfaces, i) } netInterfaces = newInterfaces } } var allMACAddresses = []string{} for _, netInterface := range netInterfaces { var macAddr = strings.TrimSpace(netInterface.HardwareAddr.String()) if len(macAddr) == 0 { continue } allMACAddresses = append(allMACAddresses, macAddr) } // check mac addresses for _, macAddress := range allMACAddresses { if !lists.ContainsString(key.MacAddresses, macAddress) { return false, "003" } } return true, "" } func findAllNetInterfaces() ([]net.Interface, error) { interfaces, err := net.Interfaces() if err != nil { return nil, err } var dockerReg = regexp.MustCompile(`^docker\d+$`) var resultInterfaces []net.Interface for _, i := range interfaces { if i.Flags&net.FlagLoopback == net.FlagLoopback { continue } // ignore docker if dockerReg.MatchString(i.Name) { continue } if i.Flags&net.FlagUp == net.FlagUp { resultInterfaces = append(resultInterfaces, i) } } return resultInterfaces, nil } func generateHardwareUUID() string { if runtime.GOOS != "linux" { return "" } var diskUUID string { var cmd = NewCmd("ls", "/dev/disk/by-uuid") cmd.WithStdout() err := cmd.Run() if err != nil { return "" } var stdout = strings.TrimSpace(cmd.Stdout()) if len(stdout) == 0 { return "" } var pieces = regexp.MustCompile(`\s+`).Split(stdout, -1) sort.Strings(pieces) diskUUID = stringutil.Md5(strings.Join(pieces, "\n")) } var hardwareUUID string { var cmd = NewCmd("dmidecode") cmd.WithStdout() err := cmd.Run() if err != nil { return "" } var stdout = strings.TrimSpace(cmd.Stdout()) if len(stdout) == 0 { return "" } hardwareUUID = stringutil.Md5(stdout) } return stringutil.Md5(diskUUID + hardwareUUID) }