package debug import ( "fmt" "net" "net/netip" "sort" "strings" "github.com/netbirdio/netbird/client/anonymize" "github.com/netbirdio/netbird/client/internal/peer" "github.com/netbirdio/netbird/client/internal/routemanager/systemops" "github.com/netbirdio/netbird/management/domain" ) func formatInterfaces(interfaces []net.Interface, anonymize bool, anonymizer *anonymize.Anonymizer) string { sort.Slice(interfaces, func(i, j int) bool { return interfaces[i].Name < interfaces[j].Name }) var builder strings.Builder builder.WriteString("Network Interfaces:\n") for _, iface := range interfaces { builder.WriteString(fmt.Sprintf("\nInterface: %s\n", iface.Name)) builder.WriteString(fmt.Sprintf(" Index: %d\n", iface.Index)) builder.WriteString(fmt.Sprintf(" MTU: %d\n", iface.MTU)) builder.WriteString(fmt.Sprintf(" Flags: %v\n", iface.Flags)) addrs, err := iface.Addrs() if err != nil { builder.WriteString(fmt.Sprintf(" Addresses: Error retrieving addresses: %v\n", err)) } else { builder.WriteString(" Addresses:\n") for _, addr := range addrs { prefix, err := netip.ParsePrefix(addr.String()) if err != nil { builder.WriteString(fmt.Sprintf(" Error parsing address: %v\n", err)) continue } ip := prefix.Addr() if anonymize { ip = anonymizer.AnonymizeIP(ip) } builder.WriteString(fmt.Sprintf(" %s/%d\n", ip, prefix.Bits())) } } } return builder.String() } func formatResolvedDomains(resolvedDomains map[domain.Domain]peer.ResolvedDomainInfo, anonymize bool, anonymizer *anonymize.Anonymizer) string { if len(resolvedDomains) == 0 { return "No resolved domains found.\n" } var builder strings.Builder builder.WriteString("Resolved Domains:\n") builder.WriteString("=================\n\n") var sortedParents []domain.Domain for parentDomain := range resolvedDomains { sortedParents = append(sortedParents, parentDomain) } sort.Slice(sortedParents, func(i, j int) bool { return sortedParents[i].SafeString() < sortedParents[j].SafeString() }) for _, parentDomain := range sortedParents { info := resolvedDomains[parentDomain] parentKey := parentDomain.SafeString() if anonymize { parentKey = anonymizer.AnonymizeDomain(parentKey) } builder.WriteString(fmt.Sprintf("%s:\n", parentKey)) var sortedIPs []string for _, prefix := range info.Prefixes { ipStr := prefix.String() if anonymize { anonymizedIP := anonymizer.AnonymizeIP(prefix.Addr()) ipStr = fmt.Sprintf("%s/%d", anonymizedIP, prefix.Bits()) } sortedIPs = append(sortedIPs, ipStr) } sort.Strings(sortedIPs) for _, ipStr := range sortedIPs { builder.WriteString(fmt.Sprintf(" %s\n", ipStr)) } builder.WriteString("\n") } return builder.String() } func formatRoutesTable(detailedRoutes []systemops.DetailedRoute, anonymize bool, anonymizer *anonymize.Anonymizer) string { if len(detailedRoutes) == 0 { return "No routes found.\n" } sort.Slice(detailedRoutes, func(i, j int) bool { if detailedRoutes[i].Table != detailedRoutes[j].Table { return detailedRoutes[i].Table < detailedRoutes[j].Table } return detailedRoutes[i].Route.Dst.String() < detailedRoutes[j].Route.Dst.String() }) headers, rows := buildPlatformSpecificRouteTable(detailedRoutes, anonymize, anonymizer) return formatTable("Routing Table:", headers, rows) } func formatRouteDestination(destination netip.Prefix, anonymize bool, anonymizer *anonymize.Anonymizer) string { if anonymize { anonymizedDestIP := anonymizer.AnonymizeIP(destination.Addr()) return fmt.Sprintf("%s/%d", anonymizedDestIP, destination.Bits()) } return destination.String() } func formatRouteGateway(gateway netip.Addr, anonymize bool, anonymizer *anonymize.Anonymizer) string { if gateway.IsValid() { if anonymize { return anonymizer.AnonymizeIP(gateway).String() } return gateway.String() } return "-" } func formatRouteInterface(iface *net.Interface) string { if iface != nil { return iface.Name } return "-" } func formatInterfaceIndex(index int) string { if index <= 0 { return "-" } return fmt.Sprintf("%d", index) } func formatRouteMetric(metric int) string { if metric < 0 { return "-" } return fmt.Sprintf("%d", metric) } func formatTable(title string, headers []string, rows [][]string) string { widths := make([]int, len(headers)) for i, header := range headers { widths[i] = len(header) } for _, row := range rows { for i, cell := range row { if len(cell) > widths[i] { widths[i] = len(cell) } } } for i := range widths { widths[i] += 2 } var formatParts []string for _, width := range widths { formatParts = append(formatParts, fmt.Sprintf("%%-%ds", width)) } formatStr := strings.Join(formatParts, "") + "\n" var builder strings.Builder builder.WriteString(title + "\n") builder.WriteString(strings.Repeat("=", len(title)) + "\n\n") headerArgs := make([]interface{}, len(headers)) for i, header := range headers { headerArgs[i] = header } builder.WriteString(fmt.Sprintf(formatStr, headerArgs...)) separatorArgs := make([]interface{}, len(headers)) for i, width := range widths { separatorArgs[i] = strings.Repeat("-", width-2) } builder.WriteString(fmt.Sprintf(formatStr, separatorArgs...)) for _, row := range rows { rowArgs := make([]interface{}, len(row)) for i, cell := range row { rowArgs[i] = cell } builder.WriteString(fmt.Sprintf(formatStr, rowArgs...)) } return builder.String() }