//go:build windows package deploy import ( "encoding/json" "net" "strings" "time" ) type smbHostShares struct { Host string `json:"host"` Shares []string `json:"shares,omitempty"` Accessible bool `json:"accessible"` Error string `json:"error,omitempty"` } type smbSharesResult struct { Hosts []smbHostShares `json:"hosts"` Count int `json:"count"` } // EnumerateSMBShares probes LAN hosts for reachable SMB shares (net view). func EnumerateSMBShares(maxHosts int) string { if maxHosts <= 0 { maxHosts = 32 } targets := smbShareTargets(maxHosts) hosts := make([]smbHostShares, 0, len(targets)) for _, host := range targets { hosts = append(hosts, probeSMBShares(host)) } result := smbSharesResult{Hosts: hosts, Count: len(hosts)} b, _ := json.Marshal(result) return string(b) } func smbShareTargets(maxHosts int) []string { targets := arpHosts() local := getLocalIPs() localSet := make(map[string]bool, len(local)) for _, ip := range local { localSet[ip] = true } filtered := make([]string, 0, len(targets)) seen := make(map[string]bool) for _, t := range targets { if localSet[t] || seen[t] { continue } seen[t] = true filtered = append(filtered, t) } if len(filtered) < 3 { for _, ip := range local { if !isIPv4(ip) { continue } subnet := getSubnet(ip) if subnet == "" { continue } for i := 1; i < 255 && len(filtered) < maxHosts; i++ { candidate, ok := ipv4SweepHost(subnet, i) if !ok { break } if candidate == ip || seen[candidate] { continue } conn, err := net.DialTimeout("tcp", candidate+":445", 400*time.Millisecond) if err == nil { conn.Close() seen[candidate] = true filtered = append(filtered, candidate) } } } } if len(filtered) > maxHosts { filtered = filtered[:maxHosts] } return filtered } func probeSMBShares(host string) smbHostShares { conn, err := net.DialTimeout("tcp", host+":445", 1500*time.Millisecond) if err != nil { return smbHostShares{Host: host, Error: "port 445 closed"} } conn.Close() out, err := HiddenOutput("net", "view", "\\\\"+host) text := strings.TrimSpace(string(out)) if err != nil { msg := text if msg == "" { msg = err.Error() } return smbHostShares{Host: host, Error: msg} } shares := parseNetViewShares(text) return smbHostShares{ Host: host, Shares: shares, Accessible: len(shares) > 0, } }