I'm trying to play to make some kind of port scanner using stdlib. This is more an exercise than anything else, so please do not comment on the logic involved.
Take a look at the following code:
package main
import (
"flag"
"fmt"
"net"
"time"
"strings"
"strconv"
"log"
"sync"
)
var commonPorts = map[int]string {
21: "ftp",
22: "sftp",
80: "http",
110: "pop3",
143: "imap",
443: "https",
631: "ipp",
993: "imaps",
995: "pop3s",
}
type OP struct {
mu sync.Mutex
ports []string
}
func (o *OP) SafeAdd(port string) {
o.mu.Lock()
defer o.mu.Unlock()
o.ports = append(o.ports, port)
}
func worker(host string, port int) string {
address := fmt.Sprintf("%s:%d", host, port)
conn, err := net.DialTimeout("tcp", address, time.Second * 3)
if err != nil {
return "";
}
conn.Close()
stringI := strconv.Itoa(port)
if name, ok := commonPorts[port]; ok {
stringI += fmt.Sprintf("(%s)", name)
}
return stringI
}
func processWithChannels(host string) <-chan string{
openPort := make(chan string, 1000)
var wg sync.WaitGroup
for i := 1; i <= 65535; i++ {
wg.Add(1)
go func(openPort chan string, host string, i int) {
defer wg.Done()
port := worker(host, i)
if port != "" {
openPort <- port
}
}(openPort, host, i)
}
wg.Wait()
close(openPort)
return openPort
}
func main() {
var host = flag.String("host", "127.0.0.1", "please insert the host")
var pType = flag.Int("type", 2, "please insert the type")
flag.Parse()
fmt.Printf("Scanning: %s...\n", *host)
if _, err := net.LookupHost(*host); err != nil {
log.Fatal(err)
}
openPorts := &OP{ports: []string{}};
if *pType == 1 {
ports := processWithChannels(*host);
for port := range ports {
openPorts.SafeAdd(port)
}
} else {
var wg sync.WaitGroup
for i := 1; i <= 65535; i++ {
wg.Add(1)
go func(o *OP, host string, i int){
defer wg.Done()
if port := worker(host, i); port != "" {
o.SafeAdd(port)
}
}(openPorts, *host, i)
}
wg.Wait()
}
if len(openPorts.ports) > 0 {
fmt.Printf("Following ports are opened: %s\n", strings.Join(openPorts.ports, ", "))
} else {
fmt.Printf("No open port on the host: %s!\n", *host)
}
}
There are two ways to start a scan: either using a buffered channel, or using sync.GroupWait and exit it as soon as all checks have been completed.
It seems to me that in this case, using sync.GroupWait makes more sense than using a buffered channel and looping through it until it is empty. However, using the buffer channel here, I see no way to detect that there is nothing else in the channel, and that I should break out of the for loop, except for using another sync.WaitGroup block.
, , , , , , ? ( , , -).
, , - :
MacBook-Pro:PortScanner c$ time ./PortScanner -host yahoo.com -type 1
Scanning: yahoo.com...
Following ports are opened: 80(http), 143(imap), 110(pop3), 995(pop3s), 993(imaps)
real 0m4.620s
user 0m1.193s
sys 0m1.284s
MacBook-Pro:PortScanner c$ time ./PortScanner -host yahoo.com -type 2
Scanning: yahoo.com...
Following ports are opened: 110(pop3), 80(http), 143(imap), 995(pop3s), 993(imaps)
real 0m4.055s
user 0m1.051s
sys 0m0.946s