package discovery import ( "context" "encoding/json" "fmt" "net" "strings" "networktool/internal/config" "networktool/internal/deviceinfo" "networktool/internal/logger" "networktool/internal/model" ) type Server struct { cfg config.Config log *logger.Logger deviceSvc *deviceinfo.Service } func New(cfg config.Config, log *logger.Logger, deviceSvc *deviceinfo.Service) *Server { return &Server{cfg: cfg, log: log, deviceSvc: deviceSvc} } func (s *Server) Run(ctx context.Context) error { addr, err := net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%d", s.cfg.UDPHost, s.cfg.UDPPort)) if err != nil { return err } conn, err := net.ListenUDP("udp4", addr) if err != nil { return err } defer conn.Close() go func() { <-ctx.Done() _ = conn.Close() }() s.log.Info("udp discovery listening", "addr", conn.LocalAddr().String()) buf := make([]byte, 2048) for { n, remote, err := conn.ReadFromUDP(buf) if err != nil { if ctx.Err() != nil { return nil } return err } var req model.DiscoverRequest if err := json.Unmarshal(buf[:n], &req); err != nil || req.MessageType != "discover" { continue } device := s.deviceSvc.Get() resp := model.DiscoverResponse{ ProtocolVersion: 1, MessageType: "discover_response", RequestID: req.RequestID, DeviceID: device.DeviceID, Hostname: device.Hostname, ServerVersion: device.ServerVersion, MAC: findMACByIP(s.cfg.MaintenanceIP), LAN2IP: s.cfg.MaintenanceIP, AuthRequired: true, } payload, _ := json.Marshal(resp) _, _ = conn.WriteToUDP(payload, remote) } } func findMACByIP(ip string) string { ifaces, err := net.Interfaces() if err != nil { return "" } var fallback string for _, iface := range ifaces { if iface.Flags&net.FlagLoopback != 0 || len(iface.HardwareAddr) == 0 { continue } addrs, err := iface.Addrs() if err != nil { continue } for _, addr := range addrs { var current net.IP switch value := addr.(type) { case *net.IPNet: current = value.IP case *net.IPAddr: current = value.IP } current = current.To4() if current == nil { continue } if current.String() == ip { return iface.HardwareAddr.String() } if fallback == "" && strings.HasPrefix(current.String(), "169.254.") { fallback = iface.HardwareAddr.String() } } } return fallback }