swapped to golang
This commit is contained in:
parent
2f5cacd57b
commit
98cfb706cf
2 changed files with 372 additions and 262 deletions
372
main.go
Normal file
372
main.go
Normal file
|
@ -0,0 +1,372 @@
|
|||
package main
|
||||
// sorry in advance for anyone who has to read this
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type OpenPort struct {
|
||||
IP string
|
||||
Port int
|
||||
Hostname string
|
||||
Fingerprint string
|
||||
}
|
||||
const batchSize = 255
|
||||
var printMutex sync.Mutex
|
||||
var (
|
||||
summaryFile *os.File
|
||||
writer *bufio.Writer
|
||||
fileLock sync.Mutex
|
||||
)
|
||||
var (
|
||||
openPorts []OpenPort
|
||||
openPortsLock sync.Mutex
|
||||
ipLineMap = make(map[string]int)
|
||||
openPortMsgs []string
|
||||
)
|
||||
|
||||
func clearScreen() {
|
||||
fmt.Print("\033[2J")
|
||||
}
|
||||
|
||||
func moveCursor(row, col int) {
|
||||
fmt.Printf("\033[%d;%dH", row, col)
|
||||
}
|
||||
|
||||
func clearLine() {
|
||||
fmt.Print("\033[K")
|
||||
}
|
||||
|
||||
func safePrint(format string, a ...interface{}) {
|
||||
printMutex.Lock()
|
||||
defer printMutex.Unlock()
|
||||
fmt.Printf(format, a...)
|
||||
}
|
||||
|
||||
func safePrintln(a ...interface{}) {
|
||||
printMutex.Lock()
|
||||
defer printMutex.Unlock()
|
||||
fmt.Println(a...)
|
||||
}
|
||||
|
||||
func printStatusLine(ip, msg string) {
|
||||
if line, ok := ipLineMap[ip]; ok {
|
||||
moveCursor(line, 0)
|
||||
clearLine()
|
||||
fmt.Printf("[%s] %s", ip, msg)
|
||||
}
|
||||
}
|
||||
|
||||
func printOpenPortLine(msg string) {
|
||||
openPortsLock.Lock()
|
||||
defer openPortsLock.Unlock()
|
||||
openPortMsgs = append(openPortMsgs, msg)
|
||||
moveCursor(len(ipLineMap)+2+len(openPortMsgs), 0)
|
||||
clearLine()
|
||||
fmt.Println(msg)
|
||||
}
|
||||
|
||||
func isPrivateIP(ipStr string) bool {
|
||||
ip := net.ParseIP(ipStr)
|
||||
if ip == nil {
|
||||
return false
|
||||
}
|
||||
privateBlocks := []string{
|
||||
"10.0.0.0/8",
|
||||
"172.16.0.0/12",
|
||||
"192.168.0.0/16",
|
||||
"127.0.0.0/8",
|
||||
}
|
||||
for _, cidr := range privateBlocks {
|
||||
_, block, _ := net.ParseCIDR(cidr)
|
||||
if block.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
func pingIP(ip string, pingCmd []string) bool {
|
||||
cmd := exec.Command(pingCmd[0], append(pingCmd[1:], ip)...)
|
||||
err := cmd.Run()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func grabBanner(ip string, port int) string {
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), 1*time.Second)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer conn.Close()
|
||||
conn.SetReadDeadline(time.Now().Add(1 * time.Second))
|
||||
buf := make([]byte, 1024)
|
||||
n, _ := conn.Read(buf)
|
||||
return strings.TrimSpace(string(buf[:n]))
|
||||
}
|
||||
|
||||
func identifyServiceAndOS(ip string, port int) string {
|
||||
data, err := os.ReadFile("services.json")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
var services map[string]map[string]string
|
||||
json.Unmarshal(data, &services)
|
||||
|
||||
portStr := strconv.Itoa(port)
|
||||
if info, ok := services[portStr]; ok {
|
||||
banner := grabBanner(ip, port)
|
||||
if banner != "" {
|
||||
return fmt.Sprintf("%s banner: %s", info["name"], banner)
|
||||
}
|
||||
return info["name"]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func scanPort(ip string, port int, wg *sync.WaitGroup) {
|
||||
printStatusLine(ip, fmt.Sprintf("scanning port %d...", port))
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), 500*time.Millisecond)
|
||||
if err == nil {
|
||||
conn.Close()
|
||||
host, err := net.LookupAddr(ip)
|
||||
hostname := "unknown hostname"
|
||||
if err == nil && len(host) > 0 {
|
||||
hostname = strings.TrimSuffix(host[0], ".")
|
||||
}
|
||||
fingerprint := identifyServiceAndOS(ip, port)
|
||||
openPortsLock.Lock()
|
||||
openPorts = append(openPorts, OpenPort{ip, port, hostname, fingerprint})
|
||||
line := fmt.Sprintf(" - %s : port %d open - hostname: %s", ip, port, hostname)
|
||||
if fingerprint != "" {
|
||||
line += " | " + fingerprint
|
||||
}
|
||||
|
||||
fileLock.Lock()
|
||||
summaryFile.WriteString(line + "\n")
|
||||
fileLock.Unlock()
|
||||
openPortsLock.Unlock()
|
||||
msg := fmt.Sprintf("[%s] port %d is OPEN (%s)", ip, port, hostname)
|
||||
if fingerprint != "" {
|
||||
msg += " | " + fingerprint
|
||||
}
|
||||
printOpenPortLine(msg)
|
||||
printStatusLine(ip, fmt.Sprintf("open port found: %d", port))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func scanPortsForIP(ip string, portStart, portEnd int) {
|
||||
const maxGoroutines = 50
|
||||
guard := make(chan struct{}, maxGoroutines)
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for port := portStart; port <= portEnd; port++ {
|
||||
guard <- struct{}{}
|
||||
wg.Add(1)
|
||||
go func(p int) {
|
||||
defer wg.Done()
|
||||
scanPort(ip, p, &wg)
|
||||
<-guard
|
||||
}(port)
|
||||
}
|
||||
wg.Wait()
|
||||
printStatusLine(ip, "scan done.")
|
||||
}
|
||||
|
||||
func pingIPsConcurrently(ips []string, pingCmd []string) []string {
|
||||
var alive []string
|
||||
var wg sync.WaitGroup
|
||||
var lock sync.Mutex
|
||||
for _, ip := range ips {
|
||||
wg.Add(1)
|
||||
go func(ip string) {
|
||||
defer wg.Done()
|
||||
if !isPrivateIP(ip) {
|
||||
fmt.Printf("[+] skipping ping for public IP %s\n", ip)
|
||||
lock.Lock()
|
||||
alive = append(alive, ip)
|
||||
lock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
if pingIP(ip, pingCmd) {
|
||||
fmt.Printf("[+] found %s\n", ip)
|
||||
lock.Lock()
|
||||
alive = append(alive, ip)
|
||||
lock.Unlock()
|
||||
} else {
|
||||
fmt.Printf("[~] no response from %s\n", ip)
|
||||
}
|
||||
|
||||
}(ip)
|
||||
}
|
||||
wg.Wait()
|
||||
return alive
|
||||
}
|
||||
|
||||
func ipRange(start, end string) ([]string, error) {
|
||||
startIP := net.ParseIP(start).To4()
|
||||
endIP := net.ParseIP(end).To4()
|
||||
if startIP == nil || endIP == nil {
|
||||
return nil, fmt.Errorf("invalid IP")
|
||||
}
|
||||
|
||||
var ips []string
|
||||
for ip := startIP; !ipAfter(ip, endIP); ip = nextIP(ip) {
|
||||
ips = append(ips, ip.String())
|
||||
}
|
||||
return ips, nil
|
||||
}
|
||||
|
||||
func ipAfter(a, b net.IP) bool {
|
||||
for i := 0; i < 4; i++ {
|
||||
if a[i] > b[i] {
|
||||
return true
|
||||
}
|
||||
if a[i] < b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func nextIP(ip net.IP) net.IP {
|
||||
next := make(net.IP, len(ip))
|
||||
copy(next, ip)
|
||||
for i := 3; i >= 0; i-- {
|
||||
next[i]++
|
||||
if next[i] != 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
summaryFile, err = os.Create("summary.txt")
|
||||
if err != nil {
|
||||
fmt.Println("[!] failed to open summary file:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer summaryFile.Close()
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
fmt.Print("enter IP range (e.g., 192.168.1.1-192.168.1.20): ")
|
||||
ipInput, _ := reader.ReadString('\n')
|
||||
ipInput = strings.TrimSpace(ipInput)
|
||||
if ipInput == "" {
|
||||
ipInput = "192.168.0.0-192.168.255.255"
|
||||
}
|
||||
fmt.Print("port range (e.g., 1-65535): ")
|
||||
portInput, _ := reader.ReadString('\n')
|
||||
portInput = strings.TrimSpace(portInput)
|
||||
if portInput == "" {
|
||||
portInput = "1-10000"
|
||||
}
|
||||
ipParts := strings.Split(ipInput, "-")
|
||||
portParts := strings.Split(portInput, "-")
|
||||
if len(ipParts) != 2 || len(portParts) != 2 {
|
||||
fmt.Println("[!] bad input.")
|
||||
os.Exit(1)
|
||||
}
|
||||
ipList, err := ipRange(ipParts[0], ipParts[1])
|
||||
if err != nil {
|
||||
fmt.Println("[!] bad IP range:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
portStart, _ := strconv.Atoi(portParts[0])
|
||||
portEnd, _ := strconv.Atoi(portParts[1])
|
||||
|
||||
fmt.Println("--------------------------------------------------")
|
||||
fmt.Printf("scanning %s to %s\n", ipParts[0], ipParts[1])
|
||||
fmt.Printf("ports %d to %d\n", portStart, portEnd)
|
||||
fmt.Println("started at:", time.Now())
|
||||
fmt.Println("--------------------------------------------------")
|
||||
|
||||
pingCmd := []string{}
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
pingCmd = []string{"ping", "-n", "1", "-w", "1000"}
|
||||
case "darwin":
|
||||
pingCmd = []string{"ping", "-c", "1"}
|
||||
default:
|
||||
pingCmd = []string{"ping", "-c", "1", "-W", "1"}
|
||||
}
|
||||
|
||||
fmt.Println("[~] pinging IPs...")
|
||||
var alive []string
|
||||
for i := 0; i < len(ipList); i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > len(ipList) {
|
||||
end = len(ipList)
|
||||
}
|
||||
batch := ipList[i:end]
|
||||
fmt.Printf("[~] pinging IPs %d to %d...\n", i+1, end)
|
||||
batchAlive := pingIPsConcurrently(batch, pingCmd)
|
||||
alive = append(alive, batchAlive...)
|
||||
}
|
||||
if len(alive) == 0 {
|
||||
fmt.Println("[-] no IPs responded to ping. exiting.")
|
||||
return
|
||||
}
|
||||
|
||||
clearScreen()
|
||||
for idx, ip := range alive {
|
||||
ipLineMap[ip] = idx + 1
|
||||
printStatusLine(ip, "waiting to scan...")
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < len(alive); i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > len(alive) {
|
||||
end = len(alive)
|
||||
}
|
||||
batch := alive[i:end]
|
||||
for _, ip := range batch {
|
||||
wg.Add(1)
|
||||
go func(ip string) {
|
||||
defer wg.Done()
|
||||
scanPortsForIP(ip, portStart, portEnd)
|
||||
}(ip)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
moveCursor(len(ipLineMap)+3+len(openPortMsgs), 0)
|
||||
clearLine()
|
||||
fmt.Println("\n--------------------------------------------------")
|
||||
fmt.Println("scan done at:", time.Now())
|
||||
fmt.Println("--------------------------------------------------")
|
||||
|
||||
file, err := os.Create("summary.txt")
|
||||
if err != nil {
|
||||
fmt.Println("[!] failed to write summary:", err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if len(openPorts) == 0 {
|
||||
file.WriteString("no open ports found.\n")
|
||||
} else {
|
||||
file.WriteString("[+] scan summary: open ports found with fingerprints\n")
|
||||
for _, p := range openPorts {
|
||||
line := fmt.Sprintf(" - %s : port %d open - hostname: %s", p.IP, p.Port, p.Hostname)
|
||||
if p.Fingerprint != "" {
|
||||
line += " | " + p.Fingerprint
|
||||
}
|
||||
file.WriteString(line + "\n")
|
||||
}
|
||||
}
|
||||
fmt.Println("[+] scan summary written to summary.txt")
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue