mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2026-04-22 23:27:15 +08:00
chore: better uuid generation
This commit is contained in:
@@ -69,7 +69,7 @@ func NewBase(opt BaseOption) *Base {
|
||||
iface: opt.Interface,
|
||||
rmark: opt.RoutingMark,
|
||||
prefer: opt.Prefer,
|
||||
id: utils.NewUUIDV6(),
|
||||
id: utils.NewUUIDV7(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+123
-49
@@ -1,71 +1,145 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"encoding/binary"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/metacubex/randv2"
|
||||
)
|
||||
|
||||
func UnsafeRandRead(p []byte) {
|
||||
for len(p) > 0 {
|
||||
v := randv2.Uint64()
|
||||
if v == 0 {
|
||||
continue
|
||||
}
|
||||
i := copy(p, (*[8]byte)(unsafe.Pointer(&v))[:])
|
||||
p = p[i:]
|
||||
}
|
||||
}
|
||||
|
||||
type unsafeRandReader struct{}
|
||||
|
||||
func (r unsafeRandReader) Read(p []byte) (n int, err error) {
|
||||
// modify from https://github.com/golang/go/blob/587c3847da81aa7cfc3b3db2677c8586c94df13a/src/runtime/rand.go#L70-L89
|
||||
// Inspired by wyrand.
|
||||
n = len(p)
|
||||
v := randv2.Uint64()
|
||||
for len(p) > 0 {
|
||||
v ^= 0xa0761d6478bd642f
|
||||
v *= 0xe7037ed1a0b428db
|
||||
size := 8
|
||||
if len(p) < 8 {
|
||||
size = len(p)
|
||||
}
|
||||
for i := 0; i < size; i++ {
|
||||
p[i] ^= byte(v >> (8 * i))
|
||||
}
|
||||
p = p[size:]
|
||||
v = v>>32 | v<<32
|
||||
func (r *unsafeRandReader) Read(p []byte) (n int, err error) {
|
||||
UnsafeRandRead(p)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
var UnsafeRandReader = (*unsafeRandReader)(nil)
|
||||
|
||||
// NewUUIDV3 returns a UUID based on the MD5 hash of the namespace UUID and name.
|
||||
func NewUUIDV3(ns uuid.UUID, name string) (u uuid.UUID) {
|
||||
h := md5.New()
|
||||
h.Write(ns[:])
|
||||
h.Write([]byte(name))
|
||||
copy(u[:], h.Sum(make([]byte, 0, md5.Size)))
|
||||
|
||||
u.SetVersion(uuid.V3)
|
||||
u.SetVariant(uuid.VariantRFC9562)
|
||||
return u
|
||||
}
|
||||
|
||||
// NewUUIDV4 returns a new version 4 UUID.
|
||||
//
|
||||
// Version 4 UUIDs contain 122 bits of random data.
|
||||
func NewUUIDV4() (u uuid.UUID) {
|
||||
UnsafeRandRead(u[:])
|
||||
u.SetVersion(uuid.V4)
|
||||
u.SetVariant(uuid.VariantRFC9562)
|
||||
return u
|
||||
}
|
||||
|
||||
// NewUUIDV5 returns a UUID based on SHA-1 hash of the namespace UUID and name.
|
||||
func NewUUIDV5(ns uuid.UUID, name string) (u uuid.UUID) {
|
||||
h := sha1.New()
|
||||
h.Write(ns[:])
|
||||
h.Write([]byte(name))
|
||||
copy(u[:], h.Sum(make([]byte, 0, sha1.Size)))
|
||||
|
||||
u.SetVersion(uuid.V5)
|
||||
u.SetVariant(uuid.VariantRFC9562)
|
||||
return u
|
||||
}
|
||||
|
||||
var (
|
||||
v7mu sync.Mutex
|
||||
v7lastSecs uint64
|
||||
v7lastTimestamp uint64
|
||||
)
|
||||
|
||||
// NewUUIDV7 returns a new version 7 UUID.
|
||||
//
|
||||
// Version 7 UUIDs contain a timestamp in the most significant 48 bits,
|
||||
// and at least 62 bits of random data.
|
||||
//
|
||||
// NewUUIDV7 always returns UUIDs which sort in increasing order,
|
||||
// except when the system clock moves backwards.
|
||||
func NewUUIDV7() (u uuid.UUID) {
|
||||
// UUIDv7 is defined in RFC 9562 section 5.7 as:
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | unix_ts_ms |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | unix_ts_ms | ver | rand_a |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |var| rand_b |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | rand_b |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// We store a 12 bit sub-millisecond timestamp fraction in the rand_a section,
|
||||
// as optionally permitted by the RFC.
|
||||
v7mu.Lock()
|
||||
|
||||
// Generate our 60-bit timestamp: 48 bits of millisecond-resolution,
|
||||
// followed by 12 bits of 1/4096-millisecond resolution.
|
||||
now := time.Now()
|
||||
secs := uint64(now.Unix())
|
||||
nanos := uint64(now.Nanosecond())
|
||||
msecs := nanos / 1000000
|
||||
frac := nanos - (1000000 * msecs)
|
||||
timestamp := (1000*secs + msecs) << 12 // ms shifted into position
|
||||
timestamp += (frac * 4096) / 1000000 // ns converted to 1/4096-ms units
|
||||
|
||||
if v7lastSecs > secs {
|
||||
// Time has gone backwards.
|
||||
// This presumably indicates the system clock has changed.
|
||||
// Ignore previously-generated UUIDs.
|
||||
} else if timestamp <= v7lastTimestamp {
|
||||
// This timestamp is the same as a previously-generated UUID.
|
||||
// To preserve the property that we generate UUIDs in order,
|
||||
// use a timestamp 1/4096 millisecond later than the most recently
|
||||
// generated UUID.
|
||||
timestamp = v7lastTimestamp + 1
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
v7lastSecs = secs
|
||||
v7lastTimestamp = timestamp
|
||||
v7mu.Unlock()
|
||||
|
||||
var UnsafeRandReader = unsafeRandReader{}
|
||||
// Insert a gap for the 4 bits of the ver field into the timestamp.
|
||||
hibits := ((timestamp << 4) & 0xffff_ffff_ffff_0000) | (timestamp & 0x0ffff)
|
||||
|
||||
var UnsafeUUIDGenerator = uuid.NewGenWithOptions(uuid.WithRandomReader(UnsafeRandReader))
|
||||
binary.BigEndian.PutUint64(u[0:8], hibits)
|
||||
UnsafeRandRead(u[8:])
|
||||
|
||||
func NewUUIDV1() uuid.UUID {
|
||||
u, _ := UnsafeUUIDGenerator.NewV1() // unsafeRandReader wouldn't cause error, so ignore err is safe
|
||||
return u
|
||||
}
|
||||
|
||||
func NewUUIDV3(ns uuid.UUID, name string) uuid.UUID {
|
||||
return UnsafeUUIDGenerator.NewV3(ns, name)
|
||||
}
|
||||
|
||||
func NewUUIDV4() uuid.UUID {
|
||||
u, _ := UnsafeUUIDGenerator.NewV4() // unsafeRandReader wouldn't cause error, so ignore err is safe
|
||||
return u
|
||||
}
|
||||
|
||||
func NewUUIDV5(ns uuid.UUID, name string) uuid.UUID {
|
||||
return UnsafeUUIDGenerator.NewV5(ns, name)
|
||||
}
|
||||
|
||||
func NewUUIDV6() uuid.UUID {
|
||||
u, _ := UnsafeUUIDGenerator.NewV6() // unsafeRandReader wouldn't cause error, so ignore err is safe
|
||||
return u
|
||||
}
|
||||
|
||||
func NewUUIDV7() uuid.UUID {
|
||||
u, _ := UnsafeUUIDGenerator.NewV7() // unsafeRandReader wouldn't cause error, so ignore err is safe
|
||||
u.SetVersion(uuid.V7)
|
||||
u.SetVariant(uuid.VariantRFC9562)
|
||||
return u
|
||||
}
|
||||
|
||||
// UUIDMap https://github.com/XTLS/Xray-core/issues/158#issue-783294090
|
||||
func UUIDMap(str string) (uuid.UUID, error) {
|
||||
func UUIDMap(str string) uuid.UUID {
|
||||
u, err := uuid.FromString(str)
|
||||
if err != nil {
|
||||
return NewUUIDV5(uuid.Nil, str), nil
|
||||
return NewUUIDV5(uuid.Nil, str)
|
||||
}
|
||||
return u, nil
|
||||
return u
|
||||
}
|
||||
|
||||
+19
-16
@@ -1,11 +1,23 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
)
|
||||
|
||||
func TestUnsafeRandRead(t *testing.T) {
|
||||
for i := 1; i < 100; i++ {
|
||||
data := make([]byte, i)
|
||||
UnsafeRandRead(data)
|
||||
if bytes.Equal(data, make([]byte, i)) {
|
||||
t.Fatal("UnsafeRandRead should not return all zero bytes")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUUIDMap(t *testing.T) {
|
||||
type args struct {
|
||||
str string
|
||||
@@ -22,24 +34,21 @@ func TestUUIDMap(t *testing.T) {
|
||||
args: args{
|
||||
str: "82410302-039e-41b6-98b0-d964084b4170",
|
||||
},
|
||||
want: uuid.FromStringOrNil("82410302-039e-41b6-98b0-d964084b4170"),
|
||||
wantErr: false,
|
||||
want: uuid.FromStringOrNil("82410302-039e-41b6-98b0-d964084b4170"),
|
||||
},
|
||||
{
|
||||
name: "uuid-test-2",
|
||||
args: args{
|
||||
str: "88c502e6-d7eb-4c8e-8259-94cb13d83c77",
|
||||
},
|
||||
want: uuid.FromStringOrNil("88c502e6-d7eb-4c8e-8259-94cb13d83c77"),
|
||||
wantErr: false,
|
||||
want: uuid.FromStringOrNil("88c502e6-d7eb-4c8e-8259-94cb13d83c77"),
|
||||
},
|
||||
{
|
||||
name: "uuid-map-1",
|
||||
args: args{
|
||||
str: "123456",
|
||||
},
|
||||
want: uuid.FromStringOrNil("f8598425-92f2-5508-a071-4fc67f9040ac"),
|
||||
wantErr: false,
|
||||
want: uuid.FromStringOrNil("f8598425-92f2-5508-a071-4fc67f9040ac"),
|
||||
},
|
||||
// GENERATED BY 'xray uuid -i'
|
||||
{
|
||||
@@ -47,25 +56,19 @@ func TestUUIDMap(t *testing.T) {
|
||||
args: args{
|
||||
str: "a9dk23bz0",
|
||||
},
|
||||
want: uuid.FromStringOrNil("c91481b6-fc0f-5d9e-b166-5ddf07b9c3c5"),
|
||||
wantErr: false,
|
||||
want: uuid.FromStringOrNil("c91481b6-fc0f-5d9e-b166-5ddf07b9c3c5"),
|
||||
},
|
||||
{
|
||||
name: "uuid-map-2",
|
||||
args: args{
|
||||
str: "中文123",
|
||||
},
|
||||
want: uuid.FromStringOrNil("145c544c-2229-59e5-8dbb-3f33b7610d26"),
|
||||
wantErr: false,
|
||||
want: uuid.FromStringOrNil("145c544c-2229-59e5-8dbb-3f33b7610d26"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := UUIDMap(tt.args.str)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("UUIDMap() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
got := UUIDMap(tt.args.str)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("UUIDMap() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/metacubex/mihomo/common/utils"
|
||||
"github.com/metacubex/mihomo/transport/vless"
|
||||
"github.com/metacubex/mihomo/transport/vless/vision"
|
||||
|
||||
@@ -44,10 +45,7 @@ func (s *Service[T]) UpdateUsers(userList []T, userUUIDList []string, userFlowLi
|
||||
userMap := make(map[[16]byte]T)
|
||||
userFlowMap := make(map[T]string)
|
||||
for i, userName := range userList {
|
||||
userID, err := uuid.FromString(userUUIDList[i])
|
||||
if err != nil {
|
||||
userID = uuid.NewV5(uuid.Nil, userUUIDList[i])
|
||||
}
|
||||
userID := utils.UUIDMap(userUUIDList[i])
|
||||
userMap[userID] = userName
|
||||
userFlowMap[userName] = userFlowList[i]
|
||||
}
|
||||
|
||||
@@ -57,10 +57,7 @@ func (c *Client) PacketConn(conn net.Conn, rAddr net.Addr) net.PacketConn {
|
||||
|
||||
// NewClient return Client instance
|
||||
func NewClient(uuidStr string, addons *Addons) (*Client, error) {
|
||||
uid, err := utils.UUIDMap(uuidStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uid := utils.UUIDMap(uuidStr)
|
||||
|
||||
return &Client{
|
||||
uuid: uid,
|
||||
|
||||
@@ -84,10 +84,7 @@ func (c *Client) StreamConn(conn net.Conn, dst *DstAddr) (net.Conn, error) {
|
||||
|
||||
// NewClient return Client instance
|
||||
func NewClient(config Config) (*Client, error) {
|
||||
uid, err := utils.UUIDMap(config.UUID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uid := utils.UUIDMap(config.UUID)
|
||||
|
||||
var security Security
|
||||
switch config.Security {
|
||||
|
||||
Reference in New Issue
Block a user