Files
wild-central/internal/network/detect.go
Paul Payne 3172e56288 Standardize codebase consistency: naming, JSON tags, logging, error wrapping
- JSON tags: fix snake_case to camelCase in dnsmasq (configFile, domainsConfigured,
  lastRestart), crowdsec Machine (lastPush, lastHeartbeat), network (primaryIP,
  primaryInterface). cscli raw parsing structs keep snake_case to match CLI output.
- Error wrapping: fix %v to %w in enableAuthelia for proper error chain preservation
- Naming: rename dnsmasq.ConfigGenerator to dnsmasq.Manager (matches all other packages),
  rename ServiceStatus to Status in dnsmasq and haproxy (matches authelia, crowdsec, etc.)
- Logging: standardize all slog calls to use "component" key instead of message prefixes.
  Affects reconcile, dnsfilter, ddns — now consistent with dnsmasq, haproxy, nftables, sse.
2026-07-14 04:38:48 +00:00

105 lines
2.7 KiB
Go

package network
import (
"fmt"
"net"
"os/exec"
"strings"
)
// NetworkInfo holds detected network configuration
type NetworkInfo struct {
PrimaryIP string `json:"primaryIP"`
PrimaryInterface string `json:"primaryInterface"`
Gateway string `json:"gateway"`
}
// DetectNetworkInfo detects the machine's primary network configuration
// It finds the interface and IP used for internet-bound traffic by checking
// the route to a public DNS server (8.8.8.8)
func DetectNetworkInfo() (*NetworkInfo, error) {
// Use ip route to find the default route interface and IP
// This tells us which interface would be used to reach the internet
cmd := exec.Command("ip", "route", "get", "8.8.8.8")
output, err := cmd.Output()
if err != nil {
return nil, fmt.Errorf("failed to get network route: %w", err)
}
line := string(output)
// Example output: "8.8.8.8 via 192.168.8.1 dev wlan0 src 192.168.8.152 uid 1000"
// Extract gateway (after "via")
gateway := ""
if idx := strings.Index(line, "via "); idx != -1 {
rest := line[idx+4:]
parts := strings.Fields(rest)
if len(parts) > 0 {
gateway = parts[0]
}
}
// Extract interface (after "dev")
iface := ""
if idx := strings.Index(line, "dev "); idx != -1 {
rest := line[idx+4:]
parts := strings.Fields(rest)
if len(parts) > 0 {
iface = parts[0]
}
}
// Extract source IP (after "src")
// This is the IP that would be used as the source for outbound traffic
ip := ""
if idx := strings.Index(line, "src "); idx != -1 {
rest := line[idx+4:]
parts := strings.Fields(rest)
if len(parts) > 0 {
ip = parts[0]
}
}
if ip == "" || iface == "" {
return nil, fmt.Errorf("could not detect network information from route output: %s", line)
}
return &NetworkInfo{
PrimaryIP: ip,
PrimaryInterface: iface,
Gateway: gateway,
}, nil
}
// ResolveDomain resolves a domain name to an IP address using the system DNS resolver
func ResolveDomain(domain string) (string, error) {
ips, err := net.LookupIP(domain)
if err != nil {
return "", fmt.Errorf("failed to resolve domain: %w", err)
}
if len(ips) == 0 {
return "", fmt.Errorf("no IP addresses found for domain")
}
// Return the first IPv4 address
for _, ip := range ips {
if ipv4 := ip.To4(); ipv4 != nil {
return ipv4.String(), nil
}
}
// If no IPv4, return first IP
return ips[0].String(), nil
}
// GetWildCentralIP returns the IP address of the Wild Central server
// This is the primary IP address that the server uses for network communication
func GetWildCentralIP() (string, error) {
netInfo, err := DetectNetworkInfo()
if err != nil {
return "", fmt.Errorf("failed to detect Wild Central IP: %w", err)
}
return netInfo.PrimaryIP, nil
}