289 lines
6.3 KiB
Go
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)
|
|
}
|