1
0
Fork 0
mirror of https://github.com/prometheus-operator/prometheus-operator.git synced 2025-04-16 09:16:38 +00:00

vendor: Update ant31/crd-validation

This allows us to specify our own version of dependencies that the
package relies on.
This commit is contained in:
Frederic Branczyk 2018-03-22 11:35:08 +01:00
parent df92241c26
commit c042bb3171
No known key found for this signature in database
GPG key ID: 7741A52782A90069
245 changed files with 5047 additions and 7575 deletions

30
Gopkg.lock generated
View file

@ -32,13 +32,13 @@
branch = "master" branch = "master"
name = "github.com/ant31/crd-validation" name = "github.com/ant31/crd-validation"
packages = ["pkg"] packages = ["pkg"]
revision = "a7560c85183cef6f2e9e912eaf4570bd368ea745" revision = "eabcf70a1bd73e9296fa0c5f57de604689200a1b"
[[projects]] [[projects]]
branch = "master" branch = "master"
name = "github.com/beorn7/perks" name = "github.com/beorn7/perks"
packages = ["quantile"] packages = ["quantile"]
revision = "4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9" revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
[[projects]] [[projects]]
name = "github.com/blang/semver" name = "github.com/blang/semver"
@ -252,8 +252,8 @@
[[projects]] [[projects]]
name = "github.com/json-iterator/go" name = "github.com/json-iterator/go"
packages = ["."] packages = ["."]
revision = "3353055b2a1a5ae1b6a8dfde887a524e7088f3a2" revision = "ca39e5af3ece67bbcda3d0f4f56a8e24d9f2dad4"
version = "1.1.2" version = "1.1.3"
[[projects]] [[projects]]
name = "github.com/juju/ratelimit" name = "github.com/juju/ratelimit"
@ -275,7 +275,7 @@
"jlexer", "jlexer",
"jwriter" "jwriter"
] ]
revision = "4a8a4c12c4d1b0c0a7de630426ce6dcb07141b17" revision = "517203d186eb343d3df4068565cc0446b450d2c4"
[[projects]] [[projects]]
name = "github.com/matttproud/golang_protobuf_extensions" name = "github.com/matttproud/golang_protobuf_extensions"
@ -348,7 +348,7 @@
"internal/bitbucket.org/ww/goautoneg", "internal/bitbucket.org/ww/goautoneg",
"model" "model"
] ]
revision = "6fb6fce6f8b75884b92e1889c150403fc0872c5e" revision = "e4aa40a9169a88835b849a6efb71e05dc04b88f0"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -359,7 +359,7 @@
"nfs", "nfs",
"xfs" "xfs"
] ]
revision = "1c7ff3de94ae006f58cba483a4c9c6d7c61e1d98" revision = "780932d4fbbe0e69b84c34c20f5c8d0981e109ea"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -380,7 +380,7 @@
branch = "master" branch = "master"
name = "golang.org/x/crypto" name = "golang.org/x/crypto"
packages = ["ssh/terminal"] packages = ["ssh/terminal"]
revision = "c7dcf104e3a7a1417abc0230cb0d5240d764159d" revision = "80db560fac1fb3e6ac81dbc7f8ae4c061f5257bd"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -392,13 +392,13 @@
"idna", "idna",
"lex/httplex" "lex/httplex"
] ]
revision = "d0aafc73d5cdc42264b0af071c261abac580695e" revision = "6078986fec03a1dcc236c34816c71b0e05018fda"
[[projects]] [[projects]]
branch = "master" branch = "master"
name = "golang.org/x/sync" name = "golang.org/x/sync"
packages = ["errgroup"] packages = ["errgroup"]
revision = "fd80eb99c8f653c847d294a001bdf2a3a6f768f5" revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -407,7 +407,7 @@
"unix", "unix",
"windows" "windows"
] ]
revision = "7dca6fe1f43775aa6d1334576870ff63f978f539" revision = "c488ab1dd8481ef762f96a79a9577c27825be697"
[[projects]] [[projects]]
name = "golang.org/x/text" name = "golang.org/x/text"
@ -439,7 +439,7 @@
"go/buildutil", "go/buildutil",
"go/loader" "go/loader"
] ]
revision = "059bec968c61383b574810040ba9410712de36c5" revision = "2226533658007779ffd629b495a088530c84dc50"
[[projects]] [[projects]]
name = "gopkg.in/alecthomas/kingpin.v2" name = "gopkg.in/alecthomas/kingpin.v2"
@ -495,7 +495,6 @@
version = "kubernetes-1.9.0" version = "kubernetes-1.9.0"
[[projects]] [[projects]]
branch = "master"
name = "k8s.io/apiextensions-apiserver" name = "k8s.io/apiextensions-apiserver"
packages = [ packages = [
"pkg/apis/apiextensions", "pkg/apis/apiextensions",
@ -504,7 +503,8 @@
"pkg/client/clientset/clientset/scheme", "pkg/client/clientset/clientset/scheme",
"pkg/client/clientset/clientset/typed/apiextensions/v1beta1" "pkg/client/clientset/clientset/typed/apiextensions/v1beta1"
] ]
revision = "f7df68b83f2a71fa37ed33c5057553c924b2277a" revision = "98ecf7bbd60f9f11a72000e4f05203f542136219"
version = "kubernetes-1.9.0"
[[projects]] [[projects]]
name = "k8s.io/apimachinery" name = "k8s.io/apimachinery"
@ -618,6 +618,6 @@
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
analyzer-version = 1 analyzer-version = 1
inputs-digest = "974708309f89f39c2d96ca1677337cc36a0cfdd4dbc2cbd69a47d870a992470b" inputs-digest = "c4e63707f13149a6995bc6ac18d596c29ed698f87459304225ec3fde16b32e00"
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1

View file

@ -41,8 +41,8 @@
version = "kubernetes-1.9.0" version = "kubernetes-1.9.0"
[[constraint]] [[constraint]]
branch = "master"
name = "k8s.io/apiextensions-apiserver" name = "k8s.io/apiextensions-apiserver"
version = "kubernetes-1.9.0"
[[constraint]] [[constraint]]
name = "k8s.io/apimachinery" name = "k8s.io/apimachinery"

View file

@ -1,191 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/PuerkitoBio/purell"
packages = ["."]
revision = "0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4"
version = "v1.1.0"
[[projects]]
branch = "master"
name = "github.com/PuerkitoBio/urlesc"
packages = ["."]
revision = "de5bf2ad457846296e2031421a34e2568e304e35"
[[projects]]
name = "github.com/emicklei/go-restful"
packages = [
".",
"log"
]
revision = "2dd44038f0b95ae693b266c5f87593b5d2fdd78d"
version = "v2.5.0"
[[projects]]
name = "github.com/ghodss/yaml"
packages = ["."]
revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7"
version = "v1.0.0"
[[projects]]
branch = "master"
name = "github.com/go-openapi/jsonpointer"
packages = ["."]
revision = "779f45308c19820f1a69e9a4cd965f496e0da10f"
[[projects]]
branch = "master"
name = "github.com/go-openapi/jsonreference"
packages = ["."]
revision = "36d33bfe519efae5632669801b180bf1a245da3b"
[[projects]]
branch = "master"
name = "github.com/go-openapi/spec"
packages = ["."]
revision = "fa03337d7da5735229ee8f5e9d5d0b996014b7f8"
[[projects]]
branch = "master"
name = "github.com/go-openapi/swag"
packages = ["."]
revision = "84f4bee7c0a6db40e3166044c7983c1c32125429"
[[projects]]
name = "github.com/gogo/protobuf"
packages = [
"proto",
"sortkeys"
]
revision = "342cbe0a04158f6dcb03ca0079991a51a4248c02"
version = "v0.5"
[[projects]]
branch = "master"
name = "github.com/golang/glog"
packages = ["."]
revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998"
[[projects]]
branch = "master"
name = "github.com/google/gofuzz"
packages = ["."]
revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1"
[[projects]]
branch = "master"
name = "github.com/mailru/easyjson"
packages = [
"buffer",
"jlexer",
"jwriter"
]
revision = "32fa128f234d041f196a9f3e0fea5ac9772c08e1"
[[projects]]
name = "github.com/spf13/pflag"
packages = ["."]
revision = "e57e3eeb33f795204c1ca35f56c44f83227c6e66"
version = "v1.0.0"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = [
"http2",
"http2/hpack",
"idna",
"lex/httplex"
]
revision = "0ed95abb35c445290478a5348a7b38bb154135fd"
[[projects]]
branch = "master"
name = "golang.org/x/text"
packages = [
"collate",
"collate/build",
"internal/colltab",
"internal/gen",
"internal/tag",
"internal/triegen",
"internal/ucd",
"language",
"secure/bidirule",
"transform",
"unicode/bidi",
"unicode/cldr",
"unicode/norm",
"unicode/rangetable",
"width"
]
revision = "e19ae1496984b1c655b8044a65c0300a3c878dd3"
[[projects]]
name = "gopkg.in/inf.v0"
packages = ["."]
revision = "3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4"
version = "v0.9.0"
[[projects]]
branch = "v2"
name = "gopkg.in/yaml.v2"
packages = ["."]
revision = "d670f9405373e636a5a2765eea47fac0c9bc91a4"
[[projects]]
branch = "master"
name = "k8s.io/api"
packages = ["core/v1"]
revision = "dc0dd48d5a5cae9f8736bb0643cfe6052e450f1b"
[[projects]]
branch = "master"
name = "k8s.io/apiextensions-apiserver"
packages = [
"pkg/apis/apiextensions",
"pkg/apis/apiextensions/v1beta1"
]
revision = "ddd9f73609e9a03a76dc8566ba6296db0cda63d3"
[[projects]]
branch = "master"
name = "k8s.io/apimachinery"
packages = [
"pkg/api/resource",
"pkg/apis/meta/v1",
"pkg/conversion",
"pkg/conversion/queryparams",
"pkg/fields",
"pkg/labels",
"pkg/runtime",
"pkg/runtime/schema",
"pkg/selection",
"pkg/types",
"pkg/util/errors",
"pkg/util/intstr",
"pkg/util/json",
"pkg/util/net",
"pkg/util/runtime",
"pkg/util/sets",
"pkg/util/validation",
"pkg/util/validation/field",
"pkg/util/wait",
"pkg/watch",
"third_party/forked/golang/reflect"
]
revision = "b621949a1923cee3fce8bca9613e9a83609f0bbc"
[[projects]]
branch = "master"
name = "k8s.io/kube-openapi"
packages = ["pkg/common"]
revision = "a07b7bbb58e7fdc5144f8d7046331d29fc9ad3b3"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "9af3a0bdc9fb071b8a24e4c2320f53398bf7db3bd96b1933811a214e5be8ca3d"
solver-name = "gps-cdcl"
solver-version = 1

View file

@ -1,33 +0,0 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
[[constraint]]
branch = "master"
name = "github.com/go-openapi/spec"
[[constraint]]
branch = "master"
name = "k8s.io/apiextensions-apiserver"
[[constraint]]
branch = "master"
name = "k8s.io/kube-openapi"

View file

@ -77,15 +77,20 @@ func NewHighBiased(epsilon float64) *Stream {
// is guaranteed to be within (Quantile±Epsilon). // is guaranteed to be within (Quantile±Epsilon).
// //
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties. // See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
func NewTargeted(targets map[float64]float64) *Stream { func NewTargeted(targetMap map[float64]float64) *Stream {
// Convert map to slice to avoid slow iterations on a map.
// ƒ is called on the hot path, so converting the map to a slice
// beforehand results in significant CPU savings.
targets := targetMapToSlice(targetMap)
ƒ := func(s *stream, r float64) float64 { ƒ := func(s *stream, r float64) float64 {
var m = math.MaxFloat64 var m = math.MaxFloat64
var f float64 var f float64
for quantile, epsilon := range targets { for _, t := range targets {
if quantile*s.n <= r { if t.quantile*s.n <= r {
f = (2 * epsilon * r) / quantile f = (2 * t.epsilon * r) / t.quantile
} else { } else {
f = (2 * epsilon * (s.n - r)) / (1 - quantile) f = (2 * t.epsilon * (s.n - r)) / (1 - t.quantile)
} }
if f < m { if f < m {
m = f m = f
@ -96,6 +101,25 @@ func NewTargeted(targets map[float64]float64) *Stream {
return newStream(ƒ) return newStream(ƒ)
} }
type target struct {
quantile float64
epsilon float64
}
func targetMapToSlice(targetMap map[float64]float64) []target {
targets := make([]target, 0, len(targetMap))
for quantile, epsilon := range targetMap {
t := target{
quantile: quantile,
epsilon: epsilon,
}
targets = append(targets, t)
}
return targets
}
// Stream computes quantiles for a stream of float64s. It is not thread-safe by // Stream computes quantiles for a stream of float64s. It is not thread-safe by
// design. Take care when using across multiple goroutines. // design. Take care when using across multiple goroutines.
type Stream struct { type Stream struct {

View file

@ -2,11 +2,12 @@ package jsoniter
import ( import (
"encoding/json" "encoding/json"
"github.com/modern-go/concurrent"
"github.com/modern-go/reflect2" "github.com/modern-go/reflect2"
"io" "io"
"reflect"
"sync" "sync"
"unsafe" "unsafe"
"github.com/modern-go/concurrent"
) )
// Config customize how the API should behave. // Config customize how the API should behave.
@ -39,6 +40,8 @@ type API interface {
NewDecoder(reader io.Reader) *Decoder NewDecoder(reader io.Reader) *Decoder
Valid(data []byte) bool Valid(data []byte) bool
RegisterExtension(extension Extension) RegisterExtension(extension Extension)
DecoderOf(typ reflect2.Type) ValDecoder
EncoderOf(typ reflect2.Type) ValEncoder
} }
// ConfigDefault the default API // ConfigDefault the default API
@ -60,7 +63,6 @@ var ConfigFastest = Config{
ObjectFieldMustBeSimpleString: true, // do not unescape object field ObjectFieldMustBeSimpleString: true, // do not unescape object field
}.Froze() }.Froze()
type frozenConfig struct { type frozenConfig struct {
configBeforeFrozen Config configBeforeFrozen Config
sortMapKeys bool sortMapKeys bool
@ -104,7 +106,7 @@ func (cfg *frozenConfig) getEncoderFromCache(cacheKey uintptr) ValEncoder {
return nil return nil
} }
var cfgCache = &sync.Map{} var cfgCache = concurrent.NewMap()
func getFrozenConfigFromCache(cfg Config) *frozenConfig { func getFrozenConfigFromCache(cfg Config) *frozenConfig {
obj, found := cfgCache.Load(cfg) obj, found := cfgCache.Load(cfg)
@ -192,6 +194,11 @@ func (cfg *frozenConfig) validateJsonRawMessage(extension EncoderExtension) {
func (cfg *frozenConfig) useNumber(extension DecoderExtension) { func (cfg *frozenConfig) useNumber(extension DecoderExtension) {
extension[reflect2.TypeOfPtr((*interface{})(nil)).Elem()] = &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) { extension[reflect2.TypeOfPtr((*interface{})(nil)).Elem()] = &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) {
exitingValue := *((*interface{})(ptr))
if exitingValue != nil && reflect.TypeOf(exitingValue).Kind() == reflect.Ptr {
iter.ReadVal(exitingValue)
return
}
if iter.WhatIsNext() == NumberValue { if iter.WhatIsNext() == NumberValue {
*((*interface{})(ptr)) = json.Number(iter.readNumberAsString()) *((*interface{})(ptr)) = json.Number(iter.readNumberAsString())
} else { } else {

View file

@ -2,8 +2,8 @@ package test
import ( import (
"github.com/json-iterator/go" "github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"github.com/modern-go/reflect2" "github.com/modern-go/reflect2"
"github.com/stretchr/testify/require"
"reflect" "reflect"
"strconv" "strconv"
"testing" "testing"

View file

@ -2,9 +2,9 @@ package extra
import ( import (
"github.com/json-iterator/go" "github.com/json-iterator/go"
"unsafe"
"unicode/utf8"
"github.com/modern-go/reflect2" "github.com/modern-go/reflect2"
"unicode/utf8"
"unsafe"
) )
// safeSet holds the value true if the ASCII character with the given array // safeSet holds the value true if the ASCII character with the given array
@ -171,18 +171,18 @@ func (codec *binaryAsStringCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.St
func readHex(iter *jsoniter.Iterator, b1, b2 byte) byte { func readHex(iter *jsoniter.Iterator, b1, b2 byte) byte {
var ret byte var ret byte
if b1 >= '0' && b1 <= '9' { if b1 >= '0' && b1 <= '9' {
ret = b1-'0' ret = b1 - '0'
} else if b1 >= 'a' && b1 <= 'f' { } else if b1 >= 'a' && b1 <= 'f' {
ret = b1-'a'+10 ret = b1 - 'a' + 10
} else { } else {
iter.ReportError("read hex", "expects 0~9 or a~f, but found "+string([]byte{b1})) iter.ReportError("read hex", "expects 0~9 or a~f, but found "+string([]byte{b1}))
return 0 return 0
} }
ret = ret * 16 ret = ret * 16
if b2 >= '0' && b2 <= '9' { if b2 >= '0' && b2 <= '9' {
ret = b2-'0' ret = b2 - '0'
} else if b2 >= 'a' && b2 <= 'f' { } else if b2 >= 'a' && b2 <= 'f' {
ret = b2-'a'+10 ret = b2 - 'a' + 10
} else { } else {
iter.ReportError("read hex", "expects 0~9 or a~f, but found "+string([]byte{b2})) iter.ReportError("read hex", "expects 0~9 or a~f, but found "+string([]byte{b2}))
return 0 return 0

View file

@ -1,9 +1,9 @@
package extra package extra
import ( import (
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go" "github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"testing"
) )
func init() { func init() {

View file

@ -31,3 +31,14 @@ func Test_read_map_with_reader(t *testing.T) {
should.Equal(m2, m1) should.Equal(m2, m1)
should.Equal("1.0.76", m1["note"].(map[string]interface{})["CoreServices"].(map[string]interface{})["version_name"]) should.Equal("1.0.76", m1["note"].(map[string]interface{})["CoreServices"].(map[string]interface{})["version_name"])
} }
func Test_map_eface_of_eface(t *testing.T) {
should := require.New(t)
json := jsoniter.ConfigCompatibleWithStandardLibrary
output, err := json.MarshalToString(map[interface{}]interface{}{
"1": 2,
3: "4",
})
should.NoError(err)
should.Equal(`{"1":2,"3":"4"}`, output)
}

View file

@ -130,3 +130,20 @@ func Test_reader_and_load_more(t *testing.T) {
obj := TestObject{} obj := TestObject{}
should.Nil(decoder.Decode(&obj)) should.Nil(decoder.Decode(&obj))
} }
func Test_unmarshal_into_existing_value(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 int
Field2 interface{}
}
var obj TestObject
m := map[string]interface{}{}
obj.Field2 = &m
cfg := jsoniter.Config{UseNumber: true}.Froze()
err := cfg.Unmarshal([]byte(`{"Field1":1,"Field2":{"k":"v"}}`), &obj)
should.NoError(err)
should.Equal(map[string]interface{}{
"k": "v",
}, m)
}

View file

@ -3,6 +3,7 @@ package jsoniter
import ( import (
"fmt" "fmt"
"github.com/modern-go/reflect2" "github.com/modern-go/reflect2"
"io"
"reflect" "reflect"
"sort" "sort"
"unsafe" "unsafe"
@ -107,6 +108,9 @@ func encoderOfMapKey(ctx *ctx, typ reflect2.Type) ValEncoder {
stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")),
} }
} }
if typ.Kind() == reflect.Interface {
return &dynamicMapKeyEncoder{ctx, typ}
}
return &lazyErrorEncoder{err: fmt.Errorf("unsupported map key type: %v", typ)} return &lazyErrorEncoder{err: fmt.Errorf("unsupported map key type: %v", typ)}
} }
} }
@ -203,6 +207,21 @@ func (encoder *numericMapKeyEncoder) IsEmpty(ptr unsafe.Pointer) bool {
return false return false
} }
type dynamicMapKeyEncoder struct {
ctx *ctx
valType reflect2.Type
}
func (encoder *dynamicMapKeyEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
obj := encoder.valType.UnsafeIndirect(ptr)
encoderOfMapKey(encoder.ctx, reflect2.TypeOf(obj)).Encode(reflect2.PtrOf(obj), stream)
}
func (encoder *dynamicMapKeyEncoder) IsEmpty(ptr unsafe.Pointer) bool {
obj := encoder.valType.UnsafeIndirect(ptr)
return encoderOfMapKey(encoder.ctx, reflect2.TypeOf(obj)).IsEmpty(reflect2.PtrOf(obj))
}
type mapEncoder struct { type mapEncoder struct {
mapType *reflect2.UnsafeMapType mapType *reflect2.UnsafeMapType
keyEncoder ValEncoder keyEncoder ValEncoder
@ -253,6 +272,9 @@ func (encoder *sortKeysMapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
subStream.buf = make([]byte, 0, 64) subStream.buf = make([]byte, 0, 64)
key, elem := mapIter.UnsafeNext() key, elem := mapIter.UnsafeNext()
encoder.keyEncoder.Encode(key, subStream) encoder.keyEncoder.Encode(key, subStream)
if subStream.Error != nil && subStream.Error != io.EOF && stream.Error == nil {
stream.Error = subStream.Error
}
encodedKey := subStream.Buffer() encodedKey := subStream.Buffer()
subIter.ResetBytes(encodedKey) subIter.ResetBytes(encodedKey)
decodedKey := subIter.ReadString() decodedKey := subIter.ReadString()

View file

@ -6,7 +6,7 @@ func init() {
[]interface{}{"hello"}, []interface{}{"hello"},
nilSlice, nilSlice,
&nilSlice, &nilSlice,
selectedMarshalCase{[]byte{1,2,3}}, []byte{1, 2, 3},
) )
unmarshalCases = append(unmarshalCases, unmarshalCase{ unmarshalCases = append(unmarshalCases, unmarshalCase{
ptr: (*[]string)(nil), ptr: (*[]string)(nil),
@ -20,6 +20,5 @@ func init() {
}, unmarshalCase{ }, unmarshalCase{
ptr: (*[]byte)(nil), ptr: (*[]byte)(nil),
input: `"aGVsbG8="`, input: `"aGVsbG8="`,
selected: true,
}) })
} }

View file

@ -4,8 +4,8 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/json-iterator/go" "github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"github.com/modern-go/reflect2" "github.com/modern-go/reflect2"
"github.com/stretchr/testify/require"
"testing" "testing"
) )

View file

@ -48,10 +48,12 @@ var primitiveStringDecoders = map[reflect.Kind]string{
reflect.Uint32: "in.Uint32Str()", reflect.Uint32: "in.Uint32Str()",
reflect.Uint64: "in.Uint64Str()", reflect.Uint64: "in.Uint64Str()",
reflect.Uintptr: "in.UintptrStr()", reflect.Uintptr: "in.UintptrStr()",
reflect.Float32: "in.Float32Str()",
reflect.Float64: "in.Float64Str()",
} }
var customDecoders = map[string]string{ var customDecoders = map[string]string{
"json.Number": "in.JsonNumber()", "json.Number": "in.JsonNumber()",
} }
// genTypeDecoder generates decoding code for the type t, but uses unmarshaler interface if implemented by t. // genTypeDecoder generates decoding code for the type t, but uses unmarshaler interface if implemented by t.
@ -96,7 +98,7 @@ func hasCustomUnmarshaler(t reflect.Type) bool {
func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags fieldTags, indent int) error { func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags fieldTags, indent int) error {
ws := strings.Repeat(" ", indent) ws := strings.Repeat(" ", indent)
// Check whether type is primitive, needs to be done after interface check. // Check whether type is primitive, needs to be done after interface check.
if dec := customDecoders[t.String()]; dec != "" { if dec := customDecoders[t.String()]; dec != "" {
fmt.Fprintln(g.out, ws+out+" = "+dec) fmt.Fprintln(g.out, ws+out+" = "+dec)
return nil return nil
} else if dec := primitiveStringDecoders[t.Kind()]; dec != "" && tags.asString { } else if dec := primitiveStringDecoders[t.Kind()]; dec != "" && tags.asString {
@ -178,7 +180,7 @@ func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags field
fmt.Fprintln(g.out, ws+" for !in.IsDelim(']') {") fmt.Fprintln(g.out, ws+" for !in.IsDelim(']') {")
fmt.Fprintln(g.out, ws+" if "+iterVar+" < "+fmt.Sprint(length)+" {") fmt.Fprintln(g.out, ws+" if "+iterVar+" < "+fmt.Sprint(length)+" {")
if err := g.genTypeDecoder(elem, out+"["+iterVar+"]", tags, indent+3); err != nil { if err := g.genTypeDecoder(elem, "("+out+")["+iterVar+"]", tags, indent+3); err != nil {
return err return err
} }

View file

@ -45,6 +45,8 @@ var primitiveStringEncoders = map[reflect.Kind]string{
reflect.Uint32: "out.Uint32Str(uint32(%v))", reflect.Uint32: "out.Uint32Str(uint32(%v))",
reflect.Uint64: "out.Uint64Str(uint64(%v))", reflect.Uint64: "out.Uint64Str(uint64(%v))",
reflect.Uintptr: "out.UintptrStr(uintptr(%v))", reflect.Uintptr: "out.UintptrStr(uintptr(%v))",
reflect.Float32: "out.Float32Str(float32(%v))",
reflect.Float64: "out.Float64Str(float64(%v))",
} }
// fieldTags contains parsed version of json struct field tags. // fieldTags contains parsed version of json struct field tags.
@ -173,7 +175,7 @@ func (g *Generator) genTypeEncoderNoCheck(t reflect.Type, in string, tags fieldT
fmt.Fprintln(g.out, ws+" out.RawByte(',')") fmt.Fprintln(g.out, ws+" out.RawByte(',')")
fmt.Fprintln(g.out, ws+" }") fmt.Fprintln(g.out, ws+" }")
if err := g.genTypeEncoder(elem, in+"["+iVar+"]", tags, indent+1, false); err != nil { if err := g.genTypeEncoder(elem, "("+in+")["+iVar+"]", tags, indent+1, false); err != nil {
return err return err
} }

View file

@ -997,6 +997,22 @@ func (r *Lexer) Float32() float32 {
return float32(n) return float32(n)
} }
func (r *Lexer) Float32Str() float32 {
s, b := r.unsafeString()
if !r.Ok() {
return 0
}
n, err := strconv.ParseFloat(s, 32)
if err != nil {
r.addNonfatalError(&LexerError{
Offset: r.start,
Reason: err.Error(),
Data: string(b),
})
}
return float32(n)
}
func (r *Lexer) Float64() float64 { func (r *Lexer) Float64() float64 {
s := r.number() s := r.number()
if !r.Ok() { if !r.Ok() {
@ -1014,6 +1030,22 @@ func (r *Lexer) Float64() float64 {
return n return n
} }
func (r *Lexer) Float64Str() float64 {
s, b := r.unsafeString()
if !r.Ok() {
return 0
}
n, err := strconv.ParseFloat(s, 64)
if err != nil {
r.addNonfatalError(&LexerError{
Offset: r.start,
Reason: err.Error(),
Data: string(b),
})
}
return n
}
func (r *Lexer) Error() error { func (r *Lexer) Error() error {
return r.fatalError return r.fatalError
} }

View file

@ -240,11 +240,25 @@ func (w *Writer) Float32(n float32) {
w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32) w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32)
} }
func (w *Writer) Float32Str(n float32) {
w.Buffer.EnsureSpace(20)
w.Buffer.Buf = append(w.Buffer.Buf, '"')
w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32)
w.Buffer.Buf = append(w.Buffer.Buf, '"')
}
func (w *Writer) Float64(n float64) { func (w *Writer) Float64(n float64) {
w.Buffer.EnsureSpace(20) w.Buffer.EnsureSpace(20)
w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, n, 'g', -1, 64) w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, n, 'g', -1, 64)
} }
func (w *Writer) Float64Str(n float64) {
w.Buffer.EnsureSpace(20)
w.Buffer.Buf = append(w.Buffer.Buf, '"')
w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 64)
w.Buffer.Buf = append(w.Buffer.Buf, '"')
}
func (w *Writer) Bool(v bool) { func (w *Writer) Bool(v bool) {
w.Buffer.EnsureSpace(5) w.Buffer.EnsureSpace(5)
if v { if v {
@ -340,12 +354,11 @@ func (w *Writer) base64(in []byte) {
return return
} }
w.Buffer.EnsureSpace(((len(in) - 1) / 3 + 1) * 4) w.Buffer.EnsureSpace(((len(in)-1)/3 + 1) * 4)
si := 0 si := 0
n := (len(in) / 3) * 3 n := (len(in) / 3) * 3
for si < n { for si < n {
// Convert 3x 8bit source bytes into 4 bytes // Convert 3x 8bit source bytes into 4 bytes
val := uint(in[si+0])<<16 | uint(in[si+1])<<8 | uint(in[si+2]) val := uint(in[si+0])<<16 | uint(in[si+1])<<8 | uint(in[si+2])

View file

@ -49,6 +49,7 @@ var testCases = []struct {
{&mapUint64StringValue, mapUint64StringValueString}, {&mapUint64StringValue, mapUint64StringValueString},
{&mapUintptrStringValue, mapUintptrStringValueString}, {&mapUintptrStringValue, mapUintptrStringValueString},
{&intKeyedMapStructValue, intKeyedMapStructValueString}, {&intKeyedMapStructValue, intKeyedMapStructValueString},
{&intArrayStructValue, intArrayStructValueString},
} }
func TestMarshal(t *testing.T) { func TestMarshal(t *testing.T) {

View file

@ -41,6 +41,9 @@ type PrimitiveTypes struct {
Float32 float32 Float32 float32
Float64 float64 Float64 float64
Float32String float32 `json:",string"`
Float64String float64 `json:",string"`
Ptr *string Ptr *string
PtrNil *string PtrNil *string
} }
@ -77,6 +80,9 @@ var primitiveTypesValue = PrimitiveTypes{
Float32: 1.5, Float32: 1.5,
Float64: math.MaxFloat64, Float64: math.MaxFloat64,
Float32String: 1.5,
Float64String: math.MaxFloat64,
Ptr: &str, Ptr: &str,
} }
@ -110,6 +116,9 @@ var primitiveTypesString = "{" +
`"Float32":` + fmt.Sprint(1.5) + `,` + `"Float32":` + fmt.Sprint(1.5) + `,` +
`"Float64":` + fmt.Sprint(math.MaxFloat64) + `,` + `"Float64":` + fmt.Sprint(math.MaxFloat64) + `,` +
`"Float32String":"` + fmt.Sprint(1.5) + `",` +
`"Float64String":"` + fmt.Sprint(math.MaxFloat64) + `",` +
`"Ptr":"bla",` + `"Ptr":"bla",` +
`"PtrNil":null` + `"PtrNil":null` +
@ -757,3 +766,21 @@ var intKeyedMapStructValueString = `{` +
`"foo":{"42":"life"},` + `"foo":{"42":"life"},` +
`"bar":{"32":{"354634382":"life"}}` + `"bar":{"32":{"354634382":"life"}}` +
`}` `}`
type IntArray [2]int
//easyjson:json
type IntArrayStruct struct {
Pointer *IntArray `json:"pointer"`
Value IntArray `json:"value"`
}
var intArrayStructValue = IntArrayStruct{
Pointer: &IntArray{1, 2},
Value: IntArray{1, 2},
}
var intArrayStructValueString = `{` +
`"pointer":[1,2],` +
`"value":[1,2]` +
`}`

View file

@ -19,11 +19,12 @@ func WithParam(ctx context.Context, p, v string) context.Context {
return context.WithValue(ctx, param(p), v) return context.WithValue(ctx, param(p), v)
} }
// Router wraps httprouter.Router and adds support for prefixed sub-routers // Router wraps httprouter.Router and adds support for prefixed sub-routers,
// and per-request context injections. // per-request context injections and instrumentation.
type Router struct { type Router struct {
rtr *httprouter.Router rtr *httprouter.Router
prefix string prefix string
instrh func(handlerName string, handler http.HandlerFunc) http.HandlerFunc
} }
// New returns a new Router. // New returns a new Router.
@ -33,13 +34,18 @@ func New() *Router {
} }
} }
// WithInstrumentation returns a router with instrumentation support.
func (r *Router) WithInstrumentation(instrh func(handlerName string, handler http.HandlerFunc) http.HandlerFunc) *Router {
return &Router{rtr: r.rtr, prefix: r.prefix, instrh: instrh}
}
// WithPrefix returns a router that prefixes all registered routes with prefix. // WithPrefix returns a router that prefixes all registered routes with prefix.
func (r *Router) WithPrefix(prefix string) *Router { func (r *Router) WithPrefix(prefix string) *Router {
return &Router{rtr: r.rtr, prefix: r.prefix + prefix} return &Router{rtr: r.rtr, prefix: r.prefix + prefix, instrh: r.instrh}
} }
// handle turns a HandlerFunc into an httprouter.Handle. // handle turns a HandlerFunc into an httprouter.Handle.
func (r *Router) handle(h http.HandlerFunc) httprouter.Handle { func (r *Router) handle(handlerName string, h http.HandlerFunc) httprouter.Handle {
return func(w http.ResponseWriter, req *http.Request, params httprouter.Params) { return func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
ctx, cancel := context.WithCancel(req.Context()) ctx, cancel := context.WithCancel(req.Context())
defer cancel() defer cancel()
@ -47,33 +53,36 @@ func (r *Router) handle(h http.HandlerFunc) httprouter.Handle {
for _, p := range params { for _, p := range params {
ctx = context.WithValue(ctx, param(p.Key), p.Value) ctx = context.WithValue(ctx, param(p.Key), p.Value)
} }
if r.instrh != nil {
h = r.instrh(handlerName, h)
}
h(w, req.WithContext(ctx)) h(w, req.WithContext(ctx))
} }
} }
// Get registers a new GET route. // Get registers a new GET route.
func (r *Router) Get(path string, h http.HandlerFunc) { func (r *Router) Get(path string, h http.HandlerFunc) {
r.rtr.GET(r.prefix+path, r.handle(h)) r.rtr.GET(r.prefix+path, r.handle(path, h))
} }
// Options registers a new OPTIONS route. // Options registers a new OPTIONS route.
func (r *Router) Options(path string, h http.HandlerFunc) { func (r *Router) Options(path string, h http.HandlerFunc) {
r.rtr.OPTIONS(r.prefix+path, r.handle(h)) r.rtr.OPTIONS(r.prefix+path, r.handle(path, h))
} }
// Del registers a new DELETE route. // Del registers a new DELETE route.
func (r *Router) Del(path string, h http.HandlerFunc) { func (r *Router) Del(path string, h http.HandlerFunc) {
r.rtr.DELETE(r.prefix+path, r.handle(h)) r.rtr.DELETE(r.prefix+path, r.handle(path, h))
} }
// Put registers a new PUT route. // Put registers a new PUT route.
func (r *Router) Put(path string, h http.HandlerFunc) { func (r *Router) Put(path string, h http.HandlerFunc) {
r.rtr.PUT(r.prefix+path, r.handle(h)) r.rtr.PUT(r.prefix+path, r.handle(path, h))
} }
// Post registers a new POST route. // Post registers a new POST route.
func (r *Router) Post(path string, h http.HandlerFunc) { func (r *Router) Post(path string, h http.HandlerFunc) {
r.rtr.POST(r.prefix+path, r.handle(h)) r.rtr.POST(r.prefix+path, r.handle(path, h))
} }
// Redirect takes an absolute path and sends an internal HTTP redirect for it, // Redirect takes an absolute path and sends an internal HTTP redirect for it,

View file

@ -42,3 +42,35 @@ func TestContext(t *testing.T) {
} }
router.ServeHTTP(nil, r) router.ServeHTTP(nil, r)
} }
func TestInstrumentation(t *testing.T) {
var got string
cases := []struct {
router *Router
want string
}{
{
router: New(),
want: "",
}, {
router: New().WithInstrumentation(func(handlerName string, handler http.HandlerFunc) http.HandlerFunc {
got = handlerName
return handler
}),
want: "/foo",
},
}
for _, c := range cases {
c.router.Get("/foo", func(w http.ResponseWriter, r *http.Request) {})
r, err := http.NewRequest("GET", "http://localhost:9090/foo", nil)
if err != nil {
t.Fatalf("Error building test request: %s", err)
}
c.router.ServeHTTP(nil, r)
if c.want != got {
t.Fatalf("Unexpected value: want %q, got %q", c.want, got)
}
}
}

View file

@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// Package nfsd implements parsing of /proc/net/rpc/nfsd. // Package nfs implements parsing of /proc/net/rpc/nfsd.
// Fields are documented in https://www.svennd.be/nfsd-stats-explained-procnetrpcnfsd/ // Fields are documented in https://www.svennd.be/nfsd-stats-explained-procnetrpcnfsd/
package nfs package nfs
@ -136,8 +136,8 @@ type ClientV4Stats struct {
Setattr uint64 Setattr uint64
FsInfo uint64 FsInfo uint64
Renew uint64 Renew uint64
SetClientId uint64 SetClientID uint64
SetClientIdConfirm uint64 SetClientIDConfirm uint64
Lock uint64 Lock uint64
Lockt uint64 Lockt uint64
Locku uint64 Locku uint64
@ -156,13 +156,13 @@ type ClientV4Stats struct {
ReadDir uint64 ReadDir uint64
ServerCaps uint64 ServerCaps uint64
DelegReturn uint64 DelegReturn uint64
GetAcl uint64 GetACL uint64
SetAcl uint64 SetACL uint64
FsLocations uint64 FsLocations uint64
ReleaseLockowner uint64 ReleaseLockowner uint64
Secinfo uint64 Secinfo uint64
FsidPresent uint64 FsidPresent uint64
ExchangeId uint64 ExchangeID uint64
CreateSession uint64 CreateSession uint64
DestroySession uint64 DestroySession uint64
Sequence uint64 Sequence uint64
@ -173,11 +173,11 @@ type ClientV4Stats struct {
LayoutCommit uint64 LayoutCommit uint64
LayoutReturn uint64 LayoutReturn uint64
SecinfoNoName uint64 SecinfoNoName uint64
TestStateId uint64 TestStateID uint64
FreeStateId uint64 FreeStateID uint64
GetDeviceList uint64 GetDeviceList uint64
BindConnToSession uint64 BindConnToSession uint64
DestroyClientId uint64 DestroyClientID uint64
Seek uint64 Seek uint64
Allocate uint64 Allocate uint64
DeAllocate uint64 DeAllocate uint64
@ -238,7 +238,7 @@ type V4Ops struct {
RelLockOwner uint64 RelLockOwner uint64
} }
// RPCStats models all stats from /proc/net/rpc/nfs. // ClientRPCStats models all stats from /proc/net/rpc/nfs.
type ClientRPCStats struct { type ClientRPCStats struct {
Network Network Network Network
ClientRPC ClientRPC ClientRPC ClientRPC

View file

@ -204,8 +204,8 @@ func parseClientV4Stats(v []uint64) (ClientV4Stats, error) {
Setattr: v[10], Setattr: v[10],
FsInfo: v[11], FsInfo: v[11],
Renew: v[12], Renew: v[12],
SetClientId: v[13], SetClientID: v[13],
SetClientIdConfirm: v[14], SetClientIDConfirm: v[14],
Lock: v[15], Lock: v[15],
Lockt: v[16], Lockt: v[16],
Locku: v[17], Locku: v[17],
@ -224,13 +224,13 @@ func parseClientV4Stats(v []uint64) (ClientV4Stats, error) {
ReadDir: v[30], ReadDir: v[30],
ServerCaps: v[31], ServerCaps: v[31],
DelegReturn: v[32], DelegReturn: v[32],
GetAcl: v[33], GetACL: v[33],
SetAcl: v[34], SetACL: v[34],
FsLocations: v[35], FsLocations: v[35],
ReleaseLockowner: v[36], ReleaseLockowner: v[36],
Secinfo: v[37], Secinfo: v[37],
FsidPresent: v[38], FsidPresent: v[38],
ExchangeId: v[39], ExchangeID: v[39],
CreateSession: v[40], CreateSession: v[40],
DestroySession: v[41], DestroySession: v[41],
Sequence: v[42], Sequence: v[42],
@ -241,11 +241,11 @@ func parseClientV4Stats(v []uint64) (ClientV4Stats, error) {
LayoutCommit: v[47], LayoutCommit: v[47],
LayoutReturn: v[48], LayoutReturn: v[48],
SecinfoNoName: v[49], SecinfoNoName: v[49],
TestStateId: v[50], TestStateID: v[50],
FreeStateId: v[51], FreeStateID: v[51],
GetDeviceList: v[52], GetDeviceList: v[52],
BindConnToSession: v[53], BindConnToSession: v[53],
DestroyClientId: v[54], DestroyClientID: v[54],
Seek: v[55], Seek: v[55],
Allocate: v[56], Allocate: v[56],
DeAllocate: v[57], DeAllocate: v[57],

View file

@ -108,8 +108,8 @@ proc4 48 98 51 54 83 85 23 24 1 28 73 68 83 12 84 39 68 59 58 88 29 74 69 96 21
Setattr: 73, Setattr: 73,
FsInfo: 68, FsInfo: 68,
Renew: 83, Renew: 83,
SetClientId: 12, SetClientID: 12,
SetClientIdConfirm: 84, SetClientIDConfirm: 84,
Lock: 39, Lock: 39,
Lockt: 68, Lockt: 68,
Locku: 59, Locku: 59,
@ -128,13 +128,13 @@ proc4 48 98 51 54 83 85 23 24 1 28 73 68 83 12 84 39 68 59 58 88 29 74 69 96 21
ReadDir: 66, ReadDir: 66,
ServerCaps: 56, ServerCaps: 56,
DelegReturn: 97, DelegReturn: 97,
GetAcl: 36, GetACL: 36,
SetAcl: 49, SetACL: 49,
FsLocations: 32, FsLocations: 32,
ReleaseLockowner: 85, ReleaseLockowner: 85,
Secinfo: 81, Secinfo: 81,
FsidPresent: 11, FsidPresent: 11,
ExchangeId: 58, ExchangeID: 58,
CreateSession: 32, CreateSession: 32,
DestroySession: 67, DestroySession: 67,
Sequence: 13, Sequence: 13,
@ -145,11 +145,11 @@ proc4 48 98 51 54 83 85 23 24 1 28 73 68 83 12 84 39 68 59 58 88 29 74 69 96 21
LayoutCommit: 26, LayoutCommit: 26,
LayoutReturn: 1337, LayoutReturn: 1337,
SecinfoNoName: 0, SecinfoNoName: 0,
TestStateId: 0, TestStateID: 0,
FreeStateId: 0, FreeStateID: 0,
GetDeviceList: 0, GetDeviceList: 0,
BindConnToSession: 0, BindConnToSession: 0,
DestroyClientId: 0, DestroyClientID: 0,
Seek: 0, Seek: 0,
Allocate: 0, Allocate: 0,
DeAllocate: 0, DeAllocate: 0,
@ -234,8 +234,8 @@ proc4 61 1 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Setattr: 0, Setattr: 0,
FsInfo: 0, FsInfo: 0,
Renew: 0, Renew: 0,
SetClientId: 1, SetClientID: 1,
SetClientIdConfirm: 1, SetClientIDConfirm: 1,
Lock: 0, Lock: 0,
Lockt: 0, Lockt: 0,
Locku: 0, Locku: 0,
@ -254,13 +254,13 @@ proc4 61 1 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0
ReadDir: 0, ReadDir: 0,
ServerCaps: 0, ServerCaps: 0,
DelegReturn: 0, DelegReturn: 0,
GetAcl: 0, GetACL: 0,
SetAcl: 0, SetACL: 0,
FsLocations: 0, FsLocations: 0,
ReleaseLockowner: 0, ReleaseLockowner: 0,
Secinfo: 0, Secinfo: 0,
FsidPresent: 0, FsidPresent: 0,
ExchangeId: 0, ExchangeID: 0,
CreateSession: 0, CreateSession: 0,
DestroySession: 0, DestroySession: 0,
Sequence: 0, Sequence: 0,
@ -271,11 +271,11 @@ proc4 61 1 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0
LayoutCommit: 0, LayoutCommit: 0,
LayoutReturn: 0, LayoutReturn: 0,
SecinfoNoName: 0, SecinfoNoName: 0,
TestStateId: 0, TestStateID: 0,
FreeStateId: 0, FreeStateID: 0,
GetDeviceList: 0, GetDeviceList: 0,
BindConnToSession: 0, BindConnToSession: 0,
DestroyClientId: 0, DestroyClientID: 0,
Seek: 0, Seek: 0,
Allocate: 0, Allocate: 0,
DeAllocate: 0, DeAllocate: 0,

View file

@ -229,13 +229,13 @@ function extract {
# The repeated pattern makes up for sed's lack of negative # The repeated pattern makes up for sed's lack of negative
# lookbehind assertions (for consecutive null bytes). # lookbehind assertions (for consecutive null bytes).
echo -n "$line" | \ echo -n "$line" | \
sed 's/^NULLBYTE/\x0/g' | \ sed -e 's/^NULLBYTE/\x0/g;
sed 's/\([^\\]\)NULLBYTE/\1\x0/g' | \ s/\([^\\]\)NULLBYTE/\1\x0/g;
sed 's/\([^\\]\)NULLBYTE/\1\x0/g' | \ s/\([^\\]\)NULLBYTE/\1\x0/g;
sed 's/\\NULLBYTE/NULLBYTE/g' | \ s/\\NULLBYTE/NULLBYTE/g;
sed 's/\([^\\]\)EOF/\1/g' | \ s/\([^\\]\)EOF/\1/g;
sed 's/\\EOF/EOF/g' \ s/\\EOF/EOF/g;
>> "$path" ' >> "$path"
fi fi
if [[ "$eof_without_newline" -eq 0 ]]; then if [[ "$eof_without_newline" -eq 0 ]]; then
echo >> "$path" echo >> "$path"
@ -283,11 +283,14 @@ function get_mode {
local mfile=$1 local mfile=$1
if [ -z "${STAT_OPTION:-}" ]; then if [ -z "${STAT_OPTION:-}" ]; then
if stat -c '%a' "$mfile" >/dev/null 2>&1; then if stat -c '%a' "$mfile" >/dev/null 2>&1; then
# GNU stat
STAT_OPTION='-c' STAT_OPTION='-c'
STAT_FORMAT='%a' STAT_FORMAT='%a'
else else
# BSD stat
STAT_OPTION='-f' STAT_OPTION='-f'
STAT_FORMAT='%A' # Octal output, user/group/other (omit file type, sticky bit)
STAT_FORMAT='%OLp'
fi fi
fi fi
stat "${STAT_OPTION}" "${STAT_FORMAT}" "$mfile" stat "${STAT_OPTION}" "${STAT_FORMAT}" "$mfile"
@ -334,9 +337,10 @@ function _create {
< "$file" python -c "$PYTHON_CREATE_FILTER" < "$file" python -c "$PYTHON_CREATE_FILTER"
else else
< "$file" \ < "$file" \
sed 's/EOF/\\EOF/g' | \ sed 's/EOF/\\EOF/g;
sed 's/NULLBYTE/\\NULLBYTE/g' | \ s/NULLBYTE/\\NULLBYTE/g;
sed 's/\x0/NULLBYTE/g' s/\x0/NULLBYTE/g;
'
fi fi
if [[ "$eof_without_newline" -eq 1 ]]; then if [[ "$eof_without_newline" -eq 1 ]]; then
# Finish line with EOF to indicate that the original line did # Finish line with EOF to indicate that the original line did

View file

@ -4,16 +4,15 @@ Go is an open source project.
It is the work of hundreds of contributors. We appreciate your help! It is the work of hundreds of contributors. We appreciate your help!
## Filing issues ## Filing issues
When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions: When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
1. What version of Go are you using (`go version`)? 1. What version of Go are you using (`go version`)?
2. What operating system and processor architecture are you using? 2. What operating system and processor architecture are you using?
3. What did you do? 3. What did you do?
4. What did you expect to see? 4. What did you expect to see?
5. What did you see instead? 5. What did you see instead?
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker. General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
The gophers there will answer or ask you to file an issue if you've tripped over a bug. The gophers there will answer or ask you to file an issue if you've tripped over a bug.
@ -23,9 +22,5 @@ The gophers there will answer or ask you to file an issue if you've tripped over
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html) Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
before sending patches. before sending patches.
**We do not accept GitHub pull requests**
(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review).
Unless otherwise noted, the Go source files are distributed under Unless otherwise noted, the Go source files are distributed under
the BSD-style license found in the LICENSE file. the BSD-style license found in the LICENSE file.

View file

@ -71,12 +71,21 @@ func (dr *domainRenewal) renew() {
testDidRenewLoop(next, err) testDidRenewLoop(next, err)
} }
// updateState locks and replaces the relevant Manager.state item with the given
// state. It additionally updates dr.key with the given state's key.
func (dr *domainRenewal) updateState(state *certState) {
dr.m.stateMu.Lock()
defer dr.m.stateMu.Unlock()
dr.key = state.key
dr.m.state[dr.domain] = state
}
// do is similar to Manager.createCert but it doesn't lock a Manager.state item. // do is similar to Manager.createCert but it doesn't lock a Manager.state item.
// Instead, it requests a new certificate independently and, upon success, // Instead, it requests a new certificate independently and, upon success,
// replaces dr.m.state item with a new one and updates cache for the given domain. // replaces dr.m.state item with a new one and updates cache for the given domain.
// //
// It may return immediately if the expiration date of the currently cached cert // It may lock and update the Manager.state if the expiration date of the currently
// is far enough in the future. // cached cert is far enough in the future.
// //
// The returned value is a time interval after which the renewal should occur again. // The returned value is a time interval after which the renewal should occur again.
func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) { func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
@ -85,7 +94,16 @@ func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
if tlscert, err := dr.m.cacheGet(ctx, dr.domain); err == nil { if tlscert, err := dr.m.cacheGet(ctx, dr.domain); err == nil {
next := dr.next(tlscert.Leaf.NotAfter) next := dr.next(tlscert.Leaf.NotAfter)
if next > dr.m.renewBefore()+renewJitter { if next > dr.m.renewBefore()+renewJitter {
return next, nil signer, ok := tlscert.PrivateKey.(crypto.Signer)
if ok {
state := &certState{
key: signer,
cert: tlscert.Certificate,
leaf: tlscert.Leaf,
}
dr.updateState(state)
return next, nil
}
} }
} }
@ -105,10 +123,7 @@ func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
if err := dr.m.cachePut(ctx, dr.domain, tlscert); err != nil { if err := dr.m.cachePut(ctx, dr.domain, tlscert); err != nil {
return 0, err return 0, err
} }
dr.m.stateMu.Lock() dr.updateState(state)
defer dr.m.stateMu.Unlock()
// m.state is guaranteed to be non-nil at this point
dr.m.state[dr.domain] = state
return dr.next(leaf.NotAfter), nil return dr.next(leaf.NotAfter), nil
} }

View file

@ -189,3 +189,149 @@ func TestRenewFromCache(t *testing.T) {
case <-done: case <-done:
} }
} }
func TestRenewFromCacheAlreadyRenewed(t *testing.T) {
const domain = "example.org"
// use EC key to run faster on 386
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
man := &Manager{
Prompt: AcceptTOS,
Cache: newMemCache(),
RenewBefore: 24 * time.Hour,
Client: &acme.Client{
Key: key,
DirectoryURL: "invalid",
},
}
defer man.stopRenew()
// cache a recently renewed cert with a different private key
newKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
now := time.Now()
newCert, err := dateDummyCert(newKey.Public(), now.Add(-2*time.Hour), now.Add(time.Hour*24*90), domain)
if err != nil {
t.Fatal(err)
}
newLeaf, err := validCert(domain, [][]byte{newCert}, newKey)
if err != nil {
t.Fatal(err)
}
newTLSCert := &tls.Certificate{PrivateKey: newKey, Certificate: [][]byte{newCert}, Leaf: newLeaf}
if err := man.cachePut(context.Background(), domain, newTLSCert); err != nil {
t.Fatal(err)
}
// set internal state to an almost expired cert
oldCert, err := dateDummyCert(key.Public(), now.Add(-2*time.Hour), now.Add(time.Minute), domain)
if err != nil {
t.Fatal(err)
}
oldLeaf, err := validCert(domain, [][]byte{oldCert}, key)
if err != nil {
t.Fatal(err)
}
man.stateMu.Lock()
if man.state == nil {
man.state = make(map[string]*certState)
}
s := &certState{
key: key,
cert: [][]byte{oldCert},
leaf: oldLeaf,
}
man.state[domain] = s
man.stateMu.Unlock()
// veriy the renewal accepted the newer cached cert
defer func() {
testDidRenewLoop = func(next time.Duration, err error) {}
}()
done := make(chan struct{})
testDidRenewLoop = func(next time.Duration, err error) {
defer close(done)
if err != nil {
t.Errorf("testDidRenewLoop: %v", err)
}
// Next should be about 90 days
// Previous expiration was within 1 min.
future := 88 * 24 * time.Hour
if next < future {
t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future)
}
// ensure the cached cert was not modified
tlscert, err := man.cacheGet(context.Background(), domain)
if err != nil {
t.Fatalf("man.cacheGet: %v", err)
}
if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) {
t.Errorf("cache leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
}
// verify the old cert is also replaced in memory
man.stateMu.Lock()
defer man.stateMu.Unlock()
s := man.state[domain]
if s == nil {
t.Fatalf("m.state[%q] is nil", domain)
}
stateKey := s.key.Public().(*ecdsa.PublicKey)
if stateKey.X.Cmp(newKey.X) != 0 || stateKey.Y.Cmp(newKey.Y) != 0 {
t.Fatalf("state key was not updated from cache x: %v y: %v; want x: %v y: %v", stateKey.X, stateKey.Y, newKey.X, newKey.Y)
}
tlscert, err = s.tlscert()
if err != nil {
t.Fatalf("s.tlscert: %v", err)
}
if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) {
t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
}
// verify the private key is replaced in the renewal state
r := man.renewal[domain]
if r == nil {
t.Fatalf("m.renewal[%q] is nil", domain)
}
renewalKey := r.key.Public().(*ecdsa.PublicKey)
if renewalKey.X.Cmp(newKey.X) != 0 || renewalKey.Y.Cmp(newKey.Y) != 0 {
t.Fatalf("renewal private key was not updated from cache x: %v y: %v; want x: %v y: %v", renewalKey.X, renewalKey.Y, newKey.X, newKey.Y)
}
}
// assert the expiring cert is returned from state
hello := &tls.ClientHelloInfo{ServerName: domain}
tlscert, err := man.GetCertificate(hello)
if err != nil {
t.Fatal(err)
}
if !oldLeaf.NotAfter.Equal(tlscert.Leaf.NotAfter) {
t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, oldLeaf.NotAfter)
}
// trigger renew
go man.renew(domain, s.key, s.leaf.NotAfter)
// wait for renew loop
select {
case <-time.After(10 * time.Second):
t.Fatal("renew took too long to occur")
case <-done:
// assert the new cert is returned from state after renew
hello := &tls.ClientHelloInfo{ServerName: domain}
tlscert, err := man.GetCertificate(hello)
if err != nil {
t.Fatal(err)
}
if !newTLSCert.Leaf.NotAfter.Equal(tlscert.Leaf.NotAfter) {
t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newTLSCert.Leaf.NotAfter)
}
}
}

View file

@ -171,9 +171,16 @@ func Verify(publicKey PublicKey, message, sig []byte) bool {
edwards25519.ScReduce(&hReduced, &digest) edwards25519.ScReduce(&hReduced, &digest)
var R edwards25519.ProjectiveGroupElement var R edwards25519.ProjectiveGroupElement
var b [32]byte var s [32]byte
copy(b[:], sig[32:]) copy(s[:], sig[32:])
edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &b)
// https://tools.ietf.org/html/rfc8032#section-5.1.7 requires that s be in
// the range [0, order) in order to prevent signature malleability.
if !edwards25519.ScMinimal(&s) {
return false
}
edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &s)
var checkR [32]byte var checkR [32]byte
R.ToBytes(&checkR) R.ToBytes(&checkR)

View file

@ -146,6 +146,30 @@ func TestGolden(t *testing.T) {
} }
} }
func TestMalleability(t *testing.T) {
// https://tools.ietf.org/html/rfc8032#section-5.1.7 adds an additional test
// that s be in [0, order). This prevents someone from adding a multiple of
// order to s and obtaining a second valid signature for the same message.
msg := []byte{0x54, 0x65, 0x73, 0x74}
sig := []byte{
0x7c, 0x38, 0xe0, 0x26, 0xf2, 0x9e, 0x14, 0xaa, 0xbd, 0x05, 0x9a,
0x0f, 0x2d, 0xb8, 0xb0, 0xcd, 0x78, 0x30, 0x40, 0x60, 0x9a, 0x8b,
0xe6, 0x84, 0xdb, 0x12, 0xf8, 0x2a, 0x27, 0x77, 0x4a, 0xb0, 0x67,
0x65, 0x4b, 0xce, 0x38, 0x32, 0xc2, 0xd7, 0x6f, 0x8f, 0x6f, 0x5d,
0xaf, 0xc0, 0x8d, 0x93, 0x39, 0xd4, 0xee, 0xf6, 0x76, 0x57, 0x33,
0x36, 0xa5, 0xc5, 0x1e, 0xb6, 0xf9, 0x46, 0xb3, 0x1d,
}
publicKey := []byte{
0x7d, 0x4d, 0x0e, 0x7f, 0x61, 0x53, 0xa6, 0x9b, 0x62, 0x42, 0xb5,
0x22, 0xab, 0xbe, 0xe6, 0x85, 0xfd, 0xa4, 0x42, 0x0f, 0x88, 0x34,
0xb1, 0x08, 0xc3, 0xbd, 0xae, 0x36, 0x9e, 0xf5, 0x49, 0xfa,
}
if Verify(publicKey, msg, sig) {
t.Fatal("non-canonical signature accepted")
}
}
func BenchmarkKeyGeneration(b *testing.B) { func BenchmarkKeyGeneration(b *testing.B) {
var zero zeroReader var zero zeroReader
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {

View file

@ -4,6 +4,8 @@
package edwards25519 package edwards25519
import "encoding/binary"
// This code is a port of the public domain, “ref10” implementation of ed25519 // This code is a port of the public domain, “ref10” implementation of ed25519
// from SUPERCOP. // from SUPERCOP.
@ -1769,3 +1771,23 @@ func ScReduce(out *[32]byte, s *[64]byte) {
out[30] = byte(s11 >> 9) out[30] = byte(s11 >> 9)
out[31] = byte(s11 >> 17) out[31] = byte(s11 >> 17)
} }
// order is the order of Curve25519 in little-endian form.
var order = [4]uint64{0x5812631a5cf5d3ed, 0x14def9dea2f79cd6, 0, 0x1000000000000000}
// ScMinimal returns true if the given scalar is less than the order of the
// curve.
func ScMinimal(scalar *[32]byte) bool {
for i := 3; ; i-- {
v := binary.LittleEndian.Uint64(scalar[i*8:])
if v > order[i] {
return false
} else if v < order[i] {
break
} else if i == 0 {
return false
}
}
return true
}

83
vendor/golang.org/x/crypto/nacl/sign/sign.go generated vendored Normal file
View file

@ -0,0 +1,83 @@
// Copyright 2018 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 sign signs small messages using public-key cryptography.
//
// Sign uses Ed25519 to sign messages. The length of messages is not hidden.
// Messages should be small because:
// 1. The whole message needs to be held in memory to be processed.
// 2. Using large messages pressures implementations on small machines to process
// plaintext without verifying the signature. This is very dangerous, and this API
// discourages it, but a protocol that uses excessive message sizes might present
// some implementations with no other choice.
// 3. Performance may be improved by working with messages that fit into data caches.
// Thus large amounts of data should be chunked so that each message is small.
//
// This package is not interoperable with the current release of NaCl
// (https://nacl.cr.yp.to/sign.html), which does not support Ed25519 yet. However,
// it is compatible with the NaCl fork libsodium (https://www.libsodium.org), as well
// as TweetNaCl (https://tweetnacl.cr.yp.to/).
package sign
import (
"io"
"golang.org/x/crypto/ed25519"
)
// Overhead is the number of bytes of overhead when signing a message.
const Overhead = 64
// GenerateKey generates a new public/private key pair suitable for use with
// Sign and Open.
func GenerateKey(rand io.Reader) (publicKey *[32]byte, privateKey *[64]byte, err error) {
pub, priv, err := ed25519.GenerateKey(rand)
if err != nil {
return nil, nil, err
}
publicKey, privateKey = new([32]byte), new([64]byte)
copy((*publicKey)[:], pub)
copy((*privateKey)[:], priv)
return publicKey, privateKey, nil
}
// Sign appends a signed copy of message to out, which will be Overhead bytes
// longer than the original and must not overlap it.
func Sign(out, message []byte, privateKey *[64]byte) []byte {
sig := ed25519.Sign(ed25519.PrivateKey((*privateKey)[:]), message)
ret, out := sliceForAppend(out, Overhead+len(message))
copy(out, sig)
copy(out[Overhead:], message)
return ret
}
// Open verifies a signed message produced by Sign and appends the message to
// out, which must not overlap the signed message. The output will be Overhead
// bytes smaller than the signed message.
func Open(out, signedMessage []byte, publicKey *[32]byte) ([]byte, bool) {
if len(signedMessage) < Overhead {
return nil, false
}
if !ed25519.Verify(ed25519.PublicKey((*publicKey)[:]), signedMessage[Overhead:], signedMessage[:Overhead]) {
return nil, false
}
ret, out := sliceForAppend(out, len(signedMessage)-Overhead)
copy(out, signedMessage[Overhead:])
return ret, true
}
// sliceForAppend takes a slice and a requested number of bytes. It returns a
// slice with the contents of the given slice followed by that many bytes and a
// second slice that aliases into it and contains only the extra bytes. If the
// original slice has sufficient capacity then no allocation is performed.
func sliceForAppend(in []byte, n int) (head, tail []byte) {
if total := len(in) + n; cap(in) >= total {
head = in[:total]
} else {
head = make([]byte, total)
copy(head, in)
}
tail = head[len(in):]
return
}

74
vendor/golang.org/x/crypto/nacl/sign/sign_test.go generated vendored Normal file
View file

@ -0,0 +1,74 @@
// Copyright 2018 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 sign
import (
"bytes"
"crypto/rand"
"encoding/hex"
"testing"
)
var testSignedMessage, _ = hex.DecodeString("26a0a47f733d02ddb74589b6cbd6f64a7dab1947db79395a1a9e00e4c902c0f185b119897b89b248d16bab4ea781b5a3798d25c2984aec833dddab57e0891e0d68656c6c6f20776f726c64")
var testMessage = testSignedMessage[Overhead:]
var testPublicKey [32]byte
var testPrivateKey = [64]byte{
0x98, 0x3c, 0x6a, 0xa6, 0x21, 0xcc, 0xbb, 0xb2, 0xa7, 0xe8, 0x97, 0x94, 0xde, 0x5f, 0xf8, 0x11,
0x8a, 0xf3, 0x33, 0x1a, 0x03, 0x5c, 0x43, 0x99, 0x03, 0x13, 0x2d, 0xd7, 0xb4, 0xc4, 0x8b, 0xb0,
0xf6, 0x33, 0x20, 0xa3, 0x34, 0x8b, 0x7b, 0xe2, 0xfe, 0xb4, 0xe7, 0x3a, 0x54, 0x08, 0x2d, 0xd7,
0x0c, 0xb7, 0xc0, 0xe3, 0xbf, 0x62, 0x6c, 0x55, 0xf0, 0x33, 0x28, 0x52, 0xf8, 0x48, 0x7d, 0xfd,
}
func init() {
copy(testPublicKey[:], testPrivateKey[32:])
}
func TestSign(t *testing.T) {
signedMessage := Sign(nil, testMessage, &testPrivateKey)
if !bytes.Equal(signedMessage, testSignedMessage) {
t.Fatalf("signed message did not match, got\n%x\n, expected\n%x", signedMessage, testSignedMessage)
}
}
func TestOpen(t *testing.T) {
message, ok := Open(nil, testSignedMessage, &testPublicKey)
if !ok {
t.Fatalf("valid signed message not successfully verified")
}
if !bytes.Equal(message, testMessage) {
t.Fatalf("message did not match, got\n%x\n, expected\n%x", message, testMessage)
}
message, ok = Open(nil, testSignedMessage[1:], &testPublicKey)
if ok {
t.Fatalf("invalid signed message successfully verified")
}
badMessage := make([]byte, len(testSignedMessage))
copy(badMessage, testSignedMessage)
badMessage[5] ^= 1
if _, ok := Open(nil, badMessage, &testPublicKey); ok {
t.Fatalf("Open succeeded with a corrupt message")
}
var badPublicKey [32]byte
copy(badPublicKey[:], testPublicKey[:])
badPublicKey[5] ^= 1
if _, ok := Open(nil, testSignedMessage, &badPublicKey); ok {
t.Fatalf("Open succeeded with a corrupt public key")
}
}
func TestGenerateSignOpen(t *testing.T) {
publicKey, privateKey, _ := GenerateKey(rand.Reader)
signedMessage := Sign(nil, testMessage, privateKey)
message, ok := Open(nil, signedMessage, publicKey)
if !ok {
t.Fatalf("failed to verify signed message")
}
if !bytes.Equal(message, testMessage) {
t.Fatalf("verified message does not match signed messge, got\n%x\n, expected\n%x", message, testMessage)
}
}

View file

@ -42,12 +42,18 @@ func (e *EncryptedKey) parse(r io.Reader) (err error) {
switch e.Algo { switch e.Algo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r) e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r)
if err != nil {
return
}
case PubKeyAlgoElGamal: case PubKeyAlgoElGamal:
e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r) e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r)
if err != nil { if err != nil {
return return
} }
e.encryptedMPI2.bytes, e.encryptedMPI2.bitLength, err = readMPI(r) e.encryptedMPI2.bytes, e.encryptedMPI2.bitLength, err = readMPI(r)
if err != nil {
return
}
} }
_, err = consumeAll(r) _, err = consumeAll(r)
return return
@ -72,7 +78,8 @@ func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error {
// padding oracle attacks. // padding oracle attacks.
switch priv.PubKeyAlgo { switch priv.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
b, err = rsa.DecryptPKCS1v15(config.Random(), priv.PrivateKey.(*rsa.PrivateKey), e.encryptedMPI1.bytes) k := priv.PrivateKey.(*rsa.PrivateKey)
b, err = rsa.DecryptPKCS1v15(config.Random(), k, padToKeySize(&k.PublicKey, e.encryptedMPI1.bytes))
case PubKeyAlgoElGamal: case PubKeyAlgoElGamal:
c1 := new(big.Int).SetBytes(e.encryptedMPI1.bytes) c1 := new(big.Int).SetBytes(e.encryptedMPI1.bytes)
c2 := new(big.Int).SetBytes(e.encryptedMPI2.bytes) c2 := new(big.Int).SetBytes(e.encryptedMPI2.bytes)

View file

@ -39,39 +39,44 @@ var encryptedKeyPriv = &PrivateKey{
} }
func TestDecryptingEncryptedKey(t *testing.T) { func TestDecryptingEncryptedKey(t *testing.T) {
const encryptedKeyHex = "c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8" for i, encryptedKeyHex := range []string{
const expectedKeyHex = "d930363f7e0308c333b9618617ea728963d8df993665ae7be1092d4926fd864b" "c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8",
// MPI can be shorter than the length of the key.
"c18b032a67d68660df41c70103f8e520c52ae9807183c669ce26e772e482dc5d8cf60e6f59316e145be14d2e5221ee69550db1d5618a8cb002a719f1f0b9345bde21536d410ec90ba86cac37748dec7933eb7f9873873b2d61d3321d1cd44535014f6df58f7bc0c7afb5edc38e1a974428997d2f747f9a173bea9ca53079b409517d332df62d805564cffc9be6",
} {
const expectedKeyHex = "d930363f7e0308c333b9618617ea728963d8df993665ae7be1092d4926fd864b"
p, err := Read(readerFromHex(encryptedKeyHex)) p, err := Read(readerFromHex(encryptedKeyHex))
if err != nil { if err != nil {
t.Errorf("error from Read: %s", err) t.Errorf("#%d: error from Read: %s", i, err)
return return
} }
ek, ok := p.(*EncryptedKey) ek, ok := p.(*EncryptedKey)
if !ok { if !ok {
t.Errorf("didn't parse an EncryptedKey, got %#v", p) t.Errorf("#%d: didn't parse an EncryptedKey, got %#v", i, p)
return return
} }
if ek.KeyId != 0x2a67d68660df41c7 || ek.Algo != PubKeyAlgoRSA { if ek.KeyId != 0x2a67d68660df41c7 || ek.Algo != PubKeyAlgoRSA {
t.Errorf("unexpected EncryptedKey contents: %#v", ek) t.Errorf("#%d: unexpected EncryptedKey contents: %#v", i, ek)
return return
} }
err = ek.Decrypt(encryptedKeyPriv, nil) err = ek.Decrypt(encryptedKeyPriv, nil)
if err != nil { if err != nil {
t.Errorf("error from Decrypt: %s", err) t.Errorf("#%d: error from Decrypt: %s", i, err)
return return
} }
if ek.CipherFunc != CipherAES256 { if ek.CipherFunc != CipherAES256 {
t.Errorf("unexpected EncryptedKey contents: %#v", ek) t.Errorf("#%d: unexpected EncryptedKey contents: %#v", i, ek)
return return
} }
keyHex := fmt.Sprintf("%x", ek.Key) keyHex := fmt.Sprintf("%x", ek.Key)
if keyHex != expectedKeyHex { if keyHex != expectedKeyHex {
t.Errorf("bad key, got %s want %x", keyHex, expectedKeyHex) t.Errorf("#%d: bad key, got %s want %s", i, keyHex, expectedKeyHex)
}
} }
} }
@ -121,7 +126,7 @@ func TestEncryptingEncryptedKey(t *testing.T) {
keyHex := fmt.Sprintf("%x", ek.Key) keyHex := fmt.Sprintf("%x", ek.Key)
if keyHex != expectedKeyHex { if keyHex != expectedKeyHex {
t.Errorf("bad key, got %s want %x", keyHex, expectedKeyHex) t.Errorf("bad key, got %s want %s", keyHex, expectedKeyHex)
} }
} }

View file

@ -11,10 +11,12 @@ import (
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/des" "crypto/des"
"golang.org/x/crypto/cast5" "crypto/rsa"
"golang.org/x/crypto/openpgp/errors"
"io" "io"
"math/big" "math/big"
"golang.org/x/crypto/cast5"
"golang.org/x/crypto/openpgp/errors"
) )
// readFull is the same as io.ReadFull except that reading zero bytes returns // readFull is the same as io.ReadFull except that reading zero bytes returns
@ -500,19 +502,17 @@ func readMPI(r io.Reader) (mpi []byte, bitLength uint16, err error) {
numBytes := (int(bitLength) + 7) / 8 numBytes := (int(bitLength) + 7) / 8
mpi = make([]byte, numBytes) mpi = make([]byte, numBytes)
_, err = readFull(r, mpi) _, err = readFull(r, mpi)
return // According to RFC 4880 3.2. we should check that the MPI has no leading
} // zeroes (at least when not an encrypted MPI?), but this implementation
// does generate leading zeroes, so we keep accepting them.
// mpiLength returns the length of the given *big.Int when serialized as an
// MPI.
func mpiLength(n *big.Int) (mpiLengthInBytes int) {
mpiLengthInBytes = 2 /* MPI length */
mpiLengthInBytes += (n.BitLen() + 7) / 8
return return
} }
// writeMPI serializes a big integer to w. // writeMPI serializes a big integer to w.
func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err error) { func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err error) {
// Note that we can produce leading zeroes, in violation of RFC 4880 3.2.
// Implementations seem to be tolerant of them, and stripping them would
// make it complex to guarantee matching re-serialization.
_, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)}) _, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)})
if err == nil { if err == nil {
_, err = w.Write(mpiBytes) _, err = w.Write(mpiBytes)
@ -525,6 +525,18 @@ func writeBig(w io.Writer, i *big.Int) error {
return writeMPI(w, uint16(i.BitLen()), i.Bytes()) return writeMPI(w, uint16(i.BitLen()), i.Bytes())
} }
// padToKeySize left-pads a MPI with zeroes to match the length of the
// specified RSA public.
func padToKeySize(pub *rsa.PublicKey, b []byte) []byte {
k := (pub.N.BitLen() + 7) / 8
if len(b) >= k {
return b
}
bb := make([]byte, k)
copy(bb[len(bb)-len(b):], b)
return bb
}
// CompressionAlgo Represents the different compression algorithms // CompressionAlgo Represents the different compression algorithms
// supported by OpenPGP (except for BZIP2, which is not currently // supported by OpenPGP (except for BZIP2, which is not currently
// supported). See Section 9.3 of RFC 4880. // supported). See Section 9.3 of RFC 4880.

View file

@ -244,7 +244,12 @@ func NewECDSAPublicKey(creationTime time.Time, pub *ecdsa.PublicKey) *PublicKey
} }
pk.ec.p.bytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y) pk.ec.p.bytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
pk.ec.p.bitLength = uint16(8 * len(pk.ec.p.bytes))
// The bit length is 3 (for the 0x04 specifying an uncompressed key)
// plus two field elements (for x and y), which are rounded up to the
// nearest byte. See https://tools.ietf.org/html/rfc6637#section-6
fieldBytes := (pub.Curve.Params().BitSize + 7) & ^7
pk.ec.p.bitLength = uint16(3 + fieldBytes + fieldBytes)
pk.setFingerPrintAndKeyId() pk.setFingerPrintAndKeyId()
return pk return pk
@ -515,7 +520,7 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err erro
switch pk.PubKeyAlgo { switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey) rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey)
err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes) err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, padToKeySize(rsaPublicKey, sig.RSASignature.bytes))
if err != nil { if err != nil {
return errors.SignatureError("RSA verification failure") return errors.SignatureError("RSA verification failure")
} }
@ -566,7 +571,7 @@ func (pk *PublicKey) VerifySignatureV3(signed hash.Hash, sig *SignatureV3) (err
switch pk.PubKeyAlgo { switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
rsaPublicKey := pk.PublicKey.(*rsa.PublicKey) rsaPublicKey := pk.PublicKey.(*rsa.PublicKey)
if err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes); err != nil { if err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, padToKeySize(rsaPublicKey, sig.RSASignature.bytes)); err != nil {
return errors.SignatureError("RSA verification failure") return errors.SignatureError("RSA verification failure")
} }
return return

View file

@ -6,7 +6,10 @@ package packet
import ( import (
"bytes" "bytes"
"crypto/ecdsa"
"crypto/elliptic"
"encoding/hex" "encoding/hex"
"math/big"
"testing" "testing"
"time" "time"
) )
@ -186,6 +189,29 @@ func TestEcc384Serialize(t *testing.T) {
} }
} }
func TestP256KeyID(t *testing.T) {
// Confirm that key IDs are correctly calculated for ECC keys.
ecdsaPub := &ecdsa.PublicKey{
Curve: elliptic.P256(),
X: fromHex("81fbbc20eea9e8d1c3ceabb0a8185925b113d1ac42cd5c78403bd83da19235c6"),
Y: fromHex("5ed6db13d91db34507d0129bf88981878d29adbf8fcd1720afdb767bb3fcaaff"),
}
pub := NewECDSAPublicKey(time.Unix(1297309478, 0), ecdsaPub)
const want = uint64(0xd01055fbcadd268e)
if pub.KeyId != want {
t.Errorf("want key ID: %x, got %x", want, pub.KeyId)
}
}
func fromHex(hex string) *big.Int {
n, ok := new(big.Int).SetString(hex, 16)
if !ok {
panic("bad hex number: " + hex)
}
return n
}
const rsaFingerprintHex = "5fb74b1d03b1e3cb31bc2f8aa34d7e18c20c31bb" const rsaFingerprintHex = "5fb74b1d03b1e3cb31bc2f8aa34d7e18c20c31bb"
const rsaPkDataHex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001" const rsaPkDataHex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001"

View file

@ -40,7 +40,7 @@ func (d *state) Clone() ShakeHash {
// least 32 bytes of its output are used. // least 32 bytes of its output are used.
func NewShake128() ShakeHash { return &state{rate: 168, dsbyte: 0x1f} } func NewShake128() ShakeHash { return &state{rate: 168, dsbyte: 0x1f} }
// NewShake256 creates a new SHAKE128 variable-output-length ShakeHash. // NewShake256 creates a new SHAKE256 variable-output-length ShakeHash.
// Its generic security strength is 256 bits against all attacks if // Its generic security strength is 256 bits against all attacks if
// at least 64 bytes of its output are used. // at least 64 bytes of its output are used.
func NewShake256() ShakeHash { return &state{rate: 136, dsbyte: 0x1f} } func NewShake256() ShakeHash { return &state{rate: 136, dsbyte: 0x1f} }

View file

@ -4,16 +4,15 @@ Go is an open source project.
It is the work of hundreds of contributors. We appreciate your help! It is the work of hundreds of contributors. We appreciate your help!
## Filing issues ## Filing issues
When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions: When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
1. What version of Go are you using (`go version`)? 1. What version of Go are you using (`go version`)?
2. What operating system and processor architecture are you using? 2. What operating system and processor architecture are you using?
3. What did you do? 3. What did you do?
4. What did you expect to see? 4. What did you expect to see?
5. What did you see instead? 5. What did you see instead?
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker. General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
The gophers there will answer or ask you to file an issue if you've tripped over a bug. The gophers there will answer or ask you to file an issue if you've tripped over a bug.
@ -23,9 +22,5 @@ The gophers there will answer or ask you to file an issue if you've tripped over
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html) Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
before sending patches. before sending patches.
**We do not accept GitHub pull requests**
(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review).
Unless otherwise noted, the Go source files are distributed under Unless otherwise noted, the Go source files are distributed under
the BSD-style license found in the LICENSE file. the BSD-style license found in the LICENSE file.

View file

@ -90,6 +90,8 @@ var (
errTooManyAuthorities = errors.New("too many Authorities to pack (>65535)") errTooManyAuthorities = errors.New("too many Authorities to pack (>65535)")
errTooManyAdditionals = errors.New("too many Additionals to pack (>65535)") errTooManyAdditionals = errors.New("too many Additionals to pack (>65535)")
errNonCanonicalName = errors.New("name is not in canonical format (it must end with a .)") errNonCanonicalName = errors.New("name is not in canonical format (it must end with a .)")
errStringTooLong = errors.New("character string exceeds maximum length (255)")
errCompressedSRV = errors.New("compressed name in SRV resource data")
) )
// Internal constants. // Internal constants.
@ -218,6 +220,7 @@ func (h *header) count(sec section) uint16 {
return 0 return 0
} }
// pack appends the wire format of the header to msg.
func (h *header) pack(msg []byte) []byte { func (h *header) pack(msg []byte) []byte {
msg = packUint16(msg, h.id) msg = packUint16(msg, h.id)
msg = packUint16(msg, h.bits) msg = packUint16(msg, h.bits)
@ -280,6 +283,7 @@ type ResourceBody interface {
realType() Type realType() Type
} }
// pack appends the wire format of the Resource to msg.
func (r *Resource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { func (r *Resource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
if r.Body == nil { if r.Body == nil {
return msg, errNilResouceBody return msg, errNilResouceBody
@ -1311,9 +1315,10 @@ type ResourceHeader struct {
Length uint16 Length uint16
} }
// pack packs all of the fields in a ResourceHeader except for the length. The // pack appends the wire format of the ResourceHeader to oldMsg.
// length bytes are returned as a slice so they can be filled in after the rest //
// of the Resource has been packed. // The bytes where length was packed are returned as a slice so they can be
// updated after the rest of the Resource has been packed.
func (h *ResourceHeader) pack(oldMsg []byte, compression map[string]int, compressionOff int) (msg []byte, length []byte, err error) { func (h *ResourceHeader) pack(oldMsg []byte, compression map[string]int, compressionOff int) (msg []byte, length []byte, err error) {
msg = oldMsg msg = oldMsg
if msg, err = h.Name.pack(msg, compression, compressionOff); err != nil { if msg, err = h.Name.pack(msg, compression, compressionOff); err != nil {
@ -1385,6 +1390,7 @@ func skipResource(msg []byte, off int) (int, error) {
return newOff, nil return newOff, nil
} }
// packUint16 appends the wire format of field to msg.
func packUint16(msg []byte, field uint16) []byte { func packUint16(msg []byte, field uint16) []byte {
return append(msg, byte(field>>8), byte(field)) return append(msg, byte(field>>8), byte(field))
} }
@ -1403,6 +1409,7 @@ func skipUint16(msg []byte, off int) (int, error) {
return off + uint16Len, nil return off + uint16Len, nil
} }
// packType appends the wire format of field to msg.
func packType(msg []byte, field Type) []byte { func packType(msg []byte, field Type) []byte {
return packUint16(msg, uint16(field)) return packUint16(msg, uint16(field))
} }
@ -1416,6 +1423,7 @@ func skipType(msg []byte, off int) (int, error) {
return skipUint16(msg, off) return skipUint16(msg, off)
} }
// packClass appends the wire format of field to msg.
func packClass(msg []byte, field Class) []byte { func packClass(msg []byte, field Class) []byte {
return packUint16(msg, uint16(field)) return packUint16(msg, uint16(field))
} }
@ -1429,6 +1437,7 @@ func skipClass(msg []byte, off int) (int, error) {
return skipUint16(msg, off) return skipUint16(msg, off)
} }
// packUint32 appends the wire format of field to msg.
func packUint32(msg []byte, field uint32) []byte { func packUint32(msg []byte, field uint32) []byte {
return append( return append(
msg, msg,
@ -1454,17 +1463,16 @@ func skipUint32(msg []byte, off int) (int, error) {
return off + uint32Len, nil return off + uint32Len, nil
} }
func packText(msg []byte, field string) []byte { // packText appends the wire format of field to msg.
for len(field) > 0 { func packText(msg []byte, field string) ([]byte, error) {
l := len(field) l := len(field)
if l > 255 { if l > 255 {
l = 255 return nil, errStringTooLong
}
msg = append(msg, byte(l))
msg = append(msg, field[:l]...)
field = field[l:]
} }
return msg msg = append(msg, byte(l))
msg = append(msg, field...)
return msg, nil
} }
func unpackText(msg []byte, off int) (string, int, error) { func unpackText(msg []byte, off int) (string, int, error) {
@ -1490,6 +1498,7 @@ func skipText(msg []byte, off int) (int, error) {
return endOff, nil return endOff, nil
} }
// packBytes appends the wire format of field to msg.
func packBytes(msg []byte, field []byte) []byte { func packBytes(msg []byte, field []byte) []byte {
return append(msg, field...) return append(msg, field...)
} }
@ -1534,7 +1543,7 @@ func (n Name) String() string {
return string(n.Data[:n.Length]) return string(n.Data[:n.Length])
} }
// pack packs a domain name. // pack appends the wire format of the Name to msg.
// //
// Domain names are a sequence of counted strings split at the dots. They end // Domain names are a sequence of counted strings split at the dots. They end
// with a zero-length string. Compression can be used to reuse domain suffixes. // with a zero-length string. Compression can be used to reuse domain suffixes.
@ -1602,6 +1611,10 @@ func (n *Name) pack(msg []byte, compression map[string]int, compressionOff int)
// unpack unpacks a domain name. // unpack unpacks a domain name.
func (n *Name) unpack(msg []byte, off int) (int, error) { func (n *Name) unpack(msg []byte, off int) (int, error) {
return n.unpackCompressed(msg, off, true /* allowCompression */)
}
func (n *Name) unpackCompressed(msg []byte, off int, allowCompression bool) (int, error) {
// currOff is the current working offset. // currOff is the current working offset.
currOff := off currOff := off
@ -1637,6 +1650,9 @@ Loop:
name = append(name, '.') name = append(name, '.')
currOff = endOff currOff = endOff
case 0xC0: // Pointer case 0xC0: // Pointer
if !allowCompression {
return off, errCompressedSRV
}
if currOff >= len(msg) { if currOff >= len(msg) {
return off, errInvalidPtr return off, errInvalidPtr
} }
@ -1716,6 +1732,7 @@ type Question struct {
Class Class Class Class
} }
// pack appends the wire format of the Question to msg.
func (q *Question) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { func (q *Question) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
msg, err := q.Name.pack(msg, compression, compressionOff) msg, err := q.Name.pack(msg, compression, compressionOff)
if err != nil { if err != nil {
@ -1796,6 +1813,7 @@ func (r *CNAMEResource) realType() Type {
return TypeCNAME return TypeCNAME
} }
// pack appends the wire format of the CNAMEResource to msg.
func (r *CNAMEResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { func (r *CNAMEResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
return r.CNAME.pack(msg, compression, compressionOff) return r.CNAME.pack(msg, compression, compressionOff)
} }
@ -1818,6 +1836,7 @@ func (r *MXResource) realType() Type {
return TypeMX return TypeMX
} }
// pack appends the wire format of the MXResource to msg.
func (r *MXResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { func (r *MXResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
oldMsg := msg oldMsg := msg
msg = packUint16(msg, r.Pref) msg = packUint16(msg, r.Pref)
@ -1849,6 +1868,7 @@ func (r *NSResource) realType() Type {
return TypeNS return TypeNS
} }
// pack appends the wire format of the NSResource to msg.
func (r *NSResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { func (r *NSResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
return r.NS.pack(msg, compression, compressionOff) return r.NS.pack(msg, compression, compressionOff)
} }
@ -1870,6 +1890,7 @@ func (r *PTRResource) realType() Type {
return TypePTR return TypePTR
} }
// pack appends the wire format of the PTRResource to msg.
func (r *PTRResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { func (r *PTRResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
return r.PTR.pack(msg, compression, compressionOff) return r.PTR.pack(msg, compression, compressionOff)
} }
@ -1901,6 +1922,7 @@ func (r *SOAResource) realType() Type {
return TypeSOA return TypeSOA
} }
// pack appends the wire format of the SOAResource to msg.
func (r *SOAResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { func (r *SOAResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
oldMsg := msg oldMsg := msg
msg, err := r.NS.pack(msg, compression, compressionOff) msg, err := r.NS.pack(msg, compression, compressionOff)
@ -1953,19 +1975,28 @@ func unpackSOAResource(msg []byte, off int) (SOAResource, error) {
// A TXTResource is a TXT Resource record. // A TXTResource is a TXT Resource record.
type TXTResource struct { type TXTResource struct {
Txt string // Not a domain name. TXT []string
} }
func (r *TXTResource) realType() Type { func (r *TXTResource) realType() Type {
return TypeTXT return TypeTXT
} }
// pack appends the wire format of the TXTResource to msg.
func (r *TXTResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { func (r *TXTResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
return packText(msg, r.Txt), nil oldMsg := msg
for _, s := range r.TXT {
var err error
msg, err = packText(msg, s)
if err != nil {
return oldMsg, err
}
}
return msg, nil
} }
func unpackTXTResource(msg []byte, off int, length uint16) (TXTResource, error) { func unpackTXTResource(msg []byte, off int, length uint16) (TXTResource, error) {
var txt string txts := make([]string, 0, 1)
for n := uint16(0); n < length; { for n := uint16(0); n < length; {
var t string var t string
var err error var err error
@ -1977,9 +2008,9 @@ func unpackTXTResource(msg []byte, off int, length uint16) (TXTResource, error)
return TXTResource{}, errCalcLen return TXTResource{}, errCalcLen
} }
n += uint16(len(t)) + 1 n += uint16(len(t)) + 1
txt += t txts = append(txts, t)
} }
return TXTResource{txt}, nil return TXTResource{txts}, nil
} }
// An SRVResource is an SRV Resource record. // An SRVResource is an SRV Resource record.
@ -1994,6 +2025,7 @@ func (r *SRVResource) realType() Type {
return TypeSRV return TypeSRV
} }
// pack appends the wire format of the SRVResource to msg.
func (r *SRVResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { func (r *SRVResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
oldMsg := msg oldMsg := msg
msg = packUint16(msg, r.Priority) msg = packUint16(msg, r.Priority)
@ -2020,7 +2052,7 @@ func unpackSRVResource(msg []byte, off int) (SRVResource, error) {
return SRVResource{}, &nestedError{"Port", err} return SRVResource{}, &nestedError{"Port", err}
} }
var target Name var target Name
if _, err := target.unpack(msg, off); err != nil { if _, err := target.unpackCompressed(msg, off, false /* allowCompression */); err != nil {
return SRVResource{}, &nestedError{"Target", err} return SRVResource{}, &nestedError{"Target", err}
} }
return SRVResource{priority, weight, port, target}, nil return SRVResource{priority, weight, port, target}, nil
@ -2035,6 +2067,7 @@ func (r *AResource) realType() Type {
return TypeA return TypeA
} }
// pack appends the wire format of the AResource to msg.
func (r *AResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { func (r *AResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
return packBytes(msg, r.A[:]), nil return packBytes(msg, r.A[:]), nil
} }
@ -2056,6 +2089,7 @@ func (r *AAAAResource) realType() Type {
return TypeAAAA return TypeAAAA
} }
// pack appends the wire format of the AAAAResource to msg.
func (r *AAAAResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { func (r *AAAAResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
return packBytes(msg, r.AAAA[:]), nil return packBytes(msg, r.AAAA[:]), nil
} }

View file

@ -8,6 +8,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"reflect" "reflect"
"strings"
"testing" "testing"
) )
@ -157,6 +158,28 @@ func TestNamePackUnpack(t *testing.T) {
} }
} }
func TestIncompressibleName(t *testing.T) {
name := mustNewName("example.com.")
compression := map[string]int{}
buf, err := name.pack(make([]byte, 0, 100), compression, 0)
if err != nil {
t.Fatal("First packing failed:", err)
}
buf, err = name.pack(buf, compression, 0)
if err != nil {
t.Fatal("Second packing failed:", err)
}
var n1 Name
off, err := n1.unpackCompressed(buf, 0, false /* allowCompression */)
if err != nil {
t.Fatal("Unpacking incompressible name without pointers failed:", err)
}
var n2 Name
if _, err := n2.unpackCompressed(buf, off, false /* allowCompression */); err != errCompressedSRV {
t.Errorf("Unpacking compressed incompressible name with pointers: got err = %v, want = %v", err, errCompressedSRV)
}
}
func checkErrorPrefix(err error, prefix string) bool { func checkErrorPrefix(err error, prefix string) bool {
e, ok := err.(*nestedError) e, ok := err.(*nestedError)
return ok && e.s == prefix return ok && e.s == prefix
@ -444,7 +467,15 @@ func TestVeryLongTxt(t *testing.T) {
Type: TypeTXT, Type: TypeTXT,
Class: ClassINET, Class: ClassINET,
}, },
&TXTResource{loremIpsum}, &TXTResource{[]string{
"",
"",
"foo bar",
"",
"www.example.com",
"www.example.com.",
strings.Repeat(".", 255),
}},
} }
buf, err := want.pack(make([]byte, 0, 8000), map[string]int{}, 0) buf, err := want.pack(make([]byte, 0, 8000), map[string]int{}, 0)
if err != nil { if err != nil {
@ -468,6 +499,13 @@ func TestVeryLongTxt(t *testing.T) {
} }
} }
func TestTooLongTxt(t *testing.T) {
rb := TXTResource{[]string{strings.Repeat(".", 256)}}
if _, err := rb.pack(make([]byte, 0, 8000), map[string]int{}, 0); err != errStringTooLong {
t.Errorf("Packing TXTRecord with 256 character string: got err = %v, want = %v", err, errStringTooLong)
}
}
func TestStartAppends(t *testing.T) { func TestStartAppends(t *testing.T) {
buf := make([]byte, 2, 514) buf := make([]byte, 2, 514)
wantBuf := []byte{4, 44} wantBuf := []byte{4, 44}
@ -1084,7 +1122,7 @@ func largeTestMsg() Message {
Type: TypeTXT, Type: TypeTXT,
Class: ClassINET, Class: ClassINET,
}, },
&TXTResource{"So Long, and Thanks for All the Fish"}, &TXTResource{[]string{"So Long, and Thanks for All the Fish"}},
}, },
{ {
ResourceHeader{ ResourceHeader{
@ -1092,139 +1130,8 @@ func largeTestMsg() Message {
Type: TypeTXT, Type: TypeTXT,
Class: ClassINET, Class: ClassINET,
}, },
&TXTResource{"Hamster Huey and the Gooey Kablooie"}, &TXTResource{[]string{"Hamster Huey and the Gooey Kablooie"}},
}, },
}, },
} }
} }
const loremIpsum = `
Lorem ipsum dolor sit amet, nec enim antiopam id, an ullum choro
nonumes qui, pro eu debet honestatis mediocritatem. No alia enim eos,
magna signiferumque ex vis. Mei no aperiri dissentias, cu vel quas
regione. Malorum quaeque vim ut, eum cu semper aliquid invidunt, ei
nam ipsum assentior.
Nostrum appellantur usu no, vis ex probatus adipiscing. Cu usu illum
facilis eleifend. Iusto conceptam complectitur vim id. Tale omnesque
no usu, ei oblique sadipscing vim. At nullam voluptua usu, mei laudem
reformidans et. Qui ei eros porro reformidans, ius suas veritus
torquatos ex. Mea te facer alterum consequat.
Soleat torquatos democritum sed et, no mea congue appareat, facer
aliquam nec in. Has te ipsum tritani. At justo dicta option nec, movet
phaedrum ad nam. Ea detracto verterem liberavisse has, delectus
suscipiantur in mei. Ex nam meliore complectitur. Ut nam omnis
honestatis quaerendum, ea mea nihil affert detracto, ad vix rebum
mollis.
Ut epicurei praesent neglegentur pri, prima fuisset intellegebat ad
vim. An habemus comprehensam usu, at enim dignissim pro. Eam reque
vivendum adipisci ea. Vel ne odio choro minimum. Sea admodum
dissentiet ex. Mundi tamquam evertitur ius cu. Homero postea iisque ut
pro, vel ne saepe senserit consetetur.
Nulla utamur facilisis ius ea, in viderer diceret pertinax eum. Mei no
enim quodsi facilisi, ex sed aeterno appareat mediocritatem, eum
sententiae deterruisset ut. At suas timeam euismod cum, offendit
appareat interpretaris ne vix. Vel ea civibus albucius, ex vim quidam
accusata intellegebat, noluisse instructior sea id. Nec te nonumes
habemus appellantur, quis dignissim vituperata eu nam.
At vix apeirian patrioque vituperatoribus, an usu agam assum. Debet
iisque an mea. Per eu dicant ponderum accommodare. Pri alienum
placerat senserit an, ne eum ferri abhorreant vituperatoribus. Ut mea
eligendi disputationi. Ius no tation everti impedit, ei magna quidam
mediocritatem pri.
Legendos perpetua iracundia ne usu, no ius ullum epicurei intellegam,
ad modus epicuri lucilius eam. In unum quaerendum usu. Ne diam paulo
has, ea veri virtute sed. Alia honestatis conclusionemque mea eu, ut
iudico albucius his.
Usu essent probatus eu, sed omnis dolor delicatissimi ex. No qui augue
dissentias dissentiet. Laudem recteque no usu, vel an velit noluisse,
an sed utinam eirmod appetere. Ne mea fuisset inimicus ocurreret. At
vis dicant abhorreant, utinam forensibus nec ne, mei te docendi
consequat. Brute inermis persecuti cum id. Ut ipsum munere propriae
usu, dicit graeco disputando id has.
Eros dolore quaerendum nam ei. Timeam ornatus inciderint pro id. Nec
torquatos sadipscing ei, ancillae molestie per in. Malis principes duo
ea, usu liber postulant ei.
Graece timeam voluptatibus eu eam. Alia probatus quo no, ea scripta
feugiat duo. Congue option meliore ex qui, noster invenire appellantur
ea vel. Eu exerci legendos vel. Consetetur repudiandae vim ut. Vix an
probo minimum, et nam illud falli tempor.
Cum dico signiferumque eu. Sed ut regione maiorum, id veritus insolens
tacimates vix. Eu mel sint tamquam lucilius, duo no oporteat
tacimates. Atqui augue concludaturque vix ei, id mel utroque menandri.
Ad oratio blandit aliquando pro. Vis et dolorum rationibus
philosophia, ad cum nulla molestie. Hinc fuisset adversarium eum et,
ne qui nisl verear saperet, vel te quaestio forensibus. Per odio
option delenit an. Alii placerat has no, in pri nihil platonem
cotidieque. Est ut elit copiosae scaevola, debet tollit maluisset sea
an.
Te sea hinc debet pericula, liber ridens fabulas cu sed, quem mutat
accusam mea et. Elitr labitur albucius et pri, an labore feugait mel.
Velit zril melius usu ea. Ad stet putent interpretaris qui. Mel no
error volumus scripserit. In pro paulo iudico, quo ei dolorem
verterem, affert fabellas dissentiet ea vix.
Vis quot deserunt te. Error aliquid detraxit eu usu, vis alia eruditi
salutatus cu. Est nostrud bonorum an, ei usu alii salutatus. Vel at
nisl primis, eum ex aperiri noluisse reformidans. Ad veri velit
utroque vis, ex equidem detraxit temporibus has.
Inermis appareat usu ne. Eros placerat periculis mea ad, in dictas
pericula pro. Errem postulant at usu, ea nec amet ornatus mentitum. Ad
mazim graeco eum, vel ex percipit volutpat iudicabit, sit ne delicata
interesset. Mel sapientem prodesset abhorreant et, oblique suscipit
eam id.
An maluisset disputando mea, vidit mnesarchum pri et. Malis insolens
inciderint no sea. Ea persius maluisset vix, ne vim appellantur
instructior, consul quidam definiebas pri id. Cum integre feugiat
pericula in, ex sed persius similique, mel ne natum dicit percipitur.
Primis discere ne pri, errem putent definitionem at vis. Ei mel dolore
neglegentur, mei tincidunt percipitur ei. Pro ad simul integre
rationibus. Eu vel alii honestatis definitiones, mea no nonumy
reprehendunt.
Dicta appareat legendos est cu. Eu vel congue dicunt omittam, no vix
adhuc minimum constituam, quot noluisse id mel. Eu quot sale mutat
duo, ex nisl munere invenire duo. Ne nec ullum utamur. Pro alterum
debitis nostrum no, ut vel aliquid vivendo.
Aliquip fierent praesent quo ne, id sit audiam recusabo delicatissimi.
Usu postulant incorrupte cu. At pro dicit tibique intellegam, cibo
dolore impedit id eam, et aeque feugait assentior has. Quando sensibus
nec ex. Possit sensibus pri ad, unum mutat periculis cu vix.
Mundi tibique vix te, duo simul partiendo qualisque id, est at vidit
sonet tempor. No per solet aeterno deseruisse. Petentium salutandi
definiebas pri cu. Munere vivendum est in. Ei justo congue eligendi
vis, modus offendit omittantur te mel.
Integre voluptaria in qui, sit habemus tractatos constituam no. Utinam
melius conceptam est ne, quo in minimum apeirian delicata, ut ius
porro recusabo. Dicant expetenda vix no, ludus scripserit sed ex, eu
his modo nostro. Ut etiam sonet his, quodsi inciderint philosophia te
per. Nullam lobortis eu cum, vix an sonet efficiendi repudiandae. Vis
ad idque fabellas intellegebat.
Eum commodo senserit conclusionemque ex. Sed forensibus sadipscing ut,
mei in facer delicata periculis, sea ne hinc putent cetero. Nec ne
alia corpora invenire, alia prima soleat te cum. Eleifend posidonium
nam at.
Dolorum indoctum cu quo, ex dolor legendos recteque eam, cu pri zril
discere. Nec civibus officiis dissentiunt ex, est te liber ludus
elaboraret. Cum ea fabellas invenire. Ex vim nostrud eripuit
comprehensam, nam te inermis delectus, saepe inermis senserit.
`

274
vendor/golang.org/x/net/icmp/diag_test.go generated vendored Normal file
View file

@ -0,0 +1,274 @@
// 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.
package icmp_test
import (
"errors"
"fmt"
"net"
"os"
"runtime"
"sync"
"testing"
"time"
"golang.org/x/net/icmp"
"golang.org/x/net/internal/iana"
"golang.org/x/net/internal/nettest"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
)
type diagTest struct {
network, address string
protocol int
m icmp.Message
}
func TestDiag(t *testing.T) {
if testing.Short() {
t.Skip("avoid external network")
}
t.Run("Ping/NonPrivileged", func(t *testing.T) {
switch runtime.GOOS {
case "darwin":
case "linux":
t.Log("you may need to adjust the net.ipv4.ping_group_range kernel state")
default:
t.Logf("not supported on %s", runtime.GOOS)
return
}
for i, dt := range []diagTest{
{
"udp4", "0.0.0.0", iana.ProtocolICMP,
icmp.Message{
Type: ipv4.ICMPTypeEcho, Code: 0,
Body: &icmp.Echo{
ID: os.Getpid() & 0xffff,
Data: []byte("HELLO-R-U-THERE"),
},
},
},
{
"udp6", "::", iana.ProtocolIPv6ICMP,
icmp.Message{
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
Body: &icmp.Echo{
ID: os.Getpid() & 0xffff,
Data: []byte("HELLO-R-U-THERE"),
},
},
},
} {
if err := doDiag(dt, i); err != nil {
t.Error(err)
}
}
})
t.Run("Ping/Privileged", func(t *testing.T) {
if m, ok := nettest.SupportsRawIPSocket(); !ok {
t.Skip(m)
}
for i, dt := range []diagTest{
{
"ip4:icmp", "0.0.0.0", iana.ProtocolICMP,
icmp.Message{
Type: ipv4.ICMPTypeEcho, Code: 0,
Body: &icmp.Echo{
ID: os.Getpid() & 0xffff,
Data: []byte("HELLO-R-U-THERE"),
},
},
},
{
"ip6:ipv6-icmp", "::", iana.ProtocolIPv6ICMP,
icmp.Message{
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
Body: &icmp.Echo{
ID: os.Getpid() & 0xffff,
Data: []byte("HELLO-R-U-THERE"),
},
},
},
} {
if err := doDiag(dt, i); err != nil {
t.Error(err)
}
}
})
t.Run("Probe/Privileged", func(t *testing.T) {
if m, ok := nettest.SupportsRawIPSocket(); !ok {
t.Skip(m)
}
for i, dt := range []diagTest{
{
"ip4:icmp", "0.0.0.0", iana.ProtocolICMP,
icmp.Message{
Type: ipv4.ICMPTypeExtendedEchoRequest, Code: 0,
Body: &icmp.ExtendedEchoRequest{
ID: os.Getpid() & 0xffff,
Local: true,
Extensions: []icmp.Extension{
&icmp.InterfaceIdent{
Class: 3, Type: 1,
Name: "doesnotexist",
},
},
},
},
},
{
"ip6:ipv6-icmp", "::", iana.ProtocolIPv6ICMP,
icmp.Message{
Type: ipv6.ICMPTypeExtendedEchoRequest, Code: 0,
Body: &icmp.ExtendedEchoRequest{
ID: os.Getpid() & 0xffff,
Local: true,
Extensions: []icmp.Extension{
&icmp.InterfaceIdent{
Class: 3, Type: 1,
Name: "doesnotexist",
},
},
},
},
},
} {
if err := doDiag(dt, i); err != nil {
t.Error(err)
}
}
})
}
func doDiag(dt diagTest, seq int) error {
c, err := icmp.ListenPacket(dt.network, dt.address)
if err != nil {
return err
}
defer c.Close()
dst, err := googleAddr(c, dt.protocol)
if err != nil {
return err
}
if dt.network != "udp6" && dt.protocol == iana.ProtocolIPv6ICMP {
var f ipv6.ICMPFilter
f.SetAll(true)
f.Accept(ipv6.ICMPTypeDestinationUnreachable)
f.Accept(ipv6.ICMPTypePacketTooBig)
f.Accept(ipv6.ICMPTypeTimeExceeded)
f.Accept(ipv6.ICMPTypeParameterProblem)
f.Accept(ipv6.ICMPTypeEchoReply)
f.Accept(ipv6.ICMPTypeExtendedEchoReply)
if err := c.IPv6PacketConn().SetICMPFilter(&f); err != nil {
return err
}
}
switch m := dt.m.Body.(type) {
case *icmp.Echo:
m.Seq = 1 << uint(seq)
case *icmp.ExtendedEchoRequest:
m.Seq = 1 << uint(seq)
}
wb, err := dt.m.Marshal(nil)
if err != nil {
return err
}
if n, err := c.WriteTo(wb, dst); err != nil {
return err
} else if n != len(wb) {
return fmt.Errorf("got %v; want %v", n, len(wb))
}
rb := make([]byte, 1500)
if err := c.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
return err
}
n, peer, err := c.ReadFrom(rb)
if err != nil {
return err
}
rm, err := icmp.ParseMessage(dt.protocol, rb[:n])
if err != nil {
return err
}
switch {
case dt.m.Type == ipv4.ICMPTypeEcho && rm.Type == ipv4.ICMPTypeEchoReply:
fallthrough
case dt.m.Type == ipv6.ICMPTypeEchoRequest && rm.Type == ipv6.ICMPTypeEchoReply:
fallthrough
case dt.m.Type == ipv4.ICMPTypeExtendedEchoRequest && rm.Type == ipv4.ICMPTypeExtendedEchoReply:
fallthrough
case dt.m.Type == ipv6.ICMPTypeExtendedEchoRequest && rm.Type == ipv6.ICMPTypeExtendedEchoReply:
return nil
default:
return fmt.Errorf("got %+v from %v; want echo reply or extended echo reply", rm, peer)
}
}
func googleAddr(c *icmp.PacketConn, protocol int) (net.Addr, error) {
host := "ipv4.google.com"
if protocol == iana.ProtocolIPv6ICMP {
host = "ipv6.google.com"
}
ips, err := net.LookupIP(host)
if err != nil {
return nil, err
}
netaddr := func(ip net.IP) (net.Addr, error) {
switch c.LocalAddr().(type) {
case *net.UDPAddr:
return &net.UDPAddr{IP: ip}, nil
case *net.IPAddr:
return &net.IPAddr{IP: ip}, nil
default:
return nil, errors.New("neither UDPAddr nor IPAddr")
}
}
if len(ips) > 0 {
return netaddr(ips[0])
}
return nil, errors.New("no A or AAAA record")
}
func TestConcurrentNonPrivilegedListenPacket(t *testing.T) {
if testing.Short() {
t.Skip("avoid external network")
}
switch runtime.GOOS {
case "darwin":
case "linux":
t.Log("you may need to adjust the net.ipv4.ping_group_range kernel state")
default:
t.Skipf("not supported on %s", runtime.GOOS)
}
network, address := "udp4", "127.0.0.1"
if !nettest.SupportsIPv4() {
network, address = "udp6", "::1"
}
const N = 1000
var wg sync.WaitGroup
wg.Add(N)
for i := 0; i < N; i++ {
go func() {
defer wg.Done()
c, err := icmp.ListenPacket(network, address)
if err != nil {
t.Error(err)
return
}
c.Close()
}()
}
wg.Wait()
}

View file

@ -16,24 +16,24 @@ func (p *DstUnreach) Len(proto int) int {
if p == nil { if p == nil {
return 0 return 0
} }
l, _ := multipartMessageBodyDataLen(proto, p.Data, p.Extensions) l, _ := multipartMessageBodyDataLen(proto, true, p.Data, p.Extensions)
return 4 + l return 4 + l
} }
// Marshal implements the Marshal method of MessageBody interface. // Marshal implements the Marshal method of MessageBody interface.
func (p *DstUnreach) Marshal(proto int) ([]byte, error) { func (p *DstUnreach) Marshal(proto int) ([]byte, error) {
return marshalMultipartMessageBody(proto, p.Data, p.Extensions) return marshalMultipartMessageBody(proto, true, p.Data, p.Extensions)
} }
// parseDstUnreach parses b as an ICMP destination unreachable message // parseDstUnreach parses b as an ICMP destination unreachable message
// body. // body.
func parseDstUnreach(proto int, b []byte) (MessageBody, error) { func parseDstUnreach(proto int, typ Type, b []byte) (MessageBody, error) {
if len(b) < 4 { if len(b) < 4 {
return nil, errMessageTooShort return nil, errMessageTooShort
} }
p := &DstUnreach{} p := &DstUnreach{}
var err error var err error
p.Data, p.Extensions, err = parseMultipartMessageBody(proto, b) p.Data, p.Extensions, err = parseMultipartMessageBody(proto, typ, b)
if err != nil { if err != nil {
return nil, err return nil, err
} }

114
vendor/golang.org/x/net/icmp/echo.go generated vendored
View file

@ -31,7 +31,7 @@ func (p *Echo) Marshal(proto int) ([]byte, error) {
} }
// parseEcho parses b as an ICMP echo request or reply message body. // parseEcho parses b as an ICMP echo request or reply message body.
func parseEcho(proto int, b []byte) (MessageBody, error) { func parseEcho(proto int, _ Type, b []byte) (MessageBody, error) {
bodyLen := len(b) bodyLen := len(b)
if bodyLen < 4 { if bodyLen < 4 {
return nil, errMessageTooShort return nil, errMessageTooShort
@ -43,3 +43,115 @@ func parseEcho(proto int, b []byte) (MessageBody, error) {
} }
return p, nil return p, nil
} }
// An ExtendedEchoRequest represents an ICMP extended echo request
// message body.
type ExtendedEchoRequest struct {
ID int // identifier
Seq int // sequence number
Local bool // must be true when identifying by name or index
Extensions []Extension // extensions
}
// Len implements the Len method of MessageBody interface.
func (p *ExtendedEchoRequest) Len(proto int) int {
if p == nil {
return 0
}
l, _ := multipartMessageBodyDataLen(proto, false, nil, p.Extensions)
return 4 + l
}
// Marshal implements the Marshal method of MessageBody interface.
func (p *ExtendedEchoRequest) Marshal(proto int) ([]byte, error) {
b, err := marshalMultipartMessageBody(proto, false, nil, p.Extensions)
if err != nil {
return nil, err
}
bb := make([]byte, 4)
binary.BigEndian.PutUint16(bb[:2], uint16(p.ID))
bb[2] = byte(p.Seq)
if p.Local {
bb[3] |= 0x01
}
bb = append(bb, b...)
return bb, nil
}
// parseExtendedEchoRequest parses b as an ICMP extended echo request
// message body.
func parseExtendedEchoRequest(proto int, typ Type, b []byte) (MessageBody, error) {
if len(b) < 4+4 {
return nil, errMessageTooShort
}
p := &ExtendedEchoRequest{ID: int(binary.BigEndian.Uint16(b[:2])), Seq: int(b[2])}
if b[3]&0x01 != 0 {
p.Local = true
}
var err error
_, p.Extensions, err = parseMultipartMessageBody(proto, typ, b[4:])
if err != nil {
return nil, err
}
return p, nil
}
// An ExtendedEchoReply represents an ICMP extended echo reply message
// body.
type ExtendedEchoReply struct {
ID int // identifier
Seq int // sequence number
State int // 3-bit state working together with Message.Code
Active bool // probed interface is active
IPv4 bool // probed interface runs IPv4
IPv6 bool // probed interface runs IPv6
}
// Len implements the Len method of MessageBody interface.
func (p *ExtendedEchoReply) Len(proto int) int {
if p == nil {
return 0
}
return 4
}
// Marshal implements the Marshal method of MessageBody interface.
func (p *ExtendedEchoReply) Marshal(proto int) ([]byte, error) {
b := make([]byte, 4)
binary.BigEndian.PutUint16(b[:2], uint16(p.ID))
b[2] = byte(p.Seq)
b[3] = byte(p.State<<5) & 0xe0
if p.Active {
b[3] |= 0x04
}
if p.IPv4 {
b[3] |= 0x02
}
if p.IPv6 {
b[3] |= 0x01
}
return b, nil
}
// parseExtendedEchoReply parses b as an ICMP extended echo reply
// message body.
func parseExtendedEchoReply(proto int, _ Type, b []byte) (MessageBody, error) {
if len(b) < 4 {
return nil, errMessageTooShort
}
p := &ExtendedEchoReply{
ID: int(binary.BigEndian.Uint16(b[:2])),
Seq: int(b[2]),
State: int(b[3]) >> 5,
}
if b[3]&0x04 != 0 {
p.Active = true
}
if b[3]&0x02 != 0 {
p.IPv4 = true
}
if b[3]&0x01 != 0 {
p.IPv6 = true
}
return p, nil
}

View file

@ -4,7 +4,12 @@
package icmp package icmp
import "encoding/binary" import (
"encoding/binary"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
)
// An Extension represents an ICMP extension. // An Extension represents an ICMP extension.
type Extension interface { type Extension interface {
@ -38,7 +43,7 @@ func validExtensionHeader(b []byte) bool {
// It will return a list of ICMP extensions and an adjusted length // It will return a list of ICMP extensions and an adjusted length
// attribute that represents the length of the padded original // attribute that represents the length of the padded original
// datagram field. Otherwise, it returns an error. // datagram field. Otherwise, it returns an error.
func parseExtensions(b []byte, l int) ([]Extension, int, error) { func parseExtensions(typ Type, b []byte, l int) ([]Extension, int, error) {
// Still a lot of non-RFC 4884 compliant implementations are // Still a lot of non-RFC 4884 compliant implementations are
// out there. Set the length attribute l to 128 when it looks // out there. Set the length attribute l to 128 when it looks
// inappropriate for backwards compatibility. // inappropriate for backwards compatibility.
@ -48,20 +53,28 @@ func parseExtensions(b []byte, l int) ([]Extension, int, error) {
// header. // header.
// //
// See RFC 4884 for further information. // See RFC 4884 for further information.
if 128 > l || l+8 > len(b) { switch typ {
l = 128 case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest:
} if len(b) < 8 || !validExtensionHeader(b) {
if l+8 > len(b) {
return nil, -1, errNoExtension
}
if !validExtensionHeader(b[l:]) {
if l == 128 {
return nil, -1, errNoExtension return nil, -1, errNoExtension
} }
l = 128 l = 0
if !validExtensionHeader(b[l:]) { default:
if 128 > l || l+8 > len(b) {
l = 128
}
if l+8 > len(b) {
return nil, -1, errNoExtension return nil, -1, errNoExtension
} }
if !validExtensionHeader(b[l:]) {
if l == 128 {
return nil, -1, errNoExtension
}
l = 128
if !validExtensionHeader(b[l:]) {
return nil, -1, errNoExtension
}
}
} }
var exts []Extension var exts []Extension
for b = b[l+4:]; len(b) >= 4; { for b = b[l+4:]; len(b) >= 4; {
@ -82,6 +95,12 @@ func parseExtensions(b []byte, l int) ([]Extension, int, error) {
return nil, -1, err return nil, -1, err
} }
exts = append(exts, ext) exts = append(exts, ext)
case classInterfaceIdent:
ext, err := parseInterfaceIdent(b[:ol])
if err != nil {
return nil, -1, err
}
exts = append(exts, ext)
} }
b = b[ol:] b = b[ol:]
} }

View file

@ -5,253 +5,327 @@
package icmp package icmp
import ( import (
"fmt"
"net" "net"
"reflect" "reflect"
"testing" "testing"
"golang.org/x/net/internal/iana" "golang.org/x/net/internal/iana"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
) )
var marshalAndParseExtensionTests = []struct {
proto int
hdr []byte
obj []byte
exts []Extension
}{
// MPLS label stack with no label
{
proto: iana.ProtocolICMP,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x04, 0x01, 0x01,
},
exts: []Extension{
&MPLSLabelStack{
Class: classMPLSLabelStack,
Type: typeIncomingMPLSLabelStack,
},
},
},
// MPLS label stack with a single label
{
proto: iana.ProtocolIPv6ICMP,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x08, 0x01, 0x01,
0x03, 0xe8, 0xe9, 0xff,
},
exts: []Extension{
&MPLSLabelStack{
Class: classMPLSLabelStack,
Type: typeIncomingMPLSLabelStack,
Labels: []MPLSLabel{
{
Label: 16014,
TC: 0x4,
S: true,
TTL: 255,
},
},
},
},
},
// MPLS label stack with multiple labels
{
proto: iana.ProtocolICMP,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x0c, 0x01, 0x01,
0x03, 0xe8, 0xde, 0xfe,
0x03, 0xe8, 0xe1, 0xff,
},
exts: []Extension{
&MPLSLabelStack{
Class: classMPLSLabelStack,
Type: typeIncomingMPLSLabelStack,
Labels: []MPLSLabel{
{
Label: 16013,
TC: 0x7,
S: false,
TTL: 254,
},
{
Label: 16014,
TC: 0,
S: true,
TTL: 255,
},
},
},
},
},
// Interface information with no attribute
{
proto: iana.ProtocolICMP,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x04, 0x02, 0x00,
},
exts: []Extension{
&InterfaceInfo{
Class: classInterfaceInfo,
},
},
},
// Interface information with ifIndex and name
{
proto: iana.ProtocolICMP,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x10, 0x02, 0x0a,
0x00, 0x00, 0x00, 0x10,
0x08, byte('e'), byte('n'), byte('1'),
byte('0'), byte('1'), 0x00, 0x00,
},
exts: []Extension{
&InterfaceInfo{
Class: classInterfaceInfo,
Type: 0x0a,
Interface: &net.Interface{
Index: 16,
Name: "en101",
},
},
},
},
// Interface information with ifIndex, IPAddr, name and MTU
{
proto: iana.ProtocolIPv6ICMP,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x28, 0x02, 0x0f,
0x00, 0x00, 0x00, 0x0f,
0x00, 0x02, 0x00, 0x00,
0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0x08, byte('e'), byte('n'), byte('1'),
byte('0'), byte('1'), 0x00, 0x00,
0x00, 0x00, 0x20, 0x00,
},
exts: []Extension{
&InterfaceInfo{
Class: classInterfaceInfo,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.ParseIP("fe80::1"),
Zone: "en101",
},
},
},
},
}
func TestMarshalAndParseExtension(t *testing.T) { func TestMarshalAndParseExtension(t *testing.T) {
for i, tt := range marshalAndParseExtensionTests { fn := func(t *testing.T, proto int, typ Type, hdr, obj []byte, te Extension) error {
for j, ext := range tt.exts { b, err := te.Marshal(proto)
var err error if err != nil {
var b []byte return err
switch ext := ext.(type) { }
case *MPLSLabelStack: if !reflect.DeepEqual(b, obj) {
b, err = ext.Marshal(tt.proto) return fmt.Errorf("got %#v; want %#v", b, obj)
if err != nil { }
t.Errorf("#%v/%v: %v", i, j, err) switch typ {
case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest:
exts, l, err := parseExtensions(typ, append(hdr, obj...), 0)
if err != nil {
return err
}
if l != 0 {
return fmt.Errorf("got %d; want 0", l)
}
if !reflect.DeepEqual(exts, []Extension{te}) {
return fmt.Errorf("got %#v; want %#v", exts[0], te)
}
default:
for i, wire := range []struct {
data []byte // original datagram
inlattr int // length of padded original datagram, a hint
outlattr int // length of padded original datagram, a want
err error
}{
{nil, 0, -1, errNoExtension},
{make([]byte, 127), 128, -1, errNoExtension},
{make([]byte, 128), 127, -1, errNoExtension},
{make([]byte, 128), 128, -1, errNoExtension},
{make([]byte, 128), 129, -1, errNoExtension},
{append(make([]byte, 128), append(hdr, obj...)...), 127, 128, nil},
{append(make([]byte, 128), append(hdr, obj...)...), 128, 128, nil},
{append(make([]byte, 128), append(hdr, obj...)...), 129, 128, nil},
{append(make([]byte, 512), append(hdr, obj...)...), 511, -1, errNoExtension},
{append(make([]byte, 512), append(hdr, obj...)...), 512, 512, nil},
{append(make([]byte, 512), append(hdr, obj...)...), 513, -1, errNoExtension},
} {
exts, l, err := parseExtensions(typ, wire.data, wire.inlattr)
if err != wire.err {
return fmt.Errorf("#%d: got %v; want %v", i, err, wire.err)
}
if wire.err != nil {
continue continue
} }
case *InterfaceInfo: if l != wire.outlattr {
b, err = ext.Marshal(tt.proto) return fmt.Errorf("#%d: got %d; want %d", i, l, wire.outlattr)
if err != nil { }
t.Errorf("#%v/%v: %v", i, j, err) if !reflect.DeepEqual(exts, []Extension{te}) {
continue return fmt.Errorf("#%d: got %#v; want %#v", i, exts[0], te)
} }
} }
if !reflect.DeepEqual(b, tt.obj) {
t.Errorf("#%v/%v: got %#v; want %#v", i, j, b, tt.obj)
continue
}
}
for j, wire := range []struct {
data []byte // original datagram
inlattr int // length of padded original datagram, a hint
outlattr int // length of padded original datagram, a want
err error
}{
{nil, 0, -1, errNoExtension},
{make([]byte, 127), 128, -1, errNoExtension},
{make([]byte, 128), 127, -1, errNoExtension},
{make([]byte, 128), 128, -1, errNoExtension},
{make([]byte, 128), 129, -1, errNoExtension},
{append(make([]byte, 128), append(tt.hdr, tt.obj...)...), 127, 128, nil},
{append(make([]byte, 128), append(tt.hdr, tt.obj...)...), 128, 128, nil},
{append(make([]byte, 128), append(tt.hdr, tt.obj...)...), 129, 128, nil},
{append(make([]byte, 512), append(tt.hdr, tt.obj...)...), 511, -1, errNoExtension},
{append(make([]byte, 512), append(tt.hdr, tt.obj...)...), 512, 512, nil},
{append(make([]byte, 512), append(tt.hdr, tt.obj...)...), 513, -1, errNoExtension},
} {
exts, l, err := parseExtensions(wire.data, wire.inlattr)
if err != wire.err {
t.Errorf("#%v/%v: got %v; want %v", i, j, err, wire.err)
continue
}
if wire.err != nil {
continue
}
if l != wire.outlattr {
t.Errorf("#%v/%v: got %v; want %v", i, j, l, wire.outlattr)
}
if !reflect.DeepEqual(exts, tt.exts) {
for j, ext := range exts {
switch ext := ext.(type) {
case *MPLSLabelStack:
want := tt.exts[j].(*MPLSLabelStack)
t.Errorf("#%v/%v: got %#v; want %#v", i, j, ext, want)
case *InterfaceInfo:
want := tt.exts[j].(*InterfaceInfo)
t.Errorf("#%v/%v: got %#v; want %#v", i, j, ext, want)
}
}
continue
}
} }
return nil
} }
}
var parseInterfaceNameTests = []struct { t.Run("MPLSLabelStack", func(t *testing.T) {
b []byte for _, et := range []struct {
error proto int
}{ typ Type
{[]byte{0, 'e', 'n', '0'}, errInvalidExtension}, hdr []byte
{[]byte{4, 'e', 'n', '0'}, nil}, obj []byte
{[]byte{7, 'e', 'n', '0', 0xff, 0xff, 0xff, 0xff}, errInvalidExtension}, ext Extension
{[]byte{8, 'e', 'n', '0', 0xff, 0xff, 0xff}, errMessageTooShort}, }{
// MPLS label stack with no label
{
proto: iana.ProtocolICMP,
typ: ipv4.ICMPTypeDestinationUnreachable,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x04, 0x01, 0x01,
},
ext: &MPLSLabelStack{
Class: classMPLSLabelStack,
Type: typeIncomingMPLSLabelStack,
},
},
// MPLS label stack with a single label
{
proto: iana.ProtocolIPv6ICMP,
typ: ipv6.ICMPTypeDestinationUnreachable,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x08, 0x01, 0x01,
0x03, 0xe8, 0xe9, 0xff,
},
ext: &MPLSLabelStack{
Class: classMPLSLabelStack,
Type: typeIncomingMPLSLabelStack,
Labels: []MPLSLabel{
{
Label: 16014,
TC: 0x4,
S: true,
TTL: 255,
},
},
},
},
// MPLS label stack with multiple labels
{
proto: iana.ProtocolICMP,
typ: ipv4.ICMPTypeDestinationUnreachable,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x0c, 0x01, 0x01,
0x03, 0xe8, 0xde, 0xfe,
0x03, 0xe8, 0xe1, 0xff,
},
ext: &MPLSLabelStack{
Class: classMPLSLabelStack,
Type: typeIncomingMPLSLabelStack,
Labels: []MPLSLabel{
{
Label: 16013,
TC: 0x7,
S: false,
TTL: 254,
},
{
Label: 16014,
TC: 0,
S: true,
TTL: 255,
},
},
},
},
} {
if err := fn(t, et.proto, et.typ, et.hdr, et.obj, et.ext); err != nil {
t.Error(err)
}
}
})
t.Run("InterfaceInfo", func(t *testing.T) {
for _, et := range []struct {
proto int
typ Type
hdr []byte
obj []byte
ext Extension
}{
// Interface information with no attribute
{
proto: iana.ProtocolICMP,
typ: ipv4.ICMPTypeDestinationUnreachable,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x04, 0x02, 0x00,
},
ext: &InterfaceInfo{
Class: classInterfaceInfo,
},
},
// Interface information with ifIndex and name
{
proto: iana.ProtocolICMP,
typ: ipv4.ICMPTypeDestinationUnreachable,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x10, 0x02, 0x0a,
0x00, 0x00, 0x00, 0x10,
0x08, byte('e'), byte('n'), byte('1'),
byte('0'), byte('1'), 0x00, 0x00,
},
ext: &InterfaceInfo{
Class: classInterfaceInfo,
Type: 0x0a,
Interface: &net.Interface{
Index: 16,
Name: "en101",
},
},
},
// Interface information with ifIndex, IPAddr, name and MTU
{
proto: iana.ProtocolIPv6ICMP,
typ: ipv6.ICMPTypeDestinationUnreachable,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x28, 0x02, 0x0f,
0x00, 0x00, 0x00, 0x0f,
0x00, 0x02, 0x00, 0x00,
0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0x08, byte('e'), byte('n'), byte('1'),
byte('0'), byte('1'), 0x00, 0x00,
0x00, 0x00, 0x20, 0x00,
},
ext: &InterfaceInfo{
Class: classInterfaceInfo,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.ParseIP("fe80::1"),
Zone: "en101",
},
},
},
} {
if err := fn(t, et.proto, et.typ, et.hdr, et.obj, et.ext); err != nil {
t.Error(err)
}
}
})
t.Run("InterfaceIdent", func(t *testing.T) {
for _, et := range []struct {
proto int
typ Type
hdr []byte
obj []byte
ext Extension
}{
// Interface identification by name
{
proto: iana.ProtocolICMP,
typ: ipv4.ICMPTypeExtendedEchoRequest,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x0c, 0x03, 0x01,
byte('e'), byte('n'), byte('1'), byte('0'),
byte('1'), 0x00, 0x00, 0x00,
},
ext: &InterfaceIdent{
Class: classInterfaceIdent,
Type: typeInterfaceByName,
Name: "en101",
},
},
// Interface identification by index
{
proto: iana.ProtocolIPv6ICMP,
typ: ipv6.ICMPTypeExtendedEchoRequest,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x0c, 0x03, 0x02,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0x8f,
},
ext: &InterfaceIdent{
Class: classInterfaceIdent,
Type: typeInterfaceByIndex,
Index: 911,
},
},
// Interface identification by address
{
proto: iana.ProtocolICMP,
typ: ipv4.ICMPTypeExtendedEchoRequest,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x10, 0x03, 0x03,
byte(iana.AddrFamily48bitMAC >> 8), byte(iana.AddrFamily48bitMAC & 0x0f), 0x06, 0x00,
0x01, 0x23, 0x45, 0x67,
0x89, 0xab, 0x00, 0x00,
},
ext: &InterfaceIdent{
Class: classInterfaceIdent,
Type: typeInterfaceByAddress,
AFI: iana.AddrFamily48bitMAC,
Addr: []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab},
},
},
} {
if err := fn(t, et.proto, et.typ, et.hdr, et.obj, et.ext); err != nil {
t.Error(err)
}
}
})
} }
func TestParseInterfaceName(t *testing.T) { func TestParseInterfaceName(t *testing.T) {
ifi := InterfaceInfo{Interface: &net.Interface{}} ifi := InterfaceInfo{Interface: &net.Interface{}}
for i, tt := range parseInterfaceNameTests { for i, tt := range []struct {
b []byte
error
}{
{[]byte{0, 'e', 'n', '0'}, errInvalidExtension},
{[]byte{4, 'e', 'n', '0'}, nil},
{[]byte{7, 'e', 'n', '0', 0xff, 0xff, 0xff, 0xff}, errInvalidExtension},
{[]byte{8, 'e', 'n', '0', 0xff, 0xff, 0xff}, errMessageTooShort},
} {
if _, err := ifi.parseName(tt.b); err != tt.error { if _, err := ifi.parseName(tt.b); err != tt.error {
t.Errorf("#%d: got %v; want %v", i, err, tt.error) t.Errorf("#%d: got %v; want %v", i, err, tt.error)
} }

View file

@ -14,9 +14,6 @@ import (
const ( const (
classInterfaceInfo = 2 classInterfaceInfo = 2
afiIPv4 = 1
afiIPv6 = 2
) )
const ( const (
@ -127,11 +124,11 @@ func (ifi *InterfaceInfo) parseIfIndex(b []byte) ([]byte, error) {
func (ifi *InterfaceInfo) marshalIPAddr(proto int, b []byte) []byte { func (ifi *InterfaceInfo) marshalIPAddr(proto int, b []byte) []byte {
switch proto { switch proto {
case iana.ProtocolICMP: case iana.ProtocolICMP:
binary.BigEndian.PutUint16(b[:2], uint16(afiIPv4)) binary.BigEndian.PutUint16(b[:2], uint16(iana.AddrFamilyIPv4))
copy(b[4:4+net.IPv4len], ifi.Addr.IP.To4()) copy(b[4:4+net.IPv4len], ifi.Addr.IP.To4())
b = b[4+net.IPv4len:] b = b[4+net.IPv4len:]
case iana.ProtocolIPv6ICMP: case iana.ProtocolIPv6ICMP:
binary.BigEndian.PutUint16(b[:2], uint16(afiIPv6)) binary.BigEndian.PutUint16(b[:2], uint16(iana.AddrFamilyIPv6))
copy(b[4:4+net.IPv6len], ifi.Addr.IP.To16()) copy(b[4:4+net.IPv6len], ifi.Addr.IP.To16())
b = b[4+net.IPv6len:] b = b[4+net.IPv6len:]
} }
@ -145,14 +142,14 @@ func (ifi *InterfaceInfo) parseIPAddr(b []byte) ([]byte, error) {
afi := int(binary.BigEndian.Uint16(b[:2])) afi := int(binary.BigEndian.Uint16(b[:2]))
b = b[4:] b = b[4:]
switch afi { switch afi {
case afiIPv4: case iana.AddrFamilyIPv4:
if len(b) < net.IPv4len { if len(b) < net.IPv4len {
return nil, errMessageTooShort return nil, errMessageTooShort
} }
ifi.Addr.IP = make(net.IP, net.IPv4len) ifi.Addr.IP = make(net.IP, net.IPv4len)
copy(ifi.Addr.IP, b[:net.IPv4len]) copy(ifi.Addr.IP, b[:net.IPv4len])
b = b[net.IPv4len:] b = b[net.IPv4len:]
case afiIPv6: case iana.AddrFamilyIPv6:
if len(b) < net.IPv6len { if len(b) < net.IPv6len {
return nil, errMessageTooShort return nil, errMessageTooShort
} }
@ -234,3 +231,92 @@ func parseInterfaceInfo(b []byte) (Extension, error) {
} }
return ifi, nil return ifi, nil
} }
const (
classInterfaceIdent = 3
typeInterfaceByName = 1
typeInterfaceByIndex = 2
typeInterfaceByAddress = 3
)
// An InterfaceIdent represents interface identification.
type InterfaceIdent struct {
Class int // extension object class number
Type int // extension object sub-type
Name string // interface name
Index int // interface index
AFI int // address family identifier; see address family numbers in IANA registry
Addr []byte // address
}
// Len implements the Len method of Extension interface.
func (ifi *InterfaceIdent) Len(_ int) int {
switch ifi.Type {
case typeInterfaceByName:
l := len(ifi.Name)
if l > 255 {
l = 255
}
return 4 + (l+3)&^3
case typeInterfaceByIndex:
return 4 + 8
case typeInterfaceByAddress:
return 4 + 4 + (len(ifi.Addr)+3)&^3
default:
return 4
}
}
// Marshal implements the Marshal method of Extension interface.
func (ifi *InterfaceIdent) Marshal(proto int) ([]byte, error) {
b := make([]byte, ifi.Len(proto))
if err := ifi.marshal(proto, b); err != nil {
return nil, err
}
return b, nil
}
func (ifi *InterfaceIdent) marshal(proto int, b []byte) error {
l := ifi.Len(proto)
binary.BigEndian.PutUint16(b[:2], uint16(l))
b[2], b[3] = classInterfaceIdent, byte(ifi.Type)
switch ifi.Type {
case typeInterfaceByName:
copy(b[4:], ifi.Name)
case typeInterfaceByIndex:
binary.BigEndian.PutUint64(b[4:4+8], uint64(ifi.Index))
case typeInterfaceByAddress:
binary.BigEndian.PutUint16(b[4:4+2], uint16(ifi.AFI))
b[4+2] = byte(len(ifi.Addr))
copy(b[4+4:], ifi.Addr)
}
return nil
}
func parseInterfaceIdent(b []byte) (Extension, error) {
ifi := &InterfaceIdent{
Class: int(b[2]),
Type: int(b[3]),
}
switch ifi.Type {
case typeInterfaceByName:
ifi.Name = strings.Trim(string(b[4:]), string(0))
case typeInterfaceByIndex:
if len(b[4:]) < 8 {
return nil, errInvalidExtension
}
ifi.Index = int(binary.BigEndian.Uint64(b[4 : 4+8]))
case typeInterfaceByAddress:
if len(b[4:]) < 4 {
return nil, errInvalidExtension
}
ifi.AFI = int(binary.BigEndian.Uint16(b[4 : 4+2]))
l := int(b[4+2])
if len(b[4+4:]) < l {
return nil, errInvalidExtension
}
ifi.Addr = make([]byte, l)
copy(ifi.Addr, b[4+4:])
}
return ifi, nil
}

View file

@ -15,69 +15,61 @@ import (
"golang.org/x/net/ipv4" "golang.org/x/net/ipv4"
) )
type ipv4HeaderTest struct {
wireHeaderFromKernel [ipv4.HeaderLen]byte
wireHeaderFromTradBSDKernel [ipv4.HeaderLen]byte
Header *ipv4.Header
}
var ipv4HeaderLittleEndianTest = ipv4HeaderTest{
// TODO(mikio): Add platform dependent wire header formats when
// we support new platforms.
wireHeaderFromKernel: [ipv4.HeaderLen]byte{
0x45, 0x01, 0xbe, 0xef,
0xca, 0xfe, 0x45, 0xdc,
0xff, 0x01, 0xde, 0xad,
172, 16, 254, 254,
192, 168, 0, 1,
},
wireHeaderFromTradBSDKernel: [ipv4.HeaderLen]byte{
0x45, 0x01, 0xef, 0xbe,
0xca, 0xfe, 0x45, 0xdc,
0xff, 0x01, 0xde, 0xad,
172, 16, 254, 254,
192, 168, 0, 1,
},
Header: &ipv4.Header{
Version: ipv4.Version,
Len: ipv4.HeaderLen,
TOS: 1,
TotalLen: 0xbeef,
ID: 0xcafe,
Flags: ipv4.DontFragment,
FragOff: 1500,
TTL: 255,
Protocol: 1,
Checksum: 0xdead,
Src: net.IPv4(172, 16, 254, 254),
Dst: net.IPv4(192, 168, 0, 1),
},
}
func TestParseIPv4Header(t *testing.T) { func TestParseIPv4Header(t *testing.T) {
tt := &ipv4HeaderLittleEndianTest switch socket.NativeEndian {
if socket.NativeEndian != binary.LittleEndian { case binary.LittleEndian:
t.Skip("no test for non-little endian machine yet") t.Run("LittleEndian", func(t *testing.T) {
} // TODO(mikio): Add platform dependent wire
// header formats when we support new
var wh []byte // platforms.
switch runtime.GOOS { wireHeaderFromKernel := [ipv4.HeaderLen]byte{
case "darwin": 0x45, 0x01, 0xbe, 0xef,
wh = tt.wireHeaderFromTradBSDKernel[:] 0xca, 0xfe, 0x45, 0xdc,
case "freebsd": 0xff, 0x01, 0xde, 0xad,
if freebsdVersion >= 1000000 { 172, 16, 254, 254,
wh = tt.wireHeaderFromKernel[:] 192, 168, 0, 1,
} else { }
wh = tt.wireHeaderFromTradBSDKernel[:] wireHeaderFromTradBSDKernel := [ipv4.HeaderLen]byte{
} 0x45, 0x01, 0xef, 0xbe,
default: 0xca, 0xfe, 0x45, 0xdc,
wh = tt.wireHeaderFromKernel[:] 0xff, 0x01, 0xde, 0xad,
} 172, 16, 254, 254,
h, err := ParseIPv4Header(wh) 192, 168, 0, 1,
if err != nil { }
t.Fatal(err) th := &ipv4.Header{
} Version: ipv4.Version,
if !reflect.DeepEqual(h, tt.Header) { Len: ipv4.HeaderLen,
t.Fatalf("got %#v; want %#v", h, tt.Header) TOS: 1,
TotalLen: 0xbeef,
ID: 0xcafe,
Flags: ipv4.DontFragment,
FragOff: 1500,
TTL: 255,
Protocol: 1,
Checksum: 0xdead,
Src: net.IPv4(172, 16, 254, 254),
Dst: net.IPv4(192, 168, 0, 1),
}
var wh []byte
switch runtime.GOOS {
case "darwin":
wh = wireHeaderFromTradBSDKernel[:]
case "freebsd":
if freebsdVersion >= 1000000 {
wh = wireHeaderFromKernel[:]
} else {
wh = wireHeaderFromTradBSDKernel[:]
}
default:
wh = wireHeaderFromKernel[:]
}
h, err := ParseIPv4Header(wh)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(h, th) {
t.Fatalf("got %#v; want %#v", h, th)
}
})
} }
} }

View file

@ -11,6 +11,7 @@
// ICMP extensions for MPLS are defined in RFC 4950. // ICMP extensions for MPLS are defined in RFC 4950.
// ICMP extensions for interface and next-hop identification are // ICMP extensions for interface and next-hop identification are
// defined in RFC 5837. // defined in RFC 5837.
// PROBE: A utility for probing interfaces is defined in RFC 8335.
package icmp // import "golang.org/x/net/icmp" package icmp // import "golang.org/x/net/icmp"
import ( import (
@ -107,21 +108,25 @@ func (m *Message) Marshal(psh []byte) ([]byte, error) {
return b[len(psh):], nil return b[len(psh):], nil
} }
var parseFns = map[Type]func(int, []byte) (MessageBody, error){ var parseFns = map[Type]func(int, Type, []byte) (MessageBody, error){
ipv4.ICMPTypeDestinationUnreachable: parseDstUnreach, ipv4.ICMPTypeDestinationUnreachable: parseDstUnreach,
ipv4.ICMPTypeTimeExceeded: parseTimeExceeded, ipv4.ICMPTypeTimeExceeded: parseTimeExceeded,
ipv4.ICMPTypeParameterProblem: parseParamProb, ipv4.ICMPTypeParameterProblem: parseParamProb,
ipv4.ICMPTypeEcho: parseEcho, ipv4.ICMPTypeEcho: parseEcho,
ipv4.ICMPTypeEchoReply: parseEcho, ipv4.ICMPTypeEchoReply: parseEcho,
ipv4.ICMPTypeExtendedEchoRequest: parseExtendedEchoRequest,
ipv4.ICMPTypeExtendedEchoReply: parseExtendedEchoReply,
ipv6.ICMPTypeDestinationUnreachable: parseDstUnreach, ipv6.ICMPTypeDestinationUnreachable: parseDstUnreach,
ipv6.ICMPTypePacketTooBig: parsePacketTooBig, ipv6.ICMPTypePacketTooBig: parsePacketTooBig,
ipv6.ICMPTypeTimeExceeded: parseTimeExceeded, ipv6.ICMPTypeTimeExceeded: parseTimeExceeded,
ipv6.ICMPTypeParameterProblem: parseParamProb, ipv6.ICMPTypeParameterProblem: parseParamProb,
ipv6.ICMPTypeEchoRequest: parseEcho, ipv6.ICMPTypeEchoRequest: parseEcho,
ipv6.ICMPTypeEchoReply: parseEcho, ipv6.ICMPTypeEchoReply: parseEcho,
ipv6.ICMPTypeExtendedEchoRequest: parseExtendedEchoRequest,
ipv6.ICMPTypeExtendedEchoReply: parseExtendedEchoReply,
} }
// ParseMessage parses b as an ICMP message. // ParseMessage parses b as an ICMP message.
@ -143,7 +148,7 @@ func ParseMessage(proto int, b []byte) (*Message, error) {
if fn, ok := parseFns[m.Type]; !ok { if fn, ok := parseFns[m.Type]; !ok {
m.Body, err = parseDefaultMessageBody(proto, b[4:]) m.Body, err = parseDefaultMessageBody(proto, b[4:])
} else { } else {
m.Body, err = fn(proto, b[4:]) m.Body, err = fn(proto, m.Type, b[4:])
} }
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -15,120 +15,141 @@ import (
"golang.org/x/net/ipv6" "golang.org/x/net/ipv6"
) )
var marshalAndParseMessageForIPv4Tests = []icmp.Message{ func TestMarshalAndParseMessage(t *testing.T) {
{ fn := func(t *testing.T, proto int, tms []icmp.Message) {
Type: ipv4.ICMPTypeDestinationUnreachable, Code: 15, var pshs [][]byte
Body: &icmp.DstUnreach{ switch proto {
Data: []byte("ERROR-INVOKING-PACKET"), case iana.ProtocolICMP:
}, pshs = [][]byte{nil}
}, case iana.ProtocolIPv6ICMP:
{ pshs = [][]byte{
Type: ipv4.ICMPTypeTimeExceeded, Code: 1, icmp.IPv6PseudoHeader(net.ParseIP("fe80::1"), net.ParseIP("ff02::1")),
Body: &icmp.TimeExceeded{ nil,
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv4.ICMPTypeParameterProblem, Code: 2,
Body: &icmp.ParamProb{
Pointer: 8,
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv4.ICMPTypeEcho, Code: 0,
Body: &icmp.Echo{
ID: 1, Seq: 2,
Data: []byte("HELLO-R-U-THERE"),
},
},
{
Type: ipv4.ICMPTypePhoturis,
Body: &icmp.DefaultMessageBody{
Data: []byte{0x80, 0x40, 0x20, 0x10},
},
},
}
func TestMarshalAndParseMessageForIPv4(t *testing.T) {
for i, tt := range marshalAndParseMessageForIPv4Tests {
b, err := tt.Marshal(nil)
if err != nil {
t.Fatal(err)
}
m, err := icmp.ParseMessage(iana.ProtocolICMP, b)
if err != nil {
t.Fatal(err)
}
if m.Type != tt.Type || m.Code != tt.Code {
t.Errorf("#%v: got %v; want %v", i, m, &tt)
}
if !reflect.DeepEqual(m.Body, tt.Body) {
t.Errorf("#%v: got %v; want %v", i, m.Body, tt.Body)
}
}
}
var marshalAndParseMessageForIPv6Tests = []icmp.Message{
{
Type: ipv6.ICMPTypeDestinationUnreachable, Code: 6,
Body: &icmp.DstUnreach{
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv6.ICMPTypePacketTooBig, Code: 0,
Body: &icmp.PacketTooBig{
MTU: 1<<16 - 1,
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv6.ICMPTypeTimeExceeded, Code: 1,
Body: &icmp.TimeExceeded{
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv6.ICMPTypeParameterProblem, Code: 2,
Body: &icmp.ParamProb{
Pointer: 8,
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
Body: &icmp.Echo{
ID: 1, Seq: 2,
Data: []byte("HELLO-R-U-THERE"),
},
},
{
Type: ipv6.ICMPTypeDuplicateAddressConfirmation,
Body: &icmp.DefaultMessageBody{
Data: []byte{0x80, 0x40, 0x20, 0x10},
},
},
}
func TestMarshalAndParseMessageForIPv6(t *testing.T) {
pshicmp := icmp.IPv6PseudoHeader(net.ParseIP("fe80::1"), net.ParseIP("ff02::1"))
for i, tt := range marshalAndParseMessageForIPv6Tests {
for _, psh := range [][]byte{pshicmp, nil} {
b, err := tt.Marshal(psh)
if err != nil {
t.Fatal(err)
} }
m, err := icmp.ParseMessage(iana.ProtocolIPv6ICMP, b) }
if err != nil { for i, tm := range tms {
t.Fatal(err) for _, psh := range pshs {
} b, err := tm.Marshal(psh)
if m.Type != tt.Type || m.Code != tt.Code { if err != nil {
t.Errorf("#%v: got %v; want %v", i, m, &tt) t.Fatal(err)
} }
if !reflect.DeepEqual(m.Body, tt.Body) { m, err := icmp.ParseMessage(proto, b)
t.Errorf("#%v: got %v; want %v", i, m.Body, tt.Body) if err != nil {
t.Fatal(err)
}
if m.Type != tm.Type || m.Code != tm.Code {
t.Errorf("#%d: got %#v; want %#v", i, m, &tm)
}
if !reflect.DeepEqual(m.Body, tm.Body) {
t.Errorf("#%d: got %#v; want %#v", i, m.Body, tm.Body)
}
} }
} }
} }
t.Run("IPv4", func(t *testing.T) {
fn(t, iana.ProtocolICMP,
[]icmp.Message{
{
Type: ipv4.ICMPTypeDestinationUnreachable, Code: 15,
Body: &icmp.DstUnreach{
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv4.ICMPTypeTimeExceeded, Code: 1,
Body: &icmp.TimeExceeded{
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv4.ICMPTypeParameterProblem, Code: 2,
Body: &icmp.ParamProb{
Pointer: 8,
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv4.ICMPTypeEcho, Code: 0,
Body: &icmp.Echo{
ID: 1, Seq: 2,
Data: []byte("HELLO-R-U-THERE"),
},
},
{
Type: ipv4.ICMPTypeExtendedEchoRequest, Code: 0,
Body: &icmp.ExtendedEchoRequest{
ID: 1, Seq: 2,
},
},
{
Type: ipv4.ICMPTypeExtendedEchoReply, Code: 0,
Body: &icmp.ExtendedEchoReply{
State: 4 /* Delay */, Active: true, IPv4: true,
},
},
{
Type: ipv4.ICMPTypePhoturis,
Body: &icmp.DefaultMessageBody{
Data: []byte{0x80, 0x40, 0x20, 0x10},
},
},
})
})
t.Run("IPv6", func(t *testing.T) {
fn(t, iana.ProtocolIPv6ICMP,
[]icmp.Message{
{
Type: ipv6.ICMPTypeDestinationUnreachable, Code: 6,
Body: &icmp.DstUnreach{
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv6.ICMPTypePacketTooBig, Code: 0,
Body: &icmp.PacketTooBig{
MTU: 1<<16 - 1,
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv6.ICMPTypeTimeExceeded, Code: 1,
Body: &icmp.TimeExceeded{
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv6.ICMPTypeParameterProblem, Code: 2,
Body: &icmp.ParamProb{
Pointer: 8,
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
Body: &icmp.Echo{
ID: 1, Seq: 2,
Data: []byte("HELLO-R-U-THERE"),
},
},
{
Type: ipv6.ICMPTypeExtendedEchoRequest, Code: 0,
Body: &icmp.ExtendedEchoRequest{
ID: 1, Seq: 2,
},
},
{
Type: ipv6.ICMPTypeExtendedEchoReply, Code: 0,
Body: &icmp.ExtendedEchoReply{
State: 5 /* Probe */, Active: true, IPv6: true,
},
},
{
Type: ipv6.ICMPTypeDuplicateAddressConfirmation,
Body: &icmp.DefaultMessageBody{
Data: []byte{0x80, 0x40, 0x20, 0x10},
},
},
})
})
} }

View file

@ -10,12 +10,14 @@ import "golang.org/x/net/internal/iana"
// exts as extensions, and returns a required length for message body // exts as extensions, and returns a required length for message body
// and a required length for a padded original datagram in wire // and a required length for a padded original datagram in wire
// format. // format.
func multipartMessageBodyDataLen(proto int, b []byte, exts []Extension) (bodyLen, dataLen int) { func multipartMessageBodyDataLen(proto int, withOrigDgram bool, b []byte, exts []Extension) (bodyLen, dataLen int) {
for _, ext := range exts { for _, ext := range exts {
bodyLen += ext.Len(proto) bodyLen += ext.Len(proto)
} }
if bodyLen > 0 { if bodyLen > 0 {
dataLen = multipartMessageOrigDatagramLen(proto, b) if withOrigDgram {
dataLen = multipartMessageOrigDatagramLen(proto, b)
}
bodyLen += 4 // length of extension header bodyLen += 4 // length of extension header
} else { } else {
dataLen = len(b) dataLen = len(b)
@ -50,8 +52,8 @@ func multipartMessageOrigDatagramLen(proto int, b []byte) int {
// marshalMultipartMessageBody takes data as an original datagram and // marshalMultipartMessageBody takes data as an original datagram and
// exts as extesnsions, and returns a binary encoding of message body. // exts as extesnsions, and returns a binary encoding of message body.
// It can be used for non-multipart message bodies when exts is nil. // It can be used for non-multipart message bodies when exts is nil.
func marshalMultipartMessageBody(proto int, data []byte, exts []Extension) ([]byte, error) { func marshalMultipartMessageBody(proto int, withOrigDgram bool, data []byte, exts []Extension) ([]byte, error) {
bodyLen, dataLen := multipartMessageBodyDataLen(proto, data, exts) bodyLen, dataLen := multipartMessageBodyDataLen(proto, withOrigDgram, data, exts)
b := make([]byte, 4+bodyLen) b := make([]byte, 4+bodyLen)
copy(b[4:], data) copy(b[4:], data)
off := dataLen + 4 off := dataLen + 4
@ -71,16 +73,23 @@ func marshalMultipartMessageBody(proto int, data []byte, exts []Extension) ([]by
return nil, err return nil, err
} }
off += ext.Len(proto) off += ext.Len(proto)
case *InterfaceIdent:
if err := ext.marshal(proto, b[off:]); err != nil {
return nil, err
}
off += ext.Len(proto)
} }
} }
s := checksum(b[dataLen+4:]) s := checksum(b[dataLen+4:])
b[dataLen+4+2] ^= byte(s) b[dataLen+4+2] ^= byte(s)
b[dataLen+4+3] ^= byte(s >> 8) b[dataLen+4+3] ^= byte(s >> 8)
switch proto { if withOrigDgram {
case iana.ProtocolICMP: switch proto {
b[1] = byte(dataLen / 4) case iana.ProtocolICMP:
case iana.ProtocolIPv6ICMP: b[1] = byte(dataLen / 4)
b[0] = byte(dataLen / 8) case iana.ProtocolIPv6ICMP:
b[0] = byte(dataLen / 8)
}
} }
} }
return b, nil return b, nil
@ -88,7 +97,7 @@ func marshalMultipartMessageBody(proto int, data []byte, exts []Extension) ([]by
// parseMultipartMessageBody parses b as either a non-multipart // parseMultipartMessageBody parses b as either a non-multipart
// message body or a multipart message body. // message body or a multipart message body.
func parseMultipartMessageBody(proto int, b []byte) ([]byte, []Extension, error) { func parseMultipartMessageBody(proto int, typ Type, b []byte) ([]byte, []Extension, error) {
var l int var l int
switch proto { switch proto {
case iana.ProtocolICMP: case iana.ProtocolICMP:
@ -99,11 +108,14 @@ func parseMultipartMessageBody(proto int, b []byte) ([]byte, []Extension, error)
if len(b) == 4 { if len(b) == 4 {
return nil, nil, nil return nil, nil, nil
} }
exts, l, err := parseExtensions(b[4:], l) exts, l, err := parseExtensions(typ, b[4:], l)
if err != nil { if err != nil {
l = len(b) - 4 l = len(b) - 4
} }
data := make([]byte, l) var data []byte
copy(data, b[4:]) if l > 0 {
data = make([]byte, l)
copy(data, b[4:])
}
return data, exts, nil return data, exts, nil
} }

View file

@ -5,6 +5,7 @@
package icmp_test package icmp_test
import ( import (
"errors"
"fmt" "fmt"
"net" "net"
"reflect" "reflect"
@ -16,425 +17,557 @@ import (
"golang.org/x/net/ipv6" "golang.org/x/net/ipv6"
) )
var marshalAndParseMultipartMessageForIPv4Tests = []icmp.Message{ func TestMarshalAndParseMultipartMessage(t *testing.T) {
{ fn := func(t *testing.T, proto int, tm icmp.Message) error {
Type: ipv4.ICMPTypeDestinationUnreachable, Code: 15, b, err := tm.Marshal(nil)
Body: &icmp.DstUnreach{
Data: []byte("ERROR-INVOKING-PACKET"),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{
Class: 1,
Type: 1,
Labels: []icmp.MPLSLabel{
{
Label: 16014,
TC: 0x4,
S: true,
TTL: 255,
},
},
},
&icmp.InterfaceInfo{
Class: 2,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.IPv4(192, 168, 0, 1).To4(),
},
},
},
},
},
{
Type: ipv4.ICMPTypeTimeExceeded, Code: 1,
Body: &icmp.TimeExceeded{
Data: []byte("ERROR-INVOKING-PACKET"),
Extensions: []icmp.Extension{
&icmp.InterfaceInfo{
Class: 2,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.IPv4(192, 168, 0, 1).To4(),
},
},
&icmp.MPLSLabelStack{
Class: 1,
Type: 1,
Labels: []icmp.MPLSLabel{
{
Label: 16014,
TC: 0x4,
S: true,
TTL: 255,
},
},
},
},
},
},
{
Type: ipv4.ICMPTypeParameterProblem, Code: 2,
Body: &icmp.ParamProb{
Pointer: 8,
Data: []byte("ERROR-INVOKING-PACKET"),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{
Class: 1,
Type: 1,
Labels: []icmp.MPLSLabel{
{
Label: 16014,
TC: 0x4,
S: true,
TTL: 255,
},
},
},
&icmp.InterfaceInfo{
Class: 2,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.IPv4(192, 168, 0, 1).To4(),
},
},
&icmp.InterfaceInfo{
Class: 2,
Type: 0x2f,
Interface: &net.Interface{
Index: 16,
Name: "en102",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.IPv4(192, 168, 0, 2).To4(),
},
},
},
},
},
}
func TestMarshalAndParseMultipartMessageForIPv4(t *testing.T) {
for i, tt := range marshalAndParseMultipartMessageForIPv4Tests {
b, err := tt.Marshal(nil)
if err != nil { if err != nil {
t.Fatal(err) return err
} }
if b[5] != 32 { switch tm.Type {
t.Errorf("#%v: got %v; want 32", i, b[5]) case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest:
default:
switch proto {
case iana.ProtocolICMP:
if b[5] != 32 {
return fmt.Errorf("got %d; want 32", b[5])
}
case iana.ProtocolIPv6ICMP:
if b[4] != 16 {
return fmt.Errorf("got %d; want 16", b[4])
}
default:
return fmt.Errorf("unknown protocol: %d", proto)
}
} }
m, err := icmp.ParseMessage(iana.ProtocolICMP, b) m, err := icmp.ParseMessage(proto, b)
if err != nil { if err != nil {
t.Fatal(err) return err
} }
if m.Type != tt.Type || m.Code != tt.Code { if m.Type != tm.Type || m.Code != tm.Code {
t.Errorf("#%v: got %v; want %v", i, m, &tt) return fmt.Errorf("got %v; want %v", m, &tm)
} }
switch m.Type { switch m.Type {
case ipv4.ICMPTypeDestinationUnreachable: case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest:
got, want := m.Body.(*icmp.DstUnreach), tt.Body.(*icmp.DstUnreach) got, want := m.Body.(*icmp.ExtendedEchoRequest), tm.Body.(*icmp.ExtendedEchoRequest)
if !reflect.DeepEqual(got.Extensions, want.Extensions) { if !reflect.DeepEqual(got.Extensions, want.Extensions) {
t.Error(dumpExtensions(i, got.Extensions, want.Extensions)) return errors.New(dumpExtensions(got.Extensions, want.Extensions))
}
case ipv4.ICMPTypeDestinationUnreachable:
got, want := m.Body.(*icmp.DstUnreach), tm.Body.(*icmp.DstUnreach)
if !reflect.DeepEqual(got.Extensions, want.Extensions) {
return errors.New(dumpExtensions(got.Extensions, want.Extensions))
} }
if len(got.Data) != 128 { if len(got.Data) != 128 {
t.Errorf("#%v: got %v; want 128", i, len(got.Data)) return fmt.Errorf("got %d; want 128", len(got.Data))
} }
case ipv4.ICMPTypeTimeExceeded: case ipv4.ICMPTypeTimeExceeded:
got, want := m.Body.(*icmp.TimeExceeded), tt.Body.(*icmp.TimeExceeded) got, want := m.Body.(*icmp.TimeExceeded), tm.Body.(*icmp.TimeExceeded)
if !reflect.DeepEqual(got.Extensions, want.Extensions) { if !reflect.DeepEqual(got.Extensions, want.Extensions) {
t.Error(dumpExtensions(i, got.Extensions, want.Extensions)) return errors.New(dumpExtensions(got.Extensions, want.Extensions))
} }
if len(got.Data) != 128 { if len(got.Data) != 128 {
t.Errorf("#%v: got %v; want 128", i, len(got.Data)) return fmt.Errorf("got %d; want 128", len(got.Data))
} }
case ipv4.ICMPTypeParameterProblem: case ipv4.ICMPTypeParameterProblem:
got, want := m.Body.(*icmp.ParamProb), tt.Body.(*icmp.ParamProb) got, want := m.Body.(*icmp.ParamProb), tm.Body.(*icmp.ParamProb)
if !reflect.DeepEqual(got.Extensions, want.Extensions) { if !reflect.DeepEqual(got.Extensions, want.Extensions) {
t.Error(dumpExtensions(i, got.Extensions, want.Extensions)) return errors.New(dumpExtensions(got.Extensions, want.Extensions))
} }
if len(got.Data) != 128 { if len(got.Data) != 128 {
t.Errorf("#%v: got %v; want 128", i, len(got.Data)) return fmt.Errorf("got %d; want 128", len(got.Data))
} }
case ipv6.ICMPTypeDestinationUnreachable:
got, want := m.Body.(*icmp.DstUnreach), tm.Body.(*icmp.DstUnreach)
if !reflect.DeepEqual(got.Extensions, want.Extensions) {
return errors.New(dumpExtensions(got.Extensions, want.Extensions))
}
if len(got.Data) != 128 {
return fmt.Errorf("got %d; want 128", len(got.Data))
}
case ipv6.ICMPTypeTimeExceeded:
got, want := m.Body.(*icmp.TimeExceeded), tm.Body.(*icmp.TimeExceeded)
if !reflect.DeepEqual(got.Extensions, want.Extensions) {
return errors.New(dumpExtensions(got.Extensions, want.Extensions))
}
if len(got.Data) != 128 {
return fmt.Errorf("got %d; want 128", len(got.Data))
}
default:
return fmt.Errorf("unknown message type: %v", m.Type)
} }
return nil
} }
}
var marshalAndParseMultipartMessageForIPv6Tests = []icmp.Message{ t.Run("IPv4", func(t *testing.T) {
{ for i, tm := range []icmp.Message{
Type: ipv6.ICMPTypeDestinationUnreachable, Code: 6, {
Body: &icmp.DstUnreach{ Type: ipv4.ICMPTypeDestinationUnreachable, Code: 15,
Data: []byte("ERROR-INVOKING-PACKET"), Body: &icmp.DstUnreach{
Extensions: []icmp.Extension{ Data: []byte("ERROR-INVOKING-PACKET"),
&icmp.MPLSLabelStack{ Extensions: []icmp.Extension{
Class: 1, &icmp.MPLSLabelStack{
Type: 1, Class: 1,
Labels: []icmp.MPLSLabel{ Type: 1,
{ Labels: []icmp.MPLSLabel{
Label: 16014, {
TC: 0x4, Label: 16014,
S: true, TC: 0x4,
TTL: 255, S: true,
TTL: 255,
},
},
},
&icmp.InterfaceInfo{
Class: 2,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.IPv4(192, 168, 0, 1).To4(),
},
}, },
}, },
}, },
&icmp.InterfaceInfo{
Class: 2,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.ParseIP("fe80::1"),
Zone: "en101",
},
},
}, },
}, {
}, Type: ipv4.ICMPTypeTimeExceeded, Code: 1,
{ Body: &icmp.TimeExceeded{
Type: ipv6.ICMPTypeTimeExceeded, Code: 1, Data: []byte("ERROR-INVOKING-PACKET"),
Body: &icmp.TimeExceeded{ Extensions: []icmp.Extension{
Data: []byte("ERROR-INVOKING-PACKET"), &icmp.InterfaceInfo{
Extensions: []icmp.Extension{ Class: 2,
&icmp.InterfaceInfo{ Type: 0x0f,
Class: 2, Interface: &net.Interface{
Type: 0x0f, Index: 15,
Interface: &net.Interface{ Name: "en101",
Index: 15, MTU: 8192,
Name: "en101", },
MTU: 8192, Addr: &net.IPAddr{
}, IP: net.IPv4(192, 168, 0, 1).To4(),
Addr: &net.IPAddr{ },
IP: net.ParseIP("fe80::1"), },
Zone: "en101", &icmp.MPLSLabelStack{
}, Class: 1,
}, Type: 1,
&icmp.MPLSLabelStack{ Labels: []icmp.MPLSLabel{
Class: 1, {
Type: 1, Label: 16014,
Labels: []icmp.MPLSLabel{ TC: 0x4,
{ S: true,
Label: 16014, TTL: 255,
TC: 0x4, },
S: true, },
TTL: 255,
}, },
}, },
}, },
&icmp.InterfaceInfo{ },
Class: 2, {
Type: 0x2f, Type: ipv4.ICMPTypeParameterProblem, Code: 2,
Interface: &net.Interface{ Body: &icmp.ParamProb{
Index: 16, Pointer: 8,
Name: "en102", Data: []byte("ERROR-INVOKING-PACKET"),
MTU: 8192, Extensions: []icmp.Extension{
}, &icmp.MPLSLabelStack{
Addr: &net.IPAddr{ Class: 1,
IP: net.ParseIP("fe80::1"), Type: 1,
Zone: "en102", Labels: []icmp.MPLSLabel{
{
Label: 16014,
TC: 0x4,
S: true,
TTL: 255,
},
},
},
&icmp.InterfaceInfo{
Class: 2,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.IPv4(192, 168, 0, 1).To4(),
},
},
&icmp.InterfaceInfo{
Class: 2,
Type: 0x2f,
Interface: &net.Interface{
Index: 16,
Name: "en102",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.IPv4(192, 168, 0, 2).To4(),
},
},
}, },
}, },
}, },
}, {
}, Type: ipv4.ICMPTypeExtendedEchoRequest, Code: 0,
} Body: &icmp.ExtendedEchoRequest{
ID: 1, Seq: 2, Local: true,
func TestMarshalAndParseMultipartMessageForIPv6(t *testing.T) { Extensions: []icmp.Extension{
pshicmp := icmp.IPv6PseudoHeader(net.ParseIP("fe80::1"), net.ParseIP("ff02::1")) &icmp.InterfaceIdent{
for i, tt := range marshalAndParseMultipartMessageForIPv6Tests { Class: 3,
for _, psh := range [][]byte{pshicmp, nil} { Type: 1,
b, err := tt.Marshal(psh) Name: "en101",
if err != nil { },
t.Fatal(err) },
} },
if b[4] != 16 { },
t.Errorf("#%v: got %v; want 16", i, b[4]) {
} Type: ipv4.ICMPTypeExtendedEchoRequest, Code: 0,
m, err := icmp.ParseMessage(iana.ProtocolIPv6ICMP, b) Body: &icmp.ExtendedEchoRequest{
if err != nil { ID: 1, Seq: 2, Local: true,
t.Fatal(err) Extensions: []icmp.Extension{
} &icmp.InterfaceIdent{
if m.Type != tt.Type || m.Code != tt.Code { Class: 3,
t.Errorf("#%v: got %v; want %v", i, m, &tt) Type: 2,
} Index: 911,
switch m.Type { },
case ipv6.ICMPTypeDestinationUnreachable: &icmp.InterfaceIdent{
got, want := m.Body.(*icmp.DstUnreach), tt.Body.(*icmp.DstUnreach) Class: 3,
if !reflect.DeepEqual(got.Extensions, want.Extensions) { Type: 1,
t.Error(dumpExtensions(i, got.Extensions, want.Extensions)) Name: "en101",
} },
if len(got.Data) != 128 { },
t.Errorf("#%v: got %v; want 128", i, len(got.Data)) },
} },
case ipv6.ICMPTypeTimeExceeded: {
got, want := m.Body.(*icmp.TimeExceeded), tt.Body.(*icmp.TimeExceeded) Type: ipv4.ICMPTypeExtendedEchoRequest, Code: 0,
if !reflect.DeepEqual(got.Extensions, want.Extensions) { Body: &icmp.ExtendedEchoRequest{
t.Error(dumpExtensions(i, got.Extensions, want.Extensions)) ID: 1, Seq: 2,
} Extensions: []icmp.Extension{
if len(got.Data) != 128 { &icmp.InterfaceIdent{
t.Errorf("#%v: got %v; want 128", i, len(got.Data)) Class: 3,
} Type: 3,
AFI: iana.AddrFamily48bitMAC,
Addr: []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab},
},
},
},
},
} {
if err := fn(t, iana.ProtocolICMP, tm); err != nil {
t.Errorf("#%d: %v", i, err)
} }
} }
} })
t.Run("IPv6", func(t *testing.T) {
for i, tm := range []icmp.Message{
{
Type: ipv6.ICMPTypeDestinationUnreachable, Code: 6,
Body: &icmp.DstUnreach{
Data: []byte("ERROR-INVOKING-PACKET"),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{
Class: 1,
Type: 1,
Labels: []icmp.MPLSLabel{
{
Label: 16014,
TC: 0x4,
S: true,
TTL: 255,
},
},
},
&icmp.InterfaceInfo{
Class: 2,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.ParseIP("fe80::1"),
Zone: "en101",
},
},
},
},
},
{
Type: ipv6.ICMPTypeTimeExceeded, Code: 1,
Body: &icmp.TimeExceeded{
Data: []byte("ERROR-INVOKING-PACKET"),
Extensions: []icmp.Extension{
&icmp.InterfaceInfo{
Class: 2,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.ParseIP("fe80::1"),
Zone: "en101",
},
},
&icmp.MPLSLabelStack{
Class: 1,
Type: 1,
Labels: []icmp.MPLSLabel{
{
Label: 16014,
TC: 0x4,
S: true,
TTL: 255,
},
},
},
&icmp.InterfaceInfo{
Class: 2,
Type: 0x2f,
Interface: &net.Interface{
Index: 16,
Name: "en102",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.ParseIP("fe80::1"),
Zone: "en102",
},
},
},
},
},
{
Type: ipv6.ICMPTypeExtendedEchoRequest, Code: 0,
Body: &icmp.ExtendedEchoRequest{
ID: 1, Seq: 2, Local: true,
Extensions: []icmp.Extension{
&icmp.InterfaceIdent{
Class: 3,
Type: 1,
Name: "en101",
},
},
},
},
{
Type: ipv6.ICMPTypeExtendedEchoRequest, Code: 0,
Body: &icmp.ExtendedEchoRequest{
ID: 1, Seq: 2, Local: true,
Extensions: []icmp.Extension{
&icmp.InterfaceIdent{
Class: 3,
Type: 1,
Name: "en101",
},
&icmp.InterfaceIdent{
Class: 3,
Type: 2,
Index: 911,
},
},
},
},
{
Type: ipv6.ICMPTypeExtendedEchoRequest, Code: 0,
Body: &icmp.ExtendedEchoRequest{
ID: 1, Seq: 2,
Extensions: []icmp.Extension{
&icmp.InterfaceIdent{
Class: 3,
Type: 3,
AFI: iana.AddrFamilyIPv4,
Addr: []byte{192, 0, 2, 1},
},
},
},
},
} {
if err := fn(t, iana.ProtocolIPv6ICMP, tm); err != nil {
t.Errorf("#%d: %v", i, err)
}
}
})
} }
func dumpExtensions(i int, gotExts, wantExts []icmp.Extension) string { func dumpExtensions(gotExts, wantExts []icmp.Extension) string {
var s string var s string
for j, got := range gotExts { for i, got := range gotExts {
switch got := got.(type) { switch got := got.(type) {
case *icmp.MPLSLabelStack: case *icmp.MPLSLabelStack:
want := wantExts[j].(*icmp.MPLSLabelStack) want := wantExts[i].(*icmp.MPLSLabelStack)
if !reflect.DeepEqual(got, want) { if !reflect.DeepEqual(got, want) {
s += fmt.Sprintf("#%v/%v: got %#v; want %#v\n", i, j, got, want) s += fmt.Sprintf("#%d: got %#v; want %#v\n", i, got, want)
} }
case *icmp.InterfaceInfo: case *icmp.InterfaceInfo:
want := wantExts[j].(*icmp.InterfaceInfo) want := wantExts[i].(*icmp.InterfaceInfo)
if !reflect.DeepEqual(got, want) { if !reflect.DeepEqual(got, want) {
s += fmt.Sprintf("#%v/%v: got %#v, %#v, %#v; want %#v, %#v, %#v\n", i, j, got, got.Interface, got.Addr, want, want.Interface, want.Addr) s += fmt.Sprintf("#%d: got %#v, %#v, %#v; want %#v, %#v, %#v\n", i, got, got.Interface, got.Addr, want, want.Interface, want.Addr)
}
case *icmp.InterfaceIdent:
want := wantExts[i].(*icmp.InterfaceIdent)
if !reflect.DeepEqual(got, want) {
s += fmt.Sprintf("#%d: got %#v; want %#v\n", i, got, want)
} }
} }
} }
if len(s) == 0 {
return "<nil>"
}
return s[:len(s)-1] return s[:len(s)-1]
} }
var multipartMessageBodyLenTests = []struct {
proto int
in icmp.MessageBody
out int
}{
{
iana.ProtocolICMP,
&icmp.DstUnreach{
Data: make([]byte, ipv4.HeaderLen),
},
4 + ipv4.HeaderLen, // unused and original datagram
},
{
iana.ProtocolICMP,
&icmp.TimeExceeded{
Data: make([]byte, ipv4.HeaderLen),
},
4 + ipv4.HeaderLen, // unused and original datagram
},
{
iana.ProtocolICMP,
&icmp.ParamProb{
Data: make([]byte, ipv4.HeaderLen),
},
4 + ipv4.HeaderLen, // [pointer, unused] and original datagram
},
{
iana.ProtocolICMP,
&icmp.ParamProb{
Data: make([]byte, ipv4.HeaderLen),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 128, // [pointer, length, unused], extension header, object header, object payload, original datagram
},
{
iana.ProtocolICMP,
&icmp.ParamProb{
Data: make([]byte, 128),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 128, // [pointer, length, unused], extension header, object header, object payload and original datagram
},
{
iana.ProtocolICMP,
&icmp.ParamProb{
Data: make([]byte, 129),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 132, // [pointer, length, unused], extension header, object header, object payload and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.DstUnreach{
Data: make([]byte, ipv6.HeaderLen),
},
4 + ipv6.HeaderLen, // unused and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.PacketTooBig{
Data: make([]byte, ipv6.HeaderLen),
},
4 + ipv6.HeaderLen, // mtu and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.TimeExceeded{
Data: make([]byte, ipv6.HeaderLen),
},
4 + ipv6.HeaderLen, // unused and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.ParamProb{
Data: make([]byte, ipv6.HeaderLen),
},
4 + ipv6.HeaderLen, // pointer and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.DstUnreach{
Data: make([]byte, 127),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 128, // [length, unused], extension header, object header, object payload and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.DstUnreach{
Data: make([]byte, 128),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 128, // [length, unused], extension header, object header, object payload and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.DstUnreach{
Data: make([]byte, 129),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 136, // [length, unused], extension header, object header, object payload and original datagram
},
}
func TestMultipartMessageBodyLen(t *testing.T) { func TestMultipartMessageBodyLen(t *testing.T) {
for i, tt := range multipartMessageBodyLenTests { for i, tt := range []struct {
proto int
in icmp.MessageBody
out int
}{
{
iana.ProtocolICMP,
&icmp.DstUnreach{
Data: make([]byte, ipv4.HeaderLen),
},
4 + ipv4.HeaderLen, // unused and original datagram
},
{
iana.ProtocolICMP,
&icmp.TimeExceeded{
Data: make([]byte, ipv4.HeaderLen),
},
4 + ipv4.HeaderLen, // unused and original datagram
},
{
iana.ProtocolICMP,
&icmp.ParamProb{
Data: make([]byte, ipv4.HeaderLen),
},
4 + ipv4.HeaderLen, // [pointer, unused] and original datagram
},
{
iana.ProtocolICMP,
&icmp.ParamProb{
Data: make([]byte, ipv4.HeaderLen),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 128, // [pointer, length, unused], extension header, object header, object payload, original datagram
},
{
iana.ProtocolICMP,
&icmp.ParamProb{
Data: make([]byte, 128),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 128, // [pointer, length, unused], extension header, object header, object payload and original datagram
},
{
iana.ProtocolICMP,
&icmp.ParamProb{
Data: make([]byte, 129),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 132, // [pointer, length, unused], extension header, object header, object payload and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.DstUnreach{
Data: make([]byte, ipv6.HeaderLen),
},
4 + ipv6.HeaderLen, // unused and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.PacketTooBig{
Data: make([]byte, ipv6.HeaderLen),
},
4 + ipv6.HeaderLen, // mtu and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.TimeExceeded{
Data: make([]byte, ipv6.HeaderLen),
},
4 + ipv6.HeaderLen, // unused and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.ParamProb{
Data: make([]byte, ipv6.HeaderLen),
},
4 + ipv6.HeaderLen, // pointer and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.DstUnreach{
Data: make([]byte, 127),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 128, // [length, unused], extension header, object header, object payload and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.DstUnreach{
Data: make([]byte, 128),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 128, // [length, unused], extension header, object header, object payload and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.DstUnreach{
Data: make([]byte, 129),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 136, // [length, unused], extension header, object header, object payload and original datagram
},
{
iana.ProtocolICMP,
&icmp.ExtendedEchoRequest{},
4, // [id, seq, l-bit]
},
{
iana.ProtocolICMP,
&icmp.ExtendedEchoRequest{
Extensions: []icmp.Extension{
&icmp.InterfaceIdent{},
},
},
4 + 4 + 4, // [id, seq, l-bit], extension header, object header
},
{
iana.ProtocolIPv6ICMP,
&icmp.ExtendedEchoRequest{
Extensions: []icmp.Extension{
&icmp.InterfaceIdent{
Type: 3,
AFI: iana.AddrFamilyNSAP,
Addr: []byte{0x49, 0x00, 0x01, 0xaa, 0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0x00},
},
},
},
4 + 4 + 4 + 16, // [id, seq, l-bit], extension header, object header, object payload
},
} {
if out := tt.in.Len(tt.proto); out != tt.out { if out := tt.in.Len(tt.proto); out != tt.out {
t.Errorf("#%d: got %d; want %d", i, out, tt.out) t.Errorf("#%d: got %d; want %d", i, out, tt.out)
} }

View file

@ -29,7 +29,7 @@ func (p *PacketTooBig) Marshal(proto int) ([]byte, error) {
} }
// parsePacketTooBig parses b as an ICMP packet too big message body. // parsePacketTooBig parses b as an ICMP packet too big message body.
func parsePacketTooBig(proto int, b []byte) (MessageBody, error) { func parsePacketTooBig(proto int, _ Type, b []byte) (MessageBody, error) {
bodyLen := len(b) bodyLen := len(b)
if bodyLen < 4 { if bodyLen < 4 {
return nil, errMessageTooShort return nil, errMessageTooShort

View file

@ -21,7 +21,7 @@ func (p *ParamProb) Len(proto int) int {
if p == nil { if p == nil {
return 0 return 0
} }
l, _ := multipartMessageBodyDataLen(proto, p.Data, p.Extensions) l, _ := multipartMessageBodyDataLen(proto, true, p.Data, p.Extensions)
return 4 + l return 4 + l
} }
@ -33,7 +33,7 @@ func (p *ParamProb) Marshal(proto int) ([]byte, error) {
copy(b[4:], p.Data) copy(b[4:], p.Data)
return b, nil return b, nil
} }
b, err := marshalMultipartMessageBody(proto, p.Data, p.Extensions) b, err := marshalMultipartMessageBody(proto, true, p.Data, p.Extensions)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -42,7 +42,7 @@ func (p *ParamProb) Marshal(proto int) ([]byte, error) {
} }
// parseParamProb parses b as an ICMP parameter problem message body. // parseParamProb parses b as an ICMP parameter problem message body.
func parseParamProb(proto int, b []byte) (MessageBody, error) { func parseParamProb(proto int, typ Type, b []byte) (MessageBody, error) {
if len(b) < 4 { if len(b) < 4 {
return nil, errMessageTooShort return nil, errMessageTooShort
} }
@ -55,7 +55,7 @@ func parseParamProb(proto int, b []byte) (MessageBody, error) {
} }
p.Pointer = uintptr(b[0]) p.Pointer = uintptr(b[0])
var err error var err error
p.Data, p.Extensions, err = parseMultipartMessageBody(proto, b) p.Data, p.Extensions, err = parseMultipartMessageBody(proto, typ, b)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -1,200 +0,0 @@
// 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.
package icmp_test
import (
"errors"
"fmt"
"net"
"os"
"runtime"
"sync"
"testing"
"time"
"golang.org/x/net/icmp"
"golang.org/x/net/internal/iana"
"golang.org/x/net/internal/nettest"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
)
func googleAddr(c *icmp.PacketConn, protocol int) (net.Addr, error) {
const host = "www.google.com"
ips, err := net.LookupIP(host)
if err != nil {
return nil, err
}
netaddr := func(ip net.IP) (net.Addr, error) {
switch c.LocalAddr().(type) {
case *net.UDPAddr:
return &net.UDPAddr{IP: ip}, nil
case *net.IPAddr:
return &net.IPAddr{IP: ip}, nil
default:
return nil, errors.New("neither UDPAddr nor IPAddr")
}
}
for _, ip := range ips {
switch protocol {
case iana.ProtocolICMP:
if ip.To4() != nil {
return netaddr(ip)
}
case iana.ProtocolIPv6ICMP:
if ip.To16() != nil && ip.To4() == nil {
return netaddr(ip)
}
}
}
return nil, errors.New("no A or AAAA record")
}
type pingTest struct {
network, address string
protocol int
mtype icmp.Type
}
var nonPrivilegedPingTests = []pingTest{
{"udp4", "0.0.0.0", iana.ProtocolICMP, ipv4.ICMPTypeEcho},
{"udp6", "::", iana.ProtocolIPv6ICMP, ipv6.ICMPTypeEchoRequest},
}
func TestNonPrivilegedPing(t *testing.T) {
if testing.Short() {
t.Skip("avoid external network")
}
switch runtime.GOOS {
case "darwin":
case "linux":
t.Log("you may need to adjust the net.ipv4.ping_group_range kernel state")
default:
t.Skipf("not supported on %s", runtime.GOOS)
}
for i, tt := range nonPrivilegedPingTests {
if err := doPing(tt, i); err != nil {
t.Error(err)
}
}
}
var privilegedPingTests = []pingTest{
{"ip4:icmp", "0.0.0.0", iana.ProtocolICMP, ipv4.ICMPTypeEcho},
{"ip6:ipv6-icmp", "::", iana.ProtocolIPv6ICMP, ipv6.ICMPTypeEchoRequest},
}
func TestPrivilegedPing(t *testing.T) {
if testing.Short() {
t.Skip("avoid external network")
}
if m, ok := nettest.SupportsRawIPSocket(); !ok {
t.Skip(m)
}
for i, tt := range privilegedPingTests {
if err := doPing(tt, i); err != nil {
t.Error(err)
}
}
}
func doPing(tt pingTest, seq int) error {
c, err := icmp.ListenPacket(tt.network, tt.address)
if err != nil {
return err
}
defer c.Close()
dst, err := googleAddr(c, tt.protocol)
if err != nil {
return err
}
if tt.network != "udp6" && tt.protocol == iana.ProtocolIPv6ICMP {
var f ipv6.ICMPFilter
f.SetAll(true)
f.Accept(ipv6.ICMPTypeDestinationUnreachable)
f.Accept(ipv6.ICMPTypePacketTooBig)
f.Accept(ipv6.ICMPTypeTimeExceeded)
f.Accept(ipv6.ICMPTypeParameterProblem)
f.Accept(ipv6.ICMPTypeEchoReply)
if err := c.IPv6PacketConn().SetICMPFilter(&f); err != nil {
return err
}
}
wm := icmp.Message{
Type: tt.mtype, Code: 0,
Body: &icmp.Echo{
ID: os.Getpid() & 0xffff, Seq: 1 << uint(seq),
Data: []byte("HELLO-R-U-THERE"),
},
}
wb, err := wm.Marshal(nil)
if err != nil {
return err
}
if n, err := c.WriteTo(wb, dst); err != nil {
return err
} else if n != len(wb) {
return fmt.Errorf("got %v; want %v", n, len(wb))
}
rb := make([]byte, 1500)
if err := c.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
return err
}
n, peer, err := c.ReadFrom(rb)
if err != nil {
return err
}
rm, err := icmp.ParseMessage(tt.protocol, rb[:n])
if err != nil {
return err
}
switch rm.Type {
case ipv4.ICMPTypeEchoReply, ipv6.ICMPTypeEchoReply:
return nil
default:
return fmt.Errorf("got %+v from %v; want echo reply", rm, peer)
}
}
func TestConcurrentNonPrivilegedListenPacket(t *testing.T) {
if testing.Short() {
t.Skip("avoid external network")
}
switch runtime.GOOS {
case "darwin":
case "linux":
t.Log("you may need to adjust the net.ipv4.ping_group_range kernel state")
default:
t.Skipf("not supported on %s", runtime.GOOS)
}
network, address := "udp4", "127.0.0.1"
if !nettest.SupportsIPv4() {
network, address = "udp6", "::1"
}
const N = 1000
var wg sync.WaitGroup
wg.Add(N)
for i := 0; i < N; i++ {
go func() {
defer wg.Done()
c, err := icmp.ListenPacket(network, address)
if err != nil {
t.Error(err)
return
}
c.Close()
}()
}
wg.Wait()
}

View file

@ -15,23 +15,23 @@ func (p *TimeExceeded) Len(proto int) int {
if p == nil { if p == nil {
return 0 return 0
} }
l, _ := multipartMessageBodyDataLen(proto, p.Data, p.Extensions) l, _ := multipartMessageBodyDataLen(proto, true, p.Data, p.Extensions)
return 4 + l return 4 + l
} }
// Marshal implements the Marshal method of MessageBody interface. // Marshal implements the Marshal method of MessageBody interface.
func (p *TimeExceeded) Marshal(proto int) ([]byte, error) { func (p *TimeExceeded) Marshal(proto int) ([]byte, error) {
return marshalMultipartMessageBody(proto, p.Data, p.Extensions) return marshalMultipartMessageBody(proto, true, p.Data, p.Extensions)
} }
// parseTimeExceeded parses b as an ICMP time exceeded message body. // parseTimeExceeded parses b as an ICMP time exceeded message body.
func parseTimeExceeded(proto int, b []byte) (MessageBody, error) { func parseTimeExceeded(proto int, typ Type, b []byte) (MessageBody, error) {
if len(b) < 4 { if len(b) < 4 {
return nil, errMessageTooShort return nil, errMessageTooShort
} }
p := &TimeExceeded{} p := &TimeExceeded{}
var err error var err error
p.Data, p.Extensions, err = parseMultipartMessageBody(proto, b) p.Data, p.Extensions, err = parseMultipartMessageBody(proto, typ, b)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -1,5 +1,5 @@
// go generate gen.go // go generate gen.go
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT // Code generated by the command above; DO NOT EDIT.
// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA). // Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA).
package iana // import "golang.org/x/net/internal/iana" package iana // import "golang.org/x/net/internal/iana"
@ -38,7 +38,7 @@ const (
CongestionExperienced = 0x3 // CE (Congestion Experienced) CongestionExperienced = 0x3 // CE (Congestion Experienced)
) )
// Protocol Numbers, Updated: 2016-06-22 // Protocol Numbers, Updated: 2017-10-13
const ( const (
ProtocolIP = 0 // IPv4 encapsulation, pseudo protocol number ProtocolIP = 0 // IPv4 encapsulation, pseudo protocol number
ProtocolHOPOPT = 0 // IPv6 Hop-by-Hop Option ProtocolHOPOPT = 0 // IPv6 Hop-by-Hop Option
@ -178,3 +178,50 @@ const (
ProtocolROHC = 142 // Robust Header Compression ProtocolROHC = 142 // Robust Header Compression
ProtocolReserved = 255 // Reserved ProtocolReserved = 255 // Reserved
) )
// Address Family Numbers, Updated: 2016-10-25
const (
AddrFamilyIPv4 = 1 // IP (IP version 4)
AddrFamilyIPv6 = 2 // IP6 (IP version 6)
AddrFamilyNSAP = 3 // NSAP
AddrFamilyHDLC = 4 // HDLC (8-bit multidrop)
AddrFamilyBBN1822 = 5 // BBN 1822
AddrFamily802 = 6 // 802 (includes all 802 media plus Ethernet "canonical format")
AddrFamilyE163 = 7 // E.163
AddrFamilyE164 = 8 // E.164 (SMDS, Frame Relay, ATM)
AddrFamilyF69 = 9 // F.69 (Telex)
AddrFamilyX121 = 10 // X.121 (X.25, Frame Relay)
AddrFamilyIPX = 11 // IPX
AddrFamilyAppletalk = 12 // Appletalk
AddrFamilyDecnetIV = 13 // Decnet IV
AddrFamilyBanyanVines = 14 // Banyan Vines
AddrFamilyE164withSubaddress = 15 // E.164 with NSAP format subaddress
AddrFamilyDNS = 16 // DNS (Domain Name System)
AddrFamilyDistinguishedName = 17 // Distinguished Name
AddrFamilyASNumber = 18 // AS Number
AddrFamilyXTPoverIPv4 = 19 // XTP over IP version 4
AddrFamilyXTPoverIPv6 = 20 // XTP over IP version 6
AddrFamilyXTPnativemodeXTP = 21 // XTP native mode XTP
AddrFamilyFibreChannelWorldWidePortName = 22 // Fibre Channel World-Wide Port Name
AddrFamilyFibreChannelWorldWideNodeName = 23 // Fibre Channel World-Wide Node Name
AddrFamilyGWID = 24 // GWID
AddrFamilyL2VPN = 25 // AFI for L2VPN information
AddrFamilyMPLSTPSectionEndpointID = 26 // MPLS-TP Section Endpoint Identifier
AddrFamilyMPLSTPLSPEndpointID = 27 // MPLS-TP LSP Endpoint Identifier
AddrFamilyMPLSTPPseudowireEndpointID = 28 // MPLS-TP Pseudowire Endpoint Identifier
AddrFamilyMTIPv4 = 29 // MT IP: Multi-Topology IP version 4
AddrFamilyMTIPv6 = 30 // MT IPv6: Multi-Topology IP version 6
AddrFamilyEIGRPCommonServiceFamily = 16384 // EIGRP Common Service Family
AddrFamilyEIGRPIPv4ServiceFamily = 16385 // EIGRP IPv4 Service Family
AddrFamilyEIGRPIPv6ServiceFamily = 16386 // EIGRP IPv6 Service Family
AddrFamilyLISPCanonicalAddressFormat = 16387 // LISP Canonical Address Format (LCAF)
AddrFamilyBGPLS = 16388 // BGP-LS
AddrFamily48bitMAC = 16389 // 48-bit MAC
AddrFamily64bitMAC = 16390 // 64-bit MAC
AddrFamilyOUI = 16391 // OUI
AddrFamilyMACFinal24bits = 16392 // MAC/24
AddrFamilyMACFinal40bits = 16393 // MAC/40
AddrFamilyIPv6Initial64bits = 16394 // IPv6/64
AddrFamilyRBridgePortID = 16395 // RBridge Port ID
AddrFamilyTRILLNickname = 16396 // TRILL Nickname
)

View file

@ -39,12 +39,16 @@ var registries = []struct {
"https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml", "https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml",
parseProtocolNumbers, parseProtocolNumbers,
}, },
{
"http://www.iana.org/assignments/address-family-numbers/address-family-numbers.xml",
parseAddrFamilyNumbers,
},
} }
func main() { func main() {
var bb bytes.Buffer var bb bytes.Buffer
fmt.Fprintf(&bb, "// go generate gen.go\n") fmt.Fprintf(&bb, "// go generate gen.go\n")
fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n") fmt.Fprintf(&bb, "// Code generated by the command above; DO NOT EDIT.\n\n")
fmt.Fprintf(&bb, "// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA).\n") fmt.Fprintf(&bb, "// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA).\n")
fmt.Fprintf(&bb, `package iana // import "golang.org/x/net/internal/iana"`+"\n\n") fmt.Fprintf(&bb, `package iana // import "golang.org/x/net/internal/iana"`+"\n\n")
for _, r := range registries { for _, r := range registries {
@ -291,3 +295,93 @@ func (pn *protocolNumbers) escape() []canonProtocolRecord {
} }
return prs return prs
} }
func parseAddrFamilyNumbers(w io.Writer, r io.Reader) error {
dec := xml.NewDecoder(r)
var afn addrFamilylNumbers
if err := dec.Decode(&afn); err != nil {
return err
}
afrs := afn.escape()
fmt.Fprintf(w, "// %s, Updated: %s\n", afn.Title, afn.Updated)
fmt.Fprintf(w, "const (\n")
for _, afr := range afrs {
if afr.Name == "" {
continue
}
fmt.Fprintf(w, "AddrFamily%s = %d", afr.Name, afr.Value)
fmt.Fprintf(w, "// %s\n", afr.Descr)
}
fmt.Fprintf(w, ")\n")
return nil
}
type addrFamilylNumbers struct {
XMLName xml.Name `xml:"registry"`
Title string `xml:"title"`
Updated string `xml:"updated"`
RegTitle string `xml:"registry>title"`
Note string `xml:"registry>note"`
Records []struct {
Value string `xml:"value"`
Descr string `xml:"description"`
} `xml:"registry>record"`
}
type canonAddrFamilyRecord struct {
Name string
Descr string
Value int
}
func (afn *addrFamilylNumbers) escape() []canonAddrFamilyRecord {
afrs := make([]canonAddrFamilyRecord, len(afn.Records))
sr := strings.NewReplacer(
"IP version 4", "IPv4",
"IP version 6", "IPv6",
"Identifier", "ID",
"-", "",
"-", "",
"/", "",
".", "",
" ", "",
)
for i, afr := range afn.Records {
if strings.Contains(afr.Descr, "Unassigned") ||
strings.Contains(afr.Descr, "Reserved") {
continue
}
afrs[i].Descr = afr.Descr
s := strings.TrimSpace(afr.Descr)
switch s {
case "IP (IP version 4)":
afrs[i].Name = "IPv4"
case "IP6 (IP version 6)":
afrs[i].Name = "IPv6"
case "AFI for L2VPN information":
afrs[i].Name = "L2VPN"
case "E.164 with NSAP format subaddress":
afrs[i].Name = "E164withSubaddress"
case "MT IP: Multi-Topology IP version 4":
afrs[i].Name = "MTIPv4"
case "MAC/24":
afrs[i].Name = "MACFinal24bits"
case "MAC/40":
afrs[i].Name = "MACFinal40bits"
case "IPv6/64":
afrs[i].Name = "IPv6Initial64bits"
default:
n := strings.Index(s, "(")
if n > 0 {
s = s[:n]
}
n = strings.Index(s, ":")
if n > 0 {
s = s[:n]
}
afrs[i].Name = sr.Replace(s)
}
afrs[i].Value, _ = strconv.Atoi(afr.Value)
}
return afrs
}

View file

@ -26,6 +26,11 @@ type msghdr struct {
Flags int32 Flags int32
} }
type mmsghdr struct {
Hdr msghdr
Len uint32
}
type cmsghdr struct { type cmsghdr struct {
Len uint32 Len uint32
Level int32 Level int32
@ -52,6 +57,7 @@ type sockaddrInet6 struct {
const ( const (
sizeofIovec = 0x8 sizeofIovec = 0x8
sizeofMsghdr = 0x1c sizeofMsghdr = 0x1c
sizeofMmsghdr = 0x20
sizeofCmsghdr = 0xc sizeofCmsghdr = 0xc
sizeofSockaddrInet = 0x10 sizeofSockaddrInet = 0x10

View file

@ -80,7 +80,7 @@ var registries = []struct {
func geniana() error { func geniana() error {
var bb bytes.Buffer var bb bytes.Buffer
fmt.Fprintf(&bb, "// go generate gen.go\n") fmt.Fprintf(&bb, "// go generate gen.go\n")
fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n") fmt.Fprintf(&bb, "// Code generated by the command above; DO NOT EDIT.\n\n")
fmt.Fprintf(&bb, "package ipv4\n\n") fmt.Fprintf(&bb, "package ipv4\n\n")
for _, r := range registries { for _, r := range registries {
resp, err := http.Get(r.url) resp, err := http.Get(r.url)

10
vendor/golang.org/x/net/ipv4/iana.go generated vendored
View file

@ -1,9 +1,9 @@
// go generate gen.go // go generate gen.go
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT // Code generated by the command above; DO NOT EDIT.
package ipv4 package ipv4
// Internet Control Message Protocol (ICMP) Parameters, Updated: 2013-04-19 // Internet Control Message Protocol (ICMP) Parameters, Updated: 2018-02-26
const ( const (
ICMPTypeEchoReply ICMPType = 0 // Echo Reply ICMPTypeEchoReply ICMPType = 0 // Echo Reply
ICMPTypeDestinationUnreachable ICMPType = 3 // Destination Unreachable ICMPTypeDestinationUnreachable ICMPType = 3 // Destination Unreachable
@ -16,9 +16,11 @@ const (
ICMPTypeTimestamp ICMPType = 13 // Timestamp ICMPTypeTimestamp ICMPType = 13 // Timestamp
ICMPTypeTimestampReply ICMPType = 14 // Timestamp Reply ICMPTypeTimestampReply ICMPType = 14 // Timestamp Reply
ICMPTypePhoturis ICMPType = 40 // Photuris ICMPTypePhoturis ICMPType = 40 // Photuris
ICMPTypeExtendedEchoRequest ICMPType = 42 // Extended Echo Request
ICMPTypeExtendedEchoReply ICMPType = 43 // Extended Echo Reply
) )
// Internet Control Message Protocol (ICMP) Parameters, Updated: 2013-04-19 // Internet Control Message Protocol (ICMP) Parameters, Updated: 2018-02-26
var icmpTypes = map[ICMPType]string{ var icmpTypes = map[ICMPType]string{
0: "echo reply", 0: "echo reply",
3: "destination unreachable", 3: "destination unreachable",
@ -31,4 +33,6 @@ var icmpTypes = map[ICMPType]string{
13: "timestamp", 13: "timestamp",
14: "timestamp reply", 14: "timestamp reply",
40: "photuris", 40: "photuris",
42: "extended echo request",
43: "extended echo reply",
} }

View file

@ -80,7 +80,7 @@ var registries = []struct {
func geniana() error { func geniana() error {
var bb bytes.Buffer var bb bytes.Buffer
fmt.Fprintf(&bb, "// go generate gen.go\n") fmt.Fprintf(&bb, "// go generate gen.go\n")
fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n") fmt.Fprintf(&bb, "// Code generated by the command above; DO NOT EDIT.\n\n")
fmt.Fprintf(&bb, "package ipv6\n\n") fmt.Fprintf(&bb, "package ipv6\n\n")
for _, r := range registries { for _, r := range registries {
resp, err := http.Get(r.url) resp, err := http.Get(r.url)

10
vendor/golang.org/x/net/ipv6/iana.go generated vendored
View file

@ -1,9 +1,9 @@
// go generate gen.go // go generate gen.go
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT // Code generated by the command above; DO NOT EDIT.
package ipv6 package ipv6
// Internet Control Message Protocol version 6 (ICMPv6) Parameters, Updated: 2015-07-07 // Internet Control Message Protocol version 6 (ICMPv6) Parameters, Updated: 2018-03-09
const ( const (
ICMPTypeDestinationUnreachable ICMPType = 1 // Destination Unreachable ICMPTypeDestinationUnreachable ICMPType = 1 // Destination Unreachable
ICMPTypePacketTooBig ICMPType = 2 // Packet Too Big ICMPTypePacketTooBig ICMPType = 2 // Packet Too Big
@ -40,9 +40,11 @@ const (
ICMPTypeDuplicateAddressRequest ICMPType = 157 // Duplicate Address Request ICMPTypeDuplicateAddressRequest ICMPType = 157 // Duplicate Address Request
ICMPTypeDuplicateAddressConfirmation ICMPType = 158 // Duplicate Address Confirmation ICMPTypeDuplicateAddressConfirmation ICMPType = 158 // Duplicate Address Confirmation
ICMPTypeMPLControl ICMPType = 159 // MPL Control Message ICMPTypeMPLControl ICMPType = 159 // MPL Control Message
ICMPTypeExtendedEchoRequest ICMPType = 160 // Extended Echo Request
ICMPTypeExtendedEchoReply ICMPType = 161 // Extended Echo Reply
) )
// Internet Control Message Protocol version 6 (ICMPv6) Parameters, Updated: 2015-07-07 // Internet Control Message Protocol version 6 (ICMPv6) Parameters, Updated: 2018-03-09
var icmpTypes = map[ICMPType]string{ var icmpTypes = map[ICMPType]string{
1: "destination unreachable", 1: "destination unreachable",
2: "packet too big", 2: "packet too big",
@ -79,4 +81,6 @@ var icmpTypes = map[ICMPType]string{
157: "duplicate address request", 157: "duplicate address request",
158: "duplicate address confirmation", 158: "duplicate address confirmation",
159: "mpl control message", 159: "mpl control message",
160: "extended echo request",
161: "extended echo reply",
} }

View file

@ -20,7 +20,7 @@ func sysctl(mib []int32, old *byte, oldlen *uintptr, new *byte, newlen uintptr)
} else { } else {
p = unsafe.Pointer(&zero) p = unsafe.Pointer(&zero)
} }
_, _, errno := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(p), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) _, _, errno := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(p), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), newlen)
if errno != 0 { if errno != 0 {
return error(errno) return error(errno)
} }

View file

@ -4,16 +4,15 @@ Go is an open source project.
It is the work of hundreds of contributors. We appreciate your help! It is the work of hundreds of contributors. We appreciate your help!
## Filing issues ## Filing issues
When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions: When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
1. What version of Go are you using (`go version`)? 1. What version of Go are you using (`go version`)?
2. What operating system and processor architecture are you using? 2. What operating system and processor architecture are you using?
3. What did you do? 3. What did you do?
4. What did you expect to see? 4. What did you expect to see?
5. What did you see instead? 5. What did you see instead?
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker. General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
The gophers there will answer or ask you to file an issue if you've tripped over a bug. The gophers there will answer or ask you to file an issue if you've tripped over a bug.
@ -23,9 +22,5 @@ The gophers there will answer or ask you to file an issue if you've tripped over
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html) Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
before sending patches. before sending patches.
**We do not accept GitHub pull requests**
(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review).
Unless otherwise noted, the Go source files are distributed under Unless otherwise noted, the Go source files are distributed under
the BSD-style license found in the LICENSE file. the BSD-style license found in the LICENSE file.

View file

@ -4,16 +4,15 @@ Go is an open source project.
It is the work of hundreds of contributors. We appreciate your help! It is the work of hundreds of contributors. We appreciate your help!
## Filing issues ## Filing issues
When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions: When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
1. What version of Go are you using (`go version`)? 1. What version of Go are you using (`go version`)?
2. What operating system and processor architecture are you using? 2. What operating system and processor architecture are you using?
3. What did you do? 3. What did you do?
4. What did you expect to see? 4. What did you expect to see?
5. What did you see instead? 5. What did you see instead?
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker. General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
The gophers there will answer or ask you to file an issue if you've tripped over a bug. The gophers there will answer or ask you to file an issue if you've tripped over a bug.
@ -23,9 +22,5 @@ The gophers there will answer or ask you to file an issue if you've tripped over
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html) Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
before sending patches. before sending patches.
**We do not accept GitHub pull requests**
(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review).
Unless otherwise noted, the Go source files are distributed under Unless otherwise noted, the Go source files are distributed under
the BSD-style license found in the LICENSE file. the BSD-style license found in the LICENSE file.

View file

@ -11,11 +11,14 @@
// system, set $GOOS and $GOARCH to the desired system. For example, if // system, set $GOOS and $GOARCH to the desired system. For example, if
// you want to view documentation for freebsd/arm on linux/amd64, set $GOOS // you want to view documentation for freebsd/arm on linux/amd64, set $GOOS
// to freebsd and $GOARCH to arm. // to freebsd and $GOARCH to arm.
//
// The primary use of this package is inside other packages that provide a more // The primary use of this package is inside other packages that provide a more
// portable interface to the system, such as "os", "time" and "net". Use // portable interface to the system, such as "os", "time" and "net". Use
// those packages rather than this one if you can. // those packages rather than this one if you can.
//
// For details of the functions and data types in this package consult // For details of the functions and data types in this package consult
// the manuals for the appropriate operating system. // the manuals for the appropriate operating system.
//
// These calls return err == nil to indicate success; otherwise // These calls return err == nil to indicate success; otherwise
// err represents an operating system error describing the failure and // err represents an operating system error describing the failure and
// holds a value of type syscall.ErrorString. // holds a value of type syscall.ErrorString.

19
vendor/golang.org/x/sys/unix/example_test.go generated vendored Normal file
View file

@ -0,0 +1,19 @@
// Copyright 2018 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
package unix_test
import (
"log"
"os"
"golang.org/x/sys/unix"
)
func ExampleExec() {
err := unix.Exec("/bin/ls", []string{"ls", "-al"}, os.Environ())
log.Fatal(err)
}

View file

@ -11,11 +11,14 @@
// system, set $GOOS and $GOARCH to the desired system. For example, if // system, set $GOOS and $GOARCH to the desired system. For example, if
// you want to view documentation for freebsd/arm on linux/amd64, set $GOOS // you want to view documentation for freebsd/arm on linux/amd64, set $GOOS
// to freebsd and $GOARCH to arm. // to freebsd and $GOARCH to arm.
//
// The primary use of this package is inside other packages that provide a more // The primary use of this package is inside other packages that provide a more
// portable interface to the system, such as "os", "time" and "net". Use // portable interface to the system, such as "os", "time" and "net". Use
// those packages rather than this one if you can. // those packages rather than this one if you can.
//
// For details of the functions and data types in this package consult // For details of the functions and data types in this package consult
// the manuals for the appropriate operating system. // the manuals for the appropriate operating system.
//
// These calls return err == nil to indicate success; otherwise // These calls return err == nil to indicate success; otherwise
// err represents an operating system error describing the failure and // err represents an operating system error describing the failure and
// holds a value of type syscall.Errno. // holds a value of type syscall.Errno.

View file

@ -330,6 +330,7 @@ func Uname(uname *Utsname) error {
//sys Flock(fd int, how int) (err error) //sys Flock(fd int, how int) (err error)
//sys Fpathconf(fd int, name int) (val int, err error) //sys Fpathconf(fd int, name int) (val int, err error)
//sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64 //sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64
//sys Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) = SYS_FSTATAT64
//sys Fstatfs(fd int, stat *Statfs_t) (err error) = SYS_FSTATFS64 //sys Fstatfs(fd int, stat *Statfs_t) (err error) = SYS_FSTATFS64
//sys Fsync(fd int) (err error) //sys Fsync(fd int) (err error)
//sys Ftruncate(fd int, length int64) (err error) //sys Ftruncate(fd int, length int64) (err error)

View file

@ -251,10 +251,12 @@ func Uname(uname *Utsname) error {
//sys Fchdir(fd int) (err error) //sys Fchdir(fd int) (err error)
//sys Fchflags(fd int, flags int) (err error) //sys Fchflags(fd int, flags int) (err error)
//sys Fchmod(fd int, mode uint32) (err error) //sys Fchmod(fd int, mode uint32) (err error)
//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error)
//sys Fchown(fd int, uid int, gid int) (err error) //sys Fchown(fd int, uid int, gid int) (err error)
//sys Flock(fd int, how int) (err error) //sys Flock(fd int, how int) (err error)
//sys Fpathconf(fd int, name int) (val int, err error) //sys Fpathconf(fd int, name int) (val int, err error)
//sys Fstat(fd int, stat *Stat_t) (err error) //sys Fstat(fd int, stat *Stat_t) (err error)
//sys Fstatat(fd int, path string, stat *Stat_t, flags int) (err error)
//sys Fstatfs(fd int, stat *Statfs_t) (err error) //sys Fstatfs(fd int, stat *Statfs_t) (err error)
//sys Fsync(fd int) (err error) //sys Fsync(fd int) (err error)
//sys Ftruncate(fd int, length int64) (err error) //sys Ftruncate(fd int, length int64) (err error)

View file

@ -482,6 +482,7 @@ func Uname(uname *Utsname) error {
//sys Flock(fd int, how int) (err error) //sys Flock(fd int, how int) (err error)
//sys Fpathconf(fd int, name int) (val int, err error) //sys Fpathconf(fd int, name int) (val int, err error)
//sys Fstat(fd int, stat *Stat_t) (err error) //sys Fstat(fd int, stat *Stat_t) (err error)
//sys Fstatat(fd int, path string, stat *Stat_t, flags int) (err error)
//sys Fstatfs(fd int, stat *Statfs_t) (err error) //sys Fstatfs(fd int, stat *Statfs_t) (err error)
//sys Fsync(fd int) (err error) //sys Fsync(fd int) (err error)
//sys Ftruncate(fd int, length int64) (err error) //sys Ftruncate(fd int, length int64) (err error)

View file

@ -7,6 +7,7 @@
package unix package unix
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT //sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
//sys Fchown(fd int, uid int, gid int) (err error) //sys Fchown(fd int, uid int, gid int) (err error)
//sys Fstat(fd int, stat *Stat_t) (err error) //sys Fstat(fd int, stat *Stat_t) (err error)
//sys Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) //sys Fstatat(fd int, path string, stat *Stat_t, flags int) (err error)

21
vendor/golang.org/x/sys/unix/syscall_linux_gccgo.go generated vendored Normal file
View file

@ -0,0 +1,21 @@
// Copyright 2018 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 linux
// +build gccgo
// +build 386 arm
package unix
import (
"syscall"
"unsafe"
)
func seek(fd int, offset int64, whence int) (newoffset int64, err syscall.Errno) {
offsetLow := uint32(offset & 0xffffffff)
offsetHigh := uint32((offset >> 32) & 0xffffffff)
_, _, err = Syscall6(SYS__LLSEEK, uintptr(fd), uintptr(offsetHigh), uintptr(offsetLow), uintptr(unsafe.Pointer(&newoffset)), uintptr(whence), 0)
return newoffset, err
}

View file

@ -9,6 +9,7 @@ package unix
//sys Dup2(oldfd int, newfd int) (err error) //sys Dup2(oldfd int, newfd int) (err error)
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) //sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
//sys Fchown(fd int, uid int, gid int) (err error) //sys Fchown(fd int, uid int, gid int) (err error)
//sys Fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) = SYS_NEWFSTATAT //sys Fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) = SYS_NEWFSTATAT
//sys Fstatfs(fd int, buf *Statfs_t) (err error) //sys Fstatfs(fd int, buf *Statfs_t) (err error)

View file

@ -15,6 +15,7 @@ import (
func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
//sys Dup2(oldfd int, newfd int) (err error) //sys Dup2(oldfd int, newfd int) (err error)
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
//sys Fchown(fd int, uid int, gid int) (err error) //sys Fchown(fd int, uid int, gid int) (err error)
//sys Ftruncate(fd int, length int64) (err error) = SYS_FTRUNCATE64 //sys Ftruncate(fd int, length int64) (err error) = SYS_FTRUNCATE64
//sysnb Getegid() (egid int) //sysnb Getegid() (egid int)

View file

@ -9,6 +9,7 @@ package unix
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) //sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
//sys Dup2(oldfd int, newfd int) (err error) //sys Dup2(oldfd int, newfd int) (err error)
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
//sys Fchown(fd int, uid int, gid int) (err error) //sys Fchown(fd int, uid int, gid int) (err error)
//sys Fstat(fd int, stat *Stat_t) (err error) //sys Fstat(fd int, stat *Stat_t) (err error)
//sys Fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) = SYS_NEWFSTATAT //sys Fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) = SYS_NEWFSTATAT

View file

@ -7,6 +7,7 @@
package unix package unix
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) //sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
//sys Dup2(oldfd int, newfd int) (err error) //sys Dup2(oldfd int, newfd int) (err error)
//sys Fchown(fd int, uid int, gid int) (err error) //sys Fchown(fd int, uid int, gid int) (err error)
//sys Fstat(fd int, stat *Stat_t) (err error) //sys Fstat(fd int, stat *Stat_t) (err error)

View file

@ -7,44 +7,15 @@
package unix_test package unix_test
import ( import (
"io/ioutil"
"os" "os"
"runtime" "runtime"
"runtime/debug"
"testing" "testing"
"time" "time"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
func TestFchmodat(t *testing.T) {
defer chtmpdir(t)()
touch(t, "file1")
err := os.Symlink("file1", "symlink1")
if err != nil {
t.Fatal(err)
}
err = unix.Fchmodat(unix.AT_FDCWD, "symlink1", 0444, 0)
if err != nil {
t.Fatalf("Fchmodat: unexpected error: %v", err)
}
fi, err := os.Stat("file1")
if err != nil {
t.Fatal(err)
}
if fi.Mode() != 0444 {
t.Errorf("Fchmodat: failed to change mode: expected %v, got %v", 0444, fi.Mode())
}
err = unix.Fchmodat(unix.AT_FDCWD, "symlink1", 0444, unix.AT_SYMLINK_NOFOLLOW)
if err != unix.EOPNOTSUPP {
t.Fatalf("Fchmodat: unexpected error: %v, expected EOPNOTSUPP", err)
}
}
func TestIoctlGetInt(t *testing.T) { func TestIoctlGetInt(t *testing.T) {
f, err := os.Open("/dev/random") f, err := os.Open("/dev/random")
if err != nil { if err != nil {
@ -173,12 +144,46 @@ func TestUtimesNanoAt(t *testing.T) {
} }
} }
func TestGetrlimit(t *testing.T) { func TestRlimitAs(t *testing.T) {
// disable GC during to avoid flaky test
defer debug.SetGCPercent(debug.SetGCPercent(-1))
var rlim unix.Rlimit var rlim unix.Rlimit
err := unix.Getrlimit(unix.RLIMIT_AS, &rlim) err := unix.Getrlimit(unix.RLIMIT_AS, &rlim)
if err != nil { if err != nil {
t.Fatalf("Getrlimit: %v", err) t.Fatalf("Getrlimit: %v", err)
} }
var zero unix.Rlimit
if zero == rlim {
t.Fatalf("Getrlimit: got zero value %#v", rlim)
}
set := rlim
set.Cur = uint64(unix.Getpagesize())
err = unix.Setrlimit(unix.RLIMIT_AS, &set)
if err != nil {
t.Fatalf("Setrlimit: set failed: %#v %v", set, err)
}
// RLIMIT_AS was set to the page size, so mmap()'ing twice the page size
// should fail. See 'man 2 getrlimit'.
_, err = unix.Mmap(-1, 0, 2*unix.Getpagesize(), unix.PROT_NONE, unix.MAP_ANON|unix.MAP_PRIVATE)
if err == nil {
t.Fatal("Mmap: unexpectedly suceeded after setting RLIMIT_AS")
}
err = unix.Setrlimit(unix.RLIMIT_AS, &rlim)
if err != nil {
t.Fatalf("Setrlimit: restore failed: %#v %v", rlim, err)
}
b, err := unix.Mmap(-1, 0, 2*unix.Getpagesize(), unix.PROT_NONE, unix.MAP_ANON|unix.MAP_PRIVATE)
if err != nil {
t.Fatalf("Mmap: %v", err)
}
err = unix.Munmap(b)
if err != nil {
t.Fatalf("Munmap: %v", err)
}
} }
func TestSelect(t *testing.T) { func TestSelect(t *testing.T) {
@ -221,47 +226,6 @@ func TestPselect(t *testing.T) {
} }
} }
func TestFstatat(t *testing.T) {
defer chtmpdir(t)()
touch(t, "file1")
var st1 unix.Stat_t
err := unix.Stat("file1", &st1)
if err != nil {
t.Fatalf("Stat: %v", err)
}
var st2 unix.Stat_t
err = unix.Fstatat(unix.AT_FDCWD, "file1", &st2, 0)
if err != nil {
t.Fatalf("Fstatat: %v", err)
}
if st1 != st2 {
t.Errorf("Fstatat: returned stat does not match Stat")
}
err = os.Symlink("file1", "symlink1")
if err != nil {
t.Fatal(err)
}
err = unix.Lstat("symlink1", &st1)
if err != nil {
t.Fatalf("Lstat: %v", err)
}
err = unix.Fstatat(unix.AT_FDCWD, "symlink1", &st2, unix.AT_SYMLINK_NOFOLLOW)
if err != nil {
t.Fatalf("Fstatat: %v", err)
}
if st1 != st2 {
t.Errorf("Fstatat: returned stat does not match Lstat")
}
}
func TestSchedSetaffinity(t *testing.T) { func TestSchedSetaffinity(t *testing.T) {
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
@ -407,37 +371,3 @@ func TestStatx(t *testing.T) {
t.Errorf("Statx: returned stat mtime does not match Lstat") t.Errorf("Statx: returned stat mtime does not match Lstat")
} }
} }
// utilities taken from os/os_test.go
func touch(t *testing.T, name string) {
f, err := os.Create(name)
if err != nil {
t.Fatal(err)
}
if err := f.Close(); err != nil {
t.Fatal(err)
}
}
// chtmpdir changes the working directory to a new temporary directory and
// provides a cleanup function. Used when PWD is read-only.
func chtmpdir(t *testing.T) func() {
oldwd, err := os.Getwd()
if err != nil {
t.Fatalf("chtmpdir: %v", err)
}
d, err := ioutil.TempDir("", "test")
if err != nil {
t.Fatalf("chtmpdir: %v", err)
}
if err := os.Chdir(d); err != nil {
t.Fatalf("chtmpdir: %v", err)
}
return func() {
if err := os.Chdir(oldwd); err != nil {
t.Fatalf("chtmpdir: %v", err)
}
os.RemoveAll(d)
}
}

View file

@ -233,13 +233,16 @@ func Uname(uname *Utsname) error {
//sys Dup(fd int) (nfd int, err error) //sys Dup(fd int) (nfd int, err error)
//sys Dup2(from int, to int) (err error) //sys Dup2(from int, to int) (err error)
//sys Exit(code int) //sys Exit(code int)
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_POSIX_FADVISE
//sys Fchdir(fd int) (err error) //sys Fchdir(fd int) (err error)
//sys Fchflags(fd int, flags int) (err error) //sys Fchflags(fd int, flags int) (err error)
//sys Fchmod(fd int, mode uint32) (err error) //sys Fchmod(fd int, mode uint32) (err error)
//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error)
//sys Fchown(fd int, uid int, gid int) (err error) //sys Fchown(fd int, uid int, gid int) (err error)
//sys Flock(fd int, how int) (err error) //sys Flock(fd int, how int) (err error)
//sys Fpathconf(fd int, name int) (val int, err error) //sys Fpathconf(fd int, name int) (val int, err error)
//sys Fstat(fd int, stat *Stat_t) (err error) //sys Fstat(fd int, stat *Stat_t) (err error)
//sys Fstatat(fd int, path string, stat *Stat_t, flags int) (err error)
//sys Fsync(fd int) (err error) //sys Fsync(fd int) (err error)
//sys Ftruncate(fd int, length int64) (err error) //sys Ftruncate(fd int, length int64) (err error)
//sysnb Getegid() (egid int) //sysnb Getegid() (egid int)
@ -320,7 +323,6 @@ func Uname(uname *Utsname) error {
// __msync13 // __msync13
// __ntp_gettime30 // __ntp_gettime30
// __posix_chown // __posix_chown
// __posix_fadvise50
// __posix_fchown // __posix_fchown
// __posix_lchown // __posix_lchown
// __posix_rename // __posix_rename

View file

@ -204,10 +204,12 @@ func Uname(uname *Utsname) error {
//sys Fchdir(fd int) (err error) //sys Fchdir(fd int) (err error)
//sys Fchflags(fd int, flags int) (err error) //sys Fchflags(fd int, flags int) (err error)
//sys Fchmod(fd int, mode uint32) (err error) //sys Fchmod(fd int, mode uint32) (err error)
//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error)
//sys Fchown(fd int, uid int, gid int) (err error) //sys Fchown(fd int, uid int, gid int) (err error)
//sys Flock(fd int, how int) (err error) //sys Flock(fd int, how int) (err error)
//sys Fpathconf(fd int, name int) (val int, err error) //sys Fpathconf(fd int, name int) (val int, err error)
//sys Fstat(fd int, stat *Stat_t) (err error) //sys Fstat(fd int, stat *Stat_t) (err error)
//sys Fstatat(fd int, path string, stat *Stat_t, flags int) (err error)
//sys Fstatfs(fd int, stat *Statfs_t) (err error) //sys Fstatfs(fd int, stat *Statfs_t) (err error)
//sys Fsync(fd int) (err error) //sys Fsync(fd int) (err error)
//sys Ftruncate(fd int, length int64) (err error) //sys Ftruncate(fd int, length int64) (err error)

View file

@ -595,9 +595,10 @@ func Poll(fds []PollFd, timeout int) (n int, err error) {
//sys Fchown(fd int, uid int, gid int) (err error) //sys Fchown(fd int, uid int, gid int) (err error)
//sys Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) //sys Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error)
//sys Fdatasync(fd int) (err error) //sys Fdatasync(fd int) (err error)
//sys Flock(fd int, how int) (err error) //sys Flock(fd int, how int) (err error)
//sys Fpathconf(fd int, name int) (val int, err error) //sys Fpathconf(fd int, name int) (val int, err error)
//sys Fstat(fd int, stat *Stat_t) (err error) //sys Fstat(fd int, stat *Stat_t) (err error)
//sys Fstatat(fd int, path string, stat *Stat_t, flags int) (err error)
//sys Fstatvfs(fd int, vfsstat *Statvfs_t) (err error) //sys Fstatvfs(fd int, vfsstat *Statvfs_t) (err error)
//sys Getdents(fd int, buf []byte, basep *uintptr) (n int, err error) //sys Getdents(fd int, buf []byte, basep *uintptr) (n int, err error)
//sysnb Getgid() (gid int) //sysnb Getgid() (gid int)

View file

@ -305,3 +305,12 @@ func SetNonblock(fd int, nonblocking bool) (err error) {
_, err = fcntl(fd, F_SETFL, flag) _, err = fcntl(fd, F_SETFL, flag)
return err return err
} }
// Exec calls execve(2), which replaces the calling executable in the process
// tree. argv0 should be the full path to an executable ("/bin/ls") and the
// executable name should also be the first argument in argv (["ls", "-l"]).
// envv are the environment variables that should be passed to the new
// process (["USER=go", "PWD=/tmp"]).
func Exec(argv0 string, argv []string, envv []string) error {
return syscall.Exec(argv0, argv, envv)
}

View file

@ -426,6 +426,101 @@ func TestGetwd(t *testing.T) {
} }
} }
func TestFstatat(t *testing.T) {
defer chtmpdir(t)()
touch(t, "file1")
var st1 unix.Stat_t
err := unix.Stat("file1", &st1)
if err != nil {
t.Fatalf("Stat: %v", err)
}
var st2 unix.Stat_t
err = unix.Fstatat(unix.AT_FDCWD, "file1", &st2, 0)
if err != nil {
t.Fatalf("Fstatat: %v", err)
}
if st1 != st2 {
t.Errorf("Fstatat: returned stat does not match Stat")
}
err = os.Symlink("file1", "symlink1")
if err != nil {
t.Fatal(err)
}
err = unix.Lstat("symlink1", &st1)
if err != nil {
t.Fatalf("Lstat: %v", err)
}
err = unix.Fstatat(unix.AT_FDCWD, "symlink1", &st2, unix.AT_SYMLINK_NOFOLLOW)
if err != nil {
t.Fatalf("Fstatat: %v", err)
}
if st1 != st2 {
t.Errorf("Fstatat: returned stat does not match Lstat")
}
}
func TestFchmodat(t *testing.T) {
defer chtmpdir(t)()
touch(t, "file1")
err := os.Symlink("file1", "symlink1")
if err != nil {
t.Fatal(err)
}
mode := os.FileMode(0444)
err = unix.Fchmodat(unix.AT_FDCWD, "symlink1", uint32(mode), 0)
if err != nil {
t.Fatalf("Fchmodat: unexpected error: %v", err)
}
fi, err := os.Stat("file1")
if err != nil {
t.Fatal(err)
}
if fi.Mode() != mode {
t.Errorf("Fchmodat: failed to change file mode: expected %v, got %v", mode, fi.Mode())
}
mode = os.FileMode(0644)
didChmodSymlink := true
err = unix.Fchmodat(unix.AT_FDCWD, "symlink1", uint32(mode), unix.AT_SYMLINK_NOFOLLOW)
if err != nil {
if (runtime.GOOS == "linux" || runtime.GOOS == "solaris") && err == unix.EOPNOTSUPP {
// Linux and Illumos don't support flags != 0
didChmodSymlink = false
} else {
t.Fatalf("Fchmodat: unexpected error: %v", err)
}
}
if !didChmodSymlink {
// Didn't change mode of the symlink. On Linux, the permissions
// of a symbolic link are always 0777 according to symlink(7)
mode = os.FileMode(0777)
}
var st unix.Stat_t
err = unix.Lstat("symlink1", &st)
if err != nil {
t.Fatal(err)
}
got := os.FileMode(st.Mode & 0777)
if got != mode {
t.Errorf("Fchmodat: failed to change symlink mode: expected %v, got %v", mode, got)
}
}
// mktmpfifo creates a temporary FIFO and provides a cleanup function. // mktmpfifo creates a temporary FIFO and provides a cleanup function.
func mktmpfifo(t *testing.T) (*os.File, func()) { func mktmpfifo(t *testing.T) (*os.File, func()) {
err := unix.Mkfifo("fifo", 0666) err := unix.Mkfifo("fifo", 0666)
@ -444,3 +539,37 @@ func mktmpfifo(t *testing.T) (*os.File, func()) {
os.Remove("fifo") os.Remove("fifo")
} }
} }
// utilities taken from os/os_test.go
func touch(t *testing.T, name string) {
f, err := os.Create(name)
if err != nil {
t.Fatal(err)
}
if err := f.Close(); err != nil {
t.Fatal(err)
}
}
// chtmpdir changes the working directory to a new temporary directory and
// provides a cleanup function. Used when PWD is read-only.
func chtmpdir(t *testing.T) func() {
oldwd, err := os.Getwd()
if err != nil {
t.Fatalf("chtmpdir: %v", err)
}
d, err := ioutil.TempDir("", "test")
if err != nil {
t.Fatalf("chtmpdir: %v", err)
}
if err := os.Chdir(d); err != nil {
t.Fatalf("chtmpdir: %v", err)
}
return func() {
if err := os.Chdir(oldwd); err != nil {
t.Fatalf("chtmpdir: %v", err)
}
os.RemoveAll(d)
}
}

View file

@ -118,6 +118,17 @@ const (
PathMax = C.PATH_MAX PathMax = C.PATH_MAX
) )
// Advice to Fadvise
const (
FADV_NORMAL = C.POSIX_FADV_NORMAL
FADV_RANDOM = C.POSIX_FADV_RANDOM
FADV_SEQUENTIAL = C.POSIX_FADV_SEQUENTIAL
FADV_WILLNEED = C.POSIX_FADV_WILLNEED
FADV_DONTNEED = C.POSIX_FADV_DONTNEED
FADV_NOREUSE = C.POSIX_FADV_NOREUSE
)
// Sockets // Sockets
type RawSockaddrInet4 C.struct_sockaddr_in type RawSockaddrInet4 C.struct_sockaddr_in

View file

@ -980,7 +980,10 @@ const (
RLIMIT_CPU = 0x0 RLIMIT_CPU = 0x0
RLIMIT_DATA = 0x2 RLIMIT_DATA = 0x2
RLIMIT_FSIZE = 0x1 RLIMIT_FSIZE = 0x1
RLIMIT_MEMLOCK = 0x6
RLIMIT_NOFILE = 0x8 RLIMIT_NOFILE = 0x8
RLIMIT_NPROC = 0x7
RLIMIT_RSS = 0x5
RLIMIT_STACK = 0x3 RLIMIT_STACK = 0x3
RLIM_INFINITY = 0x7fffffffffffffff RLIM_INFINITY = 0x7fffffffffffffff
RTAX_AUTHOR = 0x6 RTAX_AUTHOR = 0x6

View file

@ -693,6 +693,21 @@ func Fstat(fd int, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) {
var _p0 *byte
_p0, err = BytePtrFromString(path)
if err != nil {
return
}
_, _, e1 := Syscall6(SYS_FSTATAT64, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fstatfs(fd int, stat *Statfs_t) (err error) { func Fstatfs(fd int, stat *Statfs_t) (err error) {
_, _, e1 := Syscall(SYS_FSTATFS64, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) _, _, e1 := Syscall(SYS_FSTATFS64, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 { if e1 != 0 {

View file

@ -693,6 +693,21 @@ func Fstat(fd int, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) {
var _p0 *byte
_p0, err = BytePtrFromString(path)
if err != nil {
return
}
_, _, e1 := Syscall6(SYS_FSTATAT64, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fstatfs(fd int, stat *Statfs_t) (err error) { func Fstatfs(fd int, stat *Statfs_t) (err error) {
_, _, e1 := Syscall(SYS_FSTATFS64, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) _, _, e1 := Syscall(SYS_FSTATFS64, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 { if e1 != 0 {

View file

@ -693,6 +693,21 @@ func Fstat(fd int, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) {
var _p0 *byte
_p0, err = BytePtrFromString(path)
if err != nil {
return
}
_, _, e1 := Syscall6(SYS_FSTATAT64, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fstatfs(fd int, stat *Statfs_t) (err error) { func Fstatfs(fd int, stat *Statfs_t) (err error) {
_, _, e1 := Syscall(SYS_FSTATFS64, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) _, _, e1 := Syscall(SYS_FSTATFS64, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 { if e1 != 0 {

View file

@ -693,6 +693,21 @@ func Fstat(fd int, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) {
var _p0 *byte
_p0, err = BytePtrFromString(path)
if err != nil {
return
}
_, _, e1 := Syscall6(SYS_FSTATAT64, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fstatfs(fd int, stat *Statfs_t) (err error) { func Fstatfs(fd int, stat *Statfs_t) (err error) {
_, _, e1 := Syscall(SYS_FSTATFS64, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) _, _, e1 := Syscall(SYS_FSTATFS64, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 { if e1 != 0 {

View file

@ -618,6 +618,21 @@ func Fchmod(fd int, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) {
var _p0 *byte
_p0, err = BytePtrFromString(path)
if err != nil {
return
}
_, _, e1 := Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fchown(fd int, uid int, gid int) (err error) { func Fchown(fd int, uid int, gid int) (err error) {
_, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)) _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid))
if e1 != 0 { if e1 != 0 {
@ -659,6 +674,21 @@ func Fstat(fd int, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) {
var _p0 *byte
_p0, err = BytePtrFromString(path)
if err != nil {
return
}
_, _, e1 := Syscall6(SYS_FSTATAT, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fstatfs(fd int, stat *Statfs_t) (err error) { func Fstatfs(fd int, stat *Statfs_t) (err error) {
_, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) _, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 { if e1 != 0 {

View file

@ -924,6 +924,21 @@ func Fstat(fd int, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) {
var _p0 *byte
_p0, err = BytePtrFromString(path)
if err != nil {
return
}
_, _, e1 := Syscall6(SYS_FSTATAT, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fstatfs(fd int, stat *Statfs_t) (err error) { func Fstatfs(fd int, stat *Statfs_t) (err error) {
_, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) _, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 { if e1 != 0 {

View file

@ -924,6 +924,21 @@ func Fstat(fd int, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) {
var _p0 *byte
_p0, err = BytePtrFromString(path)
if err != nil {
return
}
_, _, e1 := Syscall6(SYS_FSTATAT, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fstatfs(fd int, stat *Statfs_t) (err error) { func Fstatfs(fd int, stat *Statfs_t) (err error) {
_, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) _, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 { if e1 != 0 {

View file

@ -924,6 +924,21 @@ func Fstat(fd int, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) {
var _p0 *byte
_p0, err = BytePtrFromString(path)
if err != nil {
return
}
_, _, e1 := Syscall6(SYS_FSTATAT, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fstatfs(fd int, stat *Statfs_t) (err error) { func Fstatfs(fd int, stat *Statfs_t) (err error) {
_, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) _, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 { if e1 != 0 {

View file

@ -1541,6 +1541,16 @@ func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fchown(fd int, uid int, gid int) (err error) { func Fchown(fd int, uid int, gid int) (err error) {
_, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)) _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid))
if e1 != 0 { if e1 != 0 {

View file

@ -1534,6 +1534,16 @@ func Dup2(oldfd int, newfd int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
_, _, e1 := Syscall9(SYS_FADVISE64, uintptr(fd), 0, uintptr(offset>>32), uintptr(offset), uintptr(length>>32), uintptr(length), uintptr(advice), 0, 0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fchown(fd int, uid int, gid int) (err error) { func Fchown(fd int, uid int, gid int) (err error) {
_, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)) _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid))
if e1 != 0 { if e1 != 0 {

View file

@ -1551,6 +1551,16 @@ func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fchown(fd int, uid int, gid int) (err error) { func Fchown(fd int, uid int, gid int) (err error) {
_, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)) _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid))
if e1 != 0 { if e1 != 0 {

View file

@ -1551,6 +1551,16 @@ func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fchown(fd int, uid int, gid int) (err error) { func Fchown(fd int, uid int, gid int) (err error) {
_, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)) _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid))
if e1 != 0 { if e1 != 0 {

Some files were not shown because too many files have changed in this diff Show more