121 lines
2.4 KiB
Go
121 lines
2.4 KiB
Go
package ping
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"time"
|
|
|
|
"golang.org/x/net/icmp"
|
|
"golang.org/x/net/ipv4"
|
|
"golang.org/x/net/ipv6"
|
|
)
|
|
|
|
type IPType struct {
|
|
Type string
|
|
ListenAddr string
|
|
Network string
|
|
ICMPNetwork string
|
|
ProtocolNumber int
|
|
RequestMessageType icmp.Type
|
|
ReplyMessageType icmp.Type
|
|
}
|
|
|
|
var (
|
|
IPType4 = IPType{
|
|
Type: "4",
|
|
ListenAddr: "0.0.0.0",
|
|
Network: "ip4",
|
|
ICMPNetwork: "ip4:icmp",
|
|
ProtocolNumber: 1,
|
|
RequestMessageType: ipv4.ICMPTypeEcho,
|
|
ReplyMessageType: ipv4.ICMPTypeEchoReply,
|
|
}
|
|
IPType6 = IPType{
|
|
Type: "6",
|
|
ListenAddr: "::",
|
|
Network: "ip6",
|
|
ICMPNetwork: "ip6:ipv6-icmp",
|
|
ProtocolNumber: 58,
|
|
RequestMessageType: ipv6.ICMPTypeEchoRequest,
|
|
ReplyMessageType: ipv6.ICMPTypeEchoReply,
|
|
}
|
|
)
|
|
|
|
func New(address string) (*net.IPAddr, time.Duration, error) {
|
|
// Check ip type
|
|
// Resolve address
|
|
var err error
|
|
var dst *net.IPAddr
|
|
var ipType IPType
|
|
dst, err = net.ResolveIPAddr("ip4", address)
|
|
if err != nil {
|
|
dst, err = net.ResolveIPAddr("ip6", address)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
} else {
|
|
ipType = IPType6
|
|
}
|
|
} else {
|
|
ipType = IPType4
|
|
}
|
|
|
|
// Start listening for icmp replies
|
|
c, err := icmp.ListenPacket(ipType.ICMPNetwork, ipType.ListenAddr)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
defer c.Close()
|
|
|
|
// Make a new ICMP message
|
|
m := icmp.Message{
|
|
Type: ipType.RequestMessageType,
|
|
Code: 0,
|
|
Body: &icmp.Echo{
|
|
ID: os.Getpid() & 0xffff, Seq: 1,
|
|
Data: []byte(""),
|
|
},
|
|
}
|
|
b, err := m.Marshal(nil)
|
|
if err != nil {
|
|
return dst, 0, err
|
|
}
|
|
|
|
// Send it
|
|
start := time.Now()
|
|
n, err := c.WriteTo(b, dst)
|
|
if err != nil {
|
|
return dst, 0, err
|
|
} else if n != len(b) {
|
|
return dst, 0, fmt.Errorf("got %v; want %v", n, len(b))
|
|
}
|
|
|
|
// Wait for a reply
|
|
reply := make([]byte, 1500)
|
|
err = c.SetReadDeadline(time.Now().Add(3 * time.Second))
|
|
if err != nil {
|
|
return dst, 0, err
|
|
}
|
|
n, peer, err := c.ReadFrom(reply)
|
|
if err != nil {
|
|
return dst, 0, err
|
|
}
|
|
duration := time.Since(start)
|
|
|
|
// Pack it up boys, we're done here
|
|
rm, err := icmp.ParseMessage(ipType.ProtocolNumber, reply[:n])
|
|
if err != nil {
|
|
return dst, 0, err
|
|
}
|
|
|
|
//return dst, duration, nil
|
|
switch rm.Type {
|
|
case ipType.ReplyMessageType:
|
|
return dst, duration, nil
|
|
default:
|
|
return dst, 0, fmt.Errorf("got %+v from %v; want echo reply", rm, peer)
|
|
}
|
|
|
|
return dst, 0, err
|
|
}
|