Files
waf-platform/EdgePlus/pkg/utils/request_key.go
2026-02-04 20:27:13 +08:00

289 lines
6.3 KiB
Go

// 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)
}