mirror of
https://github.com/rwinkhart/go-winio.git
synced 2026-08-31 14:26:43 -04:00
+14
-13
@@ -94,26 +94,27 @@ func providerCallbackAdapter(sourceID *guid.GUID, state uintptr, level uintptr,
|
||||
// uses the same algorithm as used by .NET's EventSource class, which is based
|
||||
// on RFC 4122. More information on the algorithm can be found here:
|
||||
// https://blogs.msdn.microsoft.com/dcook/2015/09/08/etw-provider-names-and-guids/
|
||||
// The algorithm is roughly:
|
||||
// Hash = Sha1(namespace + arg.ToUpper().ToUtf16be())
|
||||
// Guid = Hash[0..15], with Hash[7] tweaked according to RFC 4122
|
||||
//
|
||||
// The algorithm is roughly the RFC 4122 algorithm for a V5 UUID, but differs in
|
||||
// the following ways:
|
||||
// - The input name is first upper-cased, UTF16-encoded, and converted to
|
||||
// big-endian.
|
||||
// - No variant is set on the result UUID.
|
||||
// - The result UUID is treated as being in little-endian format, rather than
|
||||
// big-endian.
|
||||
func providerIDFromName(name string) guid.GUID {
|
||||
buffer := sha1.New()
|
||||
|
||||
namespace := []byte{0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8, 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB}
|
||||
buffer.Write(namespace)
|
||||
|
||||
namespace := guid.GUID{0x482C2DB2, 0xC390, 0x47C8, [8]byte{0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB}}
|
||||
namespaceBytes := namespace.ToArray()
|
||||
buffer.Write(namespaceBytes[:])
|
||||
binary.Write(buffer, binary.BigEndian, utf16.Encode([]rune(strings.ToUpper(name))))
|
||||
|
||||
sum := buffer.Sum(nil)
|
||||
sum[7] = (sum[7] & 0xf) | 0x50
|
||||
|
||||
return guid.GUID{
|
||||
Data1: binary.LittleEndian.Uint32(sum[0:4]),
|
||||
Data2: binary.LittleEndian.Uint16(sum[4:6]),
|
||||
Data3: binary.LittleEndian.Uint16(sum[6:8]),
|
||||
Data4: [8]byte{sum[8], sum[9], sum[10], sum[11], sum[12], sum[13], sum[14], sum[15]},
|
||||
}
|
||||
a := [16]byte{}
|
||||
copy(a[:], sum)
|
||||
return guid.FromWindowsArray(a)
|
||||
}
|
||||
|
||||
// NewProvider creates and registers a new ETW provider. The provider ID is
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package etw
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/Microsoft/go-winio/pkg/guid"
|
||||
)
|
||||
|
||||
func mustGUIDFromString(t *testing.T, s string) guid.GUID {
|
||||
g, err := guid.FromString(s)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
func Test_ProviderIDFromName(t *testing.T) {
|
||||
type testCase struct {
|
||||
name string
|
||||
g guid.GUID
|
||||
}
|
||||
testCases := []testCase{
|
||||
{"wincni", mustGUIDFromString(t, "c822b598-f4cc-5a72-7933-ce2a816d033f")},
|
||||
{"Moby", mustGUIDFromString(t, "6996f090-c5de-5082-a81e-5841acc3a635")},
|
||||
{"ContainerD", mustGUIDFromString(t, "2acb92c0-eb9b-571a-69cf-8f3410f383ad")},
|
||||
{"Microsoft.Virtualization.RunHCS", mustGUIDFromString(t, "0B52781F-B24D-5685-DDF6-69830ED40EC3")},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
g := providerIDFromName(tc.name)
|
||||
if g != tc.g {
|
||||
t.Fatalf("Incorrect provider GUID.\nExpected: %s\nActual: %s", tc.g, g)
|
||||
}
|
||||
}
|
||||
}
|
||||
+51
-3
@@ -7,6 +7,7 @@ package guid
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
@@ -52,10 +53,34 @@ func NewV4() (GUID, error) {
|
||||
return GUID{}, err
|
||||
}
|
||||
|
||||
b[6] = (b[6] & 0x0f) | 0x40 // Version 4 (randomly generated)
|
||||
b[8] = (b[8] & 0x3f) | 0x80 // RFC4122 variant
|
||||
g := FromArray(b)
|
||||
g.setVersion(4) // Version 4 means randomly generated.
|
||||
g.setVariant(VariantRFC4122)
|
||||
|
||||
return FromArray(b), nil
|
||||
return g, nil
|
||||
}
|
||||
|
||||
// NewV5 returns a new version 5 (generated from a string via SHA-1 hashing)
|
||||
// GUID, as defined by RFC 4122. The RFC is unclear on the encoding of the name,
|
||||
// and the sample code treats it as a series of bytes, so we do the same here.
|
||||
//
|
||||
// Some implementations, such as those found on Windows, treat the name as a
|
||||
// big-endian UTF16 stream of bytes. If that is desired, the string can be
|
||||
// encoded as such before being passed to this function.
|
||||
func NewV5(namespace GUID, name []byte) (GUID, error) {
|
||||
b := sha1.New()
|
||||
namespaceBytes := namespace.ToArray()
|
||||
b.Write(namespaceBytes[:])
|
||||
b.Write(name)
|
||||
|
||||
a := [16]byte{}
|
||||
copy(a[:], b.Sum(nil))
|
||||
|
||||
g := FromArray(a)
|
||||
g.setVersion(5) // Version 5 means generated from a string.
|
||||
g.setVariant(VariantRFC4122)
|
||||
|
||||
return g, nil
|
||||
}
|
||||
|
||||
func fromArray(b [16]byte, order binary.ByteOrder) GUID {
|
||||
@@ -150,6 +175,25 @@ func FromString(s string) (GUID, error) {
|
||||
return g, nil
|
||||
}
|
||||
|
||||
func (g *GUID) setVariant(v Variant) {
|
||||
d := g.Data4[0]
|
||||
switch v {
|
||||
case VariantNCS:
|
||||
d = (d & 0x7f)
|
||||
case VariantRFC4122:
|
||||
d = (d & 0x3f) | 0x80
|
||||
case VariantMicrosoft:
|
||||
d = (d & 0x1f) | 0xc0
|
||||
case VariantFuture:
|
||||
d = (d & 0x0f) | 0xe0
|
||||
case VariantUnknown:
|
||||
fallthrough
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid variant: %d", v))
|
||||
}
|
||||
g.Data4[0] = d
|
||||
}
|
||||
|
||||
// Variant returns the GUID variant, as defined in RFC 4122.
|
||||
func (g GUID) Variant() Variant {
|
||||
b := g.Data4[0]
|
||||
@@ -165,6 +209,10 @@ func (g GUID) Variant() Variant {
|
||||
return VariantUnknown
|
||||
}
|
||||
|
||||
func (g *GUID) setVersion(v Version) {
|
||||
g.Data3 = (g.Data3 & 0x0fff) | (uint16(v) << 12)
|
||||
}
|
||||
|
||||
// Version returns the GUID version, as defined in RFC 4122.
|
||||
func (g GUID) Version() Version {
|
||||
return Version((g.Data3 & 0xF000) >> 12)
|
||||
|
||||
@@ -14,6 +14,14 @@ func mustNewV4(t *testing.T) GUID {
|
||||
return g
|
||||
}
|
||||
|
||||
func mustNewV5(t *testing.T, namespace GUID, name []byte) GUID {
|
||||
g, err := NewV5(namespace, name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
func mustFromString(t *testing.T, s string) GUID {
|
||||
g, err := FromString(s)
|
||||
if err != nil {
|
||||
@@ -22,6 +30,78 @@ func mustFromString(t *testing.T, s string) GUID {
|
||||
return g
|
||||
}
|
||||
|
||||
func Test_Variant(t *testing.T) {
|
||||
type testCase struct {
|
||||
g GUID
|
||||
v Variant
|
||||
}
|
||||
testCases := []testCase{
|
||||
{mustFromString(t, "f5cbc1a9-4cba-45a0-0fdd-b6761fc7dcc0"), VariantNCS},
|
||||
{mustFromString(t, "f5cbc1a9-4cba-45a0-7fdd-b6761fc7dcc0"), VariantNCS},
|
||||
{mustFromString(t, "f5cbc1a9-4cba-45a0-bfdd-b6761fc7dcc0"), VariantRFC4122},
|
||||
{mustFromString(t, "f5cbc1a9-4cba-45a0-9fdd-b6761fc7dcc0"), VariantRFC4122},
|
||||
{mustFromString(t, "f5cbc1a9-4cba-45a0-cfdd-b6761fc7dcc0"), VariantMicrosoft},
|
||||
{mustFromString(t, "f5cbc1a9-4cba-45a0-dfdd-b6761fc7dcc0"), VariantMicrosoft},
|
||||
{mustFromString(t, "f5cbc1a9-4cba-45a0-efdd-b6761fc7dcc0"), VariantFuture},
|
||||
{mustFromString(t, "f5cbc1a9-4cba-45a0-ffdd-b6761fc7dcc0"), VariantFuture},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
actualVariant := tc.g.Variant()
|
||||
if actualVariant != tc.v {
|
||||
t.Fatalf("Variant is not correct.\nExpected: %d\nActual: %d\nGUID: %s", tc.v, actualVariant, tc.g)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_SetVariant(t *testing.T) {
|
||||
testCases := []Variant{
|
||||
VariantNCS,
|
||||
VariantRFC4122,
|
||||
VariantMicrosoft,
|
||||
VariantFuture,
|
||||
}
|
||||
g := mustFromString(t, "f5cbc1a9-4cba-45a0-bfdd-b6761fc7dcc0")
|
||||
for _, tc := range testCases {
|
||||
t.Logf("Test case: %d", tc)
|
||||
g.setVariant(tc)
|
||||
if g.Variant() != tc {
|
||||
t.Fatalf("Variant is incorrect.\nExpected: %d\nActual: %d", tc, g.Variant())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Version(t *testing.T) {
|
||||
type testCase struct {
|
||||
g GUID
|
||||
v Version
|
||||
}
|
||||
testCases := []testCase{
|
||||
{mustFromString(t, "f5cbc1a9-4cba-15a0-0fdd-b6761fc7dcc0"), 1},
|
||||
{mustFromString(t, "f5cbc1a9-4cba-25a0-0fdd-b6761fc7dcc0"), 2},
|
||||
{mustFromString(t, "f5cbc1a9-4cba-35a0-0fdd-b6761fc7dcc0"), 3},
|
||||
{mustFromString(t, "f5cbc1a9-4cba-45a0-0fdd-b6761fc7dcc0"), 4},
|
||||
{mustFromString(t, "f5cbc1a9-4cba-55a0-0fdd-b6761fc7dcc0"), 5},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
actualVersion := tc.g.Version()
|
||||
if actualVersion != tc.v {
|
||||
t.Fatalf("Version is not correct.\nExpected: %d\nActual: %d\nGUID: %s", tc.v, actualVersion, tc.g)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_SetVersion(t *testing.T) {
|
||||
g := mustFromString(t, "f5cbc1a9-4cba-45a0-bfdd-b6761fc7dcc0")
|
||||
for tc := 0; tc < 16; tc++ {
|
||||
t.Logf("Test case: %d", tc)
|
||||
v := Version(tc)
|
||||
g.setVersion(v)
|
||||
if g.Version() != v {
|
||||
t.Fatalf("Version is incorrect.\nExpected: %d\nActual: %d", v, g.Version())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NewV4IsUnique(t *testing.T) {
|
||||
g := mustNewV4(t)
|
||||
g2 := mustNewV4(t)
|
||||
@@ -40,6 +120,53 @@ func Test_V4HasCorrectVersionAndVariant(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_V5HasCorrectVersionAndVariant(t *testing.T) {
|
||||
namespace := mustFromString(t, "f5cbc1a9-4cba-45a0-bfdd-b6761fc7dcc0")
|
||||
g := mustNewV5(t, namespace, []byte("Foo"))
|
||||
if g.Version() != 5 {
|
||||
t.Fatalf("Version is not 5: %s", g)
|
||||
}
|
||||
if g.Variant() != VariantRFC4122 {
|
||||
t.Fatalf("Variant is not RFC4122: %s", g)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_V5KnownValues(t *testing.T) {
|
||||
type testCase struct {
|
||||
ns GUID
|
||||
name string
|
||||
g GUID
|
||||
}
|
||||
testCases := []testCase{
|
||||
{
|
||||
mustFromString(t, "6ba7b810-9dad-11d1-80b4-00c04fd430c8"),
|
||||
"www.sample.com",
|
||||
mustFromString(t, "4e4463eb-b0e8-54fa-8c28-12d1ab1d45b3"),
|
||||
},
|
||||
{
|
||||
mustFromString(t, "6ba7b811-9dad-11d1-80b4-00c04fd430c8"),
|
||||
"https://www.sample.com/test",
|
||||
mustFromString(t, "9e44625a-0d85-5e0a-99bc-8e8a77df5ea2"),
|
||||
},
|
||||
{
|
||||
mustFromString(t, "6ba7b812-9dad-11d1-80b4-00c04fd430c8"),
|
||||
"1.3.6.1.4.1.343",
|
||||
mustFromString(t, "6aab0456-7392-582a-b92a-ba5a7096945d"),
|
||||
},
|
||||
{
|
||||
mustFromString(t, "6ba7b814-9dad-11d1-80b4-00c04fd430c8"),
|
||||
"CN=John Smith, ou=People, o=FakeCorp, L=Seattle, S=Washington, C=US",
|
||||
mustFromString(t, "badff8dd-c869-5b64-a260-00092e66be00"),
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
g := mustNewV5(t, tc.ns, []byte(tc.name))
|
||||
if g != tc.g {
|
||||
t.Fatalf("GUIDs are not equal.\nExpected: %s\nActual: %s", tc.g, g)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ToArray(t *testing.T) {
|
||||
g := mustFromString(t, "73c39589-192e-4c64-9acf-6c5d0aa18528")
|
||||
b := g.ToArray()
|
||||
|
||||
Reference in New Issue
Block a user