diff --git a/duidreader.exe b/duidreader.exe new file mode 100644 index 0000000..94d66a0 Binary files /dev/null and b/duidreader.exe differ diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a01f7af --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module git.send.nrw/sendnrw/duidreader + +go 1.23.1 + +require golang.org/x/sys v0.33.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..9e6ab4c --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= diff --git a/main.go b/main.go new file mode 100644 index 0000000..8b70ff3 --- /dev/null +++ b/main.go @@ -0,0 +1,129 @@ +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) + } +}