mirror of
https://github.com/TwiN/gatus.git
synced 2024-11-26 18:03:38 +01:00
171 lines
4.2 KiB
Go
171 lines
4.2 KiB
Go
// Copyright 2015 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package icmp
|
|
|
|
import (
|
|
"encoding/binary"
|
|
|
|
"golang.org/x/net/ipv4"
|
|
"golang.org/x/net/ipv6"
|
|
)
|
|
|
|
// An Extension represents an ICMP extension.
|
|
type Extension interface {
|
|
// Len returns the length of ICMP extension.
|
|
// The provided proto must be either the ICMPv4 or ICMPv6
|
|
// protocol number.
|
|
Len(proto int) int
|
|
|
|
// Marshal returns the binary encoding of ICMP extension.
|
|
// The provided proto must be either the ICMPv4 or ICMPv6
|
|
// protocol number.
|
|
Marshal(proto int) ([]byte, error)
|
|
}
|
|
|
|
const extensionVersion = 2
|
|
|
|
func validExtensionHeader(b []byte) bool {
|
|
v := int(b[0]&0xf0) >> 4
|
|
s := binary.BigEndian.Uint16(b[2:4])
|
|
if s != 0 {
|
|
s = checksum(b)
|
|
}
|
|
if v != extensionVersion || s != 0 {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// parseExtensions parses b as a list of ICMP extensions.
|
|
// The length attribute l must be the length attribute field in
|
|
// received icmp messages.
|
|
//
|
|
// It will return a list of ICMP extensions and an adjusted length
|
|
// attribute that represents the length of the padded original
|
|
// datagram field. Otherwise, it returns an error.
|
|
func parseExtensions(typ Type, b []byte, l int) ([]Extension, int, error) {
|
|
// Still a lot of non-RFC 4884 compliant implementations are
|
|
// out there. Set the length attribute l to 128 when it looks
|
|
// inappropriate for backwards compatibility.
|
|
//
|
|
// A minimal extension at least requires 8 octets; 4 octets
|
|
// for an extension header, and 4 octets for a single object
|
|
// header.
|
|
//
|
|
// See RFC 4884 for further information.
|
|
switch typ {
|
|
case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest:
|
|
if len(b) < 8 || !validExtensionHeader(b) {
|
|
return nil, -1, errNoExtension
|
|
}
|
|
l = 0
|
|
default:
|
|
if 128 > l || l+8 > len(b) {
|
|
l = 128
|
|
}
|
|
if l+8 > len(b) {
|
|
return nil, -1, errNoExtension
|
|
}
|
|
if !validExtensionHeader(b[l:]) {
|
|
if l == 128 {
|
|
return nil, -1, errNoExtension
|
|
}
|
|
l = 128
|
|
if !validExtensionHeader(b[l:]) {
|
|
return nil, -1, errNoExtension
|
|
}
|
|
}
|
|
}
|
|
var exts []Extension
|
|
for b = b[l+4:]; len(b) >= 4; {
|
|
ol := int(binary.BigEndian.Uint16(b[:2]))
|
|
if 4 > ol || ol > len(b) {
|
|
break
|
|
}
|
|
switch b[2] {
|
|
case classMPLSLabelStack:
|
|
ext, err := parseMPLSLabelStack(b[:ol])
|
|
if err != nil {
|
|
return nil, -1, err
|
|
}
|
|
exts = append(exts, ext)
|
|
case classInterfaceInfo:
|
|
ext, err := parseInterfaceInfo(b[:ol])
|
|
if err != nil {
|
|
return nil, -1, err
|
|
}
|
|
exts = append(exts, ext)
|
|
case classInterfaceIdent:
|
|
ext, err := parseInterfaceIdent(b[:ol])
|
|
if err != nil {
|
|
return nil, -1, err
|
|
}
|
|
exts = append(exts, ext)
|
|
default:
|
|
ext := &RawExtension{Data: make([]byte, ol)}
|
|
copy(ext.Data, b[:ol])
|
|
exts = append(exts, ext)
|
|
}
|
|
b = b[ol:]
|
|
}
|
|
return exts, l, nil
|
|
}
|
|
|
|
func validExtensions(typ Type, exts []Extension) bool {
|
|
switch typ {
|
|
case ipv4.ICMPTypeDestinationUnreachable, ipv4.ICMPTypeTimeExceeded, ipv4.ICMPTypeParameterProblem,
|
|
ipv6.ICMPTypeDestinationUnreachable, ipv6.ICMPTypeTimeExceeded:
|
|
for i := range exts {
|
|
switch exts[i].(type) {
|
|
case *MPLSLabelStack, *InterfaceInfo, *RawExtension:
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest:
|
|
var n int
|
|
for i := range exts {
|
|
switch exts[i].(type) {
|
|
case *InterfaceIdent:
|
|
n++
|
|
case *RawExtension:
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
// Not a single InterfaceIdent object or a combo of
|
|
// RawExtension and InterfaceIdent objects is not
|
|
// allowed.
|
|
if n == 1 && len(exts) > 1 {
|
|
return false
|
|
}
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// A RawExtension represents a raw extension.
|
|
//
|
|
// A raw extension is excluded from message processing and can be used
|
|
// to construct applications such as protocol conformance testing.
|
|
type RawExtension struct {
|
|
Data []byte // data
|
|
}
|
|
|
|
// Len implements the Len method of Extension interface.
|
|
func (p *RawExtension) Len(proto int) int {
|
|
if p == nil {
|
|
return 0
|
|
}
|
|
return len(p.Data)
|
|
}
|
|
|
|
// Marshal implements the Marshal method of Extension interface.
|
|
func (p *RawExtension) Marshal(proto int) ([]byte, error) {
|
|
return p.Data, nil
|
|
}
|