summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikhail Osipov <mike.osipov@gmail.com>2021-03-08 23:35:20 +0300
committerMikhail Osipov <mike.osipov@gmail.com>2021-03-08 23:35:20 +0300
commita5ca191edd3c1d7c2795987d0435849bdd4894fb (patch)
treec809294c29f1e2ea4db665fbc739f47f95dfa89b
init
-rw-r--r--go.mod3
-rw-r--r--main.go150
2 files changed, 153 insertions, 0 deletions
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..3e2c40a
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module porter
+
+go 1.16
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..0218d9f
--- /dev/null
+++ b/main.go
@@ -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()
+ }
+}