text:
// Copyright 2011 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 xml

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"reflect"
	"strconv"
	"strings"
	"sync"
	"testing"
	"time"
)

type DriveType int

const (
	HyperDrive DriveType = iota
	ImprobabilityDrive
)

type Passenger struct {
	Name   []string `xml:"name"`
	Weight float32  `xml:"weight"`
}

type Ship struct {
	XMLName struct{} `xml:"spaceship"`

	Name      string       `xml:"name,attr"`
	Pilot     string       `xml:"pilot,attr"`
	Drive     DriveType    `xml:"drive"`
	Age       uint         `xml:"age"`
	Passenger []*Passenger `xml:"passenger"`
	secret    string
}

type NamedType string

type Port struct {
	XMLName struct{} `xml:"port"`
	Type    string   `xml:"type,attr,omitempty"`
	Comment string   `xml:",comment"`
	Number  string   `xml:",chardata"`
}

type Domain struct {
	XMLName struct{} `xml:"domain"`
	Country string   `xml:",attr,omitempty"`
	Name    []byte   `xml:",chardata"`
	Comment []byte   `xml:",comment"`
}

type Book struct {
	XMLName struct{} `xml:"book"`
	Title   string   `xml:",chardata"`
}

type Event struct {
	XMLName struct{} `xml:"event"`
	Year    int      `xml:",chardata"`
}

type Movie struct {
	XMLName struct{} `xml:"movie"`
	Length  uint     `xml:",chardata"`
}

type Pi struct {
	XMLName       struct{} `xml:"pi"`
	Approximation float32  `xml:",chardata"`
}

type Universe struct {
	XMLName struct{} `xml:"universe"`
	Visible float64  `xml:",chardata"`
}

type Particle struct {
	XMLName struct{} `xml:"particle"`
	HasMass bool     `xml:",chardata"`
}

type Departure struct {
	XMLName struct{}  `xml:"departure"`
	When    time.Time `xml:",chardata"`
}

type SecretAgent struct {
	XMLName   struct{} `xml:"agent"`
	Handle    string   `xml:"handle,attr"`
	Identity  string
	Obfuscate string `xml:",innerxml"`
}

type NestedItems struct {
	XMLName struct{} `xml:"result"`
	Items   []string `xml:">item"`
	Item1   []string `xml:"Items>item1"`
}

type NestedOrder struct {
	XMLName struct{} `xml:"result"`
	Field1  string   `xml:"parent>c"`
	Field2  string   `xml:"parent>b"`
	Field3  string   `xml:"parent>a"`
}

type MixedNested struct {
	XMLName struct{} `xml:"result"`
	A       string   `xml:"parent1>a"`
	B       string   `xml:"b"`
	C       string   `xml:"parent1>parent2>c"`
	D       string   `xml:"parent1>d"`
}

type NilTest struct {
	A interface{} `xml:"parent1>parent2>a"`
	B interface{} `xml:"parent1>b"`
	C interface{} `xml:"parent1>parent2>c"`
}

type Service struct {
	XMLName struct{} `xml:"service"`
	Domain  *Domain  `xml:"host>domain"`
	Port    *Port    `xml:"host>port"`
	Extra1  interface{}
	Extra2  interface{} `xml:"host>extra2"`
}

var nilStruct *Ship

type EmbedA struct {
	EmbedC
	EmbedB EmbedB
	FieldA string
}

type EmbedB struct {
	FieldB string
	*EmbedC
}

type EmbedC struct {
	FieldA1 string `xml:"FieldA>A1"`
	FieldA2 string `xml:"FieldA>A2"`
	FieldB  string
	FieldC  string
}

type NameCasing struct {
	XMLName struct{} `xml:"casing"`
	Xy      string
	XY      string
	XyA     string `xml:"Xy,attr"`
	XYA     string `xml:"XY,attr"`
}

type NamePrecedence struct {
	XMLName     Name              `xml:"Parent"`
	FromTag     XMLNameWithoutTag `xml:"InTag"`
	FromNameVal XMLNameWithoutTag
	FromNameTag XMLNameWithTag
	InFieldName string
}

type XMLNameWithTag struct {
	XMLName Name   `xml:"InXMLNameTag"`
	Value   string `xml:",chardata"`
}

type XMLNameWithNSTag struct {
	XMLName Name   `xml:"ns InXMLNameWithNSTag"`
	Value   string `xml:",chardata"`
}

type XMLNameWithoutTag struct {
	XMLName Name
	Value   string `xml:",chardata"`
}

type NameInField struct {
	Foo Name `xml:"ns foo"`
}

type AttrTest struct {
	Int   int     `xml:",attr"`
	Named int     `xml:"int,attr"`
	Float float64 `xml:",attr"`
	Uint8 uint8   `xml:",attr"`
	Bool  bool    `xml:",attr"`
	Str   string  `xml:",attr"`
	Bytes []byte  `xml:",attr"`
}

type OmitAttrTest struct {
	Int   int     `xml:",attr,omitempty"`
	Named int     `xml:"int,attr,omitempty"`
	Float float64 `xml:",attr,omitempty"`
	Uint8 uint8   `xml:",attr,omitempty"`
	Bool  bool    `xml:",attr,omitempty"`
	Str   string  `xml:",attr,omitempty"`
	Bytes []byte  `xml:",attr,omitempty"`
}

type OmitFieldTest struct {
	Int   int           `xml:",omitempty"`
	Named int           `xml:"int,omitempty"`
	Float float64       `xml:",omitempty"`
	Uint8 uint8         `xml:",omitempty"`
	Bool  bool          `xml:",omitempty"`
	Str   string        `xml:",omitempty"`
	Bytes []byte        `xml:",omitempty"`
	Ptr   *PresenceTest `xml:",omitempty"`
}

type AnyTest struct {
	XMLName  struct{}  `xml:"a"`
	Nested   string    `xml:"nested>value"`
	AnyField AnyHolder `xml:",any"`
}

type AnyOmitTest struct {
	XMLName  struct{}   `xml:"a"`
	Nested   string     `xml:"nested>value"`
	AnyField *AnyHolder `xml:",any,omitempty"`
}

type AnySliceTest struct {
	XMLName  struct{}    `xml:"a"`
	Nested   string      `xml:"nested>value"`
	AnyField []AnyHolder `xml:",any"`
}

type AnyHolder struct {
	XMLName Name
	XML     string `xml:",innerxml"`
}

type RecurseA struct {
	A string
	B *RecurseB
}

type RecurseB struct {
	A *RecurseA
	B string
}

type PresenceTest struct {
	Exists *struct{}
}

type IgnoreTest struct {
	PublicSecret string `xml:"-"`
}

type MyBytes []byte

type Data struct {
	Bytes  []byte
	Attr   []byte `xml:",attr"`
	Custom MyBytes
}

type Plain struct {
	V interface{}
}

type MyInt int

type EmbedInt struct {
	MyInt
}

type Strings struct {
	X []string `xml:"A>B,omitempty"`
}

type PointerFieldsTest struct {
	XMLName  Name    `xml:"dummy"`
	Name     *string `xml:"name,attr"`
	Age      *uint   `xml:"age,attr"`
	Empty    *string `xml:"empty,attr"`
	Contents *string `xml:",chardata"`
}

type ChardataEmptyTest struct {
	XMLName  Name    `xml:"test"`
	Contents *string `xml:",chardata"`
}

type MyMarshalerTest struct {
}

var _ Marshaler = (*MyMarshalerTest)(nil)

func (m *MyMarshalerTest) MarshalXML(e *Encoder, start StartElement) error {
	e.EncodeToken(start)
	e.EncodeToken(CharData([]byte("hello world")))
	e.EncodeToken(EndElement{start.Name})
	return nil
}

type MyMarshalerAttrTest struct{}

var _ MarshalerAttr = (*MyMarshalerAttrTest)(nil)

func (m *MyMarshalerAttrTest) MarshalXMLAttr(name Name) (Attr, error) {
	return Attr{name, "hello world"}, nil
}

type MyMarshalerValueAttrTest struct{}

var _ MarshalerAttr = MyMarshalerValueAttrTest{}

func (m MyMarshalerValueAttrTest) MarshalXMLAttr(name Name) (Attr, error) {
	return Attr{name, "hello world"}, nil
}

type MarshalerStruct struct {
	Foo MyMarshalerAttrTest `xml:",attr"`
}

type MarshalerValueStruct struct {
	Foo MyMarshalerValueAttrTest `xml:",attr"`
}

type InnerStruct struct {
	XMLName Name `xml:"testns outer"`
}

type OuterStruct struct {
	InnerStruct
	IntAttr int `xml:"int,attr"`
}

type OuterNamedStruct struct {
	InnerStruct
	XMLName Name `xml:"outerns test"`
	IntAttr int  `xml:"int,attr"`
}

type OuterNamedOrderedStruct struct {
	XMLName Name `xml:"outerns test"`
	InnerStruct
	IntAttr int `xml:"int,attr"`
}

type OuterOuterStruct struct {
	OuterStruct
}

type NestedAndChardata struct {
	AB       []string `xml:"A>B"`
	Chardata string   `xml:",chardata"`
}

type NestedAndComment struct {
	AB      []string `xml:"A>B"`
	Comment string   `xml:",comment"`
}

type XMLNSFieldStruct struct {
	Ns   string `xml:"xmlns,attr"`
	Body string
}

type NamedXMLNSFieldStruct struct {
	XMLName struct{} `xml:"testns test"`
	Ns      string   `xml:"xmlns,attr"`
	Body    string
}

type XMLNSFieldStructWithOmitEmpty struct {
	Ns   string `xml:"xmlns,attr,omitempty"`
	Body string
}

type NamedXMLNSFieldStructWithEmptyNamespace struct {
	XMLName struct{} `xml:"test"`
	Ns      string   `xml:"xmlns,attr"`
	Body    string
}

type RecursiveXMLNSFieldStruct struct {
	Ns   string                     `xml:"xmlns,attr"`
	Body *RecursiveXMLNSFieldStruct `xml:",omitempty"`
	Text string                     `xml:",omitempty"`
}

func ifaceptr(x interface{}) interface{} {
	return &x
}

var (
	nameAttr     = "Sarah"
	ageAttr      = uint(12)
	contentsAttr = "lorem ipsum"
)

// Unless explicitly stated as such (or *Plain), all of the
// tests below are two-way tests. When introducing new tests,
// please try to make them two-way as well to ensure that
// marshalling and unmarshalling are as symmetrical as feasible.
var marshalTests = []struct {
	Value         interface{}
	ExpectXML     string
	MarshalOnly   bool
	UnmarshalOnly bool
}{
	// Test nil marshals to nothing
	{Value: nil, ExpectXML: ``, MarshalOnly: true},
	{Value: nilStruct, ExpectXML: ``, MarshalOnly: true},

	// Test value types
	{Value: &Plain{true}, ExpectXML: `<Plain><V>true</V></Plain>`},
	{Value: &Plain{false}, ExpectXML: `<Plain><V>false</V></Plain>`},
	{Value: &Plain{int(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
	{Value: &Plain{int8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
	{Value: &Plain{int16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
	{Value: &Plain{int32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
	{Value: &Plain{uint(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
	{Value: &Plain{uint8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
	{Value: &Plain{uint16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
	{Value: &Plain{uint32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
	{Value: &Plain{float32(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
	{Value: &Plain{float64(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
	{Value: &Plain{uintptr(0xFFDD)}, ExpectXML: `<Plain><V>65501</V></Plain>`},
	{Value: &Plain{"gopher"}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
	{Value: &Plain{[]byte("gopher")}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
	{Value: &Plain{"</>"}, ExpectXML: `<Plain><V></></V></Plain>`},
	{Value: &Plain{[]byte("</>")}, ExpectXML: `<Plain><V></></V></Plain>`},
	{Value: &Plain{[3]byte{'<', '/', '>'}}, ExpectXML: `<Plain><V></></V></Plain>`},
	{Value: &Plain{NamedType("potato")}, ExpectXML: `<Plain><V>potato</V></Plain>`},
	{Value: &Plain{[]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
	{Value: &Plain{[3]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
	{Value: ifaceptr(true), MarshalOnly: true, ExpectXML: `<bool>true</bool>`},

	// Test time.
	{
		Value:     &Plain{time.Unix(1e9, 123456789).UTC()},
		ExpectXML: `<Plain><V>2001-09-09T01:46:40.123456789Z</V></Plain>`,
	},

	// A pointer to struct{} may be used to test for an element's presence.
	{
		Value:     &PresenceTest{new(struct{})},
		ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
	},
	{
		Value:     &PresenceTest{},
		ExpectXML: `<PresenceTest></PresenceTest>`,
	},

	// A pointer to struct{} may be used to test for an element's presence.
	{
		Value:     &PresenceTest{new(struct{})},
		ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
	},
	{
		Value:     &PresenceTest{},
		ExpectXML: `<PresenceTest></PresenceTest>`,
	},

	// A []byte field is only nil if the element was not found.
	{
		Value:         &Data{},
		ExpectXML:     `<Data></Data>`,
		UnmarshalOnly: true,
	},
	{
		Value:         &Data{Bytes: []byte{}, Custom: MyBytes{}, Attr: []byte{}},
		ExpectXML:     `<Data Attr=""><Bytes></Bytes><Custom></Custom></Data>`,
		UnmarshalOnly: true,
	},

	// Check that []byte works, including named []byte types.
	{
		Value:     &Data{Bytes: []byte("ab"), Custom: MyBytes("cd"), Attr: []byte{'v'}},
		ExpectXML: `<Data Attr="v"><Bytes>ab</Bytes><Custom>cd</Custom></Data>`,
	},

	// Test innerxml
	{
		Value: &SecretAgent{
			Handle:    "007",
			Identity:  "James Bond",
			Obfuscate: "<redacted/>",
		},
		ExpectXML:   `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
		MarshalOnly: true,
	},
	{
		Value: &SecretAgent{
			Handle:    "007",
			Identity:  "James Bond",
			Obfuscate: "<Identity>James Bond</Identity><redacted/>",
		},
		ExpectXML:     `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
		UnmarshalOnly: true,
	},

	// Test structs
	{Value: &Port{Type: "ssl", Number: "443"}, ExpectXML: `<port type="ssl">443</port>`},
	{Value: &Port{Number: "443"}, ExpectXML: `<port>443</port>`},
	{Value: &Port{Type: "<unix>"}, ExpectXML: `<port type="<unix>"></port>`},
	{Value: &Port{Number: "443", Comment: "https"}, ExpectXML: `<port><!--https-->443</port>`},
	{Value: &Port{Number: "443", Comment: "add space-"}, ExpectXML: `<port><!--add space- -->443</port>`, MarshalOnly: true},
	{Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `<domain>google.com&friends</domain>`},
	{Value: &Domain{Name: []byte("google.com"), Comment: []byte(" &friends ")}, ExpectXML: `<domain>google.com<!-- &friends --></domain>`},
	{Value: &Book{Title: "Pride & Prejudice"}, ExpectXML: `<book>Pride & Prejudice</book>`},
	{Value: &Event{Year: -3114}, ExpectXML: `<event>-3114</event>`},
	{Value: &Movie{Length: 13440}, ExpectXML: `<movie>13440</movie>`},
	{Value: &Pi{Approximation: 3.14159265}, ExpectXML: `<pi>3.1415927</pi>`},
	{Value: &Universe{Visible: 9.3e13}, ExpectXML: `<universe>9.3e+13</universe>`},
	{Value: &Particle{HasMass: true}, ExpectXML: `<particle>true</particle>`},
	{Value: &Departure{When: ParseTime("2013-01-09T00:15:00-09:00")}, ExpectXML: `<departure>2013-01-09T00:15:00-09:00</departure>`},
	{Value: atomValue, ExpectXML: atomXml},
	{
		Value: &Ship{
			Name:  "Heart of Gold",
			Pilot: "Computer",
			Age:   1,
			Drive: ImprobabilityDrive,
			Passenger: []*Passenger{
				{
					Name:   []string{"Zaphod", "Beeblebrox"},
					Weight: 7.25,
				},
				{
					Name:   []string{"Trisha", "McMillen"},
					Weight: 5.5,
				},
				{
					Name:   []string{"Ford", "Prefect"},
					Weight: 7,
				},
				{
					Name:   []string{"Arthur", "Dent"},
					Weight: 6.75,
				},
			},
		},
		ExpectXML: `<spaceship name="Heart of Gold" pilot="Computer">` +
			`<drive>` + strconv.Itoa(int(ImprobabilityDrive)) + `</drive>` +
			`<age>1</age>` +
			`<passenger>` +
			`<name>Zaphod</name>` +
			`<name>Beeblebrox</name>` +
			`<weight>7.25</weight>` +
			`</passenger>` +
			`<passenger>` +
			`<name>Trisha</name>` +
			`<name>McMillen</name>` +
			`<weight>5.5</weight>` +
			`</passenger>` +
			`<passenger>` +
			`<name>Ford</name>` +
			`<name>Prefect</name>` +
			`<weight>7</weight>` +
			`</passenger>` +
			`<passenger>` +
			`<name>Arthur</name>` +
			`<name>Dent</name>` +
			`<weight>6.75</weight>` +
			`</passenger>` +
			`</spaceship>`,
	},

	// Test a>b
	{
		Value: &NestedItems{Items: nil, Item1: nil},
		ExpectXML: `<result>` +
			`<Items>` +
			`</Items>` +
			`</result>`,
	},
	{
		Value: &NestedItems{Items: []string{}, Item1: []string{}},
		ExpectXML: `<result>` +
			`<Items>` +
			`</Items>` +
			`</result>`,
		MarshalOnly: true,
	},
	{
		Value: &NestedItems{Items: nil, Item1: []string{"A"}},
		ExpectXML: `<result>` +
			`<Items>` +
			`<item1>A</item1>` +
			`</Items>` +
			`</result>`,
	},
	{
		Value: &NestedItems{Items: []string{"A", "B"}, Item1: nil},
		ExpectXML: `<result>` +
			`<Items>` +
			`<item>A</item>` +
			`<item>B</item>` +
			`</Items>` +
			`</result>`,
	},
	{
		Value: &NestedItems{Items: []string{"A", "B"}, Item1: []string{"C"}},
		ExpectXML: `<result>` +
			`<Items>` +
			`<item>A</item>` +
			`<item>B</item>` +
			`<item1>C</item1>` +
			`</Items>` +
			`</result>`,
	},
	{
		Value: &NestedOrder{Field1: "C", Field2: "B", Field3: "A"},
		ExpectXML: `<result>` +
			`<parent>` +
			`<c>C</c>` +
			`<b>B</b>` +
			`<a>A</a>` +
			`</parent>` +
			`</result>`,
	},
	{
		Value: &NilTest{A: "A", B: nil, C: "C"},
		ExpectXML: `<NilTest>` +
			`<parent1>` +
			`<parent2><a>A</a></parent2>` +
			`<parent2><c>C</c></parent2>` +
			`</parent1>` +
			`</NilTest>`,
		MarshalOnly: true, // Uses interface{}
	},
	{
		Value: &MixedNested{A: "A", B: "B", C: "C", D: "D"},
		ExpectXML: `<result>` +
			`<parent1><a>A</a></parent1>` +
			`<b>B</b>` +
			`<parent1>` +
			`<parent2><c>C</c></parent2>` +
			`<d>D</d>` +
			`</parent1>` +
			`</result>`,
	},
	{
		Value:     &Service{Port: &Port{Number: "80"}},
		ExpectXML: `<service><host><port>80</port></host></service>`,
	},
	{
		Value:     &Service{},
		ExpectXML: `<service></service>`,
	},
	{
		Value: &Service{Port: &Port{Number: "80"}, Extra1: "A", Extra2: "B"},
		ExpectXML: `<service>` +
			`<host><port>80</port></host>` +
			`<Extra1>A</Extra1>` +
			`<host><extra2>B</extra2></host>` +
			`</service>`,
		MarshalOnly: true,
	},
	{
		Value: &Service{Port: &Port{Number: "80"}, Extra2: "example"},
		ExpectXML: `<service>` +
			`<host><port>80</port></host>` +
			`<host><extra2>example</extra2></host>` +
			`</service>`,
		MarshalOnly: true,
	},
	{
		Value: &struct {
			XMLName struct{} `xml:"space top"`
			A       string   `xml:"x>a"`
			B       string   `xml:"x>b"`
			C       string   `xml:"space x>c"`
			C1      string   `xml:"space1 x>c"`
			D1      string   `xml:"space1 x>d"`
			E1      string   `xml:"x>e"`
		}{
			A:  "a",
			B:  "b",
			C:  "c",
			C1: "c1",
			D1: "d1",
			E1: "e1",
		},
		ExpectXML: `<top xmlns="space">` +
			`<x><a>a</a><b>b</b><c>c</c></x>` +
			`<x xmlns="space1">` +
			`<c>c1</c>` +
			`<d>d1</d>` +
			`</x>` +
			`<x>` +
			`<e>e1</e>` +
			`</x>` +
			`</top>`,
	},
	{
		Value: &struct {
			XMLName Name
			A       string `xml:"x>a"`
			B       string `xml:"x>b"`
			C       string `xml:"space x>c"`
			C1      string `xml:"space1 x>c"`
			D1      string `xml:"space1 x>d"`
		}{
			XMLName: Name{
				Space: "space0",
				Local: "top",
			},
			A:  "a",
			B:  "b",
			C:  "c",
			C1: "c1",
			D1: "d1",
		},
		ExpectXML: `<top xmlns="space0">` +
			`<x><a>a</a><b>b</b></x>` +
			`<x xmlns="space"><c>c</c></x>` +
			`<x xmlns="space1">` +
			`<c>c1</c>` +
			`<d>d1</d>` +
			`</x>` +
			`</top>`,
	},
	{
		Value: &struct {
			XMLName struct{} `xml:"top"`
			B       string   `xml:"space x>b"`
			B1      string   `xml:"space1 x>b"`
		}{
			B:  "b",
			B1: "b1",
		},
		ExpectXML: `<top>` +
			`<x xmlns="space"><b>b</b></x>` +
			`<x xmlns="space1"><b>b1</b></x>` +
			`</top>`,
	},

	// Test struct embedding
	{
		Value: &EmbedA{
			EmbedC: EmbedC{
				FieldA1: "", // Shadowed by A.A
				FieldA2: "", // Shadowed by A.A
				FieldB:  "A.C.B",
				FieldC:  "A.C.C",
			},
			EmbedB: EmbedB{
				FieldB: "A.B.B",
				EmbedC: &EmbedC{
					FieldA1: "A.B.C.A1",
					FieldA2: "A.B.C.A2",
					FieldB:  "", // Shadowed by A.B.B
					FieldC:  "A.B.C.C",
				},
			},
			FieldA: "A.A",
		},
		ExpectXML: `<EmbedA>` +
			`<FieldB>A.C.B</FieldB>` +
			`<FieldC>A.C.C</FieldC>` +
			`<EmbedB>` +
			`<FieldB>A.B.B</FieldB>` +
			`<FieldA>` +
			`<A1>A.B.C.A1</A1>` +
			`<A2>A.B.C.A2</A2>` +
			`</FieldA>` +
			`<FieldC>A.B.C.C</FieldC>` +
			`</EmbedB>` +
			`<FieldA>A.A</FieldA>` +
			`</EmbedA>`,
	},

	// Test that name casing matters
	{
		Value:     &NameCasing{Xy: "mixed", XY: "upper", XyA: "mixedA", XYA: "upperA"},
		ExpectXML: `<casing Xy="mixedA" XY="upperA"><Xy>mixed</Xy><XY>upper</XY></casing>`,
	},

	// Test the order in which the XML element name is chosen
	{
		Value: &NamePrecedence{
			FromTag:     XMLNameWithoutTag{Value: "A"},
			FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "InXMLName"}, Value: "B"},
			FromNameTag: XMLNameWithTag{Value: "C"},
			InFieldName: "D",
		},
		ExpectXML: `<Parent>` +
			`<InTag>A</InTag>` +
			`<InXMLName>B</InXMLName>` +
			`<InXMLNameTag>C</InXMLNameTag>` +
			`<InFieldName>D</InFieldName>` +
			`</Parent>`,
		MarshalOnly: true,
	},
	{
		Value: &NamePrecedence{
			XMLName:     Name{Local: "Parent"},
			FromTag:     XMLNameWithoutTag{XMLName: Name{Local: "InTag"}, Value: "A"},
			FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "FromNameVal"}, Value: "B"},
			FromNameTag: XMLNameWithTag{XMLName: Name{Local: "InXMLNameTag"}, Value: "C"},
			InFieldName: "D",
		},
		ExpectXML: `<Parent>` +
			`<InTag>A</InTag>` +
			`<FromNameVal>B</FromNameVal>` +
			`<InXMLNameTag>C</InXMLNameTag>` +
			`<InFieldName>D</InFieldName>` +
			`</Parent>`,
		UnmarshalOnly: true,
	},

	// xml.Name works in a plain field as well.
	{
		Value:     &NameInField{Name{Space: "ns", Local: "foo"}},
		ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`,
	},
	{
		Value:         &NameInField{Name{Space: "ns", Local: "foo"}},
		ExpectXML:     `<NameInField><foo xmlns="ns"><ignore></ignore></foo></NameInField>`,
		UnmarshalOnly: true,
	},

	// Marshaling zero xml.Name uses the tag or field name.
	{
		Value:       &NameInField{},
		ExpectXML:   `<NameInField><foo xmlns="ns"></foo></NameInField>`,
		MarshalOnly: true,
	},

	// Test attributes
	{
		Value: &AttrTest{
			Int:   8,
			Named: 9,
			Float: 23.5,
			Uint8: 255,
			Bool:  true,
			Str:   "str",
			Bytes: []byte("byt"),
		},
		ExpectXML: `<AttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
			` Bool="true" Str="str" Bytes="byt"></AttrTest>`,
	},
	{
		Value: &AttrTest{Bytes: []byte{}},
		ExpectXML: `<AttrTest Int="0" int="0" Float="0" Uint8="0"` +
			` Bool="false" Str="" Bytes=""></AttrTest>`,
	},
	{
		Value: &OmitAttrTest{
			Int:   8,
			Named: 9,
			Float: 23.5,
			Uint8: 255,
			Bool:  true,
			Str:   "str",
			Bytes: []byte("byt"),
		},
		ExpectXML: `<OmitAttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
			` Bool="true" Str="str" Bytes="byt"></OmitAttrTest>`,
	},
	{
		Value:     &OmitAttrTest{},
		ExpectXML: `<OmitAttrTest></OmitAttrTest>`,
	},

	// pointer fields
	{
		Value:       &PointerFieldsTest{Name: &nameAttr, Age: &ageAttr, Contents: &contentsAttr},
		ExpectXML:   `<dummy name="Sarah" age="12">lorem ipsum</dummy>`,
		MarshalOnly: true,
	},

	// empty chardata pointer field
	{
		Value:       &ChardataEmptyTest{},
		ExpectXML:   `<test></test>`,
		MarshalOnly: true,
	},

	// omitempty on fields
	{
		Value: &OmitFieldTest{
			Int:   8,
			Named: 9,
			Float: 23.5,
			Uint8: 255,
			Bool:  true,
			Str:   "str",
			Bytes: []byte("byt"),
			Ptr:   &PresenceTest{},
		},
		ExpectXML: `<OmitFieldTest>` +
			`<Int>8</Int>` +
			`<int>9</int>` +
			`<Float>23.5</Float>` +
			`<Uint8>255</Uint8>` +
			`<Bool>true</Bool>` +
			`<Str>str</Str>` +
			`<Bytes>byt</Bytes>` +
			`<Ptr></Ptr>` +
			`</OmitFieldTest>`,
	},
	{
		Value:     &OmitFieldTest{},
		ExpectXML: `<OmitFieldTest></OmitFieldTest>`,
	},

	// Test ",any"
	{
		ExpectXML: `<a><nested><value>known</value></nested><other><sub>unknown</sub></other></a>`,
		Value: &AnyTest{
			Nested: "known",
			AnyField: AnyHolder{
				XMLName: Name{Local: "other"},
				XML:     "<sub>unknown</sub>",
			},
		},
	},
	{
		Value: &AnyTest{Nested: "known",
			AnyField: AnyHolder{
				XML:     "<unknown/>",
				XMLName: Name{Local: "AnyField"},
			},
		},
		ExpectXML: `<a><nested><value>known</value></nested><AnyField><unknown/></AnyField></a>`,
	},
	{
		ExpectXML: `<a><nested><value>b</value></nested></a>`,
		Value: &AnyOmitTest{
			Nested: "b",
		},
	},
	{
		ExpectXML: `<a><nested><value>b</value></nested><c><d>e</d></c><g xmlns="f"><h>i</h></g></a>`,
		Value: &AnySliceTest{
			Nested: "b",
			AnyField: []AnyHolder{
				{
					XMLName: Name{Local: "c"},
					XML:     "<d>e</d>",
				},
				{
					XMLName: Name{Space: "f", Local: "g"},
					XML:     "<h>i</h>",
				},
			},
		},
	},
	{
		ExpectXML: `<a><nested><value>b</value></nested></a>`,
		Value: &AnySliceTest{
			Nested: "b",
		},
	},

	// Test recursive types.
	{
		Value: &RecurseA{
			A: "a1",
			B: &RecurseB{
				A: &RecurseA{"a2", nil},
				B: "b1",
			},
		},
		ExpectXML: `<RecurseA><A>a1</A><B><A><A>a2</A></A><B>b1</B></B></RecurseA>`,
	},

	// Test ignoring fields via "-" tag
	{
		ExpectXML: `<IgnoreTest></IgnoreTest>`,
		Value:     &IgnoreTest{},
	},
	{
		ExpectXML:   `<IgnoreTest></IgnoreTest>`,
		Value:       &IgnoreTest{PublicSecret: "can't tell"},
		MarshalOnly: true,
	},
	{
		ExpectXML:     `<IgnoreTest><PublicSecret>ignore me</PublicSecret></IgnoreTest>`,
		Value:         &IgnoreTest{},
		UnmarshalOnly: true,
	},

	// Test escaping.
	{
		ExpectXML: `<a><nested><value>dquote: "; squote: '; ampersand: &; less: <; greater: >;</value></nested><empty></empty></a>`,
		Value: &AnyTest{
			Nested:   `dquote: "; squote: '; ampersand: &; less: <; greater: >;`,
			AnyField: AnyHolder{XMLName: Name{Local: "empty"}},
		},
	},
	{
		ExpectXML: `<a><nested><value>newline:
; cr:
; tab: 	;</value></nested><AnyField></AnyField></a>`,
		Value: &AnyTest{
			Nested:   "newline: \n; cr: \r; tab: \t;",
			AnyField: AnyHolder{XMLName: Name{Local: "AnyField"}},
		},
	},
	{
		ExpectXML: "<a><nested><value>1\r2\r\n3\n\r4\n5</value></nested></a>",
		Value: &AnyTest{
			Nested: "1\n2\n3\n\n4\n5",
		},
		UnmarshalOnly: true,
	},
	{
		ExpectXML: `<EmbedInt><MyInt>42</MyInt></EmbedInt>`,
		Value: &EmbedInt{
			MyInt: 42,
		},
	},
	// Test omitempty with parent chain; see golang.org/issue/4168.
	{
		ExpectXML: `<Strings><A></A></Strings>`,
		Value:     &Strings{},
	},
	// Custom marshalers.
	{
		ExpectXML: `<MyMarshalerTest>hello world</MyMarshalerTest>`,
		Value:     &MyMarshalerTest{},
	},
	{
		ExpectXML: `<MarshalerStruct Foo="hello world"></MarshalerStruct>`,
		Value:     &MarshalerStruct{},
	},
	{
		ExpectXML: `<MarshalerValueStruct Foo="hello world"></MarshalerValueStruct>`,
		Value:     &MarshalerValueStruct{},
	},
	{
		ExpectXML: `<outer xmlns="testns" int="10"></outer>`,
		Value:     &OuterStruct{IntAttr: 10},
	},
	{
		ExpectXML: `<test xmlns="outerns" int="10"></test>`,
		Value:     &OuterNamedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10},
	},
	{
		ExpectXML: `<test xmlns="outerns" int="10"></test>`,
		Value:     &OuterNamedOrderedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10},
	},
	{
		ExpectXML: `<outer xmlns="testns" int="10"></outer>`,
		Value:     &OuterOuterStruct{OuterStruct{IntAttr: 10}},
	},
	{
		ExpectXML: `<NestedAndChardata><A><B></B><B></B></A>test</NestedAndChardata>`,
		Value:     &NestedAndChardata{AB: make([]string, 2), Chardata: "test"},
	},
	{
		ExpectXML: `<NestedAndComment><A><B></B><B></B></A><!--test--></NestedAndComment>`,
		Value:     &NestedAndComment{AB: make([]string, 2), Comment: "test"},
	},
	{
		ExpectXML: `<XMLNSFieldStruct xmlns="http://example.com/ns"><Body>hello world</Body></XMLNSFieldStruct>`,
		Value:     &XMLNSFieldStruct{Ns: "http://example.com/ns", Body: "hello world"},
	},
	{
		ExpectXML: `<testns:test xmlns:testns="testns" xmlns="http://example.com/ns"><Body>hello world</Body></testns:test>`,
		Value:     &NamedXMLNSFieldStruct{Ns: "http://example.com/ns", Body: "hello world"},
	},
	{
		ExpectXML: `<testns:test xmlns:testns="testns"><Body>hello world</Body></testns:test>`,
		Value:     &NamedXMLNSFieldStruct{Ns: "", Body: "hello world"},
	},
	{
		ExpectXML: `<XMLNSFieldStructWithOmitEmpty><Body>hello world</Body></XMLNSFieldStructWithOmitEmpty>`,
		Value:     &XMLNSFieldStructWithOmitEmpty{Body: "hello world"},
	},
	{
		// The xmlns attribute must be ignored because the <test>
		// element is in the empty namespace, so it's not possible
		// to set the default namespace to something non-empty.
		ExpectXML:   `<test><Body>hello world</Body></test>`,
		Value:       &NamedXMLNSFieldStructWithEmptyNamespace{Ns: "foo", Body: "hello world"},
		MarshalOnly: true,
	},
	{
		ExpectXML: `<RecursiveXMLNSFieldStruct xmlns="foo"><Body xmlns=""><Text>hello world</Text></Body></RecursiveXMLNSFieldStruct>`,
		Value: &RecursiveXMLNSFieldStruct{
			Ns: "foo",
			Body: &RecursiveXMLNSFieldStruct{
				Text: "hello world",
			},
		},
	},
}

func TestMarshal(t *testing.T) {
	for idx, test := range marshalTests {
		if test.UnmarshalOnly {
			continue
		}
		data, err := Marshal(test.Value)
		if err != nil {
			t.Errorf("#%d: marshal(%#v): %s", idx, test.Value, err)
			continue
		}
		if got, want := string(data), test.ExpectXML; got != want {
			if strings.Contains(want, "\n") {
				t.Errorf("#%d: marshal(%#v):\nHAVE:\n%s\nWANT:\n%s", idx, test.Value, got, want)
			} else {
				t.Errorf("#%d: marshal(%#v):\nhave %#q\nwant %#q", idx, test.Value, got, want)
			}
		}
	}
}

type AttrParent struct {
	X string `xml:"X>Y,attr"`
}

type BadAttr struct {
	Name []string `xml:"name,attr"`
}

var marshalErrorTests = []struct {
	Value interface{}
	Err   string
	Kind  reflect.Kind
}{
	{
		Value: make(chan bool),
		Err:   "xml: unsupported type: chan bool",
		Kind:  reflect.Chan,
	},
	{
		Value: map[string]string{
			"question": "What do you get when you multiply six by nine?",
			"answer":   "42",
		},
		Err:  "xml: unsupported type: map[string]string",
		Kind: reflect.Map,
	},
	{
		Value: map[*Ship]bool{nil: false},
		Err:   "xml: unsupported type: map[*xml.Ship]bool",
		Kind:  reflect.Map,
	},
	{
		Value: &Domain{Comment: []byte("f--bar")},
		Err:   `xml: comments must not contain "--"`,
	},
	// Reject parent chain with attr, never worked; see golang.org/issue/5033.
	{
		Value: &AttrParent{},
		Err:   `xml: X>Y chain not valid with attr flag`,
	},
	{
		Value: BadAttr{[]string{"X", "Y"}},
		Err:   `xml: unsupported type: []string`,
	},
}

var marshalIndentTests = []struct {
	Value     interface{}
	Prefix    string
	Indent    string
	ExpectXML string
}{
	{
		Value: &SecretAgent{
			Handle:    "007",
			Identity:  "James Bond",
			Obfuscate: "<redacted/>",
		},
		Prefix:    "",
		Indent:    "\t",
		ExpectXML: fmt.Sprintf("<agent handle=\"007\">\n\t<Identity>James Bond</Identity><redacted/>\n</agent>"),
	},
}

func TestMarshalErrors(t *testing.T) {
	for idx, test := range marshalErrorTests {
		data, err := Marshal(test.Value)
		if err == nil {
			t.Errorf("#%d: marshal(%#v) = [success] %q, want error %v", idx, test.Value, data, test.Err)
			continue
		}
		if err.Error() != test.Err {
			t.Errorf("#%d: marshal(%#v) = [error] %v, want %v", idx, test.Value, err, test.Err)
		}
		if test.Kind != reflect.Invalid {
			if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kind {
				t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, kind, test.Kind)
			}
		}
	}
}

// Do invertibility testing on the various structures that we test
func TestUnmarshal(t *testing.T) {
	for i, test := range marshalTests {
		if test.MarshalOnly {
			continue
		}
		if _, ok := test.Value.(*Plain); ok {
			continue
		}
		vt := reflect.TypeOf(test.Value)
		dest := reflect.New(vt.Elem()).Interface()
		err := Unmarshal([]byte(test.ExpectXML), dest)

		switch fix := dest.(type) {
		case *Feed:
			fix.Author.InnerXML = ""
			for i := range fix.Entry {
				fix.Entry[i].Author.InnerXML = ""
			}
		}

		if err != nil {
			t.Errorf("#%d: unexpected error: %#v", i, err)
		} else if got, want := dest, test.Value; !reflect.DeepEqual(got, want) {
			t.Errorf("#%d: unmarshal(%q):\nhave %#v\nwant %#v", i, test.ExpectXML, got, want)
		}
	}
}

func TestMarshalIndent(t *testing.T) {
	for i, test := range marshalIndentTests {
		data, err := MarshalIndent(test.Value, test.Prefix, test.Indent)
		if err != nil {
			t.Errorf("#%d: Error: %s", i, err)
			continue
		}
		if got, want := string(data), test.ExpectXML; got != want {
			t.Errorf("#%d: MarshalIndent:\nGot:%s\nWant:\n%s", i, got, want)
		}
	}
}

type limitedBytesWriter struct {
	w      io.Writer
	remain int // until writes fail
}

func (lw *limitedBytesWriter) Write(p []byte) (n int, err error) {
	if lw.remain <= 0 {
		println("error")
		return 0, errors.New("write limit hit")
	}
	if len(p) > lw.remain {
		p = p[:lw.remain]
		n, _ = lw.w.Write(p)
		lw.remain = 0
		return n, errors.New("write limit hit")
	}
	n, err = lw.w.Write(p)
	lw.remain -= n
	return n, err
}

func TestMarshalWriteErrors(t *testing.T) {
	var buf bytes.Buffer
	const writeCap = 1024
	w := &limitedBytesWriter{&buf, writeCap}
	enc := NewEncoder(w)
	var err error
	var i int
	const n = 4000
	for i = 1; i <= n; i++ {
		err = enc.Encode(&Passenger{
			Name:   []string{"Alice", "Bob"},
			Weight: 5,
		})
		if err != nil {
			break
		}
	}
	if err == nil {
		t.Error("expected an error")
	}
	if i == n {
		t.Errorf("expected to fail before the end")
	}
	if buf.Len() != writeCap {
		t.Errorf("buf.Len() = %d; want %d", buf.Len(), writeCap)
	}
}

func TestMarshalWriteIOErrors(t *testing.T) {
	enc := NewEncoder(errWriter{})

	expectErr := "unwritable"
	err := enc.Encode(&Passenger{})
	if err == nil || err.Error() != expectErr {
		t.Errorf("EscapeTest = [error] %v, want %v", err, expectErr)
	}
}

func TestMarshalFlush(t *testing.T) {
	var buf bytes.Buffer
	enc := NewEncoder(&buf)
	if err := enc.EncodeToken(CharData("hello world")); err != nil {
		t.Fatalf("enc.EncodeToken: %v", err)
	}
	if buf.Len() > 0 {
		t.Fatalf("enc.EncodeToken caused actual write: %q", buf.Bytes())
	}
	if err := enc.Flush(); err != nil {
		t.Fatalf("enc.Flush: %v", err)
	}
	if buf.String() != "hello world" {
		t.Fatalf("after enc.Flush, buf.String() = %q, want %q", buf.String(), "hello world")
	}
}

var encodeElementTests = []struct {
	desc      string
	value     interface{}
	start     StartElement
	expectXML string
}{{
	desc:  "simple string",
	value: "hello",
	start: StartElement{
		Name: Name{Local: "a"},
	},
	expectXML: `<a>hello</a>`,
}, {
	desc:  "string with added attributes",
	value: "hello",
	start: StartElement{
		Name: Name{Local: "a"},
		Attr: []Attr{{
			Name:  Name{Local: "x"},
			Value: "y",
		}, {
			Name:  Name{Local: "foo"},
			Value: "bar",
		}},
	},
	expectXML: `<a x="y" foo="bar">hello</a>`,
}, {
	desc: "start element with default name space",
	value: struct {
		Foo XMLNameWithNSTag
	}{
		Foo: XMLNameWithNSTag{
			Value: "hello",
		},
	},
	start: StartElement{
		Name: Name{Space: "ns", Local: "a"},
		Attr: []Attr{{
			Name: Name{Local: "xmlns"},
			// "ns" is the name space defined in XMLNameWithNSTag
			Value: "ns",
		}},
	},
	expectXML: `<a xmlns="ns"><InXMLNameWithNSTag>hello</InXMLNameWithNSTag></a>`,
}, {
	desc: "start element in name space with different default name space",
	value: struct {
		Foo XMLNameWithNSTag
	}{
		Foo: XMLNameWithNSTag{
			Value: "hello",
		},
	},
	start: StartElement{
		Name: Name{Space: "ns2", Local: "a"},
		Attr: []Attr{{
			Name: Name{Local: "xmlns"},
			// "ns" is the name space defined in XMLNameWithNSTag
			Value: "ns",
		}},
	},
	expectXML: `<ns2:a xmlns:ns2="ns2" xmlns="ns"><InXMLNameWithNSTag>hello</InXMLNameWithNSTag></ns2:a>`,
}, {
	desc:  "XMLMarshaler with start element with default name space",
	value: &MyMarshalerTest{},
	start: StartElement{
		Name: Name{Space: "ns2", Local: "a"},
		Attr: []Attr{{
			Name: Name{Local: "xmlns"},
			// "ns" is the name space defined in XMLNameWithNSTag
			Value: "ns",
		}},
	},
	expectXML: `<ns2:a xmlns:ns2="ns2" xmlns="ns">hello world</ns2:a>`,
}}

func TestEncodeElement(t *testing.T) {
	for idx, test := range encodeElementTests {
		var buf bytes.Buffer
		enc := NewEncoder(&buf)
		err := enc.EncodeElement(test.value, test.start)
		if err != nil {
			t.Fatalf("enc.EncodeElement: %v", err)
		}
		err = enc.Flush()
		if err != nil {
			t.Fatalf("enc.Flush: %v", err)
		}
		if got, want := buf.String(), test.expectXML; got != want {
			t.Errorf("#%d(%s): EncodeElement(%#v, %#v):\nhave %#q\nwant %#q", idx, test.desc, test.value, test.start, got, want)
		}
	}
}

func BenchmarkMarshal(b *testing.B) {
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		Marshal(atomValue)
	}
}

func BenchmarkUnmarshal(b *testing.B) {
	b.ReportAllocs()
	xml := []byte(atomXml)
	for i := 0; i < b.N; i++ {
		Unmarshal(xml, &Feed{})
	}
}

// golang.org/issue/6556
func TestStructPointerMarshal(t *testing.T) {
	type A struct {
		XMLName string `xml:"a"`
		B       []interface{}
	}
	type C struct {
		XMLName Name
		Value   string `xml:"value"`
	}

	a := new(A)
	a.B = append(a.B, &C{
		XMLName: Name{Local: "c"},
		Value:   "x",
	})

	b, err := Marshal(a)
	if err != nil {
		t.Fatal(err)
	}
	if x := string(b); x != "<a><c><value>x</value></c></a>" {
		t.Fatal(x)
	}
	var v A
	err = Unmarshal(b, &v)
	if err != nil {
		t.Fatal(err)
	}
}

var encodeTokenTests = []struct {
	desc string
	toks []Token
	want string
	err  string
}{{
	desc: "start element with name space",
	toks: []Token{
		StartElement{Name{"space", "local"}, nil},
	},
	want: `<space:local xmlns:space="space">`,
}, {
	desc: "start element with no name",
	toks: []Token{
		StartElement{Name{"space", ""}, nil},
	},
	err: "xml: start tag with no name",
}, {
	desc: "end element with no name",
	toks: []Token{
		EndElement{Name{"space", ""}},
	},
	err: "xml: end tag with no name",
}, {
	desc: "char data",
	toks: []Token{
		CharData("foo"),
	},
	want: `foo`,
}, {
	desc: "char data with escaped chars",
	toks: []Token{
		CharData(" \t\n"),
	},
	want: " 	\n",
}, {
	desc: "comment",
	toks: []Token{
		Comment("foo"),
	},
	want: `<!--foo-->`,
}, {
	desc: "comment with invalid content",
	toks: []Token{
		Comment("foo-->"),
	},
	err: "xml: EncodeToken of Comment containing --> marker",
}, {
	desc: "proc instruction",
	toks: []Token{
		ProcInst{"Target", []byte("Instruction")},
	},
	want: `<?Target Instruction?>`,
}, {
	desc: "proc instruction with empty target",
	toks: []Token{
		ProcInst{"", []byte("Instruction")},
	},
	err: "xml: EncodeToken of ProcInst with invalid Target",
}, {
	desc: "proc instruction with bad content",
	toks: []Token{
		ProcInst{"", []byte("Instruction?>")},
	},
	err: "xml: EncodeToken of ProcInst with invalid Target",
}, {
	desc: "directive",
	toks: []Token{
		Directive("foo"),
	},
	want: `<!foo>`,
}, {
	desc: "more complex directive",
	toks: []Token{
		Directive("DOCTYPE doc [ <!ELEMENT doc '>'> <!-- com>ment --> ]"),
	},
	want: `<!DOCTYPE doc [ <!ELEMENT doc '>'> <!-- com>ment --> ]>`,
}, {
	desc: "directive instruction with bad name",
	toks: []Token{
		Directive("foo>"),
	},
	err: "xml: EncodeToken of Directive containing wrong < or > markers",
}, {
	desc: "end tag without start tag",
	toks: []Token{
		EndElement{Name{"foo", "bar"}},
	},
	err: "xml: end tag </bar> without start tag",
}, {
	desc: "mismatching end tag local name",
	toks: []Token{
		StartElement{Name{"", "foo"}, nil},
		EndElement{Name{"", "bar"}},
	},
	err:  "xml: end tag </bar> does not match start tag <foo>",
	want: `<foo>`,
}, {
	desc: "mismatching end tag namespace",
	toks: []Token{
		StartElement{Name{"space", "foo"}, nil},
		EndElement{Name{"another", "foo"}},
	},
	err:  "xml: end tag </foo> in namespace another does not match start tag <foo> in namespace space",
	want: `<space:foo xmlns:space="space">`,
}, {
	desc: "start element with explicit namespace",
	toks: []Token{
		StartElement{Name{"space", "local"}, []Attr{
			{Name{"xmlns", "x"}, "space"},
			{Name{"space", "foo"}, "value"},
		}},
	},
	want: `<x:local xmlns:x="space" x:foo="value">`,
}, {
	desc: "start element with explicit namespace and colliding prefix",
	toks: []Token{
		StartElement{Name{"space", "local"}, []Attr{
			{Name{"xmlns", "x"}, "space"},
			{Name{"space", "foo"}, "value"},
			{Name{"x", "bar"}, "other"},
		}},
	},
	want: `<x:local xmlns:x_1="x" xmlns:x="space" x:foo="value" x_1:bar="other">`,
}, {
	desc: "start element using previously defined namespace",
	toks: []Token{
		StartElement{Name{"", "local"}, []Attr{
			{Name{"xmlns", "x"}, "space"},
		}},
		StartElement{Name{"space", "foo"}, []Attr{
			{Name{"space", "x"}, "y"},
		}},
	},
	want: `<local xmlns:x="space"><x:foo x:x="y">`,
}, {
	desc: "nested name space with same prefix",
	toks: []Token{
		StartElement{Name{"", "foo"}, []Attr{
			{Name{"xmlns", "x"}, "space1"},
		}},
		StartElement{Name{"", "foo"}, []Attr{
			{Name{"xmlns", "x"}, "space2"},
		}},
		StartElement{Name{"", "foo"}, []Attr{
			{Name{"space1", "a"}, "space1 value"},
			{Name{"space2", "b"}, "space2 value"},
		}},
		EndElement{Name{"", "foo"}},
		EndElement{Name{"", "foo"}},
		StartElement{Name{"", "foo"}, []Attr{
			{Name{"space1", "a"}, "space1 value"},
			{Name{"space2", "b"}, "space2 value"},
		}},
	},
	want: `<foo xmlns:x="space1"><foo xmlns:x="space2"><foo xmlns:space1="space1" space1:a="space1 value" x:b="space2 value"></foo></foo><foo xmlns:space2="space2" x:a="space1 value" space2:b="space2 value">`,
}, {
	desc: "start element defining several prefixes for the same name space",
	toks: []Token{
		StartElement{Name{"space", "foo"}, []Attr{
			{Name{"xmlns", "a"}, "space"},
			{Name{"xmlns", "b"}, "space"},
			{Name{"space", "x"}, "value"},
		}},
	},
	want: `<a:foo xmlns:a="space" a:x="value">`,
}, {
	desc: "nested element redefines name space",
	toks: []Token{
		StartElement{Name{"", "foo"}, []Attr{
			{Name{"xmlns", "x"}, "space"},
		}},
		StartElement{Name{"space", "foo"}, []Attr{
			{Name{"xmlns", "y"}, "space"},
			{Name{"space", "a"}, "value"},
		}},
	},
	want: `<foo xmlns:x="space"><x:foo x:a="value">`,
}, {
	desc: "nested element creates alias for default name space",
	toks: []Token{
		StartElement{Name{"space", "foo"}, []Attr{
			{Name{"", "xmlns"}, "space"},
		}},
		StartElement{Name{"space", "foo"}, []Attr{
			{Name{"xmlns", "y"}, "space"},
			{Name{"space", "a"}, "value"},
		}},
	},
	want: `<foo xmlns="space"><foo xmlns:y="space" y:a="value">`,
}, {
	desc: "nested element defines default name space with existing prefix",
	toks: []Token{
		StartElement{Name{"", "foo"}, []Attr{
			{Name{"xmlns", "x"}, "space"},
		}},
		StartElement{Name{"space", "foo"}, []Attr{
			{Name{"", "xmlns"}, "space"},
			{Name{"space", "a"}, "value"},
		}},
	},
	want: `<foo xmlns:x="space"><foo xmlns="space" x:a="value">`,
}, {
	desc: "nested element uses empty attribute name space when default ns defined",
	toks: []Token{
		StartElement{Name{"space", "foo"}, []Attr{
			{Name{"", "xmlns"}, "space"},
		}},
		StartElement{Name{"space", "foo"}, []Attr{
			{Name{"", "attr"}, "value"},
		}},
	},
	want: `<foo xmlns="space"><foo attr="value">`,
}, {
	desc: "redefine xmlns",
	toks: []Token{
		StartElement{Name{"", "foo"}, []Attr{
			{Name{"foo", "xmlns"}, "space"},
		}},
	},
	err: `xml: cannot redefine xmlns attribute prefix`,
}, {
	desc: "xmlns with explicit name space #1",
	toks: []Token{
		StartElement{Name{"space", "foo"}, []Attr{
			{Name{"xml", "xmlns"}, "space"},
		}},
	},
	want: `<foo xmlns="space">`,
}, {
	desc: "xmlns with explicit name space #2",
	toks: []Token{
		StartElement{Name{"space", "foo"}, []Attr{
			{Name{xmlURL, "xmlns"}, "space"},
		}},
	},
	want: `<foo xmlns="space">`,
}, {
	desc: "empty name space declaration is ignored",
	toks: []Token{
		StartElement{Name{"", "foo"}, []Attr{
			{Name{"xmlns", "foo"}, ""},
		}},
	},
	want: `<foo>`,
}, {
	desc: "attribute with no name is ignored",
	toks: []Token{
		StartElement{Name{"", "foo"}, []Attr{
			{Name{"", ""}, "value"},
		}},
	},
	want: `<foo>`,
}, {
	desc: "namespace URL with non-valid name",
	toks: []Token{
		StartElement{Name{"/34", "foo"}, []Attr{
			{Name{"/34", "x"}, "value"},
		}},
	},
	want: `<_:foo xmlns:_="/34" _:x="value">`,
}, {
	desc: "nested element resets default namespace to empty",
	toks: []Token{
		StartElement{Name{"space", "foo"}, []Attr{
			{Name{"", "xmlns"}, "space"},
		}},
		StartElement{Name{"", "foo"}, []Attr{
			{Name{"", "xmlns"}, ""},
			{Name{"", "x"}, "value"},
			{Name{"space", "x"}, "value"},
		}},
	},
	want: `<foo xmlns="space"><foo xmlns:space="space" xmlns="" x="value" space:x="value">`,
}, {
	desc: "nested element requires empty default name space",
	toks: []Token{
		StartElement{Name{"space", "foo"}, []Attr{
			{Name{"", "xmlns"}, "space"},
		}},
		StartElement{Name{"", "foo"}, nil},
	},
	want: `<foo xmlns="space"><foo xmlns="">`,
}, {
	desc: "attribute uses name space from xmlns",
	toks: []Token{
		StartElement{Name{"some/space", "foo"}, []Attr{
			{Name{"", "attr"}, "value"},
			{Name{"some/space", "other"}, "other value"},
		}},
	},
	want: `<space:foo xmlns:space="some/space" attr="value" space:other="other value">`,
}, {
	desc: "default name space should not be used by attributes",
	toks: []Token{
		StartElement{Name{"space", "foo"}, []Attr{
			{Name{"", "xmlns"}, "space"},
			{Name{"xmlns", "bar"}, "space"},
			{Name{"space", "baz"}, "foo"},
		}},
		StartElement{Name{"space", "baz"}, nil},
		EndElement{Name{"space", "baz"}},
		EndElement{Name{"space", "foo"}},
	},
	want: `<foo xmlns:bar="space" xmlns="space" bar:baz="foo"><baz></baz></foo>`,
}, {
	desc: "default name space not used by attributes, not explicitly defined",
	toks: []Token{
		StartElement{Name{"space", "foo"}, []Attr{
			{Name{"", "xmlns"}, "space"},
			{Name{"space", "baz"}, "foo"},
		}},
		StartElement{Name{"space", "baz"}, nil},
		EndElement{Name{"space", "baz"}},
		EndElement{Name{"space", "foo"}},
	},
	want: `<foo xmlns:space="space" xmlns="space" space:baz="foo"><baz></baz></foo>`,
}, {
	desc: "impossible xmlns declaration",
	toks: []Token{
		StartElement{Name{"", "foo"}, []Attr{
			{Name{"", "xmlns"}, "space"},
		}},
		StartElement{Name{"space", "bar"}, []Attr{
			{Name{"space", "attr"}, "value"},
		}},
	},
	want: `<foo><space:bar xmlns:space="space" space:attr="value">`,
}}

func TestEncodeToken(t *testing.T) {
loop:
	for i, tt := range encodeTokenTests {
		var buf bytes.Buffer
		enc := NewEncoder(&buf)
		var err error
		for j, tok := range tt.toks {
			err = enc.EncodeToken(tok)
			if err != nil && j < len(tt.toks)-1 {
				t.Errorf("#%d %s token #%d: %v", i, tt.desc, j, err)
				continue loop
			}
		}
		errorf := func(f string, a ...interface{}) {
			t.Errorf("#%d %s token #%d:%s", i, tt.desc, len(tt.toks)-1, fmt.Sprintf(f, a...))
		}
		switch {
		case tt.err != "" && err == nil:
			errorf(" expected error; got none")
			continue
		case tt.err == "" && err != nil:
			errorf(" got error: %v", err)
			continue
		case tt.err != "" && err != nil && tt.err != err.Error():
			errorf(" error mismatch; got %v, want %v", err, tt.err)
			continue
		}
		if err := enc.Flush(); err != nil {
			errorf(" %v", err)
			continue
		}
		if got := buf.String(); got != tt.want {
			errorf("\ngot  %v\nwant %v", got, tt.want)
			continue
		}
	}
}

func TestProcInstEncodeToken(t *testing.T) {
	var buf bytes.Buffer
	enc := NewEncoder(&buf)

	if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err != nil {
		t.Fatalf("enc.EncodeToken: expected to be able to encode xml target ProcInst as first token, %s", err)
	}

	if err := enc.EncodeToken(ProcInst{"Target", []byte("Instruction")}); err != nil {
		t.Fatalf("enc.EncodeToken: expected to be able to add non-xml target ProcInst")
	}

	if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err == nil {
		t.Fatalf("enc.EncodeToken: expected to not be allowed to encode xml target ProcInst when not first token")
	}
}

func TestDecodeEncode(t *testing.T) {
	var in, out bytes.Buffer
	in.WriteString(`<?xml version="1.0" encoding="UTF-8"?>
<?Target Instruction?>
<root>
</root>
`)
	dec := NewDecoder(&in)
	enc := NewEncoder(&out)
	for tok, err := dec.Token(); err == nil; tok, err = dec.Token() {
		err = enc.EncodeToken(tok)
		if err != nil {
			t.Fatalf("enc.EncodeToken: Unable to encode token (%#v), %v", tok, err)
		}
	}
}

// Issue 9796. Used to fail with GORACE="halt_on_error=1" -race.
func TestRace9796(t *testing.T) {
	type A struct{}
	type B struct {
		C []A `xml:"X>Y"`
	}
	var wg sync.WaitGroup
	for i := 0; i < 2; i++ {
		wg.Add(1)
		go func() {
			Marshal(B{[]A{{}}})
			wg.Done()
		}()
	}
	wg.Wait()
}

func TestIsValidDirective(t *testing.T) {
	testOK := []string{
		"<>",
		"< < > >",
		"<!DOCTYPE '<' '>' '>' <!--nothing-->>",
		"<!DOCTYPE doc [ <!ELEMENT doc ANY> <!ELEMENT doc ANY> ]>",
		"<!DOCTYPE doc [ <!ELEMENT doc \"ANY> '<' <!E\" LEMENT '>' doc ANY> ]>",
		"<!DOCTYPE doc <!-- just>>>> a < comment --> [ <!ITEM anything> ] >",
	}
	testKO := []string{
		"<",
		">",
		"<!--",
		"-->",
		"< > > < < >",
		"<!dummy <!-- > -->",
		"<!DOCTYPE doc '>",
		"<!DOCTYPE doc '>'",
		"<!DOCTYPE doc <!--comment>",
	}
	for _, s := range testOK {
		if !isValidDirective(Directive(s)) {
			t.Errorf("Directive %q is expected to be valid", s)
		}
	}
	for _, s := range testKO {
		if isValidDirective(Directive(s)) {
			t.Errorf("Directive %q is expected to be invalid", s)
		}
	}
}

// Issue 11719. EncodeToken used to silently eat tokens with an invalid type.
func TestSimpleUseOfEncodeToken(t *testing.T) {
	var buf bytes.Buffer
	enc := NewEncoder(&buf)
	if err := enc.EncodeToken(&StartElement{Name: Name{"", "object1"}}); err == nil {
		t.Errorf("enc.EncodeToken: pointer type should be rejected")
	}
	if err := enc.EncodeToken(&EndElement{Name: Name{"", "object1"}}); err == nil {
		t.Errorf("enc.EncodeToken: pointer type should be rejected")
	}
	if err := enc.EncodeToken(StartElement{Name: Name{"", "object2"}}); err != nil {
		t.Errorf("enc.EncodeToken: StartElement %s", err)
	}
	if err := enc.EncodeToken(EndElement{Name: Name{"", "object2"}}); err != nil {
		t.Errorf("enc.EncodeToken: EndElement %s", err)
	}
	if err := enc.EncodeToken(Universe{}); err == nil {
		t.Errorf("enc.EncodeToken: invalid type not caught")
	}
	if err := enc.Flush(); err != nil {
		t.Errorf("enc.Flush: %s", err)
	}
	if buf.Len() == 0 {
		t.Errorf("enc.EncodeToken: empty buffer")
	}
	want := "<object2></object2>"
	if buf.String() != want {
		t.Errorf("enc.EncodeToken: expected %q; got %q", want, buf.String())
	}
}


text:
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs defs_linux.go

package ipv4

const (
	sysIP_TOS             = 0x1
	sysIP_TTL             = 0x2
	sysIP_HDRINCL         = 0x3
	sysIP_OPTIONS         = 0x4
	sysIP_ROUTER_ALERT    = 0x5
	sysIP_RECVOPTS        = 0x6
	sysIP_RETOPTS         = 0x7
	sysIP_PKTINFO         = 0x8
	sysIP_PKTOPTIONS      = 0x9
	sysIP_MTU_DISCOVER    = 0xa
	sysIP_RECVERR         = 0xb
	sysIP_RECVTTL         = 0xc
	sysIP_RECVTOS         = 0xd
	sysIP_MTU             = 0xe
	sysIP_FREEBIND        = 0xf
	sysIP_TRANSPARENT     = 0x13
	sysIP_RECVRETOPTS     = 0x7
	sysIP_ORIGDSTADDR     = 0x14
	sysIP_RECVORIGDSTADDR = 0x14
	sysIP_MINTTL          = 0x15
	sysIP_NODEFRAG        = 0x16
	sysIP_UNICAST_IF      = 0x32

	sysIP_MULTICAST_IF           = 0x20
	sysIP_MULTICAST_TTL          = 0x21
	sysIP_MULTICAST_LOOP         = 0x22
	sysIP_ADD_MEMBERSHIP         = 0x23
	sysIP_DROP_MEMBERSHIP        = 0x24
	sysIP_UNBLOCK_SOURCE         = 0x25
	sysIP_BLOCK_SOURCE           = 0x26
	sysIP_ADD_SOURCE_MEMBERSHIP  = 0x27
	sysIP_DROP_SOURCE_MEMBERSHIP = 0x28
	sysIP_MSFILTER               = 0x29
	sysMCAST_JOIN_GROUP          = 0x2a
	sysMCAST_LEAVE_GROUP         = 0x2d
	sysMCAST_JOIN_SOURCE_GROUP   = 0x2e
	sysMCAST_LEAVE_SOURCE_GROUP  = 0x2f
	sysMCAST_BLOCK_SOURCE        = 0x2b
	sysMCAST_UNBLOCK_SOURCE      = 0x2c
	sysMCAST_MSFILTER            = 0x30
	sysIP_MULTICAST_ALL          = 0x31

	sysICMP_FILTER = 0x1

	sysSO_EE_ORIGIN_NONE         = 0x0
	sysSO_EE_ORIGIN_LOCAL        = 0x1
	sysSO_EE_ORIGIN_ICMP         = 0x2
	sysSO_EE_ORIGIN_ICMP6        = 0x3
	sysSO_EE_ORIGIN_TXSTATUS     = 0x4
	sysSO_EE_ORIGIN_TIMESTAMPING = 0x4

	sysSOL_SOCKET       = 0x1
	sysSO_ATTACH_FILTER = 0x1a

	sizeofKernelSockaddrStorage = 0x80
	sizeofSockaddrInet          = 0x10
	sizeofInetPktinfo           = 0xc
	sizeofSockExtendedErr       = 0x10

	sizeofIPMreq         = 0x8
	sizeofIPMreqn        = 0xc
	sizeofIPMreqSource   = 0xc
	sizeofGroupReq       = 0x84
	sizeofGroupSourceReq = 0x104

	sizeofICMPFilter = 0x4

	sizeofSockFprog = 0x8
)

type kernelSockaddrStorage struct {
	Family  uint16
	X__data [126]int8
}

type sockaddrInet struct {
	Family uint16
	Port   uint16
	Addr   [4]byte /* in_addr */
	X__pad [8]uint8
}

type inetPktinfo struct {
	Ifindex  int32
	Spec_dst [4]byte /* in_addr */
	Addr     [4]byte /* in_addr */
}

type sockExtendedErr struct {
	Errno  uint32
	Origin uint8
	Type   uint8
	Code   uint8
	Pad    uint8
	Info   uint32
	Data   uint32
}

type ipMreq struct {
	Multiaddr [4]byte /* in_addr */
	Interface [4]byte /* in_addr */
}

type ipMreqn struct {
	Multiaddr [4]byte /* in_addr */
	Address   [4]byte /* in_addr */
	Ifindex   int32
}

type ipMreqSource struct {
	Multiaddr  uint32
	Interface  uint32
	Sourceaddr uint32
}

type groupReq struct {
	Interface uint32
	Group     kernelSockaddrStorage
}

type groupSourceReq struct {
	Interface uint32
	Group     kernelSockaddrStorage
	Source    kernelSockaddrStorage
}

type icmpFilter struct {
	Data uint32
}

type sockFProg struct {
	Len       uint16
	Pad_cgo_0 [2]byte
	Filter    *sockFilter
}

type sockFilter struct {
	Code uint16
	Jt   uint8
	Jf   uint8
	K    uint32
}

repo_name: dan-v/dosxvpn

repo_language: Go

mime_type: text/x-shellscript


text:
// Copyright 2012 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 ipv4_test

import (
	"net"
	"runtime"
	"testing"

	"golang.org/x/net/internal/nettest"
	"golang.org/x/net/ipv4"
)

var udpMultipleGroupListenerTests = []net.Addr{
	&net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}, // see RFC 4727
	&net.UDPAddr{IP: net.IPv4(224, 0, 0, 250)},
	&net.UDPAddr{IP: net.IPv4(224, 0, 0, 254)},
}

func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) {
	switch runtime.GOOS {
	case "nacl", "plan9", "windows":
		t.Skipf("not supported on %s", runtime.GOOS)
	}
	if testing.Short() {
		t.Skip("to avoid external network")
	}

	for _, gaddr := range udpMultipleGroupListenerTests {
		c, err := net.ListenPacket("udp4", "0.0.0.0:0") // wildcard address with no reusable port
		if err != nil {
			t.Fatal(err)
		}
		defer c.Close()

		p := ipv4.NewPacketConn(c)
		var mift []*net.Interface

		ift, err := net.Interfaces()
		if err != nil {
			t.Fatal(err)
		}
		for i, ifi := range ift {
			if _, ok := nettest.IsMulticastCapable("ip4", &ifi); !ok {
				continue
			}
			if err := p.JoinGroup(&ifi, gaddr); err != nil {
				t.Fatal(err)
			}
			mift = append(mift, &ift[i])
		}
		for _, ifi := range mift {
			if err := p.LeaveGroup(ifi, gaddr); err != nil {
				t.Fatal(err)
			}
		}
	}
}

func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) {
	switch runtime.GOOS {
	case "nacl", "plan9", "windows":
		t.Skipf("not supported on %s", runtime.GOOS)
	}
	if testing.Short() {
		t.Skip("to avoid external network")
	}

	for _, gaddr := range udpMultipleGroupListenerTests {
		c1, err := net.ListenPacket("udp4", "224.0.0.0:0") // wildcard address with reusable port
		if err != nil {
			t.Fatal(err)
		}
		defer c1.Close()
		_, port, err := net.SplitHostPort(c1.LocalAddr().String())
		if err != nil {
			t.Fatal(err)
		}
		c2, err := net.ListenPacket("udp4", net.JoinHostPort("224.0.0.0", port)) // wildcard address with reusable port
		if err != nil {
			t.Fatal(err)
		}
		defer c2.Close()

		var ps [2]*ipv4.PacketConn
		ps[0] = ipv4.NewPacketConn(c1)
		ps[1] = ipv4.NewPacketConn(c2)
		var mift []*net.Interface

		ift, err := net.Interfaces()
		if err != nil {
			t.Fatal(err)
		}
		for i, ifi := range ift {
			if _, ok := nettest.IsMulticastCapable("ip4", &ifi); !ok {
				continue
			}
			for _, p := range ps {
				if err := p.JoinGroup(&ifi, gaddr); err != nil {
					t.Fatal(err)
				}
			}
			mift = append(mift, &ift[i])
		}
		for _, ifi := range mift {
			for _, p := range ps {
				if err := p.LeaveGroup(ifi, gaddr); err != nil {
					t.Fatal(err)
				}
			}
		}
	}
}

func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
	switch runtime.GOOS {
	case "nacl", "plan9", "windows":
		t.Skipf("not supported on %s", runtime.GOOS)
	}
	if testing.Short() {
		t.Skip("to avoid external network")
	}

	gaddr := net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
	type ml struct {
		c   *ipv4.PacketConn
		ifi *net.Interface
	}
	var mlt []*ml

	ift, err := net.Interfaces()
	if err != nil {
		t.Fatal(err)
	}
	port := "0"
	for i, ifi := range ift {
		ip, ok := nettest.IsMulticastCapable("ip4", &ifi)
		if !ok {
			continue
		}
		c, err := net.ListenPacket("udp4", net.JoinHostPort(ip.String(), port)) // unicast address with non-reusable port
		if err != nil {
			// The listen may fail when the serivce is
			// already in use, but it's fine because the
			// purpose of this is not to test the
			// bookkeeping of IP control block inside the
			// kernel.
			t.Log(err)
			continue
		}
		defer c.Close()
		if port == "0" {
			_, port, err = net.SplitHostPort(c.LocalAddr().String())
			if err != nil {
				t.Fatal(err)
			}
		}
		p := ipv4.NewPacketConn(c)
		if err := p.JoinGroup(&ifi, &gaddr); err != nil {
			t.Fatal(err)
		}
		mlt = append(mlt, &ml{p, &ift[i]})
	}
	for _, m := range mlt {
		if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil {
			t.Fatal(err)
		}
	}
}

func TestIPSingleRawConnWithSingleGroupListener(t *testing.T) {
	switch runtime.GOOS {
	case "nacl", "plan9", "windows":
		t.Skipf("not supported on %s", runtime.GOOS)
	}
	if testing.Short() {
		t.Skip("to avoid external network")
	}
	if m, ok := nettest.SupportsRawIPSocket(); !ok {
		t.Skip(m)
	}

	c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") // wildcard address
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	r, err := ipv4.NewRawConn(c)
	if err != nil {
		t.Fatal(err)
	}
	gaddr := net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
	var mift []*net.Interface

	ift, err := net.Interfaces()
	if err != nil {
		t.Fatal(err)
	}
	for i, ifi := range ift {
		if _, ok := nettest.IsMulticastCapable("ip4", &ifi); !ok {
			continue
		}
		if err := r.JoinGroup(&ifi, &gaddr); err != nil {
			t.Fatal(err)
		}
		mift = append(mift, &ift[i])
	}
	for _, ifi := range mift {
		if err := r.LeaveGroup(ifi, &gaddr); err != nil {
			t.Fatal(err)
		}
	}
}

func TestIPPerInterfaceSingleRawConnWithSingleGroupListener(t *testing.T) {
	switch runtime.GOOS {
	case "nacl", "plan9", "windows":
		t.Skipf("not supported on %s", runtime.GOOS)
	}
	if testing.Short() {
		t.Skip("to avoid external network")
	}
	if m, ok := nettest.SupportsRawIPSocket(); !ok {
		t.Skip(m)
	}

	gaddr := net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
	type ml struct {
		c   *ipv4.RawConn
		ifi *net.Interface
	}
	var mlt []*ml

	ift, err := net.Interfaces()
	if err != nil {
		t.Fatal(err)
	}
	for i, ifi := range ift {
		ip, ok := nettest.IsMulticastCapable("ip4", &ifi)
		if !ok {
			continue
		}
		c, err := net.ListenPacket("ip4:253", ip.String()) // unicast address
		if err != nil {
			t.Fatal(err)
		}
		defer c.Close()
		r, err := ipv4.NewRawConn(c)
		if err != nil {
			t.Fatal(err)
		}
		if err := r.JoinGroup(&ifi, &gaddr); err != nil {
			t.Fatal(err)
		}
		mlt = append(mlt, &ml{r, &ift[i]})
	}
	for _, m := range mlt {
		if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil {
			t.Fatal(err)
		}
	}
}


text:
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs defs_solaris.go

package ipv4

const (
	sysIP_OPTIONS     = 0x1
	sysIP_HDRINCL     = 0x2
	sysIP_TOS         = 0x3
	sysIP_TTL         = 0x4
	sysIP_RECVOPTS    = 0x5
	sysIP_RECVRETOPTS = 0x6
	sysIP_RECVDSTADDR = 0x7
	sysIP_RETOPTS     = 0x8
	sysIP_RECVIF      = 0x9
	sysIP_RECVSLLA    = 0xa
	sysIP_RECVTTL     = 0xb

	sysIP_MULTICAST_IF           = 0x10
	sysIP_MULTICAST_TTL          = 0x11
	sysIP_MULTICAST_LOOP         = 0x12
	sysIP_ADD_MEMBERSHIP         = 0x13
	sysIP_DROP_MEMBERSHIP        = 0x14
	sysIP_BLOCK_SOURCE           = 0x15
	sysIP_UNBLOCK_SOURCE         = 0x16
	sysIP_ADD_SOURCE_MEMBERSHIP  = 0x17
	sysIP_DROP_SOURCE_MEMBERSHIP = 0x18
	sysIP_NEXTHOP                = 0x19

	sysIP_PKTINFO     = 0x1a
	sysIP_RECVPKTINFO = 0x1a
	sysIP_DONTFRAG    = 0x1b

	sysIP_BOUND_IF      = 0x41
	sysIP_UNSPEC_SRC    = 0x42
	sysIP_BROADCAST_TTL = 0x43
	sysIP_DHCPINIT_IF   = 0x45

	sysIP_REUSEADDR = 0x104
	sysIP_DONTROUTE = 0x105
	sysIP_BROADCAST = 0x106

	sysMCAST_JOIN_GROUP         = 0x29
	sysMCAST_LEAVE_GROUP        = 0x2a
	sysMCAST_BLOCK_SOURCE       = 0x2b
	sysMCAST_UNBLOCK_SOURCE     = 0x2c
	sysMCAST_JOIN_SOURCE_GROUP  = 0x2d
	sysMCAST_LEAVE_SOURCE_GROUP = 0x2e

	sizeofSockaddrStorage = 0x100
	sizeofSockaddrInet    = 0x10
	sizeofInetPktinfo     = 0xc

	sizeofIPMreq         = 0x8
	sizeofIPMreqSource   = 0xc
	sizeofGroupReq       = 0x104
	sizeofGroupSourceReq = 0x204
)

type sockaddrStorage struct {
	Family     uint16
	X_ss_pad1  [6]int8
	X_ss_align float64
	X_ss_pad2  [240]int8
}

type sockaddrInet struct {
	Family uint16
	Port   uint16
	Addr   [4]byte /* in_addr */
	Zero   [8]int8
}

type inetPktinfo struct {
	Ifindex  uint32
	Spec_dst [4]byte /* in_addr */
	Addr     [4]byte /* in_addr */
}

type ipMreq struct {
	Multiaddr [4]byte /* in_addr */
	Interface [4]byte /* in_addr */
}

type ipMreqSource struct {
	Multiaddr  [4]byte /* in_addr */
	Sourceaddr [4]byte /* in_addr */
	Interface  [4]byte /* in_addr */
}

type groupReq struct {
	Interface uint32
	Pad_cgo_0 [256]byte
}

type groupSourceReq struct {
	Interface uint32
	Pad_cgo_0 [256]byte
	Pad_cgo_1 [256]byte
}


text:
// Copyright 2017 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 ipv4_test

import (
	"testing"

	"golang.org/x/net/ipv4"
)

func TestControlMessageParseWithFuzz(t *testing.T) {
	var cm ipv4.ControlMessage
	for _, fuzz := range []string{
		"\f\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00",
		"\f\x00\x00\x00\x00\x00\x00\x00\x1a\x00\x00\x00",
	} {
		cm.Parse([]byte(fuzz))
	}
}


text:
// Copyright 2013 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.

// +build ignore

//go:generate go run gen.go

// This program generates system adaptation constants and types,
// internet protocol constants and tables by reading template files
// and IANA protocol registries.
package main

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"go/format"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"strconv"
	"strings"
)

func main() {
	if err := genzsys(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	if err := geniana(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func genzsys() error {
	defs := "defs_" + runtime.GOOS + ".go"
	f, err := os.Open(defs)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return err
	}
	f.Close()
	cmd := exec.Command("go", "tool", "cgo", "-godefs", defs)
	b, err := cmd.Output()
	if err != nil {
		return err
	}
	b, err = format.Source(b)
	if err != nil {
		return err
	}
	zsys := "zsys_" + runtime.GOOS + ".go"
	switch runtime.GOOS {
	case "freebsd", "linux":
		zsys = "zsys_" + runtime.GOOS + "_" + runtime.GOARCH + ".go"
	}
	if err := ioutil.WriteFile(zsys, b, 0644); err != nil {
		return err
	}
	return nil
}

var registries = []struct {
	url   string
	parse func(io.Writer, io.Reader) error
}{
	{
		"https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xml",
		parseICMPv4Parameters,
	},
}

func geniana() error {
	var bb bytes.Buffer
	fmt.Fprintf(&bb, "// go generate gen.go\n")
	fmt.Fprintf(&bb, "// Code generated by the command above; DO NOT EDIT.\n\n")
	fmt.Fprintf(&bb, "package ipv4\n\n")
	for _, r := range registries {
		resp, err := http.Get(r.url)
		if err != nil {
			return err
		}
		defer resp.Body.Close()
		if resp.StatusCode != http.StatusOK {
			return fmt.Errorf("got HTTP status code %v for %v\n", resp.StatusCode, r.url)
		}
		if err := r.parse(&bb, resp.Body); err != nil {
			return err
		}
		fmt.Fprintf(&bb, "\n")
	}
	b, err := format.Source(bb.Bytes())
	if err != nil {
		return err
	}
	if err := ioutil.WriteFile("iana.go", b, 0644); err != nil {
		return err
	}
	return nil
}

func parseICMPv4Parameters(w io.Writer, r io.Reader) error {
	dec := xml.NewDecoder(r)
	var icp icmpv4Parameters
	if err := dec.Decode(&icp); err != nil {
		return err
	}
	prs := icp.escape()
	fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated)
	fmt.Fprintf(w, "const (\n")
	for _, pr := range prs {
		if pr.Descr == "" {
			continue
		}
		fmt.Fprintf(w, "ICMPType%s ICMPType = %d", pr.Descr, pr.Value)
		fmt.Fprintf(w, "// %s\n", pr.OrigDescr)
	}
	fmt.Fprintf(w, ")\n\n")
	fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated)
	fmt.Fprintf(w, "var icmpTypes = map[ICMPType]string{\n")
	for _, pr := range prs {
		if pr.Descr == "" {
			continue
		}
		fmt.Fprintf(w, "%d: %q,\n", pr.Value, strings.ToLower(pr.OrigDescr))
	}
	fmt.Fprintf(w, "}\n")
	return nil
}

type icmpv4Parameters struct {
	XMLName    xml.Name `xml:"registry"`
	Title      string   `xml:"title"`
	Updated    string   `xml:"updated"`
	Registries []struct {
		Title   string `xml:"title"`
		Records []struct {
			Value string `xml:"value"`
			Descr string `xml:"description"`
		} `xml:"record"`
	} `xml:"registry"`
}

type canonICMPv4ParamRecord struct {
	OrigDescr string
	Descr     string
	Value     int
}

func (icp *icmpv4Parameters) escape() []canonICMPv4ParamRecord {
	id := -1
	for i, r := range icp.Registries {
		if strings.Contains(r.Title, "Type") || strings.Contains(r.Title, "type") {
			id = i
			break
		}
	}
	if id < 0 {
		return nil
	}
	prs := make([]canonICMPv4ParamRecord, len(icp.Registries[id].Records))
	sr := strings.NewReplacer(
		"Messages", "",
		"Message", "",
		"ICMP", "",
		"+", "P",
		"-", "",
		"/", "",
		".", "",
		" ", "",
	)
	for i, pr := range icp.Registries[id].Records {
		if strings.Contains(pr.Descr, "Reserved") ||
			strings.Contains(pr.Descr, "Unassigned") ||
			strings.Contains(pr.Descr, "Deprecated") ||
			strings.Contains(pr.Descr, "Experiment") ||
			strings.Contains(pr.Descr, "experiment") {
			continue
		}
		ss := strings.Split(pr.Descr, "\n")
		if len(ss) > 1 {
			prs[i].Descr = strings.Join(ss, " ")
		} else {
			prs[i].Descr = ss[0]
		}
		s := strings.TrimSpace(prs[i].Descr)
		prs[i].OrigDescr = s
		prs[i].Descr = sr.Replace(s)
		prs[i].Value, _ = strconv.Atoi(pr.Value)
	}
	return prs
}


text:
// Copyright 2014 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.

// +build darwin freebsd linux solaris

package ipv4

import (
	"net"
	"unsafe"

	"golang.org/x/net/internal/socket"
)

var freebsd32o64 bool

func (so *sockOpt) setGroupReq(c *socket.Conn, ifi *net.Interface, grp net.IP) error {
	var gr groupReq
	if ifi != nil {
		gr.Interface = uint32(ifi.Index)
	}
	gr.setGroup(grp)
	var b []byte
	if freebsd32o64 {
		var d [sizeofGroupReq + 4]byte
		s := (*[sizeofGroupReq]byte)(unsafe.Pointer(&gr))
		copy(d[:4], s[:4])
		copy(d[8:], s[4:])
		b = d[:]
	} else {
		b = (*[sizeofGroupReq]byte)(unsafe.Pointer(&gr))[:sizeofGroupReq]
	}
	return so.Set(c, b)
}

func (so *sockOpt) setGroupSourceReq(c *socket.Conn, ifi *net.Interface, grp, src net.IP) error {
	var gsr groupSourceReq
	if ifi != nil {
		gsr.Interface = uint32(ifi.Index)
	}
	gsr.setSourceGroup(grp, src)
	var b []byte
	if freebsd32o64 {
		var d [sizeofGroupSourceReq + 4]byte
		s := (*[sizeofGroupSourceReq]byte)(unsafe.Pointer(&gsr))
		copy(d[:4], s[:4])
		copy(d[8:], s[4:])
		b = d[:]
	} else {
		b = (*[sizeofGroupSourceReq]byte)(unsafe.Pointer(&gsr))[:sizeofGroupSourceReq]
	}
	return so.Set(c, b)
}


text:
// Copyright 2012 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 ipv4

import (
	"bytes"
	"encoding/binary"
	"net"
	"reflect"
	"runtime"
	"strings"
	"testing"

	"golang.org/x/net/internal/socket"
)

type headerTest struct {
	wireHeaderFromKernel          []byte
	wireHeaderToKernel            []byte
	wireHeaderFromTradBSDKernel   []byte
	wireHeaderToTradBSDKernel     []byte
	wireHeaderFromFreeBSD10Kernel []byte
	wireHeaderToFreeBSD10Kernel   []byte
	*Header
}

var headerLittleEndianTests = []headerTest{
	// TODO(mikio): Add platform dependent wire header formats when
	// we support new platforms.
	{
		wireHeaderFromKernel: []byte{
			0x45, 0x01, 0xbe, 0xef,
			0xca, 0xfe, 0x45, 0xdc,
			0xff, 0x01, 0xde, 0xad,
			172, 16, 254, 254,
			192, 168, 0, 1,
		},
		wireHeaderToKernel: []byte{
			0x45, 0x01, 0xbe, 0xef,
			0xca, 0xfe, 0x45, 0xdc,
			0xff, 0x01, 0xde, 0xad,
			172, 16, 254, 254,
			192, 168, 0, 1,
		},
		wireHeaderFromTradBSDKernel: []byte{
			0x45, 0x01, 0xdb, 0xbe,
			0xca, 0xfe, 0xdc, 0x45,
			0xff, 0x01, 0xde, 0xad,
			172, 16, 254, 254,
			192, 168, 0, 1,
		},
		wireHeaderToTradBSDKernel: []byte{
			0x45, 0x01, 0xef, 0xbe,
			0xca, 0xfe, 0xdc, 0x45,
			0xff, 0x01, 0xde, 0xad,
			172, 16, 254, 254,
			192, 168, 0, 1,
		},
		wireHeaderFromFreeBSD10Kernel: []byte{
			0x45, 0x01, 0xef, 0xbe,
			0xca, 0xfe, 0xdc, 0x45,
			0xff, 0x01, 0xde, 0xad,
			172, 16, 254, 254,
			192, 168, 0, 1,
		},
		wireHeaderToFreeBSD10Kernel: []byte{
			0x45, 0x01, 0xef, 0xbe,
			0xca, 0xfe, 0xdc, 0x45,
			0xff, 0x01, 0xde, 0xad,
			172, 16, 254, 254,
			192, 168, 0, 1,
		},
		Header: &Header{
			Version:  Version,
			Len:      HeaderLen,
			TOS:      1,
			TotalLen: 0xbeef,
			ID:       0xcafe,
			Flags:    DontFragment,
			FragOff:  1500,
			TTL:      255,
			Protocol: 1,
			Checksum: 0xdead,
			Src:      net.IPv4(172, 16, 254, 254),
			Dst:      net.IPv4(192, 168, 0, 1),
		},
	},

	// with option headers
	{
		wireHeaderFromKernel: []byte{
			0x46, 0x01, 0xbe, 0xf3,
			0xca, 0xfe, 0x45, 0xdc,
			0xff, 0x01, 0xde, 0xad,
			172, 16, 254, 254,
			192, 168, 0, 1,
			0xff, 0xfe, 0xfe, 0xff,
		},
		wireHeaderToKernel: []byte{
			0x46, 0x01, 0xbe, 0xf3,
			0xca, 0xfe, 0x45, 0xdc,
			0xff, 0x01, 0xde, 0xad,
			172, 16, 254, 254,
			192, 168, 0, 1,
			0xff, 0xfe, 0xfe, 0xff,
		},
		wireHeaderFromTradBSDKernel: []byte{
			0x46, 0x01, 0xdb, 0xbe,
			0xca, 0xfe, 0xdc, 0x45,
			0xff, 0x01, 0xde, 0xad,
			172, 16, 254, 254,
			192, 168, 0, 1,
			0xff, 0xfe, 0xfe, 0xff,
		},
		wireHeaderToTradBSDKernel: []byte{
			0x46, 0x01, 0xf3, 0xbe,
			0xca, 0xfe, 0xdc, 0x45,
			0xff, 0x01, 0xde, 0xad,
			172, 16, 254, 254,
			192, 168, 0, 1,
			0xff, 0xfe, 0xfe, 0xff,
		},
		wireHeaderFromFreeBSD10Kernel: []byte{
			0x46, 0x01, 0xf3, 0xbe,
			0xca, 0xfe, 0xdc, 0x45,
			0xff, 0x01, 0xde, 0xad,
			172, 16, 254, 254,
			192, 168, 0, 1,
			0xff, 0xfe, 0xfe, 0xff,
		},
		wireHeaderToFreeBSD10Kernel: []byte{
			0x46, 0x01, 0xf3, 0xbe,
			0xca, 0xfe, 0xdc, 0x45,
			0xff, 0x01, 0xde, 0xad,
			172, 16, 254, 254,
			192, 168, 0, 1,
			0xff, 0xfe, 0xfe, 0xff,
		},
		Header: &Header{
			Version:  Version,
			Len:      HeaderLen + 4,
			TOS:      1,
			TotalLen: 0xbef3,
			ID:       0xcafe,
			Flags:    DontFragment,
			FragOff:  1500,
			TTL:      255,
			Protocol: 1,
			Checksum: 0xdead,
			Src:      net.IPv4(172, 16, 254, 254),
			Dst:      net.IPv4(192, 168, 0, 1),
			Options:  []byte{0xff, 0xfe, 0xfe, 0xff},
		},
	},
}

func TestMarshalHeader(t *testing.T) {
	if socket.NativeEndian != binary.LittleEndian {
		t.Skip("no test for non-little endian machine yet")
	}

	for _, tt := range headerLittleEndianTests {
		b, err := tt.Header.Marshal()
		if err != nil {
			t.Fatal(err)
		}
		var wh []byte
		switch runtime.GOOS {
		case "darwin", "dragonfly", "netbsd":
			wh = tt.wireHeaderToTradBSDKernel
		case "freebsd":
			switch {
			case freebsdVersion < 1000000:
				wh = tt.wireHeaderToTradBSDKernel
			case 1000000 <= freebsdVersion && freebsdVersion < 1100000:
				wh = tt.wireHeaderToFreeBSD10Kernel
			default:
				wh = tt.wireHeaderToKernel
			}
		default:
			wh = tt.wireHeaderToKernel
		}
		if !bytes.Equal(b, wh) {
			t.Fatalf("got %#v; want %#v", b, wh)
		}
	}
}

func TestParseHeader(t *testing.T) {
	if socket.NativeEndian != binary.LittleEndian {
		t.Skip("no test for big endian machine yet")
	}

	for _, tt := range headerLittleEndianTests {
		var wh []byte
		switch runtime.GOOS {
		case "darwin", "dragonfly", "netbsd":
			wh = tt.wireHeaderFromTradBSDKernel
		case "freebsd":
			switch {
			case freebsdVersion < 1000000:
				wh = tt.wireHeaderFromTradBSDKernel
			case 1000000 <= freebsdVersion && freebsdVersion < 1100000:
				wh = tt.wireHeaderFromFreeBSD10Kernel
			default:
				wh = tt.wireHeaderFromKernel
			}
		default:
			wh = tt.wireHeaderFromKernel
		}
		h, err := ParseHeader(wh)
		if err != nil {
			t.Fatal(err)
		}
		if err := h.Parse(wh); err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(h, tt.Header) {
			t.Fatalf("got %#v; want %#v", h, tt.Header)
		}
		s := h.String()
		if strings.Contains(s, ",") {
			t.Fatalf("should be space-separated values: %s", s)
		}
	}
}


text:
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs defs_linux.go

package ipv4

const (
	sysIP_TOS             = 0x1
	sysIP_TTL             = 0x2
	sysIP_HDRINCL         = 0x3
	sysIP_OPTIONS         = 0x4
	sysIP_ROUTER_ALERT    = 0x5
	sysIP_RECVOPTS        = 0x6
	sysIP_RETOPTS         = 0x7
	sysIP_PKTINFO         = 0x8
	sysIP_PKTOPTIONS      = 0x9
	sysIP_MTU_DISCOVER    = 0xa
	sysIP_RECVERR         = 0xb
	sysIP_RECVTTL         = 0xc
	sysIP_RECVTOS         = 0xd
	sysIP_MTU             = 0xe
	sysIP_FREEBIND        = 0xf
	sysIP_TRANSPARENT     = 0x13
	sysIP_RECVRETOPTS     = 0x7
	sysIP_ORIGDSTADDR     = 0x14
	sysIP_RECVORIGDSTADDR = 0x14
	sysIP_MINTTL          = 0x15
	sysIP_NODEFRAG        = 0x16
	sysIP_UNICAST_IF      = 0x32

	sysIP_MULTICAST_IF           = 0x20
	sysIP_MULTICAST_TTL          = 0x21
	sysIP_MULTICAST_LOOP         = 0x22
	sysIP_ADD_MEMBERSHIP         = 0x23
	sysIP_DROP_MEMBERSHIP        = 0x24
	sysIP_UNBLOCK_SOURCE         = 0x25
	sysIP_BLOCK_SOURCE           = 0x26
	sysIP_ADD_SOURCE_MEMBERSHIP  = 0x27
	sysIP_DROP_SOURCE_MEMBERSHIP = 0x28
	sysIP_MSFILTER               = 0x29
	sysMCAST_JOIN_GROUP          = 0x2a
	sysMCAST_LEAVE_GROUP         = 0x2d
	sysMCAST_JOIN_SOURCE_GROUP   = 0x2e
	sysMCAST_LEAVE_SOURCE_GROUP  = 0x2f
	sysMCAST_BLOCK_SOURCE        = 0x2b
	sysMCAST_UNBLOCK_SOURCE      = 0x2c
	sysMCAST_MSFILTER            = 0x30
	sysIP_MULTICAST_ALL          = 0x31

	sysICMP_FILTER = 0x1

	sysSO_EE_ORIGIN_NONE         = 0x0
	sysSO_EE_ORIGIN_LOCAL        = 0x1
	sysSO_EE_ORIGIN_ICMP         = 0x2
	sysSO_EE_ORIGIN_ICMP6        = 0x3
	sysSO_EE_ORIGIN_TXSTATUS     = 0x4
	sysSO_EE_ORIGIN_TIMESTAMPING = 0x4

	sysSOL_SOCKET       = 0x1
	sysSO_ATTACH_FILTER = 0x1a

	sizeofKernelSockaddrStorage = 0x80
	sizeofSockaddrInet          = 0x10
	sizeofInetPktinfo           = 0xc
	sizeofSockExtendedErr       = 0x10

	sizeofIPMreq         = 0x8
	sizeofIPMreqn        = 0xc
	sizeofIPMreqSource   = 0xc
	sizeofGroupReq       = 0x88
	sizeofGroupSourceReq = 0x108

	sizeofICMPFilter = 0x4

	sizeofSockFprog = 0x10
)

type kernelSockaddrStorage struct {
	Family  uint16
	X__data [126]int8
}

type sockaddrInet struct {
	Family uint16
	Port   uint16
	Addr   [4]byte /* in_addr */
	X__pad [8]uint8
}

type inetPktinfo struct {
	Ifindex  int32
	Spec_dst [4]byte /* in_addr */
	Addr     [4]byte /* in_addr */
}

type sockExtendedErr struct {
	Errno  uint32
	Origin uint8
	Type   uint8
	Code   uint8
	Pad    uint8
	Info   uint32
	Data   uint32
}

type ipMreq struct {
	Multiaddr [4]byte /* in_addr */
	Interface [4]byte /* in_addr */
}

type ipMreqn struct {
	Multiaddr [4]byte /* in_addr */
	Address   [4]byte /* in_addr */
	Ifindex   int32
}

type ipMreqSource struct {
	Multiaddr  uint32
	Interface  uint32
	Sourceaddr uint32
}

type groupReq struct {
	Interface uint32
	Pad_cgo_0 [4]byte
	Group     kernelSockaddrStorage
}

type groupSourceReq struct {
	Interface uint32
	Pad_cgo_0 [4]byte
	Group     kernelSockaddrStorage
	Source    kernelSockaddrStorage
}

type icmpFilter struct {
	Data uint32
}

type sockFProg struct {
	Len       uint16
	Pad_cgo_0 [6]byte
	Filter    *sockFilter
}

type sockFilter struct {
	Code uint16
	Jt   uint8
	Jf   uint8
	K    uint32
}


text:
// Copyright 2014 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.

// +build ignore

// +godefs map struct_in_addr [4]byte /* in_addr */

package ipv4

/*
#include <netinet/in.h>
*/
import "C"

const (
	sysIP_OPTIONS     = C.IP_OPTIONS
	sysIP_HDRINCL     = C.IP_HDRINCL
	sysIP_TOS         = C.IP_TOS
	sysIP_TTL         = C.IP_TTL
	sysIP_RECVOPTS    = C.IP_RECVOPTS
	sysIP_RECVRETOPTS = C.IP_RECVRETOPTS
	sysIP_RECVDSTADDR = C.IP_RECVDSTADDR
	sysIP_RETOPTS     = C.IP_RETOPTS
	sysIP_RECVIF      = C.IP_RECVIF
	sysIP_RECVTTL     = C.IP_RECVTTL

	sysIP_MULTICAST_IF    = C.IP_MULTICAST_IF
	sysIP_MULTICAST_TTL   = C.IP_MULTICAST_TTL
	sysIP_MULTICAST_LOOP  = C.IP_MULTICAST_LOOP
	sysIP_ADD_MEMBERSHIP  = C.IP_ADD_MEMBERSHIP
	sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP

	sizeofIPMreq = C.sizeof_struct_ip_mreq
)

type ipMreq C.struct_ip_mreq


text:
// Copyright 2012 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 ipv4

import (
	"encoding/binary"
	"fmt"
	"net"
	"runtime"
	"syscall"

	"golang.org/x/net/internal/socket"
)

const (
	Version      = 4  // protocol version
	HeaderLen    = 20 // header length without extension headers
	maxHeaderLen = 60 // sensible default, revisit if later RFCs define new usage of version and header length fields
)

type HeaderFlags int

const (
	MoreFragments HeaderFlags = 1 << iota // more fragments flag
	DontFragment                          // don't fragment flag
)

// A Header represents an IPv4 header.
type Header struct {
	Version  int         // protocol version
	Len      int         // header length
	TOS      int         // type-of-service
	TotalLen int         // packet total length
	ID       int         // identification
	Flags    HeaderFlags // flags
	FragOff  int         // fragment offset
	TTL      int         // time-to-live
	Protocol int         // next protocol
	Checksum int         // checksum
	Src      net.IP      // source address
	Dst      net.IP      // destination address
	Options  []byte      // options, extension headers
}

func (h *Header) String() string {
	if h == nil {
		return "<nil>"
	}
	return fmt.Sprintf("ver=%d hdrlen=%d tos=%#x totallen=%d id=%#x flags=%#x fragoff=%#x ttl=%d proto=%d cksum=%#x src=%v dst=%v", h.Version, h.Len, h.TOS, h.TotalLen, h.ID, h.Flags, h.FragOff, h.TTL, h.Protocol, h.Checksum, h.Src, h.Dst)
}

// Marshal returns the binary encoding of h.
func (h *Header) Marshal() ([]byte, error) {
	if h == nil {
		return nil, syscall.EINVAL
	}
	if h.Len < HeaderLen {
		return nil, errHeaderTooShort
	}
	hdrlen := HeaderLen + len(h.Options)
	b := make([]byte, hdrlen)
	b[0] = byte(Version<<4 | (hdrlen >> 2 & 0x0f))
	b[1] = byte(h.TOS)
	flagsAndFragOff := (h.FragOff & 0x1fff) | int(h.Flags<<13)
	switch runtime.GOOS {
	case "darwin", "dragonfly", "netbsd":
		socket.NativeEndian.PutUint16(b[2:4], uint16(h.TotalLen))
		socket.NativeEndian.PutUint16(b[6:8], uint16(flagsAndFragOff))
	case "freebsd":
		if freebsdVersion < 1100000 {
			socket.NativeEndian.PutUint16(b[2:4], uint16(h.TotalLen))
			socket.NativeEndian.PutUint16(b[6:8], uint16(flagsAndFragOff))
		} else {
			binary.BigEndian.PutUint16(b[2:4], uint16(h.TotalLen))
			binary.BigEndian.PutUint16(b[6:8], uint16(flagsAndFragOff))
		}
	default:
		binary.BigEndian.PutUint16(b[2:4], uint16(h.TotalLen))
		binary.BigEndian.PutUint16(b[6:8], uint16(flagsAndFragOff))
	}
	binary.BigEndian.PutUint16(b[4:6], uint16(h.ID))
	b[8] = byte(h.TTL)
	b[9] = byte(h.Protocol)
	binary.BigEndian.PutUint16(b[10:12], uint16(h.Checksum))
	if ip := h.Src.To4(); ip != nil {
		copy(b[12:16], ip[:net.IPv4len])
	}
	if ip := h.Dst.To4(); ip != nil {
		copy(b[16:20], ip[:net.IPv4len])
	} else {
		return nil, errMissingAddress
	}
	if len(h.Options) > 0 {
		copy(b[HeaderLen:], h.Options)
	}
	return b, nil
}

// Parse parses b as an IPv4 header and sotres the result in h.
func (h *Header) Parse(b []byte) error {
	if h == nil || len(b) < HeaderLen {
		return errHeaderTooShort
	}
	hdrlen := int(b[0]&0x0f) << 2
	if hdrlen > len(b) {
		return errBufferTooShort
	}
	h.Version = int(b[0] >> 4)
	h.Len = hdrlen
	h.TOS = int(b[1])
	h.ID = int(binary.BigEndian.Uint16(b[4:6]))
	h.TTL = int(b[8])
	h.Protocol = int(b[9])
	h.Checksum = int(binary.BigEndian.Uint16(b[10:12]))
	h.Src = net.IPv4(b[12], b[13], b[14], b[15])
	h.Dst = net.IPv4(b[16], b[17], b[18], b[19])
	switch runtime.GOOS {
	case "darwin", "dragonfly", "netbsd":
		h.TotalLen = int(socket.NativeEndian.Uint16(b[2:4])) + hdrlen
		h.FragOff = int(socket.NativeEndian.Uint16(b[6:8]))
	case "freebsd":
		if freebsdVersion < 1100000 {
			h.TotalLen = int(socket.NativeEndian.Uint16(b[2:4]))
			if freebsdVersion < 1000000 {
				h.TotalLen += hdrlen
			}
			h.FragOff = int(socket.NativeEndian.Uint16(b[6:8]))
		} else {
			h.TotalLen = int(binary.BigEndian.Uint16(b[2:4]))
			h.FragOff = int(binary.BigEndian.Uint16(b[6:8]))
		}
	default:
		h.TotalLen = int(binary.BigEndian.Uint16(b[2:4]))
		h.FragOff = int(binary.BigEndian.Uint16(b[6:8]))
	}
	h.Flags = HeaderFlags(h.FragOff&0xe000) >> 13
	h.FragOff = h.FragOff & 0x1fff
	optlen := hdrlen - HeaderLen
	if optlen > 0 && len(b) >= hdrlen {
		if cap(h.Options) < optlen {
			h.Options = make([]byte, optlen)
		} else {
			h.Options = h.Options[:optlen]
		}
		copy(h.Options, b[HeaderLen:hdrlen])
	}
	return nil
}

// ParseHeader parses b as an IPv4 header.
func ParseHeader(b []byte) (*Header, error) {
	h := new(Header)
	if err := h.Parse(b); err != nil {
		return nil, err
	}
	return h, nil
}


text:
// Copyright 2012 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 ipv4_test

import (
	"bytes"
	"net"
	"runtime"
	"strings"
	"sync"
	"testing"

	"golang.org/x/net/internal/nettest"
	"golang.org/x/net/ipv4"
)

func BenchmarkReadWriteUnicast(b *testing.B) {
	c, err := nettest.NewLocalPacketListener("udp4")
	if err != nil {
		b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
	}
	defer c.Close()

	dst := c.LocalAddr()
	wb, rb := []byte("HELLO-R-U-THERE"), make([]byte, 128)

	b.Run("NetUDP", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			if _, err := c.WriteTo(wb, dst); err != nil {
				b.Fatal(err)
			}
			if _, _, err := c.ReadFrom(rb); err != nil {
				b.Fatal(err)
			}
		}
	})
	b.Run("IPv4UDP", func(b *testing.B) {
		p := ipv4.NewPacketConn(c)
		cf := ipv4.FlagTTL | ipv4.FlagInterface
		if err := p.SetControlMessage(cf, true); err != nil {
			b.Fatal(err)
		}
		cm := ipv4.ControlMessage{TTL: 1}
		ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
		if ifi != nil {
			cm.IfIndex = ifi.Index
		}

		for i := 0; i < b.N; i++ {
			if _, err := p.WriteTo(wb, &cm, dst); err != nil {
				b.Fatal(err)
			}
			if _, _, _, err := p.ReadFrom(rb); err != nil {
				b.Fatal(err)
			}
		}
	})
}

func TestPacketConnConcurrentReadWriteUnicastUDP(t *testing.T) {
	switch runtime.GOOS {
	case "nacl", "plan9", "windows":
		t.Skipf("not supported on %s", runtime.GOOS)
	}

	c, err := nettest.NewLocalPacketListener("udp4")
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()
	p := ipv4.NewPacketConn(c)
	defer p.Close()

	dst := c.LocalAddr()
	ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
	cf := ipv4.FlagTTL | ipv4.FlagSrc | ipv4.FlagDst | ipv4.FlagInterface
	wb := []byte("HELLO-R-U-THERE")

	if err := p.SetControlMessage(cf, true); err != nil { // probe before test
		if nettest.ProtocolNotSupported(err) {
			t.Skipf("not supported on %s", runtime.GOOS)
		}
		t.Fatal(err)
	}

	var wg sync.WaitGroup
	reader := func() {
		defer wg.Done()
		rb := make([]byte, 128)
		if n, cm, _, err := p.ReadFrom(rb); err != nil {
			t.Error(err)
			return
		} else if !bytes.Equal(rb[:n], wb) {
			t.Errorf("got %v; want %v", rb[:n], wb)
			return
		} else {
			s := cm.String()
			if strings.Contains(s, ",") {
				t.Errorf("should be space-separated values: %s", s)
			}
		}
	}
	writer := func(toggle bool) {
		defer wg.Done()
		cm := ipv4.ControlMessage{
			Src: net.IPv4(127, 0, 0, 1),
		}
		if ifi != nil {
			cm.IfIndex = ifi.Index
		}
		if err := p.SetControlMessage(cf, toggle); err != nil {
			t.Error(err)
			return
		}
		if n, err := p.WriteTo(wb, &cm, dst); err != nil {
			t.Error(err)
			return
		} else if n != len(wb) {
			t.Errorf("got %d; want %d", n, len(wb))
			return
		}
	}

	const N = 10
	wg.Add(N)
	for i := 0; i < N; i++ {
		go reader()
	}
	wg.Add(2 * N)
	for i := 0; i < 2*N; i++ {
		go writer(i%2 != 0)
	}
	wg.Add(N)
	for i := 0; i < N; i++ {
		go reader()
	}
	wg.Wait()
}


text:
// Copyright 2016 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 ipv4

import (
	"net"
	"syscall"
	"unsafe"

	"golang.org/x/net/internal/iana"
	"golang.org/x/net/internal/socket"
)

var (
	ctlOpts = [ctlMax]ctlOpt{
		ctlTTL:        {sysIP_RECVTTL, 4, marshalTTL, parseTTL},
		ctlPacketInfo: {sysIP_PKTINFO, sizeofInetPktinfo, marshalPacketInfo, parsePacketInfo},
	}

	sockOpts = map[int]sockOpt{
		ssoTOS:                {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_TOS, Len: 4}},
		ssoTTL:                {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_TTL, Len: 4}},
		ssoMulticastTTL:       {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_TTL, Len: 1}},
		ssoMulticastInterface: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_IF, Len: 4}},
		ssoMulticastLoopback:  {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_LOOP, Len: 1}},
		ssoReceiveTTL:         {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_RECVTTL, Len: 4}},
		ssoPacketInfo:         {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_RECVPKTINFO, Len: 4}},
		ssoHeaderPrepend:      {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_HDRINCL, Len: 4}},
		ssoJoinGroup:          {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_JOIN_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq},
		ssoLeaveGroup:         {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_LEAVE_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq},
		ssoJoinSourceGroup:    {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_JOIN_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
		ssoLeaveSourceGroup:   {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_LEAVE_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
		ssoBlockSourceGroup:   {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_BLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
		ssoUnblockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_UNBLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
	}
)

func (pi *inetPktinfo) setIfindex(i int) {
	pi.Ifindex = uint32(i)
}

func (gr *groupReq) setGroup(grp net.IP) {
	sa := (*sockaddrInet)(unsafe.Pointer(uintptr(unsafe.Pointer(gr)) + 4))
	sa.Family = syscall.AF_INET
	copy(sa.Addr[:], grp)
}

func (gsr *groupSourceReq) setSourceGroup(grp, src net.IP) {
	sa := (*sockaddrInet)(unsafe.Pointer(uintptr(unsafe.Pointer(gsr)) + 4))
	sa.Family = syscall.AF_INET
	copy(sa.Addr[:], grp)
	sa = (*sockaddrInet)(unsafe.Pointer(uintptr(unsafe.Pointer(gsr)) + 260))
	sa.Family = syscall.AF_INET
	copy(sa.Addr[:], src)
}


text:
// Copyright 2012 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.

// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows

package ipv4

import "golang.org/x/net/internal/socket"

func setControlMessage(c *socket.Conn, opt *rawOpt, cf ControlFlags, on bool) error {
	return errOpNoSupport
}


text:
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs defs_dragonfly.go

package ipv4

const (
	sysIP_OPTIONS     = 0x1
	sysIP_HDRINCL     = 0x2
	sysIP_TOS         = 0x3
	sysIP_TTL         = 0x4
	sysIP_RECVOPTS    = 0x5
	sysIP_RECVRETOPTS = 0x6
	sysIP_RECVDSTADDR = 0x7
	sysIP_RETOPTS     = 0x8
	sysIP_RECVIF      = 0x14
	sysIP_RECVTTL     = 0x41

	sysIP_MULTICAST_IF    = 0x9
	sysIP_MULTICAST_TTL   = 0xa
	sysIP_MULTICAST_LOOP  = 0xb
	sysIP_MULTICAST_VIF   = 0xe
	sysIP_ADD_MEMBERSHIP  = 0xc
	sysIP_DROP_MEMBERSHIP = 0xd

	sizeofIPMreq = 0x8
)

type ipMreq struct {
	Multiaddr [4]byte /* in_addr */
	Interface [4]byte /* in_addr */
}


text:
// Copyright 2012 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 ipv4

import (
	"net"
	"syscall"
	"time"

	"golang.org/x/net/internal/socket"
)

// BUG(mikio): On Windows, the JoinSourceSpecificGroup,
// LeaveSourceSpecificGroup, ExcludeSourceSpecificGroup and
// IncludeSourceSpecificGroup methods of PacketConn and RawConn are
// not implemented.

// A Conn represents a network endpoint that uses the IPv4 transport.
// It is used to control basic IP-level socket options such as TOS and
// TTL.
type Conn struct {
	genericOpt
}

type genericOpt struct {
	*socket.Conn
}

func (c *genericOpt) ok() bool { return c != nil && c.Conn != nil }

// NewConn returns a new Conn.
func NewConn(c net.Conn) *Conn {
	cc, _ := socket.NewConn(c)
	return &Conn{
		genericOpt: genericOpt{Conn: cc},
	}
}

// A PacketConn represents a packet network endpoint that uses the
// IPv4 transport. It is used to control several IP-level socket
// options including multicasting. It also provides datagram based
// network I/O methods specific to the IPv4 and higher layer protocols
// such as UDP.
type PacketConn struct {
	genericOpt
	dgramOpt
	payloadHandler
}

type dgramOpt struct {
	*socket.Conn
}

func (c *dgramOpt) ok() bool { return c != nil && c.Conn != nil }

// SetControlMessage sets the per packet IP-level socket options.
func (c *PacketConn) SetControlMessage(cf ControlFlags, on bool) error {
	if !c.payloadHandler.ok() {
		return syscall.EINVAL
	}
	return setControlMessage(c.dgramOpt.Conn, &c.payloadHandler.rawOpt, cf, on)
}

// SetDeadline sets the read and write deadlines associated with the
// endpoint.
func (c *PacketConn) SetDeadline(t time.Time) error {
	if !c.payloadHandler.ok() {
		return syscall.EINVAL
	}
	return c.payloadHandler.PacketConn.SetDeadline(t)
}

// SetReadDeadline sets the read deadline associated with the
// endpoint.
func (c *PacketConn) SetReadDeadline(t time.Time) error {
	if !c.payloadHandler.ok() {
		return syscall.EINVAL
	}
	return c.payloadHandler.PacketConn.SetReadDeadline(t)
}

// SetWriteDeadline sets the write deadline associated with the
// endpoint.
func (c *PacketConn) SetWriteDeadline(t time.Time) error {
	if !c.payloadHandler.ok() {
		return syscall.EINVAL
	}
	return c.payloadHandler.PacketConn.SetWriteDeadline(t)
}

// Close closes the endpoint.
func (c *PacketConn) Close() error {
	if !c.payloadHandler.ok() {
		return syscall.EINVAL
	}
	return c.payloadHandler.PacketConn.Close()
}

// NewPacketConn returns a new PacketConn using c as its underlying
// transport.
func NewPacketConn(c net.PacketConn) *PacketConn {
	cc, _ := socket.NewConn(c.(net.Conn))
	p := &PacketConn{
		genericOpt:     genericOpt{Conn: cc},
		dgramOpt:       dgramOpt{Conn: cc},
		payloadHandler: payloadHandler{PacketConn: c, Conn: cc},
	}
	return p
}

// A RawConn represents a packet network endpoint that uses the IPv4
// transport. It is used to control several IP-level socket options
// including IPv4 header manipulation. It also provides datagram
// based network I/O methods specific to the IPv4 and higher layer
// protocols that handle IPv4 datagram directly such as OSPF, GRE.
type RawConn struct {
	genericOpt
	dgramOpt
	packetHandler
}

// SetControlMessage sets the per packet IP-level socket options.
func (c *RawConn) SetControlMessage(cf ControlFlags, on bool) error {
	if !c.packetHandler.ok() {
		return syscall.EINVAL
	}
	return setControlMessage(c.dgramOpt.Conn, &c.packetHandler.rawOpt, cf, on)
}

// SetDeadline sets the read and write deadlines associated with the
// endpoint.
func (c *RawConn) SetDeadline(t time.Time) error {
	if !c.packetHandler.ok() {
		return syscall.EINVAL
	}
	return c.packetHandler.IPConn.SetDeadline(t)
}

// SetReadDeadline sets the read deadline associated with the
// endpoint.
func (c *RawConn) SetReadDeadline(t time.Time) error {
	if !c.packetHandler.ok() {
		return syscall.EINVAL
	}
	return c.packetHandler.IPConn.SetReadDeadline(t)
}

// SetWriteDeadline sets the write deadline associated with the
// endpoint.
func (c *RawConn) SetWriteDeadline(t time.Time) error {
	if !c.packetHandler.ok() {
		return syscall.EINVAL
	}
	return c.packetHandler.IPConn.SetWriteDeadline(t)
}

// Close closes the endpoint.
func (c *RawConn) Close() error {
	if !c.packetHandler.ok() {
		return syscall.EINVAL
	}
	return c.packetHandler.IPConn.Close()
}

// NewRawConn returns a new RawConn using c as its underlying
// transport.
func NewRawConn(c net.PacketConn) (*RawConn, error) {
	cc, err := socket.NewConn(c.(net.Conn))
	if err != nil {
		return nil, err
	}
	r := &RawConn{
		genericOpt:    genericOpt{Conn: cc},
		dgramOpt:      dgramOpt{Conn: cc},
		packetHandler: packetHandler{IPConn: c.(*net.IPConn), Conn: cc},
	}
	so, ok := sockOpts[ssoHeaderPrepend]
	if !ok {
		return nil, errOpNoSupport
	}
	if err := so.SetInt(r.dgramOpt.Conn, boolint(true)); err != nil {
		return nil, err
	}
	return r, nil
}


text:
// Copyright 2012 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 ipv4_test

import (
	"bytes"
	"net"
	"os"
	"runtime"
	"testing"
	"time"

	"golang.org/x/net/icmp"
	"golang.org/x/net/internal/iana"
	"golang.org/x/net/internal/nettest"
	"golang.org/x/net/ipv4"
)

func TestPacketConnReadWriteUnicastUDP(t *testing.T) {
	switch runtime.GOOS {
	case "nacl", "plan9", "windows":
		t.Skipf("not supported on %s", runtime.GOOS)
	}
	ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
	if ifi == nil {
		t.Skipf("not available on %s", runtime.GOOS)
	}

	c, err := nettest.NewLocalPacketListener("udp4")
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()
	p := ipv4.NewPacketConn(c)
	defer p.Close()

	dst := c.LocalAddr()
	cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface
	wb := []byte("HELLO-R-U-THERE")

	for i, toggle := range []bool{true, false, true} {
		if err := p.SetControlMessage(cf, toggle); err != nil {
			if nettest.ProtocolNotSupported(err) {
				t.Logf("not supported on %s", runtime.GOOS)
				continue
			}
			t.Fatal(err)
		}
		p.SetTTL(i + 1)
		if err := p.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
			t.Fatal(err)
		}
		if n, err := p.WriteTo(wb, nil, dst); err != nil {
			t.Fatal(err)
		} else if n != len(wb) {
			t.Fatalf("got %v; want %v", n, len(wb))
		}
		rb := make([]byte, 128)
		if err := p.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
			t.Fatal(err)
		}
		if n, _, _, err := p.ReadFrom(rb); err != nil {
			t.Fatal(err)
		} else if !bytes.Equal(rb[:n], wb) {
			t.Fatalf("got %v; want %v", rb[:n], wb)
		}
	}
}

func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
	switch runtime.GOOS {
	case "nacl", "plan9", "windows":
		t.Skipf("not supported on %s", runtime.GOOS)
	}
	if m, ok := nettest.SupportsRawIPSocket(); !ok {
		t.Skip(m)
	}
	ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
	if ifi == nil {
		t.Skipf("not available on %s", runtime.GOOS)
	}

	c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	dst, err := net.ResolveIPAddr("ip4", "127.0.0.1")
	if err != nil {
		t.Fatal(err)
	}
	p := ipv4.NewPacketConn(c)
	defer p.Close()
	cf := ipv4.FlagDst | ipv4.FlagInterface
	if runtime.GOOS != "solaris" {
		// Solaris never allows to modify ICMP properties.
		cf |= ipv4.FlagTTL
	}

	for i, toggle := range []bool{true, false, true} {
		wb, err := (&icmp.Message{
			Type: ipv4.ICMPTypeEcho, Code: 0,
			Body: &icmp.Echo{
				ID: os.Getpid() & 0xffff, Seq: i + 1,
				Data: []byte("HELLO-R-U-THERE"),
			},
		}).Marshal(nil)
		if err != nil {
			t.Fatal(err)
		}
		if err := p.SetControlMessage(cf, toggle); err != nil {
			if nettest.ProtocolNotSupported(err) {
				t.Logf("not supported on %s", runtime.GOOS)
				continue
			}
			t.Fatal(err)
		}
		p.SetTTL(i + 1)
		if err := p.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
			t.Fatal(err)
		}
		if n, err := p.WriteTo(wb, nil, dst); err != nil {
			t.Fatal(err)
		} else if n != len(wb) {
			t.Fatalf("got %v; want %v", n, len(wb))
		}
		rb := make([]byte, 128)
	loop:
		if err := p.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
			t.Fatal(err)
		}
		if n, _, _, err := p.ReadFrom(rb); err != nil {
			switch runtime.GOOS {
			case "darwin": // older darwin kernels have some limitation on receiving icmp packet through raw socket
				t.Logf("not supported on %s", runtime.GOOS)
				continue
			}
			t.Fatal(err)
		} else {
			m, err := icmp.ParseMessage(iana.ProtocolICMP, rb[:n])
			if err != nil {
				t.Fatal(err)
			}
			if runtime.GOOS == "linux" && m.Type == ipv4.ICMPTypeEcho {
				// On Linux we must handle own sent packets.
				goto loop
			}
			if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 {
				t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0)
			}
		}
	}
}

func TestRawConnReadWriteUnicastICMP(t *testing.T) {
	switch runtime.GOOS {
	case "nacl", "plan9", "windows":
		t.Skipf("not supported on %s", runtime.GOOS)
	}
	if m, ok := nettest.SupportsRawIPSocket(); !ok {
		t.Skip(m)
	}
	ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
	if ifi == nil {
		t.Skipf("not available on %s", runtime.GOOS)
	}

	c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	dst, err := net.ResolveIPAddr("ip4", "127.0.0.1")
	if err != nil {
		t.Fatal(err)
	}
	r, err := ipv4.NewRawConn(c)
	if err != nil {
		t.Fatal(err)
	}
	defer r.Close()
	cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface

	for i, toggle := range []bool{true, false, true} {
		wb, err := (&icmp.Message{
			Type: ipv4.ICMPTypeEcho, Code: 0,
			Body: &icmp.Echo{
				ID: os.Getpid() & 0xffff, Seq: i + 1,
				Data: []byte("HELLO-R-U-THERE"),
			},
		}).Marshal(nil)
		if err != nil {
			t.Fatal(err)
		}
		wh := &ipv4.Header{
			Version:  ipv4.Version,
			Len:      ipv4.HeaderLen,
			TOS:      i + 1,
			TotalLen: ipv4.HeaderLen + len(wb),
			TTL:      i + 1,
			Protocol: 1,
			Dst:      dst.IP,
		}
		if err := r.SetControlMessage(cf, toggle); err != nil {
			if nettest.ProtocolNotSupported(err) {
				t.Logf("not supported on %s", runtime.GOOS)
				continue
			}
			t.Fatal(err)
		}
		if err := r.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
			t.Fatal(err)
		}
		if err := r.WriteTo(wh, wb, nil); err != nil {
			t.Fatal(err)
		}
		rb := make([]byte, ipv4.HeaderLen+128)
	loop:
		if err := r.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
			t.Fatal(err)
		}
		if _, b, _, err := r.ReadFrom(rb); err != nil {
			switch runtime.GOOS {
			case "darwin": // older darwin kernels have some limitation on receiving icmp packet through raw socket
				t.Logf("not supported on %s", runtime.GOOS)
				continue
			}
			t.Fatal(err)
		} else {
			m, err := icmp.ParseMessage(iana.ProtocolICMP, b)
			if err != nil {
				t.Fatal(err)
			}
			if runtime.GOOS == "linux" && m.Type == ipv4.ICMPTypeEcho {
				// On Linux we must handle own sent packets.
				goto loop
			}
			if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 {
				t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0)
			}
		}
	}
}