123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- package ipscan
- import (
- "bytes"
- "fmt"
- "net"
- "sort"
- "strconv"
- "sync"
- "time"
- "github.com/go-ping/ping"
- )
- /*
- IP Scanner
- This module scan the given network range and return a list
- of nearby nodes.
- */
- type DiscoveredHost struct {
- IP string
- Ping int
- Hostname string
- HttpPortDetected bool
- HttpsPortDetected bool
- }
- // Scan an IP range given the start and ending ip address
- func ScanIpRange(start, end string) ([]*DiscoveredHost, error) {
- ipStart := net.ParseIP(start)
- ipEnd := net.ParseIP(end)
- if ipStart == nil || ipEnd == nil {
- return nil, fmt.Errorf("Invalid IP address")
- }
- if bytes.Compare(ipStart, ipEnd) > 0 {
- return nil, fmt.Errorf("Invalid IP range")
- }
- var wg sync.WaitGroup
- hosts := make([]*DiscoveredHost, 0)
- for ip := ipStart; bytes.Compare(ip, ipEnd) <= 0; inc(ip) {
- wg.Add(1)
- thisIp := ip.String()
- go func(thisIp string) {
- defer wg.Done()
- host := &DiscoveredHost{IP: thisIp}
- if err := host.CheckPing(); err != nil {
- // skip if the host is unreachable
- host.Ping = -1
- hosts = append(hosts, host)
- return
- }
- host.CheckHostname()
- host.CheckPort("http", 80, &host.HttpPortDetected)
- host.CheckPort("https", 443, &host.HttpsPortDetected)
- hosts = append(hosts, host)
- }(thisIp)
- }
- //Wait until all go routine done
- wg.Wait()
- sortByIP(hosts)
- return hosts, nil
- }
- func ScanCIDRRange(cidr string) ([]*DiscoveredHost, error) {
- _, ipNet, err := net.ParseCIDR(cidr)
- if err != nil {
- return nil, err
- }
- ip := ipNet.IP.To4()
- startIP := net.IPv4(ip[0], ip[1], ip[2], 1).String()
- endIP := net.IPv4(ip[0], ip[1], ip[2], 254).String()
- return ScanIpRange(startIP, endIP)
- }
- func inc(ip net.IP) {
- for j := len(ip) - 1; j >= 0; j-- {
- ip[j]++
- if ip[j] > 0 {
- break
- }
- }
- }
- func sortByIP(discovered []*DiscoveredHost) {
- sort.Slice(discovered, func(i, j int) bool {
- return discovered[i].IP < discovered[j].IP
- })
- }
- func (host *DiscoveredHost) CheckPing() error {
- // ping the host and set the ping time in milliseconds
- pinger, err := ping.NewPinger(host.IP)
- if err != nil {
- return err
- }
- pinger.Count = 4
- pinger.Timeout = time.Second
- pinger.SetPrivileged(true) // This line may help on some systems
- pinger.Run()
- stats := pinger.Statistics()
- if stats.PacketsRecv == 0 {
- return fmt.Errorf("Host unreachable for " + host.IP)
- }
- host.Ping = int(stats.AvgRtt.Milliseconds())
- return nil
- }
- func (host *DiscoveredHost) CheckHostname() {
- // lookup the hostname for the IP address
- names, err := net.LookupAddr(host.IP)
- //fmt.Println(names, err)
- if err == nil && len(names) > 0 {
- host.Hostname = names[0]
- }
- }
- func (host *DiscoveredHost) CheckPort(protocol string, port int, detected *bool) {
- // try to connect to the specified port on the host
- conn, err := net.DialTimeout("tcp", net.JoinHostPort(host.IP, strconv.Itoa(port)), 1*time.Second)
- if err == nil {
- conn.Close()
- *detected = true
- }
- }
- func (host *DiscoveredHost) ScanPorts(startPort, endPort int) []int {
- var openPorts []int
- for port := startPort; port <= endPort; port++ {
- target := fmt.Sprintf("%s:%d", host.IP, port)
- conn, err := net.DialTimeout("tcp", target, time.Millisecond*500)
- if err == nil {
- conn.Close()
- openPorts = append(openPorts, port)
- }
- }
- return openPorts
- }
|