130 lines
2.6 KiB
Go
130 lines
2.6 KiB
Go
package main
|
||
|
||
import (
|
||
"bytes"
|
||
"encoding/hex"
|
||
"errors"
|
||
"fmt"
|
||
"io/fs"
|
||
"os"
|
||
"path/filepath"
|
||
"runtime"
|
||
"strings"
|
||
|
||
"golang.org/x/sys/windows/registry"
|
||
)
|
||
|
||
// ---------- Linux ----------
|
||
|
||
func duidsLinux() (map[string][]byte, error) {
|
||
candidates := []string{
|
||
"/var/lib/NetworkManager",
|
||
"/var/lib/dhcp",
|
||
"/var/lib/systemd/network",
|
||
"/run/systemd/netif/leases",
|
||
}
|
||
out := map[string][]byte{}
|
||
|
||
for _, dir := range candidates {
|
||
_ = filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
|
||
if err != nil || d.IsDir() {
|
||
return nil
|
||
}
|
||
|
||
// lease/duid files are small, read entire file
|
||
buf, err := os.ReadFile(path)
|
||
if err != nil {
|
||
return nil
|
||
}
|
||
|
||
duid := extractDUID(buf)
|
||
if len(duid) > 0 {
|
||
out[path] = duid
|
||
}
|
||
return nil
|
||
})
|
||
}
|
||
if len(out) == 0 {
|
||
return nil, errors.New("keine DUID-Datei gefunden")
|
||
}
|
||
return out, nil
|
||
}
|
||
|
||
// very tolerant parser: tries common patterns
|
||
func extractDUID(b []byte) []byte {
|
||
// Pattern „default-duid "\x00\x03\x00\x01\xaa…"“
|
||
if bytes.Contains(b, []byte("default-duid")) || bytes.Contains(b, []byte("dhclient.duid")) {
|
||
start := bytes.IndexByte(b, '"')
|
||
end := bytes.LastIndexByte(b, '"')
|
||
if start >= 0 && end > start {
|
||
hexEsc := b[start+1 : end]
|
||
hexEsc = bytes.ReplaceAll(hexEsc, []byte(`\x`), nil)
|
||
duid, _ := hex.DecodeString(string(hexEsc))
|
||
return duid
|
||
}
|
||
}
|
||
// systemd-networkd file: plain hex without 0x
|
||
if allHex(b) {
|
||
duid, _ := hex.DecodeString(strings.TrimSpace(string(b)))
|
||
return duid
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func allHex(b []byte) bool {
|
||
for _, c := range bytes.TrimSpace(b) {
|
||
if !strings.ContainsRune("0123456789abcdefABCDEF", rune(c)) {
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
// ---------- Windows ----------
|
||
|
||
func duidsWindows() (map[string][]byte, error) {
|
||
k, err := registry.OpenKey(registry.LOCAL_MACHINE,
|
||
`SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters`,
|
||
registry.READ)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer k.Close()
|
||
|
||
val, _, err := k.GetBinaryValue("Dhcpv6DUID")
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return map[string][]byte{"registry": val}, nil
|
||
}
|
||
|
||
// ---------- macOS (scutil) ----------
|
||
// (Beispielskizze – nicht implementiert)
|
||
// func duidsDarwin() ...
|
||
|
||
// ---------- main ----------
|
||
|
||
func main() {
|
||
var duids map[string][]byte
|
||
var err error
|
||
|
||
switch runtime.GOOS {
|
||
case "windows":
|
||
duids, err = duidsWindows()
|
||
default: // linux, darwin, ...
|
||
duids, err = duidsLinux()
|
||
}
|
||
|
||
if err != nil {
|
||
fmt.Fprintln(os.Stderr, "Fehler:", err)
|
||
os.Exit(1)
|
||
}
|
||
|
||
hostName, _ := os.Hostname()
|
||
|
||
for _, d := range duids {
|
||
fmt.Printf("%s", hex.EncodeToString(d))
|
||
fmt.Printf("%s", hostName)
|
||
}
|
||
}
|