diff options
| -rw-r--r-- | go.mod | 3 | ||||
| -rw-r--r-- | main.go | 150 |
2 files changed, 153 insertions, 0 deletions
@@ -0,0 +1,3 @@ +module porter + +go 1.16 @@ -0,0 +1,150 @@ +package main + +import ( + "fmt" + "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 { + log.Fatal(err) + } + + 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() + } +} |
