You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
280 lines
5.8 KiB
Go
280 lines
5.8 KiB
Go
4 months ago
|
package net
|
||
|
|
||
|
import (
|
||
|
"b612.me/apps/b612/netforward"
|
||
|
"b612.me/stario"
|
||
|
"b612.me/starlog"
|
||
|
"b612.me/starnet"
|
||
|
"fmt"
|
||
|
"math"
|
||
|
"net"
|
||
|
"sync/atomic"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
type ScanIP struct {
|
||
|
Host string
|
||
|
CIDR int
|
||
|
Port int
|
||
|
Mask string
|
||
|
Threads int
|
||
|
Timeout int
|
||
|
ScanType string
|
||
|
ipNet *net.IPNet
|
||
|
Log string
|
||
|
Retry int
|
||
|
WithHostname bool
|
||
|
}
|
||
|
|
||
|
func (s *ScanIP) Parse() error {
|
||
|
if s.CIDR == 0 && s.Mask == "" {
|
||
|
return fmt.Errorf("CIDR or Mask must be set")
|
||
|
|
||
|
}
|
||
|
if s.CIDR != 0 {
|
||
|
return nil
|
||
|
}
|
||
|
//mask to cidr
|
||
|
ipMask := net.IPMask(net.ParseIP(s.Mask).To4())
|
||
|
if ipMask == nil {
|
||
|
return fmt.Errorf("invalid mask")
|
||
|
}
|
||
|
s.CIDR, _ = ipMask.Size()
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (s *ScanIP) nextIP(ipStr string) (net.IP, error) {
|
||
|
var err error
|
||
|
if s.ipNet == nil {
|
||
|
_, s.ipNet, err = net.ParseCIDR(s.Host + "/" + fmt.Sprintf("%d", s.CIDR))
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("invalid CIDR: %v", err)
|
||
|
}
|
||
|
}
|
||
|
ip := net.ParseIP(ipStr)
|
||
|
if ip == nil {
|
||
|
return nil, fmt.Errorf("invalid IP: %v", ipStr)
|
||
|
}
|
||
|
|
||
|
// Convert IP to 4-byte representation
|
||
|
ip = ip.To4()
|
||
|
if ip == nil {
|
||
|
return nil, fmt.Errorf("non-IPv4 address: %v", ipStr)
|
||
|
}
|
||
|
|
||
|
// Increment IP
|
||
|
for i := len(ip) - 1; i >= 0; i-- {
|
||
|
ip[i]++
|
||
|
if ip[i] > 0 {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check if incremented IP is still in range
|
||
|
if !s.ipNet.Contains(ip) {
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
return ip, nil
|
||
|
}
|
||
|
|
||
|
func (s *ScanIP) NetSize() (int, error) {
|
||
|
var err error
|
||
|
if s.ipNet == nil {
|
||
|
_, s.ipNet, err = net.ParseCIDR(s.Host + "/" + fmt.Sprintf("%d", s.CIDR))
|
||
|
if err != nil {
|
||
|
return 0, fmt.Errorf("invalid CIDR: %v", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
maskSize, _ := s.ipNet.Mask.Size()
|
||
|
return int(math.Pow(2, float64(32-maskSize))) - 2, nil
|
||
|
}
|
||
|
|
||
|
func (s *ScanIP) FirstLastIP() (net.IP, net.IP, error) {
|
||
|
var err error
|
||
|
if s.ipNet == nil {
|
||
|
_, s.ipNet, err = net.ParseCIDR(s.Host + "/" + fmt.Sprintf("%d", s.CIDR))
|
||
|
if err != nil {
|
||
|
return nil, nil, fmt.Errorf("invalid CIDR: %v", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
firstIP := s.ipNet.IP.Mask(s.ipNet.Mask)
|
||
|
lastIP := make(net.IP, len(firstIP))
|
||
|
copy(lastIP, firstIP)
|
||
|
for i := range firstIP {
|
||
|
lastIP[i] = firstIP[i] | ^s.ipNet.Mask[i]
|
||
|
}
|
||
|
|
||
|
return firstIP, lastIP, nil
|
||
|
}
|
||
|
|
||
|
func (s *ScanIP) ICMP() error {
|
||
|
if s.ScanType != "icmp" {
|
||
|
return fmt.Errorf("scan type must be icmp")
|
||
|
}
|
||
|
if err := s.Parse(); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if s.Log != "" {
|
||
|
starlog.SetLogFile(s.Log, starlog.Std, true)
|
||
|
}
|
||
|
firstIP, lastIP, err := s.FirstLastIP()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
ns, _ := s.NetSize()
|
||
|
starlog.Infof("Scan %s/%d\n", s.Host, s.CIDR)
|
||
|
starlog.Infof("Scan %s-%s\n", firstIP.String(), lastIP.String())
|
||
|
starlog.Infof("There are %d hosts\n", ns)
|
||
|
starlog.Infof("Threads: %d\n", s.Threads)
|
||
|
|
||
|
wg := stario.NewWaitGroup(s.Threads)
|
||
|
count := int32(0)
|
||
|
allcount := int32(0)
|
||
|
interrupt := make(chan [2]string)
|
||
|
go func() {
|
||
|
for {
|
||
|
select {
|
||
|
case <-time.After(time.Second * 2):
|
||
|
fmt.Printf("scan %d ips, %d up\r", allcount, count)
|
||
|
case ip, opened := <-interrupt:
|
||
|
if !opened {
|
||
|
return
|
||
|
}
|
||
|
if s.WithHostname {
|
||
|
starlog.Infof("Host %v is up, Name:%v\n", ip[0], ip[1])
|
||
|
} else {
|
||
|
starlog.Infof("Host %v is up\n", ip[0])
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}()
|
||
|
idx := 0
|
||
|
for {
|
||
|
ip := firstIP.String()
|
||
|
if ip == lastIP.String() {
|
||
|
break
|
||
|
}
|
||
|
idx++
|
||
|
wg.Add(1)
|
||
|
go func(ip string, idx int) {
|
||
|
defer func() {
|
||
|
atomic.AddInt32(&allcount, 1)
|
||
|
}()
|
||
|
defer wg.Done()
|
||
|
for i := 0; i < s.Retry+1; i++ {
|
||
|
_, err := starnet.Ping(ip, idx, time.Duration(s.Timeout)*time.Millisecond)
|
||
|
if err == nil {
|
||
|
atomic.AddInt32(&count, 1)
|
||
|
if s.WithHostname {
|
||
|
hostname, err := net.LookupAddr(ip)
|
||
|
if err == nil {
|
||
|
interrupt <- [2]string{ip, hostname[0]}
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
interrupt <- [2]string{ip, ""}
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}(ip, idx)
|
||
|
firstIP, _ = s.nextIP(ip)
|
||
|
}
|
||
|
wg.Wait()
|
||
|
close(interrupt)
|
||
|
starlog.Infof("scan %d ips, %d up\n", ns, count)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (s *ScanIP) TCP(port int) error {
|
||
|
if s.ScanType != "tcp" {
|
||
|
return fmt.Errorf("scan type must be tcp")
|
||
|
}
|
||
|
if err := s.Parse(); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if s.Log != "" {
|
||
|
starlog.SetLogFile(s.Log, starlog.Std, true)
|
||
|
}
|
||
|
firstIP, lastIP, err := s.FirstLastIP()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
ns, _ := s.NetSize()
|
||
|
starlog.Infof("Scan %s/%d\n", s.Host, s.CIDR)
|
||
|
starlog.Infof("Scan %s-%s\n", firstIP.String(), lastIP.String())
|
||
|
starlog.Infof("There are %d hosts\n", ns)
|
||
|
starlog.Infof("Threads: %d\n", s.Threads)
|
||
|
|
||
|
wg := stario.NewWaitGroup(s.Threads)
|
||
|
count := int32(0)
|
||
|
allcount := int32(0)
|
||
|
interrupt := make(chan [2]string)
|
||
|
go func() {
|
||
|
for {
|
||
|
select {
|
||
|
case <-time.After(time.Second * 2):
|
||
|
fmt.Printf("scan %d ips, %d up\r", allcount, count)
|
||
|
case ip, opened := <-interrupt:
|
||
|
if !opened {
|
||
|
return
|
||
|
}
|
||
|
if s.WithHostname {
|
||
|
starlog.Infof("Host %v is up, Name:%v\n", ip[0], ip[1])
|
||
|
} else {
|
||
|
starlog.Infof("Host %v is up\n", ip[0])
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}()
|
||
|
idx := 0
|
||
|
localAddr, err := net.ResolveTCPAddr("tcp", ":0")
|
||
|
if err != nil {
|
||
|
starlog.Errorln("ResolveTCPAddr error, ", err)
|
||
|
return err
|
||
|
}
|
||
|
for {
|
||
|
ip := firstIP.String()
|
||
|
if ip == lastIP.String() {
|
||
|
break
|
||
|
}
|
||
|
idx++
|
||
|
wg.Add(1)
|
||
|
go func(ip string, idx int) {
|
||
|
defer func() {
|
||
|
atomic.AddInt32(&allcount, 1)
|
||
|
}()
|
||
|
defer wg.Done()
|
||
|
for i := 0; i < s.Retry+1; i++ {
|
||
|
dialer := net.Dialer{
|
||
|
LocalAddr: localAddr,
|
||
|
Timeout: time.Duration(s.Timeout) * time.Millisecond,
|
||
|
Control: netforward.ControlSetReUseAddr,
|
||
|
}
|
||
|
_, err := dialer.Dial("tcp", fmt.Sprintf("%s:%d", ip, port))
|
||
|
if err == nil {
|
||
|
atomic.AddInt32(&count, 1)
|
||
|
if s.WithHostname {
|
||
|
hostname, err := net.LookupAddr(ip)
|
||
|
if err == nil {
|
||
|
interrupt <- [2]string{ip, hostname[0]}
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
interrupt <- [2]string{ip, ""}
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}(ip, idx)
|
||
|
firstIP, _ = s.nextIP(ip)
|
||
|
}
|
||
|
wg.Wait()
|
||
|
close(interrupt)
|
||
|
starlog.Infof("scan %d ips, %d up\n", ns, count)
|
||
|
return nil
|
||
|
}
|