mirror of
https://github.com/golang/net.git
synced 2026-03-31 10:27:08 +09:00
This change adds simple validation for multipart messages to avoid generating incorrect messages and introduces RawBody and RawExtension to control message validation. RawBody and RawExtension are excluded from normal message processing and can be used to construct crafted messages for applications such as wire format testing. Fixes golang/go#28686. Change-Id: I56f51d6566142f5e1dcc75cfce5250801e583d6d Reviewed-on: https://go-review.googlesource.com/c/net/+/155859 Run-TryBot: Mikio Hara <mikioh.public.networking@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
568 lines
14 KiB
Go
568 lines
14 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_test
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"golang.org/x/net/icmp"
|
|
"golang.org/x/net/internal/iana"
|
|
"golang.org/x/net/ipv4"
|
|
"golang.org/x/net/ipv6"
|
|
)
|
|
|
|
func TestMarshalAndParseMultipartMessage(t *testing.T) {
|
|
fn := func(t *testing.T, proto int, tm icmp.Message) error {
|
|
b, err := tm.Marshal(nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch tm.Type {
|
|
case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest:
|
|
default:
|
|
switch proto {
|
|
case iana.ProtocolICMP:
|
|
if b[5] != 32 {
|
|
return fmt.Errorf("got %d; want 32", b[5])
|
|
}
|
|
case iana.ProtocolIPv6ICMP:
|
|
if b[4] != 16 {
|
|
return fmt.Errorf("got %d; want 16", b[4])
|
|
}
|
|
default:
|
|
return fmt.Errorf("unknown protocol: %d", proto)
|
|
}
|
|
}
|
|
m, err := icmp.ParseMessage(proto, b)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if m.Type != tm.Type || m.Code != tm.Code {
|
|
return fmt.Errorf("got %v; want %v", m, &tm)
|
|
}
|
|
switch m.Type {
|
|
case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest:
|
|
got, want := m.Body.(*icmp.ExtendedEchoRequest), tm.Body.(*icmp.ExtendedEchoRequest)
|
|
if !reflect.DeepEqual(got.Extensions, want.Extensions) {
|
|
return errors.New(dumpExtensions(got.Extensions, want.Extensions))
|
|
}
|
|
case ipv4.ICMPTypeDestinationUnreachable:
|
|
got, want := m.Body.(*icmp.DstUnreach), tm.Body.(*icmp.DstUnreach)
|
|
if !reflect.DeepEqual(got.Extensions, want.Extensions) {
|
|
return errors.New(dumpExtensions(got.Extensions, want.Extensions))
|
|
}
|
|
if len(got.Data) != 128 {
|
|
return fmt.Errorf("got %d; want 128", len(got.Data))
|
|
}
|
|
case ipv4.ICMPTypeTimeExceeded:
|
|
got, want := m.Body.(*icmp.TimeExceeded), tm.Body.(*icmp.TimeExceeded)
|
|
if !reflect.DeepEqual(got.Extensions, want.Extensions) {
|
|
return errors.New(dumpExtensions(got.Extensions, want.Extensions))
|
|
}
|
|
if len(got.Data) != 128 {
|
|
return fmt.Errorf("got %d; want 128", len(got.Data))
|
|
}
|
|
case ipv4.ICMPTypeParameterProblem:
|
|
got, want := m.Body.(*icmp.ParamProb), tm.Body.(*icmp.ParamProb)
|
|
if !reflect.DeepEqual(got.Extensions, want.Extensions) {
|
|
return errors.New(dumpExtensions(got.Extensions, want.Extensions))
|
|
}
|
|
if len(got.Data) != 128 {
|
|
return fmt.Errorf("got %d; want 128", len(got.Data))
|
|
}
|
|
case ipv6.ICMPTypeDestinationUnreachable:
|
|
got, want := m.Body.(*icmp.DstUnreach), tm.Body.(*icmp.DstUnreach)
|
|
if !reflect.DeepEqual(got.Extensions, want.Extensions) {
|
|
return errors.New(dumpExtensions(got.Extensions, want.Extensions))
|
|
}
|
|
if len(got.Data) != 128 {
|
|
return fmt.Errorf("got %d; want 128", len(got.Data))
|
|
}
|
|
case ipv6.ICMPTypeTimeExceeded:
|
|
got, want := m.Body.(*icmp.TimeExceeded), tm.Body.(*icmp.TimeExceeded)
|
|
if !reflect.DeepEqual(got.Extensions, want.Extensions) {
|
|
return errors.New(dumpExtensions(got.Extensions, want.Extensions))
|
|
}
|
|
if len(got.Data) != 128 {
|
|
return fmt.Errorf("got %d; want 128", len(got.Data))
|
|
}
|
|
default:
|
|
return fmt.Errorf("unknown message type: %v", m.Type)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
t.Run("IPv4", func(t *testing.T) {
|
|
for i, tm := range []icmp.Message{
|
|
{
|
|
Type: ipv4.ICMPTypeDestinationUnreachable, Code: 15,
|
|
Body: &icmp.DstUnreach{
|
|
Data: []byte("ERROR-INVOKING-PACKET"),
|
|
Extensions: []icmp.Extension{
|
|
&icmp.MPLSLabelStack{
|
|
Class: 1,
|
|
Type: 1,
|
|
Labels: []icmp.MPLSLabel{
|
|
{
|
|
Label: 16014,
|
|
TC: 0x4,
|
|
S: true,
|
|
TTL: 255,
|
|
},
|
|
},
|
|
},
|
|
&icmp.InterfaceInfo{
|
|
Class: 2,
|
|
Type: 0x0f,
|
|
Interface: &net.Interface{
|
|
Index: 15,
|
|
Name: "en101",
|
|
MTU: 8192,
|
|
},
|
|
Addr: &net.IPAddr{
|
|
IP: net.IPv4(192, 168, 0, 1).To4(),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Type: ipv4.ICMPTypeTimeExceeded, Code: 1,
|
|
Body: &icmp.TimeExceeded{
|
|
Data: []byte("ERROR-INVOKING-PACKET"),
|
|
Extensions: []icmp.Extension{
|
|
&icmp.InterfaceInfo{
|
|
Class: 2,
|
|
Type: 0x0f,
|
|
Interface: &net.Interface{
|
|
Index: 15,
|
|
Name: "en101",
|
|
MTU: 8192,
|
|
},
|
|
Addr: &net.IPAddr{
|
|
IP: net.IPv4(192, 168, 0, 1).To4(),
|
|
},
|
|
},
|
|
&icmp.MPLSLabelStack{
|
|
Class: 1,
|
|
Type: 1,
|
|
Labels: []icmp.MPLSLabel{
|
|
{
|
|
Label: 16014,
|
|
TC: 0x4,
|
|
S: true,
|
|
TTL: 255,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Type: ipv4.ICMPTypeParameterProblem, Code: 2,
|
|
Body: &icmp.ParamProb{
|
|
Pointer: 8,
|
|
Data: []byte("ERROR-INVOKING-PACKET"),
|
|
Extensions: []icmp.Extension{
|
|
&icmp.MPLSLabelStack{
|
|
Class: 1,
|
|
Type: 1,
|
|
Labels: []icmp.MPLSLabel{
|
|
{
|
|
Label: 16014,
|
|
TC: 0x4,
|
|
S: true,
|
|
TTL: 255,
|
|
},
|
|
},
|
|
},
|
|
&icmp.InterfaceInfo{
|
|
Class: 2,
|
|
Type: 0x0f,
|
|
Interface: &net.Interface{
|
|
Index: 15,
|
|
Name: "en101",
|
|
MTU: 8192,
|
|
},
|
|
Addr: &net.IPAddr{
|
|
IP: net.IPv4(192, 168, 0, 1).To4(),
|
|
},
|
|
},
|
|
&icmp.InterfaceInfo{
|
|
Class: 2,
|
|
Type: 0x2f,
|
|
Interface: &net.Interface{
|
|
Index: 16,
|
|
Name: "en102",
|
|
MTU: 8192,
|
|
},
|
|
Addr: &net.IPAddr{
|
|
IP: net.IPv4(192, 168, 0, 2).To4(),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Type: ipv4.ICMPTypeExtendedEchoRequest, Code: 0,
|
|
Body: &icmp.ExtendedEchoRequest{
|
|
ID: 1, Seq: 2, Local: true,
|
|
Extensions: []icmp.Extension{
|
|
&icmp.InterfaceIdent{
|
|
Class: 3,
|
|
Type: 1,
|
|
Name: "en101",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Type: ipv4.ICMPTypeExtendedEchoRequest, Code: 0,
|
|
Body: &icmp.ExtendedEchoRequest{
|
|
ID: 1, Seq: 2, Local: true,
|
|
Extensions: []icmp.Extension{
|
|
&icmp.InterfaceIdent{
|
|
Class: 3,
|
|
Type: 2,
|
|
Index: 911,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Type: ipv4.ICMPTypeExtendedEchoRequest, Code: 0,
|
|
Body: &icmp.ExtendedEchoRequest{
|
|
ID: 1, Seq: 2,
|
|
Extensions: []icmp.Extension{
|
|
&icmp.InterfaceIdent{
|
|
Class: 3,
|
|
Type: 3,
|
|
AFI: iana.AddrFamily48bitMAC,
|
|
Addr: []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
} {
|
|
if err := fn(t, iana.ProtocolICMP, tm); err != nil {
|
|
t.Errorf("#%d: %v", i, err)
|
|
}
|
|
}
|
|
})
|
|
t.Run("IPv6", func(t *testing.T) {
|
|
for i, tm := range []icmp.Message{
|
|
{
|
|
Type: ipv6.ICMPTypeDestinationUnreachable, Code: 6,
|
|
Body: &icmp.DstUnreach{
|
|
Data: []byte("ERROR-INVOKING-PACKET"),
|
|
Extensions: []icmp.Extension{
|
|
&icmp.MPLSLabelStack{
|
|
Class: 1,
|
|
Type: 1,
|
|
Labels: []icmp.MPLSLabel{
|
|
{
|
|
Label: 16014,
|
|
TC: 0x4,
|
|
S: true,
|
|
TTL: 255,
|
|
},
|
|
},
|
|
},
|
|
&icmp.InterfaceInfo{
|
|
Class: 2,
|
|
Type: 0x0f,
|
|
Interface: &net.Interface{
|
|
Index: 15,
|
|
Name: "en101",
|
|
MTU: 8192,
|
|
},
|
|
Addr: &net.IPAddr{
|
|
IP: net.ParseIP("fe80::1"),
|
|
Zone: "en101",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Type: ipv6.ICMPTypeTimeExceeded, Code: 1,
|
|
Body: &icmp.TimeExceeded{
|
|
Data: []byte("ERROR-INVOKING-PACKET"),
|
|
Extensions: []icmp.Extension{
|
|
&icmp.InterfaceInfo{
|
|
Class: 2,
|
|
Type: 0x0f,
|
|
Interface: &net.Interface{
|
|
Index: 15,
|
|
Name: "en101",
|
|
MTU: 8192,
|
|
},
|
|
Addr: &net.IPAddr{
|
|
IP: net.ParseIP("fe80::1"),
|
|
Zone: "en101",
|
|
},
|
|
},
|
|
&icmp.MPLSLabelStack{
|
|
Class: 1,
|
|
Type: 1,
|
|
Labels: []icmp.MPLSLabel{
|
|
{
|
|
Label: 16014,
|
|
TC: 0x4,
|
|
S: true,
|
|
TTL: 255,
|
|
},
|
|
},
|
|
},
|
|
&icmp.InterfaceInfo{
|
|
Class: 2,
|
|
Type: 0x2f,
|
|
Interface: &net.Interface{
|
|
Index: 16,
|
|
Name: "en102",
|
|
MTU: 8192,
|
|
},
|
|
Addr: &net.IPAddr{
|
|
IP: net.ParseIP("fe80::1"),
|
|
Zone: "en102",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Type: ipv6.ICMPTypeExtendedEchoRequest, Code: 0,
|
|
Body: &icmp.ExtendedEchoRequest{
|
|
ID: 1, Seq: 2, Local: true,
|
|
Extensions: []icmp.Extension{
|
|
&icmp.InterfaceIdent{
|
|
Class: 3,
|
|
Type: 1,
|
|
Name: "en101",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Type: ipv6.ICMPTypeExtendedEchoRequest, Code: 0,
|
|
Body: &icmp.ExtendedEchoRequest{
|
|
ID: 1, Seq: 2, Local: true,
|
|
Extensions: []icmp.Extension{
|
|
&icmp.InterfaceIdent{
|
|
Class: 3,
|
|
Type: 2,
|
|
Index: 911,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Type: ipv6.ICMPTypeExtendedEchoRequest, Code: 0,
|
|
Body: &icmp.ExtendedEchoRequest{
|
|
ID: 1, Seq: 2,
|
|
Extensions: []icmp.Extension{
|
|
&icmp.InterfaceIdent{
|
|
Class: 3,
|
|
Type: 3,
|
|
AFI: iana.AddrFamilyIPv4,
|
|
Addr: []byte{192, 0, 2, 1},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
} {
|
|
if err := fn(t, iana.ProtocolIPv6ICMP, tm); err != nil {
|
|
t.Errorf("#%d: %v", i, err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func dumpExtensions(gotExts, wantExts []icmp.Extension) string {
|
|
var s string
|
|
for i, got := range gotExts {
|
|
switch got := got.(type) {
|
|
case *icmp.MPLSLabelStack:
|
|
want := wantExts[i].(*icmp.MPLSLabelStack)
|
|
if !reflect.DeepEqual(got, want) {
|
|
s += fmt.Sprintf("#%d: got %#v; want %#v\n", i, got, want)
|
|
}
|
|
case *icmp.InterfaceInfo:
|
|
want := wantExts[i].(*icmp.InterfaceInfo)
|
|
if !reflect.DeepEqual(got, want) {
|
|
s += fmt.Sprintf("#%d: got %#v, %#v, %#v; want %#v, %#v, %#v\n", i, got, got.Interface, got.Addr, want, want.Interface, want.Addr)
|
|
}
|
|
case *icmp.InterfaceIdent:
|
|
want := wantExts[i].(*icmp.InterfaceIdent)
|
|
if !reflect.DeepEqual(got, want) {
|
|
s += fmt.Sprintf("#%d: got %#v; want %#v\n", i, got, want)
|
|
}
|
|
case *icmp.RawExtension:
|
|
s += fmt.Sprintf("#%d: raw extension\n", i)
|
|
}
|
|
}
|
|
if len(s) == 0 {
|
|
s += "empty extension"
|
|
}
|
|
return s[:len(s)-1]
|
|
}
|
|
|
|
func TestMultipartMessageBodyLen(t *testing.T) {
|
|
for i, tt := range []struct {
|
|
proto int
|
|
in icmp.MessageBody
|
|
out int
|
|
}{
|
|
{
|
|
iana.ProtocolICMP,
|
|
&icmp.DstUnreach{
|
|
Data: make([]byte, ipv4.HeaderLen),
|
|
},
|
|
4 + ipv4.HeaderLen, // unused and original datagram
|
|
},
|
|
{
|
|
iana.ProtocolICMP,
|
|
&icmp.TimeExceeded{
|
|
Data: make([]byte, ipv4.HeaderLen),
|
|
},
|
|
4 + ipv4.HeaderLen, // unused and original datagram
|
|
},
|
|
{
|
|
iana.ProtocolICMP,
|
|
&icmp.ParamProb{
|
|
Data: make([]byte, ipv4.HeaderLen),
|
|
},
|
|
4 + ipv4.HeaderLen, // [pointer, unused] and original datagram
|
|
},
|
|
|
|
{
|
|
iana.ProtocolICMP,
|
|
&icmp.ParamProb{
|
|
Data: make([]byte, ipv4.HeaderLen),
|
|
Extensions: []icmp.Extension{
|
|
&icmp.MPLSLabelStack{},
|
|
},
|
|
},
|
|
4 + 4 + 4 + 0 + 128, // [pointer, length, unused], extension header, object header, object payload, original datagram
|
|
},
|
|
{
|
|
iana.ProtocolICMP,
|
|
&icmp.ParamProb{
|
|
Data: make([]byte, 128),
|
|
Extensions: []icmp.Extension{
|
|
&icmp.MPLSLabelStack{},
|
|
},
|
|
},
|
|
4 + 4 + 4 + 0 + 128, // [pointer, length, unused], extension header, object header, object payload and original datagram
|
|
},
|
|
{
|
|
iana.ProtocolICMP,
|
|
&icmp.ParamProb{
|
|
Data: make([]byte, 129),
|
|
Extensions: []icmp.Extension{
|
|
&icmp.MPLSLabelStack{},
|
|
},
|
|
},
|
|
4 + 4 + 4 + 0 + 132, // [pointer, length, unused], extension header, object header, object payload and original datagram
|
|
},
|
|
|
|
{
|
|
iana.ProtocolIPv6ICMP,
|
|
&icmp.DstUnreach{
|
|
Data: make([]byte, ipv6.HeaderLen),
|
|
},
|
|
4 + ipv6.HeaderLen, // unused and original datagram
|
|
},
|
|
{
|
|
iana.ProtocolIPv6ICMP,
|
|
&icmp.PacketTooBig{
|
|
Data: make([]byte, ipv6.HeaderLen),
|
|
},
|
|
4 + ipv6.HeaderLen, // mtu and original datagram
|
|
},
|
|
{
|
|
iana.ProtocolIPv6ICMP,
|
|
&icmp.TimeExceeded{
|
|
Data: make([]byte, ipv6.HeaderLen),
|
|
},
|
|
4 + ipv6.HeaderLen, // unused and original datagram
|
|
},
|
|
{
|
|
iana.ProtocolIPv6ICMP,
|
|
&icmp.ParamProb{
|
|
Data: make([]byte, ipv6.HeaderLen),
|
|
},
|
|
4 + ipv6.HeaderLen, // pointer and original datagram
|
|
},
|
|
|
|
{
|
|
iana.ProtocolIPv6ICMP,
|
|
&icmp.DstUnreach{
|
|
Data: make([]byte, 127),
|
|
Extensions: []icmp.Extension{
|
|
&icmp.MPLSLabelStack{},
|
|
},
|
|
},
|
|
4 + 4 + 4 + 0 + 128, // [length, unused], extension header, object header, object payload and original datagram
|
|
},
|
|
{
|
|
iana.ProtocolIPv6ICMP,
|
|
&icmp.DstUnreach{
|
|
Data: make([]byte, 128),
|
|
Extensions: []icmp.Extension{
|
|
&icmp.MPLSLabelStack{},
|
|
},
|
|
},
|
|
4 + 4 + 4 + 0 + 128, // [length, unused], extension header, object header, object payload and original datagram
|
|
},
|
|
{
|
|
iana.ProtocolIPv6ICMP,
|
|
&icmp.DstUnreach{
|
|
Data: make([]byte, 129),
|
|
Extensions: []icmp.Extension{
|
|
&icmp.MPLSLabelStack{},
|
|
},
|
|
},
|
|
4 + 4 + 4 + 0 + 136, // [length, unused], extension header, object header, object payload and original datagram
|
|
},
|
|
|
|
{
|
|
iana.ProtocolICMP,
|
|
&icmp.ExtendedEchoRequest{},
|
|
4, // [id, seq, l-bit]
|
|
},
|
|
{
|
|
iana.ProtocolICMP,
|
|
&icmp.ExtendedEchoRequest{
|
|
Extensions: []icmp.Extension{
|
|
&icmp.InterfaceIdent{},
|
|
},
|
|
},
|
|
4 + 4 + 4, // [id, seq, l-bit], extension header, object header
|
|
},
|
|
{
|
|
iana.ProtocolIPv6ICMP,
|
|
&icmp.ExtendedEchoRequest{
|
|
Extensions: []icmp.Extension{
|
|
&icmp.InterfaceIdent{
|
|
Type: 3,
|
|
AFI: iana.AddrFamilyNSAP,
|
|
Addr: []byte{0x49, 0x00, 0x01, 0xaa, 0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0x00},
|
|
},
|
|
},
|
|
},
|
|
4 + 4 + 4 + 16, // [id, seq, l-bit], extension header, object header, object payload
|
|
},
|
|
} {
|
|
if out := tt.in.Len(tt.proto); out != tt.out {
|
|
t.Errorf("#%d: got %d; want %d", i, out, tt.out)
|
|
}
|
|
}
|
|
}
|