scan_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. package utils
  2. import (
  3. "fmt"
  4. "log"
  5. "net"
  6. "os"
  7. "os/exec"
  8. "runtime"
  9. "sort"
  10. "strings"
  11. "sync"
  12. "time"
  13. "testing"
  14. )
  15. // DeviceInfo 存储设备信息
  16. type DeviceInfo struct {
  17. IP string
  18. MAC string
  19. Hostname string
  20. Vendor string
  21. }
  22. // MAC厂商前缀映射(部分常见厂商)
  23. var macVendors = map[string]string{
  24. "00:0C:29": "VMware",
  25. "00:50:56": "VMware",
  26. "00:1A:2B": "Cisco",
  27. "00:1C:42": "Apple",
  28. "00:1D:4F": "Apple",
  29. "00:23:DF": "Apple",
  30. "00:25:BC": "Apple",
  31. "08:00:27": "VirtualBox",
  32. "00:1B:21": "Intel",
  33. "00:21:5A": "Intel",
  34. "00:22:FA": "Intel",
  35. "B8:27:EB": "Raspberry Pi",
  36. "DC:A6:32": "Raspberry Pi",
  37. "28:16:AD": "TP-Link",
  38. "14:CC:20": "TP-Link",
  39. "B0:95:8E": "TP-Link",
  40. "C8:3A:35": "Tenda",
  41. "80:89:17": "Tenda",
  42. "A0:F3:C1": "TP-Link",
  43. "D4:61:DA": "TP-Link",
  44. "C0:56:27": "TP-Link",
  45. "F8:1A:67": "TP-Link",
  46. "F8:1A:2B": "TP-Link",
  47. "E4:8B:7F": "HUAWEI",
  48. "00:9A:CD": "HUAWEI",
  49. "34:29:8F": "HUAWEI",
  50. "80:E6:50": "HUAWEI",
  51. "B4:0B:44": "Xiaomi",
  52. "E4:46:DA": "Xiaomi",
  53. "0C:1D:AF": "Xiaomi",
  54. "D8:63:75": "Xiaomi",
  55. "D0:C7:C0": "Xiaomi",
  56. "FC:64:BA": "Samsung",
  57. "5C:49:79": "Samsung",
  58. "D0:33:11": "Samsung",
  59. "38:48:4C": "Samsung",
  60. "88:36:6C": "Samsung",
  61. "00:1E:65": "Samsung",
  62. "C0:BD:D1": "D-Link",
  63. "1C:AF:05": "D-Link",
  64. "BC:F6:85": "D-Link",
  65. }
  66. // getLocalIP 获取本地IP地址
  67. func getLocalIP() (net.IP, *net.IPNet) {
  68. conn, err := net.Dial("udp", "8.8.8.8:80")
  69. if err != nil {
  70. log.Fatal(err)
  71. }
  72. defer conn.Close()
  73. localAddr := conn.LocalAddr().(*net.UDPAddr)
  74. interfaces, err := net.Interfaces()
  75. if err != nil {
  76. log.Fatal(err)
  77. }
  78. for _, iface := range interfaces {
  79. addrs, err := iface.Addrs()
  80. if err != nil {
  81. continue
  82. }
  83. for _, addr := range addrs {
  84. switch v := addr.(type) {
  85. case *net.IPNet:
  86. if v.IP.To4() != nil && v.IP.Equal(localAddr.IP) {
  87. return v.IP, v
  88. }
  89. }
  90. }
  91. }
  92. return nil, nil
  93. }
  94. // getIPRange 获取IP范围
  95. func getIPRange(ipNet *net.IPNet) ([]string, error) {
  96. var ips []string
  97. // 获取网络地址和掩码
  98. mask := ipNet.Mask
  99. network := ipNet.IP.To4()
  100. if network == nil {
  101. return nil, fmt.Errorf("IPv6 not supported")
  102. }
  103. // 计算网络大小
  104. ones, bits := mask.Size()
  105. totalIPs := 1 << (bits - ones)
  106. // 排除网络地址和广播地址
  107. start := 1
  108. end := totalIPs - 2
  109. if totalIPs <= 2 { // /31 或 /32 网络
  110. start = 0
  111. end = totalIPs - 1
  112. }
  113. // 生成IP列表
  114. for i := start; i <= end; i++ {
  115. ip := make(net.IP, len(network))
  116. copy(ip, network)
  117. // 计算当前IP
  118. for j := len(ip) - 1; j >= 0; j-- {
  119. ip[j] += byte(i >> uint((len(ip)-1-j)*8))
  120. }
  121. ips = append(ips, ip.String())
  122. }
  123. return ips, nil
  124. }
  125. // pingIP 使用ping检测主机是否在线
  126. func pingIP(ip string) bool {
  127. var cmd *exec.Cmd
  128. switch runtime.GOOS {
  129. case "windows":
  130. cmd = exec.Command("ping", "-n", "1", "-w", "1000", ip)
  131. case "darwin":
  132. cmd = exec.Command("ping", "-c", "1", "-W", "1", ip)
  133. default: // Linux
  134. cmd = exec.Command("ping", "-c", "1", "-W", "1", ip)
  135. }
  136. err := cmd.Run()
  137. return err == nil
  138. }
  139. // getARPInfo 获取ARP信息(跨平台)
  140. func getARPInfo(ip string) (string, string, error) {
  141. var mac, hostname string
  142. // 尝试获取主机名
  143. names, err := net.LookupAddr(ip)
  144. if err == nil && len(names) > 0 {
  145. hostname = strings.TrimSuffix(names[0], ".")
  146. }
  147. // 获取MAC地址(不同系统的实现)
  148. switch runtime.GOOS {
  149. case "windows":
  150. mac = getMACWindows(ip)
  151. case "linux":
  152. mac = getMACLinux(ip)
  153. case "darwin":
  154. mac = getMACDarwin(ip)
  155. default:
  156. // 通用方法:尝试读取系统ARP表
  157. mac = getMACFromARPTable(ip)
  158. }
  159. return mac, hostname, nil
  160. }
  161. // getMACFromARPTable 从系统ARP表获取MAC
  162. func getMACFromARPTable(ip string) string {
  163. var cmd *exec.Cmd
  164. switch runtime.GOOS {
  165. case "windows":
  166. cmd = exec.Command("arp", "-a", ip)
  167. case "linux", "darwin":
  168. cmd = exec.Command("arp", "-n", ip)
  169. default:
  170. return ""
  171. }
  172. output, err := cmd.Output()
  173. if err != nil {
  174. return ""
  175. }
  176. outputStr := string(output)
  177. // 解析输出获取MAC地址
  178. lines := strings.Split(outputStr, "\n")
  179. for _, line := range lines {
  180. if strings.Contains(line, ip) {
  181. parts := strings.Fields(line)
  182. if len(parts) >= 3 {
  183. mac := strings.ToUpper(parts[1])
  184. // 验证MAC地址格式
  185. if isValidMAC(mac) {
  186. return mac
  187. }
  188. }
  189. }
  190. }
  191. return ""
  192. }
  193. // 平台特定的MAC获取函数
  194. func getMACWindows(ip string) string {
  195. cmd := exec.Command("arp", "-a", ip)
  196. output, err := cmd.Output()
  197. if err != nil {
  198. return ""
  199. }
  200. lines := strings.Split(string(output), "\n")
  201. for _, line := range lines {
  202. if strings.Contains(line, ip) {
  203. parts := strings.Fields(line)
  204. if len(parts) >= 2 {
  205. mac := strings.ToUpper(parts[1])
  206. if isValidMAC(mac) {
  207. return mac
  208. }
  209. }
  210. }
  211. }
  212. return ""
  213. }
  214. func getMACLinux(ip string) string {
  215. cmd := exec.Command("arp", "-n", ip)
  216. output, err := cmd.Output()
  217. if err != nil {
  218. return ""
  219. }
  220. lines := strings.Split(string(output), "\n")
  221. for _, line := range lines {
  222. if strings.Contains(line, ip) {
  223. parts := strings.Fields(line)
  224. if len(parts) >= 3 {
  225. mac := strings.ToUpper(parts[2])
  226. if isValidMAC(mac) {
  227. return mac
  228. }
  229. }
  230. }
  231. }
  232. return ""
  233. }
  234. func getMACDarwin(ip string) string {
  235. cmd := exec.Command("arp", "-n", ip)
  236. output, err := cmd.Output()
  237. if err != nil {
  238. return ""
  239. }
  240. lines := strings.Split(string(output), "\n")
  241. for _, line := range lines {
  242. if strings.Contains(line, ip) {
  243. parts := strings.Fields(line)
  244. if len(parts) >= 4 {
  245. mac := strings.ToUpper(parts[3])
  246. if isValidMAC(mac) {
  247. return mac
  248. }
  249. }
  250. }
  251. }
  252. return ""
  253. }
  254. // isValidMAC 验证MAC地址格式
  255. func isValidMAC(mac string) bool {
  256. // 简单的MAC地址格式验证
  257. return len(mac) == 17 && strings.Count(mac, ":") == 5 ||
  258. len(mac) == 17 && strings.Count(mac, "-") == 5
  259. }
  260. // getVendorFromMAC 根据MAC地址前缀获取厂商信息
  261. func getVendorFromMAC(mac string) string {
  262. if len(mac) < 8 {
  263. return "Unknown"
  264. }
  265. prefix := strings.ToUpper(mac[:8])
  266. for vendorPrefix, vendor := range macVendors {
  267. if strings.HasPrefix(prefix, vendorPrefix) {
  268. return vendor
  269. }
  270. }
  271. return "Unknown"
  272. }
  273. // scanIP 扫描单个IP
  274. func scanIP(ip string, results chan<- DeviceInfo, wg *sync.WaitGroup) {
  275. defer wg.Done()
  276. // Ping检测
  277. if !pingIP(ip) {
  278. return
  279. }
  280. // 获取MAC和主机名
  281. mac, hostname, err := getARPInfo(ip)
  282. if err != nil || mac == "" {
  283. return
  284. }
  285. // 获取厂商信息
  286. vendor := getVendorFromMAC(mac)
  287. // 格式化MAC地址
  288. mac = strings.ToUpper(mac)
  289. results <- DeviceInfo{
  290. IP: ip,
  291. MAC: mac,
  292. Hostname: hostname,
  293. Vendor: vendor,
  294. }
  295. }
  296. // printTable 打印结果表格
  297. func printTable(devices []DeviceInfo) {
  298. fmt.Printf("\n%-18s %-20s %-40s %s\n", "IP Address", "MAC Address", "Hostname", "Vendor")
  299. fmt.Println(strings.Repeat("-", 100))
  300. for _, device := range devices {
  301. hostname := device.Hostname
  302. if hostname == "" {
  303. hostname = "N/A"
  304. }
  305. fmt.Printf("%-18s %-20s %-40s %s\n", device.IP, device.MAC, hostname, device.Vendor)
  306. }
  307. fmt.Printf("\nTotal devices found: %d\n", len(devices))
  308. }
  309. func TestS(t *testing.T) {
  310. fmt.Println("=== Network Scanner ===")
  311. fmt.Println("Scanning local network...")
  312. // 获取本地IP和网络
  313. localIP, ipNet := getLocalIP()
  314. if localIP == nil {
  315. log.Fatal("Failed to get local IP address")
  316. }
  317. fmt.Printf("Local IP: %s\n", localIP)
  318. fmt.Printf("Network: %s\n", ipNet.String())
  319. // 获取要扫描的IP列表
  320. ips, err := getIPRange(ipNet)
  321. if err != nil {
  322. log.Fatal(err)
  323. }
  324. fmt.Printf("Scanning %d IP addresses...\n", len(ips))
  325. // 创建结果通道和等待组
  326. results := make(chan DeviceInfo, 100)
  327. var wg sync.WaitGroup
  328. // 启动扫描协程
  329. startTime := time.Now()
  330. maxConcurrent := 50 // 最大并发数
  331. semaphore := make(chan struct{}, maxConcurrent)
  332. for _, ip := range ips {
  333. wg.Add(1)
  334. semaphore <- struct{}{}
  335. go func(targetIP string) {
  336. defer func() { <-semaphore }()
  337. scanIP(targetIP, results, &wg)
  338. }(ip)
  339. }
  340. // 等待所有扫描完成
  341. go func() {
  342. wg.Wait()
  343. close(results)
  344. }()
  345. // 收集结果
  346. var devices []DeviceInfo
  347. for device := range results {
  348. devices = append(devices, device)
  349. }
  350. // 按IP排序
  351. sort.Slice(devices, func(i, j int) bool {
  352. ip1 := net.ParseIP(devices[i].IP)
  353. ip2 := net.ParseIP(devices[j].IP)
  354. return ip1.To4()[0] < ip2.To4()[0] ||
  355. (ip1.To4()[0] == ip2.To4()[0] && ip1.To4()[1] < ip2.To4()[1]) ||
  356. (ip1.To4()[0] == ip2.To4()[0] && ip1.To4()[1] == ip2.To4()[1] && ip1.To4()[2] < ip2.To4()[2]) ||
  357. (ip1.To4()[0] == ip2.To4()[0] && ip1.To4()[1] == ip2.To4()[1] && ip1.To4()[2] == ip2.To4()[2] && ip1.To4()[3] < ip2.To4()[3])
  358. })
  359. // 显示结果
  360. printTable(devices)
  361. duration := time.Since(startTime)
  362. fmt.Printf("Scan completed in %.2f seconds\n", duration.Seconds())
  363. // 保存结果到文件(可选)
  364. if len(devices) > 0 {
  365. saveToFile(devices)
  366. }
  367. }
  368. // saveToFile 保存结果到文件
  369. func saveToFile(devices []DeviceInfo) {
  370. file, err := os.Create("network_devices.txt")
  371. if err != nil {
  372. log.Printf("Failed to create file: %v", err)
  373. return
  374. }
  375. defer file.Close()
  376. file.WriteString("IP Address,MAC Address,Hostname,Vendor\n")
  377. for _, device := range devices {
  378. hostname := device.Hostname
  379. if hostname == "" {
  380. hostname = "N/A"
  381. }
  382. file.WriteString(fmt.Sprintf("%s,%s,%s,%s\n", device.IP, device.MAC, hostname, device.Vendor))
  383. }
  384. fmt.Println("Results saved to network_devices.txt")
  385. }