1/* 2Package pingscanner scan alive IPs of the given CIDR range in parallel. 3 4Example usage: 5 6package main 7 8import ( 9 "fmt" 10 11 ps "github.com/kotakanbe/go-pingscanner" 12) 13 14func main() { 15 scanner := ps.PingScanner{ 16 CIDR: "192.168.11.0/24", 17 PingOptions: []string{ 18 "-c1", 19 "-t1", 20 }, 21 NumOfConcurrency: 100, 22 } 23 if aliveIPs, err := scanner.Scan(); err != nil { 24 fmt.Println(err) 25 } else { 26 if len(aliveIPs) < 1 { 27 fmt.Println("no alive hosts") 28 } 29 for _, ip := range aliveIPs { 30 fmt.Println(ip) 31 } 32 } 33} 34*/ 35package pingscanner 36 37import ( 38 "net" 39 "os/exec" 40 "sort" 41 "strings" 42) 43 44// PingScanner has information of Scanning. 45type PingScanner struct { 46 // CIDR (ex. 192.168.0.0/24) 47 CIDR string 48 49 // Number of concurrency ping process. (ex. 100) 50 NumOfConcurrency int 51 52 // ping command options. (ex. []string{"-c1", "-t1"}) 53 PingOptions []string 54} 55 56type pong struct { 57 IP string 58 Alive bool 59} 60 61// Scan ping to hosts in CIDR range. 62func (d PingScanner) Scan() (aliveIPs []string, err error) { 63 var hostsInCidr []string 64 if hostsInCidr, err = expandCidrIntoIPs(d.CIDR); err != nil { 65 return nil, err 66 } 67 pingChan := make(chan string, d.NumOfConcurrency) 68 pongChan := make(chan pong, len(hostsInCidr)) 69 doneChan := make(chan []pong) 70 71 for i := 0; i < d.NumOfConcurrency; i++ { 72 go ping(pingChan, pongChan, d.PingOptions...) 73 } 74 75 go receivePong(len(hostsInCidr), pongChan, doneChan) 76 77 for _, ip := range hostsInCidr { 78 pingChan <- ip 79 } 80 81 alives := <-doneChan 82 for _, a := range alives { 83 aliveIPs = append(aliveIPs, a.IP) 84 } 85 sort.Strings(aliveIPs) 86 return 87} 88 89func expandCidrIntoIPs(cidr string) ([]string, error) { 90 splitted := strings.Split(cidr, "/") 91 if splitted[1] == "32" { 92 return []string{splitted[0]}, nil 93 } 94 ip, ipnet, err := net.ParseCIDR(cidr) 95 if err != nil { 96 return nil, err 97 } 98 99 var ips []string 100 for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) { 101 ips = append(ips, ip.String()) 102 } 103 // remove network address and broadcast address 104 return ips[1 : len(ips)-1], nil 105} 106 107// http://play.golang.org/p/m8TNTtygK0 108func inc(ip net.IP) { 109 for j := len(ip) - 1; j >= 0; j-- { 110 ip[j]++ 111 if ip[j] > 0 { 112 break 113 } 114 } 115} 116 117func ping(pingChan <-chan string, pongChan chan<- pong, pingOptions ...string) { 118 for ip := range pingChan { 119 pingOptions = append(pingOptions, ip) 120 _, err := exec.Command("ping", pingOptions...).Output() 121 var alive bool 122 if err != nil { 123 alive = false 124 } else { 125 alive = true 126 } 127 pongChan <- pong{IP: ip, Alive: alive} 128 } 129} 130 131func receivePong(pongNum int, pongChan <-chan pong, doneChan chan<- []pong) { 132 var alives []pong 133 for i := 0; i < pongNum; i++ { 134 pong := <-pongChan 135 if pong.Alive { 136 alives = append(alives, pong) 137 } 138 } 139 doneChan <- alives 140} 141