Building your own port scanner in Go ;-)

Building your own port scanner in Go ;-)

Overview

Building your own tools in Go offers a perfect balance of performance, simplicity, and scalability. Go’s efficient concurrency model with Goroutines enables high-speed parallel processing for demanding tasks. Its cross-platform portability allows creating standalone binaries with no external dependencies. The rich standard library and straightforward syntax make development quick and efficient. Ultimately, Go empowers you to craft highly reliable and customized solutions tailored to your exact needs.

This port scanner is designed to quickly scan TCP and UDP ports on a target IP address or hostname. It is highly efficient, leveraging Goroutines for concurrent scans and allowing users to specify a port range and timeout. It supports:

  1. TCP and UDP scanning.
  2. Detailed statuses: open, closed, and filtered.
  3. Customizable parameters such as port range, timeout, and worker count.

Code Documentation

Imports

import (
	"flag"       // Handles command-line flags
	"fmt"        // Used for formatted I/O
	"net"        // Provides networking functionality
	"time"       // Used for timeout handling
	"sync"       // For concurrency synchronization
)        

Main Function

The main function:

  1. Parses command-line flags using flag.
  2. Validates input and displays help if no parameters are provided.
  3. Initializes workers for both TCP and UDP scans (if enabled).
  4. Collects and displays results.

Worker Functions

tcpWorker:

  1. Scans TCP ports for their status.
  2. Uses net.DialTimeout to determine connectivity.

udpWorker:

  1. Scans UDP ports and sends test packets to detect openness.
  2. Handles timeouts and unreachable statuses.

Helper Functions

isTimeout:

  1. Checks if an error is a timeout.

displayHelp:

  1. Displays detailed usage instructions.

Enhanced Code

package main

import (
	"flag"
	"fmt"
	"net"
	"sync"
	"time"
)

// PortStatus represents the state of a port
type PortStatus struct {
	Port     int
	Protocol string
	Status   string
}

func main() {
	// Define command-line flags
	ip := flag.String("ip", "", "Target IP address or hostname")
	startPort := flag.Int("start", 1, "Start of port range")
	endPort := flag.Int("end", 65535, "End of port range")
	timeout := flag.Int("timeout", 500, "Timeout in milliseconds")
	workers := flag.Int("workers", 100, "Number of concurrent workers")
	scanUDP := flag.Bool("udp", false, "Enable UDP scanning")
	help := flag.Bool("help", false, "Display help")

	// Parse flags
	flag.Parse()

	// Display help if no parameters are provided or if help is explicitly requested
	if *help || flag.NFlag() == 0 {
		displayHelp()
		return
	}

	// Validate IP address or hostname
	if *ip == "" {
		fmt.Println("Error: Target IP address or hostname is required.")
		displayHelp()
		return
	}

	// Print scan summary
	fmt.Printf("Starting scan on %s from port %d to %d with %d workers\n", ip, startPort, endPort, workers)

	// Channels for ports and results
	ports := make(chan int, *workers)
	results := make(chan PortStatus)

	// WaitGroup for workers
	var wg sync.WaitGroup

	// Start TCP workers
	for i := 0; i < *workers; i++ {
		wg.Add(1)
		go tcpWorker(*ip, ports, results, &wg, time.Duration(*timeout)*time.Millisecond)
	}

	// Start UDP workers if enabled
	if *scanUDP {
		for i := 0; i < *workers; i++ {
			wg.Add(1)
			go udpWorker(*ip, ports, results, &wg, time.Duration(*timeout)*time.Millisecond)
		}
	}

	// Goroutine to close results channel after all workers are done
	go func() {
		wg.Wait()
		close(results)
	}()

	// Goroutine to send ports to the ports channel
	go func() {
		for port := startPort; port <= endPort; port++ {
			ports <- port
		}
		close(ports)
	}()

	// Display results as they are received
	for result := range results {
		fmt.Printf("Port %d (%s): %s\n", result.Port, result.Protocol, result.Status)
	}
	fmt.Println("Scan complete!")
}

// TCP worker scans ports using TCP
func tcpWorker(ip string, ports chan int, results chan PortStatus, wg *sync.WaitGroup, timeout time.Duration) {
	defer wg.Done()
	for port := range ports {
		address := fmt.Sprintf("%s:%d", ip, port)
		conn, err := net.DialTimeout("tcp", address, timeout)
		if err != nil {
			if isTimeout(err) {
				results <- PortStatus{Port: port, Protocol: "TCP", Status: "filtered"}
			} else {
				results <- PortStatus{Port: port, Protocol: "TCP", Status: "closed"}
			}
			continue
		}
		conn.Close()
		results <- PortStatus{Port: port, Protocol: "TCP", Status: "open"}
	}
}

// UDP worker scans ports using UDP
func udpWorker(ip string, ports chan int, results chan PortStatus, wg *sync.WaitGroup, timeout time.Duration) {
	defer wg.Done()
	for port := range ports {
		address := fmt.Sprintf("%s:%d", ip, port)
		conn, err := net.DialTimeout("udp", address, timeout)
		if err != nil {
			results <- PortStatus{Port: port, Protocol: "UDP", Status: "closed"}
			continue
		}
		conn.Close()
		results <- PortStatus{Port: port, Protocol: "UDP", Status: "open"}
	}
}

// Helper to check if an error is a timeout
func isTimeout(err error) bool {
	netErr, ok := err.(net.Error)
	return ok && netErr.Timeout()
}

// Display help message
func displayHelp() {
	fmt.Println("\n== Go Port Scanner ==")
	fmt.Println("Usage: portscanner [options]")
	fmt.Println("Options:")
	fmt.Println("  -ip <target_ip>          Target IP address or hostname (required)")
	fmt.Println("  -start <port>            Start of port range (default: 1)")
	fmt.Println("  -end <port>              End of port range (default: 65535)")
	fmt.Println("  -timeout <ms>            Timeout in milliseconds (default: 500)")
	fmt.Println("  -workers <count>         Number of concurrent workers (default: 100)")
	fmt.Println("  -udp                     Enable UDP scanning")
	fmt.Println("  -help                    Display this help message")
	fmt.Println("\nExample:")
	fmt.Println("  go run portscanner.go -ip 192.168.1.1 -start 20 -end 80 -timeout 200 -workers 50 -udp")
}        

How the code works

Help System:

  • If no flags are provided (flag.NFlag() == 0), the displayHelp function is called.
  • The -help flag also triggers the help message.

Worker Design:

  • Two separate worker functions (tcpWorker and udpWorker) handle TCP and UDP scanning, respectively.

Concurrency:

  • Channels and a sync.WaitGroup allow multiple workers to process ports concurrently.

Timeouts and Errors:

  • Timeouts are handled gracefully, and statuses are categorized as open, closed, or filtered.

How to use

Display Help:

go run portscanner.go -help        

Scan TCP Ports:

go run portscanner.go -ip <target_ip> -start 20 -end 80 -timeout 200 -workers 50        

Scan Both TCP and UDP:

go run portscanner.go -ip <target_ip> -start 20 -end 80 -timeout 200 -workers 50 -udp        

Key factors contributing to performance

Concurrency with Goroutines:

The use of workers allows multiple ports to be scanned simultaneously.

You can adjust the number of workers (default: 100) to optimize for hardware and network capabilities.

Efficient Network Dialing:

Using net.DialTimeout ensures that the scanner doesn't wait indefinitely on unresponsive ports, improving overall speed.

Minimal Overhead:

The tool avoids unnecessary computations, focusing only on sending requests and processing responses.

Recommendations for further optimization or testing

Increase Worker Count:

You can experiment with higher worker counts for even faster scans if your system can handle it:

go run portscanner.go -ip <target_ip> -start 1 -end 65535 -workers 500        

Test on Remote Hosts:

Scanning a remote host over the network introduces latency. Measure the time for such scenarios:

time go run portscanner.go -ip <target_ip> -start 1 -end 65535        

Enable UDP Scanning:

UDP scans are inherently slower since they require additional steps (sending packets and waiting for replies). Include the -udp flag:

time go run portscanner.go -ip <target_ip> -start 1 -end 65535 -udp        

Adjust Timeout:

A shorter timeout can speed up scans, but it may lead to false negatives for slower networks. Try:

go run portscanner.go -ip <target_ip> -start 1 -end 65535 -timeout 100        

Save Results to a File:

Extend the program to save scan results to a file for large scans. For example:

./portscanner -ip <target_ip> -start 1 -end 65535 > scan_results.txt        

Hoping you liked this tutorial. If you find any mistakes or have suggestions to improve this tool, please don’t hesitate to let me know, drop me an email to [email protected] —I’m always open to feedback! ??

要查看或添加评论,请登录

Luis C.的更多文章

  • Ser agradecidos!

    Ser agradecidos!

    Te invito a ver mi video de esta semana Ser agradecidos! También puedes verlo en Youtube o en Instagram. Espero que te…

    1 条评论
  • Ser una persona de influencia (Video)

    Ser una persona de influencia (Video)

    ?Quieres ser una persona de influencia? ?? Te invito a ver mi video resumen donde comparto las claves para desarrollar…

  • ZABBIX FULL DAY

    ZABBIX FULL DAY

    Es una capacitación de todo un día completo. Debes registrarte vía el formulario, y posteriormente se remitirá las…

  • Power BI Full Day

    Power BI Full Day

    Es una capacitación de todo un día completo. Debes registrarte vía el formulario, y posteriormente se remitirá las…

  • Charla: Tu Sue?o No Esperará: 5 Pasos para Empezar Ahora, No Ma?ana

    Charla: Tu Sue?o No Esperará: 5 Pasos para Empezar Ahora, No Ma?ana

    ?No te pierdas esta oportunidad única! únete a mi charla gratuita vía Zoom: "Tu Sue?o No Esperará: 5 Pasos para Empezar…

  • Día 31: Creación de un servidor web básico

    Día 31: Creación de un servidor web básico

    Enunciado del ejercicio propuesto: Crea un servidor web simple en Go que responda con un mensaje de "?Hola Mundo!" a…

    1 条评论
  • Día 30: Manejo avanzado de errores

    Día 30: Manejo avanzado de errores

    Enunciado del ejercicio propuesto: Implementa un manejo de errores avanzado que incluya errores personalizados con…

  • Día 29: Tests con Mocks

    Día 29: Tests con Mocks

    Enunciado del ejercicio propuesto: Implementa tests utilizando mocks para una función que depende de una interfaz…

  • Día 28: Trabajando con fechas y tiempos

    Día 28: Trabajando con fechas y tiempos

    Enunciado del ejercicio propuesto: Escribe un programa que calcule la diferencia en días entre dos fechas…

  • Día 27: Generación de números aleatorios

    Día 27: Generación de números aleatorios

    Enunciado del ejercicio propuesto: Genera una secuencia de cinco números aleatorios únicos entre 1 y 100 y guárdalos en…

    1 条评论

社区洞察

其他会员也浏览了