package discovery import ( "fmt" "net" "os/exec" "runtime" "time" "auto-check/pkg/model" ) // Scanner 网段扫描器(串行,按顺序逐个探测) type Scanner struct { CIDR string Timeout time.Duration } // NewScanner 创建扫描器 func NewScanner(cidr string, timeout time.Duration) *Scanner { return &Scanner{ CIDR: cidr, Timeout: timeout, } } // Scan 扫描网段,返回存活设备列表(含 MAC 地址) // 串行逐个探测,每个 IP 有超时保护 func (s *Scanner) Scan() []model.Device { ips, ok := s.candidateIPs() if !ok { return nil } fmt.Printf("[扫描] 网段 %s,共 %d 个IP,开始探测...\n", s.CIDR, len(ips)) var results []model.Device for _, ip := range ips { if dev, ok := s.probe(ip); ok { results = append(results, dev) } } fmt.Printf("[扫描] 完成,发现 %d 台存活设备\n", len(results)) return results } // candidateIPs 解析网段并生成候选 IP 列表(排除网络地址和广播地址) func (s *Scanner) candidateIPs() ([]net.IP, bool) { ip, ipnet, err := net.ParseCIDR(s.CIDR) if err != nil { fmt.Printf("[扫描] 网段解析失败: %v\n", err) return nil, false } var ips []net.IP for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) { dst := make(net.IP, len(ip)) copy(dst, ip) ips = append(ips, dst) } if len(ips) > 2 { ips = ips[1 : len(ips)-1] } return ips, true } // probe 探测单个 IP,存活则返回设备信息(MAC 通过 ARP 表查询) func (s *Scanner) probe(ip net.IP) (model.Device, bool) { if !s.isAlive(ip) { return model.Device{}, false } mac := lookupMAC(ip.String()) desc := "无MAC" if mac != "" { desc = mac } fmt.Printf(" [+] %s 存活 (MAC: %s)\n", ip, desc) return model.Device{IP: ip.String(), MAC: mac}, true } // isAlive 用 ping 探测单个 IP 是否存活(内置超时保护) func (s *Scanner) isAlive(ip net.IP) bool { waitMs := s.Timeout.Milliseconds() if waitMs < 1000 { waitMs = 1000 } var args []string switch runtime.GOOS { case "windows": args = []string{"-n", "1", "-w", fmt.Sprintf("%d", waitMs), ip.String()} case "darwin": args = []string{"-c", "1", "-W", fmt.Sprintf("%d", waitMs), ip.String()} default: args = []string{"-c", "1", "-W", fmt.Sprintf("%d", waitMs/1000), ip.String()} } return exec.Command("ping", args...).Run() == nil } // inc IP 递增 func inc(ip net.IP) { for j := len(ip) - 1; j >= 0; j-- { ip[j]++ if ip[j] > 0 { break } } }