fix: Non-privileged (UDP) ping response parsing for OSes not supporting STRIPHDR sock option

Signed-off-by: Gaius <gaius.qi@gmail.com>
This commit is contained in:
Gaius 2023-01-05 18:26:55 +08:00
parent b89bb75386
commit 62e11a6da0
No known key found for this signature in database
GPG Key ID: 8B4E5D1290FA2FFB
3 changed files with 46 additions and 5 deletions

21
ping.go
View File

@ -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}:
}
}
}

View File

@ -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.

View File

@ -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.