diff --git a/ping.go b/ping.go index ef277ab..b184cc1 100644 --- a/ping.go +++ b/ping.go @@ -49,7 +49,6 @@ // it calls the OnFinish callback. // // For a full ping example, see "cmd/ping/ping.go". -// package ping import ( @@ -580,7 +579,8 @@ func (p *Pinger) recvICMP( case <-p.done: return nil default: - bytes := make([]byte, p.getMessageLength()) + messageLength := p.getMessageLength() + bytes := make([]byte, messageLength) if err := conn.SetReadDeadline(time.Now().Add(delay)); err != nil { return err } @@ -598,10 +598,25 @@ func (p *Pinger) recvICMP( return err } + // syscall.SetsockoptInt dose not work with sysIP_STRIPHDR on the darwin, + // we need to check the bytes, if there is an IP header, we needs to strip it. + // Refer to https://go.googlesource.com/net/+/master/icmp/listen_posix.go#75. + if !p.Privileged() && n >= messageLength { + if p.ipv4 { + if hdr, err := ipv4.ParseHeader(bytes); err == nil && hdr != nil && hdr.TotalLen == n && hdr.Len == ipv4.HeaderLen { + bytes = bytes[ipv4.HeaderLen:] + } + } else { + if hdr, err := ipv6.ParseHeader(bytes); err == nil && hdr.PayloadLen == p.Size && hdr.NextHeader == protocolIPv6ICMP { + bytes = bytes[ipv6.HeaderLen:] + } + } + } + select { case <-p.done: return nil - case recv <- &packet{bytes: bytes, nbytes: n, ttl: ttl}: + case recv <- &packet{bytes: bytes, nbytes: len(bytes), ttl: ttl}: } } } diff --git a/utils_linux.go b/utils_linux.go index a3e4e8c..2bab903 100644 --- a/utils_linux.go +++ b/utils_linux.go @@ -3,9 +3,22 @@ package ping +import ( + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" +) + // Returns the length of an ICMP message. func (p *Pinger) getMessageLength() int { - return p.Size + 8 + if p.Privileged() { + return p.Size + 8 + } + + if p.ipv4 { + return p.Size + 8 + ipv4.HeaderLen + } + + return p.Size + 8 + ipv6.HeaderLen } // Attempts to match the ID of an ICMP packet. diff --git a/utils_other.go b/utils_other.go index d229c41..0d086d8 100644 --- a/utils_other.go +++ b/utils_other.go @@ -3,9 +3,22 @@ package ping +import ( + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" +) + // Returns the length of an ICMP message. func (p *Pinger) getMessageLength() int { - return p.Size + 8 + if p.Privileged() { + return p.Size + 8 + } + + if p.ipv4 { + return p.Size + 8 + ipv4.HeaderLen + } + + return p.Size + 8 + ipv6.HeaderLen } // Attempts to match the ID of an ICMP packet.