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.
starnet/ping.go

111 lines
2.0 KiB
Go

package starnet
import (
"bytes"
"encoding/binary"
"net"
"time"
)
type ICMP struct {
Type uint8
Code uint8
CheckSum uint16
Identifier uint16
SequenceNum uint16
}
func getICMP(seq uint16) ICMP {
icmp := ICMP{
Type: 8,
Code: 0,
CheckSum: 0,
Identifier: 0,
SequenceNum: seq,
}
var buffer bytes.Buffer
binary.Write(&buffer, binary.BigEndian, icmp)
icmp.CheckSum = checkSum(buffer.Bytes())
buffer.Reset()
return icmp
}
func sendICMPRequest(icmp ICMP, destAddr *net.IPAddr, timeout time.Duration) (PingResult, error) {
var res PingResult
res.RemoteIP = destAddr.String()
conn, err := net.DialIP("ip:icmp", nil, destAddr)
if err != nil {
return res, err
}
defer conn.Close()
var buffer bytes.Buffer
binary.Write(&buffer, binary.BigEndian, icmp)
if _, err := conn.Write(buffer.Bytes()); err != nil {
return res, err
}
tStart := time.Now()
conn.SetReadDeadline((time.Now().Add(timeout)))
recv := make([]byte, 1024)
res.RecvCount, err = conn.Read(recv)
if err != nil {
return res, err
}
tEnd := time.Now()
res.Duration = tEnd.Sub(tStart)
return res, err
}
func checkSum(data []byte) uint16 {
var (
sum uint32
length int = len(data)
index int
)
for length > 1 {
sum += uint32(data[index])<<8 + uint32(data[index+1])
index += 2
length -= 2
}
if length > 0 {
sum += uint32(data[index])
}
sum += (sum >> 16)
return uint16(^sum)
}
type PingResult struct {
Duration time.Duration
RecvCount int
RemoteIP string
}
func Ping(ip string, seq int, timeout time.Duration) (PingResult, error) {
var res PingResult
ipAddr, err := net.ResolveIPAddr("ip", ip)
if err != nil {
return res, err
}
icmp := getICMP(uint16(seq))
return sendICMPRequest(icmp, ipAddr, timeout)
}
func IsIpPingable(ip string, timeout time.Duration, retryLimit int) bool {
for i := 0; i < retryLimit; i++ {
_, err := Ping(ip, 29, timeout)
if err != nil {
continue
}
return true
}
return false
}