package main import ( "fmt" "errors" "io" "log" "net" "os" "strconv" "sync" "syscall" "time" ) func usage() { fmt.Fprintf(os.Stderr, "Usage: porter action host min max\n\n"+ "-- action is any of dial, listen\n"+ "-- min, max is tcp port values\n", ) os.Exit(2) } func parsePositive(s string) int { n, err := strconv.Atoi(s) if err != nil || n <= 0 { return -1 } return n } func setlimit(total int) { nfds := uint64(total) rlim := syscall.Rlimit{} if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlim); err != nil { log.Fatal(err) } if nfds > rlim.Max { log.Fatalf("too big port range %d, max %d", nfds, rlim.Max) } if nfds > rlim.Cur { rlim.Cur = nfds if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rlim); err != nil { log.Fatal(err) } } } func dial(host string, min, max int) { var wg sync.WaitGroup setlimit((max - min + 1) + 32) tout := time.Second for n := min; n <= max; n++ { wg.Add(1) go func(port int) { defer wg.Done() c, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), tout) if err != nil { fmt.Printf("fail %d dial: %s\n", port, err) return } fmt.Printf("ok %d dial\n", port) fmt.Fprint(c, "test") c.Close() }(n) } wg.Wait() } func listen(host string, min, max int) { ready := make(chan struct{}) setlimit(2*(max-min+1) + 32) for n := min; n <= max; n++ { l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", host, n)) if err != nil { fmt.Printf("fail %d listen: %s\n", n, errors.Unwrap(err)) continue } go func(l net.Listener, port int) { <-ready for { c, err := l.Accept() if err != nil { fmt.Printf("fail %d accept: %s\n", port, err) continue } fmt.Printf("ok %d accept\n", port) buf := make([]byte, 1024) for { n, err := c.Read(buf) if err != nil { if err == io.EOF { fmt.Printf("ok %d close\n", port) } else { fmt.Printf("fail %d read: %s\n", port, err) } break } fmt.Printf("ok %d read %d bytes\n", port, n) } c.Close() } }(l, n) } close(ready) syscall.Pause() } func main() { log.SetFlags(0) if len(os.Args) < 5 { usage() } action := os.Args[1] host := os.Args[2] min := parsePositive(os.Args[3]) max := parsePositive(os.Args[4]) if min < 0 || max < 0 || min > max { usage() } switch action { case "dial": dial(host, min, max) case "listen": listen(host, min, max) default: usage() } }