package connection import ( "context" "encoding/json" "log" "net" "os/exec" "strconv" "strings" "sync" "time" omd "git.loafle.net/overflow/model/discovery" omm "git.loafle.net/overflow/model/meta" omu "git.loafle.net/overflow/model/util" "git.loafle.net/overflow_scanner/probe/discovery/session" "golang.org/x/sync/semaphore" ) const ( defaultUlimit = 1024 ) func Scan(discoverySession session.DiscoverySession, targetHost *omd.Host) error { if nil == targetHost || nil == discoverySession.DiscoverPort() { return nil } dp := discoverySession.DiscoverPort() lock := semaphore.NewWeighted(Ulimit()) var wg sync.WaitGroup timeout := 500 * time.Millisecond ports := make(map[int]*omd.Port) Loop: for portNumber := dp.FirstScanRange; portNumber < dp.LastScanRange; portNumber++ { if nil != dp.ExcludePorts { for _, exPortNumber := range dp.ExcludePorts { if portNumber == exPortNumber { continue Loop } } } lock.Acquire(context.TODO(), 1) wg.Add(1) go func(port int) { defer func() { lock.Release(1) wg.Done() }() scanPort(discoverySession, ports, targetHost, port, timeout) }(portNumber) timer := time.NewTimer(time.Microsecond * 100) select { case <-timer.C: } } wg.Wait() return nil } func scanPort(discoverySession session.DiscoverySession, ports map[int]*omd.Port, targetHost *omd.Host, port int, timeout time.Duration) { addr := net.JoinHostPort(targetHost.Address, strconv.Itoa(port)) conn, err := net.DialTimeout("tcp", addr, timeout) dp := discoverySession.DiscoverPort() if err != nil { if strings.Contains(err.Error(), "too many open files") { time.Sleep(timeout) scanPort(discoverySession, ports, targetHost, port, timeout) } return } conn.Close() if _, ok := ports[port]; ok || !dp.Contains(port) { return } p := &omd.Port{ MetaPortType: omm.ToMetaPortType(omm.MetaPortTypeEnumTCP), PortNumber: json.Number(strconv.Itoa(port)), DiscoveredDate: omu.NowPtr(), } p.Host = targetHost ports[port] = p log.Printf("port: %v", p) } func Ulimit() int64 { out, err := exec.Command("ulimit", "-n").Output() if nil != err { return defaultUlimit } s := strings.TrimSpace(string(out)) i, err := strconv.ParseInt(s, 10, 64) if nil != err { return defaultUlimit } return i }