// client.go package main import ( "bufio" "bytes" "encoding/binary" "encoding/hex" "encoding/json" "errors" "fmt" "io/fs" "net/http" "os" "path/filepath" "regexp" "runtime" "strconv" "strings" "time" "golang.org/x/sys/windows/registry" ) type iaidInfo struct { Source string // Pfad oder Registry-Key IAID uint32 } // ---------- Linux ---------- var ( reDhclientIAID = regexp.MustCompile(`\biaid\s+(\d+);`) // dhclient, NM reKVIAID = regexp.MustCompile(`^IAID=(\d+)`) // systemd-networkd ) func iaidsLinux() ([]iaidInfo, error) { candidates := []string{ "/var/lib/NetworkManager", "/var/lib/dhclient", // manche Distros "/run/systemd/netif/leases", // systemd-networkd "/var/lib/dhcpcd", "/var/lib/dhcpcd5", } var out []iaidInfo visit := func(path string, d fs.DirEntry, err error) error { if err != nil || d.IsDir() { return nil } f, err := os.Open(path) if err != nil { return nil } defer f.Close() sc := bufio.NewScanner(f) for sc.Scan() { line := sc.Text() if m := reDhclientIAID.FindStringSubmatch(line); len(m) == 2 { if v, _ := strconv.ParseUint(m[1], 10, 32); v != 0 { out = append(out, iaidInfo{path, uint32(v)}) break } } if m := reKVIAID.FindStringSubmatch(line); len(m) == 2 { if v, _ := strconv.ParseUint(m[1], 10, 32); v != 0 { out = append(out, iaidInfo{path, uint32(v)}) break } } } return nil } for _, dir := range candidates { _ = filepath.WalkDir(dir, visit) } if len(out) == 0 { return nil, fmt.Errorf("keine IAID-Datei gefunden (root-Rechte?)") } return out, nil } // ---------- Windows ---------- /*func iaidsWindows() ([]iaidInfo, error) { base := `SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\Interfaces` root, err := registry.OpenKey(registry.LOCAL_MACHINE, base, registry.READ) if err != nil { return nil, err } defer root.Close() names, _ := root.ReadSubKeyNames(-1) var out []iaidInfo for _, n := range names { k, err := registry.OpenKey(root, n, registry.READ) if err != nil { continue } val, _, err := k.GetIntegerValue("IAID") k.Close() if err == nil { out = append(out, iaidInfo{ Source: base + `\` + n + `\IAID`, IAID: uint32(val), }) } } if len(out) == 0 { return nil, fmt.Errorf("kein IAID-Wert in der Registry") } return out, nil }*/ func iaidsWindows() ([]iaidInfo, error) { const base = `SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\Interfaces` root, err := registry.OpenKey(registry.LOCAL_MACHINE, base, registry.READ) if err != nil { return nil, err } defer root.Close() var out []iaidInfo guidKeys, _ := root.ReadSubKeyNames(-1) for _, g := range guidKeys { k, err := registry.OpenKey(root, g, registry.READ) if err != nil { continue } if v, _, err := k.GetIntegerValue("Dhcpv6Iaid"); err == nil { out = append(out, iaidInfo{Source: base + `\` + g, IAID: uint32(v)}) k.Close() continue } // --- Fallback: MAC → IAID (letzte 3 Bytes), falls DWORD fehlt --- if mac, _, err := k.GetBinaryValue("NetworkAddress"); err == nil && len(mac) >= 6 { iaid := uint32(mac[3])<<16 | uint32(mac[4])<<8 | uint32(mac[5]) out = append(out, iaidInfo{Source: base + `\` + g + " (computed)", IAID: iaid}) } k.Close() } if len(out) == 0 { return nil, fmt.Errorf("weder Dhcpv6Iaid noch MAC-basierten Fallback gefunden - nutzt der Client überhaupt DHCPv6?") } return out, nil } // ---------- Fallback: IAID selbst ableiten (MAC → 3 Byte) ---------- func iaidFromMAC(mac []byte, ifindex int) uint32 { if len(mac) >= 3 { return binary.BigEndian.Uint32([]byte{0, mac[len(mac)-3], mac[len(mac)-2], mac[len(mac)-1]}) } // Notlösung: Interface-Index + 0xA0 im obersten Byte return uint32(0xA0000000 | (ifindex & 0x00FFFFFF)) } /*######################################################################################################*/ /*######################################################################################################*/ /*######################################################################################################*/ /*######################################################################################################*/ /*######################################################################################################*/ // ---------- Datentyp, der an den Server geschickt wird ---------- type payload struct { Hostname string `json:"hostname"` DUIDs []string `json:"duids"` // hex-codiert IAIDs []uint32 } // ---------- Linux ---------- func duidsLinux() ([][]byte, error) { candidates := []string{ "/var/lib/NetworkManager", "/var/lib/dhcp", "/var/lib/systemd/network", "/run/systemd/netif/leases", } var out [][]byte for _, dir := range candidates { _ = filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { if err != nil || d.IsDir() { return nil } buf, err := os.ReadFile(path) if err != nil { return nil } if duid := extractDUID(buf); len(duid) > 0 { out = append(out, duid) } return nil }) } if len(out) == 0 { return nil, errors.New("keine DUID gefunden") } return out, nil } // ---------- Windows ---------- func duidsWindows() ([][]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 [][]byte{val}, nil } // ---------- Hilfsfunktionen ---------- func extractDUID(b []byte) []byte { if idx := bytes.Index(b, []byte("default-duid")); idx >= 0 { start := bytes.IndexByte(b[idx:], '"') + idx + 1 end := bytes.IndexByte(b[start:], '"') + start hexEsc := bytes.ReplaceAll(b[start:end], []byte(`\x`), nil) duid, _ := hex.DecodeString(string(hexEsc)) return duid } hexOnly := strings.TrimSpace(string(b)) if len(hexOnly) >= 28 && allHex(hexOnly) { // plausibel? duid, _ := hex.DecodeString(hexOnly) return duid } return nil } func allHex(s string) bool { for _, c := range s { if !strings.ContainsRune("0123456789abcdefABCDEF", c) { return false } } return true } // ---------- main ---------- func main() { var raw [][]byte var all []iaidInfo var err error var err1 error switch runtime.GOOS { case "windows": raw, err = duidsWindows() all, err1 = iaidsWindows() default: raw, err = duidsLinux() all, err1 = iaidsLinux() } if err != nil { fmt.Fprintln(os.Stderr, err) } if err1 != nil { fmt.Fprintln(os.Stderr, err1) } for _, ia := range all { fmt.Printf("%-60s 0x%08x (%d)\n", ia.Source, ia.IAID, ia.IAID) } var gh []uint32 for _, bh := range all { gh = append(gh, bh.IAID) } //host, _ := os.Hostname() host := "PC1020" p := payload{ Hostname: host, DUIDs: make([]string, len(raw)), IAIDs: gh, } for i, d := range raw { p.DUIDs[i] = hex.EncodeToString(d) } // --- JSON kodieren --- body, _ := json.Marshal(p) // --- HTTP senden --- url := "http://localhost:8080/register" // <-- ggf. anpassen req, _ := http.NewRequest(http.MethodPost, url, bytes.NewReader(body)) req.Header.Set("Content-Type", "application/json") client := &http.Client{Timeout: 5 * time.Second} resp, err := client.Do(req) if err != nil { fmt.Fprintln(os.Stderr, "HTTP-Fehler:", err) os.Exit(1) } defer resp.Body.Close() fmt.Println("Server-Antwort:", resp.Status) }