Simple Port Scanner with Golang
Use Go‘s net package: net.Dial(network, address string)
package main import ( "fmt" "net" ) func main() { _, err := net.Dial("tcp", "scanme.nmap.org:80") if err == nil { fmt.Println("Connection successful") }
Nonconcurrent Scanning
Scan TCP ports range from 1 to 2014 slowly.
package main import ( "fmt" "net" ) func main() { for i := 1; i <=1024; i ++ { address := fmt.Sprintf("scame.nmap.org:%d", i) conn, err := net.Dial("tcp", address) if err != nil { // port is closed or filtered. continue } conn.Close() fmt.Printf("%d open ", i) } }
Concurrent scanning
Harness the power of goroutines to scan multiple ports concurrently.
package main import ( "fmt" "net" ) func main() { for i := 1; i <= 1024; i++ { go func(j int) { address := fmt.Sprintf("scanme.nmap.org:%d", j) conn, err := net.Dial("tcp", address) if err != nil { return } conn.Close() fmt.Printf("%d open ", j) }(i) } }
But it is too fast to wait for the connection to take place, so you may not get accurate results for ports whose packets were still in-flight.
Method 1:
Synchronized Scanning Using WaitGroup
Use WaitGroup from the sync package to control concurrency in a thread-safe way.
package main import ( "fmt" "net" "sync" ) func main() { var wg sync.WaitGroup for i := 1; i <= 1024; i++ { wg.Add(1) go func(j int) { defer wg.Done() address := fmt.Sprintf("10.0.0.24:%d", j) conn, err := net.Dial("tcp", address) if err != nil { return } conn.Close() fmt.Printf("%d open ", j) }(i) } wg.Wait() }
This program is better, but still incorrect.
Port Scanning Using a Worker Pool
To avoid inconsistencies, use a pool of goroutines to manage the concurrent work being performed.
package main import ( "fmt" "sync" ) func worker(ports chan int, wg *sync.WaitGroup) { for p := range ports { fmt.Println(p) wg.Done() } } func main() { ports := make(chan int, 100) var wg sync.WaitGroup for i := 0; i < cap(ports); i++ { go worker(ports, &wg) } for i := 1; i <= 1024; i++ { wg.Add(1) ports <- i } wg.Wait() close(ports) }
The numbers printed to the screen in no particular order.
Method 2:
Multichannel Communication
Remove the dependency of a WaitGroup
package main import ( "fmt" "net" "sort" ) func worker(ports, results chan int) { for p:= range ports { address := fmt.Sprintf("10.0.0.24:%d", p) conn, err := net.Dial("tcp", address) if err != nil { results <-0 continue } conn.Close() results <- p } } func main() { ports := make(chan int, 100) results := make(chan int) var openports []int for i := 0; i < cap(ports); i++ { go worker(ports, results) } go func() { for i := 1; i <= 1024; i++ { ports <- i } }() for i := 0; i < 1024; i++ { port := <-results if port != 0 { openports = append(openports, port) } } close(ports) close(results) sort.Ints(openports) for _, port := range openports { fmt.Printf("%d open ", port) } }
A highly efficient port scanner.
You can modify the program to allow users to provide the number of workers as an option.