215 lines
5.8 KiB
Go
215 lines
5.8 KiB
Go
package starnet
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func buildICMPPacket(typ uint8, identifier, seq uint16) []byte {
|
|
icmp := ICMP{
|
|
Type: typ,
|
|
Code: 0,
|
|
CheckSum: 0,
|
|
Identifier: identifier,
|
|
SequenceNum: seq,
|
|
}
|
|
buf := marshalICMPPacket(icmp, nil)
|
|
cs := checkSum(buf)
|
|
buf[2] = byte(cs >> 8)
|
|
buf[3] = byte(cs)
|
|
return buf
|
|
}
|
|
|
|
func TestNextPingIdentifierChanges(t *testing.T) {
|
|
id1 := nextPingIdentifier()
|
|
id2 := nextPingIdentifier()
|
|
if id1 == id2 {
|
|
t.Fatalf("identifier should change between calls: %d == %d", id1, id2)
|
|
}
|
|
}
|
|
|
|
func TestIsExpectedEchoReplyIPv4(t *testing.T) {
|
|
identifier := uint16(0x1234)
|
|
seq := uint16(0x0102)
|
|
reply := buildICMPPacket(icmpTypeEchoReplyV4, identifier, seq)
|
|
|
|
if !isExpectedEchoReply(reply, 4, icmpTypeEchoReplyV4, identifier, seq) {
|
|
t.Fatal("expected IPv4 echo reply to match")
|
|
}
|
|
if isExpectedEchoReply(reply, 4, icmpTypeEchoReplyV4, identifier, seq+1) {
|
|
t.Fatal("mismatched sequence should not match")
|
|
}
|
|
}
|
|
|
|
func TestIsExpectedEchoReplyIPv4WithIPHeader(t *testing.T) {
|
|
identifier := uint16(0x1111)
|
|
seq := uint16(0x2222)
|
|
|
|
ipHeader := make([]byte, 20)
|
|
ipHeader[0] = 0x45 // version=4, ihl=5
|
|
|
|
reply := buildICMPPacket(icmpTypeEchoReplyV4, identifier, seq)
|
|
packet := append(ipHeader, reply...)
|
|
|
|
if !isExpectedEchoReply(packet, 4, icmpTypeEchoReplyV4, identifier, seq) {
|
|
t.Fatal("expected IPv4 packet with header to match")
|
|
}
|
|
}
|
|
|
|
func TestIsExpectedEchoReplyIPv6WithHeader(t *testing.T) {
|
|
identifier := uint16(0xabcd)
|
|
seq := uint16(0x00ff)
|
|
|
|
ipv6Header := make([]byte, 40)
|
|
ipv6Header[0] = 0x60 // version=6
|
|
|
|
reply := buildICMPPacket(icmpTypeEchoReplyV6, identifier, seq)
|
|
packet := append(ipv6Header, reply...)
|
|
|
|
if !isExpectedEchoReply(packet, 6, icmpTypeEchoReplyV6, identifier, seq) {
|
|
t.Fatal("expected IPv6 packet with header to match")
|
|
}
|
|
}
|
|
|
|
func TestPingInvalidTimeout(t *testing.T) {
|
|
_, err := Ping("127.0.0.1", 1, 0)
|
|
if err == nil {
|
|
t.Fatal("expected error for non-positive timeout")
|
|
}
|
|
if !errors.Is(err, ErrPingInvalidTimeout) {
|
|
t.Fatalf("expected ErrPingInvalidTimeout, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestIsIPPingableInvalidRetry(t *testing.T) {
|
|
if IsIpPingable("127.0.0.1", time.Millisecond, 0) {
|
|
t.Fatal("retryLimit=0 should return false")
|
|
}
|
|
}
|
|
|
|
func TestSocketSpecForIP(t *testing.T) {
|
|
v4, err := socketSpecForIP(net.ParseIP("127.0.0.1"))
|
|
if err != nil {
|
|
t.Fatalf("unexpected v4 error: %v", err)
|
|
}
|
|
if v4.network != "ip4:icmp" || v4.family != 4 || v4.requestType != icmpTypeEchoRequestV4 || v4.replyType != icmpTypeEchoReplyV4 {
|
|
t.Fatalf("unexpected v4 spec: %+v", v4)
|
|
}
|
|
|
|
v6, err := socketSpecForIP(net.ParseIP("::1"))
|
|
if err != nil {
|
|
t.Fatalf("unexpected v6 error: %v", err)
|
|
}
|
|
if v6.network != "ip6:ipv6-icmp" || v6.family != 6 || v6.requestType != icmpTypeEchoRequestV6 || v6.replyType != icmpTypeEchoReplyV6 {
|
|
t.Fatalf("unexpected v6 spec: %+v", v6)
|
|
}
|
|
|
|
_, err = socketSpecForIP(nil)
|
|
if err == nil {
|
|
t.Fatal("expected error for nil ip")
|
|
}
|
|
if !errors.Is(err, ErrInvalidIP) {
|
|
t.Fatalf("expected ErrInvalidIP, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestResolvePingTargetsLiteral(t *testing.T) {
|
|
v4, err := resolvePingTargets("127.0.0.1", false, false)
|
|
if err != nil {
|
|
t.Fatalf("unexpected v4 resolve error: %v", err)
|
|
}
|
|
if len(v4) != 1 || v4[0] == nil || v4[0].IP == nil || v4[0].IP.To4() == nil {
|
|
t.Fatalf("unexpected v4 targets: %+v", v4)
|
|
}
|
|
|
|
v6, err := resolvePingTargets("::1", false, false)
|
|
if err != nil {
|
|
t.Fatalf("unexpected v6 resolve error: %v", err)
|
|
}
|
|
if len(v6) != 1 || v6[0] == nil || v6[0].IP == nil || v6[0].IP.To16() == nil || v6[0].IP.To4() != nil {
|
|
t.Fatalf("unexpected v6 targets: %+v", v6)
|
|
}
|
|
}
|
|
|
|
func TestNormalizePingDialError(t *testing.T) {
|
|
perr := normalizePingDialError(errors.New("socket: operation not permitted"))
|
|
if !errors.Is(perr, ErrPingPermissionDenied) {
|
|
t.Fatalf("expected ErrPingPermissionDenied, got: %v", perr)
|
|
}
|
|
|
|
uerr := normalizePingDialError(errors.New("unknown network ip6:ipv6-icmp"))
|
|
if !errors.Is(uerr, ErrPingProtocolUnsupported) {
|
|
t.Fatalf("expected ErrPingProtocolUnsupported, got: %v", uerr)
|
|
}
|
|
}
|
|
|
|
func TestOrderPingTargets(t *testing.T) {
|
|
targets := []*net.IPAddr{
|
|
{IP: net.ParseIP("::1")},
|
|
{IP: net.ParseIP("127.0.0.1")},
|
|
}
|
|
|
|
v4First := orderPingTargets(targets, true, false)
|
|
if v4First[0].IP.To4() == nil {
|
|
t.Fatalf("expected IPv4 first, got: %v", v4First[0].IP)
|
|
}
|
|
|
|
v6First := orderPingTargets(targets, false, true)
|
|
if v6First[0].IP.To4() != nil {
|
|
t.Fatalf("expected IPv6 first, got: %v", v6First[0].IP)
|
|
}
|
|
}
|
|
|
|
func TestNormalizePingOptions(t *testing.T) {
|
|
opts, err := normalizePingOptions(nil, 3, 2*time.Second)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if opts.Count != 3 || opts.Timeout != 2*time.Second {
|
|
t.Fatalf("unexpected defaults: %+v", opts)
|
|
}
|
|
|
|
_, err = normalizePingOptions(&PingOptions{Count: -1}, 3, 2*time.Second)
|
|
if err == nil {
|
|
t.Fatal("expected error for negative count")
|
|
}
|
|
|
|
_, err = normalizePingOptions(&PingOptions{Timeout: -1}, 3, 2*time.Second)
|
|
if err == nil {
|
|
t.Fatal("expected error for negative timeout")
|
|
}
|
|
|
|
_, err = normalizePingOptions(&PingOptions{PayloadSize: maxPingPayloadSize + 1}, 3, 2*time.Second)
|
|
if err == nil {
|
|
t.Fatal("expected error for too large payload")
|
|
}
|
|
}
|
|
|
|
func TestPingWithContextCanceled(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
|
|
_, err := PingWithContext(ctx, "127.0.0.1", 1, time.Second)
|
|
if err == nil {
|
|
t.Fatal("expected canceled error")
|
|
}
|
|
if !errors.Is(err, context.Canceled) {
|
|
t.Fatalf("expected context.Canceled, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestPingableInvalidOptions(t *testing.T) {
|
|
_, err := Pingable("127.0.0.1", &PingOptions{Count: -1})
|
|
if err == nil {
|
|
t.Fatal("expected invalid count error")
|
|
}
|
|
|
|
_, err = Pingable("127.0.0.1", &PingOptions{Interval: -1})
|
|
if err == nil {
|
|
t.Fatal("expected invalid interval error")
|
|
}
|
|
}
|