vendor: update all dependencies
This commit is contained in:
+5
-10
@@ -4,16 +4,15 @@ Go is an open source project.
|
||||
|
||||
It is the work of hundreds of contributors. We appreciate your help!
|
||||
|
||||
|
||||
## Filing issues
|
||||
|
||||
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`)?
|
||||
2. What operating system and processor architecture are you using?
|
||||
3. What did you do?
|
||||
4. What did you expect to see?
|
||||
5. What did you see instead?
|
||||
1. What version of Go are you using (`go version`)?
|
||||
2. What operating system and processor architecture are you using?
|
||||
3. What did you do?
|
||||
4. What did you expect to see?
|
||||
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.
|
||||
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)
|
||||
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
|
||||
the BSD-style license found in the LICENSE file.
|
||||
|
||||
|
||||
+8
-1
@@ -400,7 +400,7 @@ func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
|
||||
|
||||
// WaitAuthorization polls an authorization at the given URL
|
||||
// until it is in one of the final states, StatusValid or StatusInvalid,
|
||||
// or the context is done.
|
||||
// the ACME CA responded with a 4xx error code, or the context is done.
|
||||
//
|
||||
// It returns a non-nil Authorization only if its Status is StatusValid.
|
||||
// In all other cases WaitAuthorization returns an error.
|
||||
@@ -412,6 +412,13 @@ func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorizat
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if res.StatusCode >= 400 && res.StatusCode <= 499 {
|
||||
// Non-retriable error. For instance, Let's Encrypt may return 404 Not Found
|
||||
// when requesting an expired authorization.
|
||||
defer res.Body.Close()
|
||||
return nil, responseError(res)
|
||||
}
|
||||
|
||||
retry := res.Header.Get("Retry-After")
|
||||
if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted {
|
||||
res.Body.Close()
|
||||
|
||||
+28
@@ -549,6 +549,34 @@ func TestWaitAuthorizationInvalid(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWaitAuthorizationClientError(t *testing.T) {
|
||||
const code = http.StatusBadRequest
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(code)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
ch := make(chan error, 1)
|
||||
go func() {
|
||||
var client Client
|
||||
_, err := client.WaitAuthorization(context.Background(), ts.URL)
|
||||
ch <- err
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(3 * time.Second):
|
||||
t.Fatal("WaitAuthz took too long to return")
|
||||
case err := <-ch:
|
||||
res, ok := err.(*Error)
|
||||
if !ok {
|
||||
t.Fatalf("err is %v (%T); want a non-nil *Error", err, err)
|
||||
}
|
||||
if res.StatusCode != code {
|
||||
t.Errorf("res.StatusCode = %d; want %d", res.StatusCode, code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWaitAuthorizationCancel(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Retry-After", "60")
|
||||
|
||||
-11
@@ -27,7 +27,6 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -942,16 +941,6 @@ func validCert(domain string, der [][]byte, key crypto.Signer) (leaf *x509.Certi
|
||||
return leaf, nil
|
||||
}
|
||||
|
||||
func retryAfter(v string) time.Duration {
|
||||
if i, err := strconv.Atoi(v); err == nil {
|
||||
return time.Duration(i) * time.Second
|
||||
}
|
||||
if t, err := http.ParseTime(v); err == nil {
|
||||
return t.Sub(timeNow())
|
||||
}
|
||||
return time.Second
|
||||
}
|
||||
|
||||
type lockedMathRand struct {
|
||||
sync.Mutex
|
||||
rnd *mathrand.Rand
|
||||
|
||||
+25
-8
@@ -71,12 +71,21 @@ func (dr *domainRenewal) renew() {
|
||||
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.
|
||||
// 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.
|
||||
//
|
||||
// It may return immediately if the expiration date of the currently cached cert
|
||||
// is far enough in the future.
|
||||
// It may lock and update the Manager.state if the expiration date of the currently
|
||||
// cached cert is far enough in the future.
|
||||
//
|
||||
// The returned value is a time interval after which the renewal should occur again.
|
||||
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 {
|
||||
next := dr.next(tlscert.Leaf.NotAfter)
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,11 +120,10 @@ func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
dr.m.cachePut(ctx, dr.domain, tlscert)
|
||||
dr.m.stateMu.Lock()
|
||||
defer dr.m.stateMu.Unlock()
|
||||
// m.state is guaranteed to be non-nil at this point
|
||||
dr.m.state[dr.domain] = state
|
||||
if err := dr.m.cachePut(ctx, dr.domain, tlscert); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
dr.updateState(state)
|
||||
return dr.next(leaf.NotAfter), nil
|
||||
}
|
||||
|
||||
|
||||
+146
@@ -189,3 +189,149 @@ func TestRenewFromCache(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+69
-12
@@ -5,7 +5,35 @@
|
||||
// Package argon2 implements the key derivation function Argon2.
|
||||
// Argon2 was selected as the winner of the Password Hashing Competition and can
|
||||
// be used to derive cryptographic keys from passwords.
|
||||
// Argon2 is specfifed at https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
|
||||
//
|
||||
// For a detailed specification of Argon2 see [1].
|
||||
//
|
||||
// If you aren't sure which function you need, use Argon2id (IDKey) and
|
||||
// the parameter recommendations for your scenario.
|
||||
//
|
||||
//
|
||||
// Argon2i
|
||||
//
|
||||
// Argon2i (implemented by Key) is the side-channel resistant version of Argon2.
|
||||
// It uses data-independent memory access, which is preferred for password
|
||||
// hashing and password-based key derivation. Argon2i requires more passes over
|
||||
// memory than Argon2id to protect from trade-off attacks. The recommended
|
||||
// parameters (taken from [2]) for non-interactive operations are time=3 and to
|
||||
// use the maximum available memory.
|
||||
//
|
||||
//
|
||||
// Argon2id
|
||||
//
|
||||
// Argon2id (implemented by IDKey) is a hybrid version of Argon2 combining
|
||||
// Argon2i and Argon2d. It uses data-independent memory access for the first
|
||||
// half of the first iteration over the memory and data-dependent memory access
|
||||
// for the rest. Argon2id is side-channel resistant and provides better brute-
|
||||
// force cost savings due to time-memory tradeoffs than Argon2i. The recommended
|
||||
// parameters for non-interactive operations (taken from [2]) are time=1 and to
|
||||
// use the maximum available memory.
|
||||
//
|
||||
// [1] https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
|
||||
// [2] https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03#section-9.3
|
||||
package argon2
|
||||
|
||||
import (
|
||||
@@ -25,23 +53,52 @@ const (
|
||||
)
|
||||
|
||||
// Key derives a key from the password, salt, and cost parameters using Argon2i
|
||||
// returning a byte slice of length keyLen that can be used as cryptographic key.
|
||||
// The CPU cost and parallism degree must be greater than zero.
|
||||
// returning a byte slice of length keyLen that can be used as cryptographic
|
||||
// key. The CPU cost and parallelism degree must be greater than zero.
|
||||
//
|
||||
// For example, you can get a derived key for e.g. AES-256 (which needs a 32-byte key) by doing:
|
||||
// `key := argon2.Key([]byte("some password"), salt, 4, 32*1024, 4, 32)`
|
||||
// For example, you can get a derived key for e.g. AES-256 (which needs a
|
||||
// 32-byte key) by doing:
|
||||
//
|
||||
// The recommended parameters for interactive logins as of 2017 are time=4, memory=32*1024.
|
||||
// The number of threads can be adjusted to the numbers of available CPUs.
|
||||
// The time parameter specifies the number of passes over the memory and the memory
|
||||
// parameter specifies the size of the memory in KiB. For example memory=32*1024 sets the
|
||||
// memory cost to ~32 MB.
|
||||
// The cost parameters should be increased as memory latency and CPU parallelism increases.
|
||||
// Remember to get a good random salt.
|
||||
// key := argon2.Key([]byte("some password"), salt, 3, 32*1024, 4, 32)
|
||||
//
|
||||
// The draft RFC recommends[2] time=3, and memory=32*1024 is a sensible number.
|
||||
// If using that amount of memory (32 MB) is not possible in some contexts then
|
||||
// the time parameter can be increased to compensate.
|
||||
//
|
||||
// The time parameter specifies the number of passes over the memory and the
|
||||
// memory parameter specifies the size of the memory in KiB. For example
|
||||
// memory=32*1024 sets the memory cost to ~32 MB. The number of threads can be
|
||||
// adjusted to the number of available CPUs. The cost parameters should be
|
||||
// increased as memory latency and CPU parallelism increases. Remember to get a
|
||||
// good random salt.
|
||||
func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
|
||||
return deriveKey(argon2i, password, salt, nil, nil, time, memory, threads, keyLen)
|
||||
}
|
||||
|
||||
// IDKey derives a key from the password, salt, and cost parameters using
|
||||
// Argon2id returning a byte slice of length keyLen that can be used as
|
||||
// cryptographic key. The CPU cost and parallelism degree must be greater than
|
||||
// zero.
|
||||
//
|
||||
// For example, you can get a derived key for e.g. AES-256 (which needs a
|
||||
// 32-byte key) by doing:
|
||||
//
|
||||
// key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32)
|
||||
//
|
||||
// The draft RFC recommends[2] time=1, and memory=64*1024 is a sensible number.
|
||||
// If using that amount of memory (64 MB) is not possible in some contexts then
|
||||
// the time parameter can be increased to compensate.
|
||||
//
|
||||
// The time parameter specifies the number of passes over the memory and the
|
||||
// memory parameter specifies the size of the memory in KiB. For example
|
||||
// memory=64*1024 sets the memory cost to ~64 MB. The number of threads can be
|
||||
// adjusted to the numbers of available CPUs. The cost parameters should be
|
||||
// increased as memory latency and CPU parallelism increases. Remember to get a
|
||||
// good random salt.
|
||||
func IDKey(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
|
||||
return deriveKey(argon2id, password, salt, nil, nil, time, memory, threads, keyLen)
|
||||
}
|
||||
|
||||
func deriveKey(mode int, password, salt, secret, data []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
|
||||
if time < 1 {
|
||||
panic("argon2: number of rounds too small")
|
||||
|
||||
+2
@@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build amd64,!gccgo,!appengine
|
||||
|
||||
package argon2
|
||||
|
||||
func init() {
|
||||
|
||||
+10
-3
@@ -171,9 +171,16 @@ func Verify(publicKey PublicKey, message, sig []byte) bool {
|
||||
edwards25519.ScReduce(&hReduced, &digest)
|
||||
|
||||
var R edwards25519.ProjectiveGroupElement
|
||||
var b [32]byte
|
||||
copy(b[:], sig[32:])
|
||||
edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &b)
|
||||
var s [32]byte
|
||||
copy(s[:], sig[32:])
|
||||
|
||||
// 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
|
||||
R.ToBytes(&checkR)
|
||||
|
||||
+24
@@ -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) {
|
||||
var zero zeroReader
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
||||
+22
@@ -4,6 +4,8 @@
|
||||
|
||||
package edwards25519
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
// This code is a port of the public domain, “ref10” implementation of ed25519
|
||||
// from SUPERCOP.
|
||||
|
||||
@@ -1769,3 +1771,23 @@ func ScReduce(out *[32]byte, s *[64]byte) {
|
||||
out[30] = byte(s11 >> 9)
|
||||
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
|
||||
}
|
||||
|
||||
+6
-1
@@ -486,7 +486,7 @@ func NewEntity(name, comment, email string, config *packet.Config) (*Entity, err
|
||||
}
|
||||
isPrimaryId := true
|
||||
e.Identities[uid.Id] = &Identity{
|
||||
Name: uid.Name,
|
||||
Name: uid.Id,
|
||||
UserId: uid,
|
||||
SelfSignature: &packet.Signature{
|
||||
CreationTime: currentTime,
|
||||
@@ -507,6 +507,11 @@ func NewEntity(name, comment, email string, config *packet.Config) (*Entity, err
|
||||
e.Identities[uid.Id].SelfSignature.PreferredHash = []uint8{hashToHashId(config.DefaultHash)}
|
||||
}
|
||||
|
||||
// Likewise for DefaultCipher.
|
||||
if config != nil && config.DefaultCipher != 0 {
|
||||
e.Identities[uid.Id].SelfSignature.PreferredSymmetric = []uint8{uint8(config.DefaultCipher)}
|
||||
}
|
||||
|
||||
e.Subkeys = make([]Subkey, 1)
|
||||
e.Subkeys[0] = Subkey{
|
||||
PublicKey: packet.NewRSAPublicKey(currentTime, &encryptingPriv.PublicKey),
|
||||
|
||||
+50
@@ -320,6 +320,56 @@ func TestNewEntityWithoutPreferredHash(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewEntityCorrectName(t *testing.T) {
|
||||
entity, err := NewEntity("Golang Gopher", "Test Key", "no-reply@golang.com", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(entity.Identities) != 1 {
|
||||
t.Fatalf("len(entity.Identities) = %d, want 1", len(entity.Identities))
|
||||
}
|
||||
var got string
|
||||
for _, i := range entity.Identities {
|
||||
got = i.Name
|
||||
}
|
||||
want := "Golang Gopher (Test Key) <no-reply@golang.com>"
|
||||
if got != want {
|
||||
t.Fatalf("Identity.Name = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewEntityWithPreferredSymmetric(t *testing.T) {
|
||||
c := &packet.Config{
|
||||
DefaultCipher: packet.CipherAES256,
|
||||
}
|
||||
entity, err := NewEntity("Golang Gopher", "Test Key", "no-reply@golang.com", c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, identity := range entity.Identities {
|
||||
if len(identity.SelfSignature.PreferredSymmetric) == 0 {
|
||||
t.Fatal("didn't find a preferred cipher in self signature")
|
||||
}
|
||||
if identity.SelfSignature.PreferredSymmetric[0] != uint8(c.DefaultCipher) {
|
||||
t.Fatalf("Expected preferred cipher to be %d, got %d", uint8(c.DefaultCipher), identity.SelfSignature.PreferredSymmetric[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewEntityWithoutPreferredSymmetric(t *testing.T) {
|
||||
entity, err := NewEntity("Golang Gopher", "Test Key", "no-reply@golang.com", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, identity := range entity.Identities {
|
||||
if len(identity.SelfSignature.PreferredSymmetric) != 0 {
|
||||
t.Fatalf("Expected preferred cipher to be empty but got length %d", len(identity.SelfSignature.PreferredSymmetric))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const expiringKeyHex = "988d0451d1ec5d010400ba3385721f2dc3f4ab096b2ee867ab77213f0a27a8538441c35d2fa225b08798a1439a66a5150e6bdc3f40f5d28d588c712394c632b6299f77db8c0d48d37903fb72ebd794d61be6aa774688839e5fdecfe06b2684cc115d240c98c66cb1ef22ae84e3aa0c2b0c28665c1e7d4d044e7f270706193f5223c8d44e0d70b7b8da830011010001b40f4578706972792074657374206b657988be041301020028050251d1ec5d021b03050900278d00060b090807030206150802090a0b0416020301021e01021780000a091072589ad75e237d8c033503fd10506d72837834eb7f994117740723adc39227104b0d326a1161871c0b415d25b4aedef946ca77ea4c05af9c22b32cf98be86ab890111fced1ee3f75e87b7cc3c00dc63bbc85dfab91c0dc2ad9de2c4d13a34659333a85c6acc1a669c5e1d6cecb0cf1e56c10e72d855ae177ddc9e766f9b2dda57ccbb75f57156438bbdb4e42b88d0451d1ec5d0104009c64906559866c5cb61578f5846a94fcee142a489c9b41e67b12bb54cfe86eb9bc8566460f9a720cb00d6526fbccfd4f552071a8e3f7744b1882d01036d811ee5a3fb91a1c568055758f43ba5d2c6a9676b012f3a1a89e47bbf624f1ad571b208f3cc6224eb378f1645dd3d47584463f9eadeacfd1ce6f813064fbfdcc4b5a53001101000188a504180102000f021b0c050251d1f06b050900093e89000a091072589ad75e237d8c20e00400ab8310a41461425b37889c4da28129b5fae6084fafbc0a47dd1adc74a264c6e9c9cc125f40462ee1433072a58384daef88c961c390ed06426a81b464a53194c4e291ddd7e2e2ba3efced01537d713bd111f48437bde2363446200995e8e0d4e528dda377fd1e8f8ede9c8e2198b393bd86852ce7457a7e3daf74d510461a5b77b88d0451d1ece8010400b3a519f83ab0010307e83bca895170acce8964a044190a2b368892f7a244758d9fc193482648acb1fb9780d28cc22d171931f38bb40279389fc9bf2110876d4f3db4fcfb13f22f7083877fe56592b3b65251312c36f83ffcb6d313c6a17f197dd471f0712aad15a8537b435a92471ba2e5b0c72a6c72536c3b567c558d7b6051001101000188a504180102000f021b0c050251d1f07b050900279091000a091072589ad75e237d8ce69e03fe286026afacf7c97ee20673864d4459a2240b5655219950643c7dba0ac384b1d4359c67805b21d98211f7b09c2a0ccf6410c8c04d4ff4a51293725d8d6570d9d8bb0e10c07d22357caeb49626df99c180be02d77d1fe8ed25e7a54481237646083a9f89a11566cd20b9e995b1487c5f9e02aeb434f3a1897cd416dd0a87861838da3e9e"
|
||||
const subkeyUsageHex = "988d04533a52bc010400d26af43085558f65b9e7dbc90cb9238015259aed5e954637adcfa2181548b2d0b60c65f1f42ec5081cbf1bc0a8aa4900acfb77070837c58f26012fbce297d70afe96e759ad63531f0037538e70dbf8e384569b9720d99d8eb39d8d0a2947233ed242436cb6ac7dfe74123354b3d0119b5c235d3dd9c9d6c004f8ffaf67ad8583001101000188b7041f010200210502533b8552170c8001ce094aa433f7040bb2ddf0be3893cb843d0fe70c020700000a0910a42704b92866382aa98404009d63d916a27543da4221c60087c33f1c44bec9998c5438018ed370cca4962876c748e94b73eb39c58eb698063f3fd6346d58dd2a11c0247934c4a9d71f24754f7468f96fb24c3e791dd2392b62f626148ad724189498cbf993db2df7c0cdc2d677c35da0f16cb16c9ce7c33b4de65a4a91b1d21a130ae9cc26067718910ef8e2b417556d627261203c756d627261407379642e65642e61753e88b80413010200220502533a52bc021b03060b090807030206150802090a0b0416020301021e01021780000a0910a42704b92866382a47840400c0c2bd04f5fca586de408b395b3c280a278259c93eaaa8b79a53b97003f8ed502a8a00446dd9947fb462677e4fcac0dac2f0701847d15130aadb6cd9e0705ea0cf5f92f129136c7be21a718d46c8e641eb7f044f2adae573e11ae423a0a9ca51324f03a8a2f34b91fa40c3cc764bee4dccadedb54c768ba0469b683ea53f1c29b88d04533a52bc01040099c92a5d6f8b744224da27bc2369127c35269b58bec179de6bbc038f749344222f85a31933224f26b70243c4e4b2d242f0c4777eaef7b5502f9dad6d8bf3aaeb471210674b74de2d7078af497d55f5cdad97c7bedfbc1b41e8065a97c9c3d344b21fc81d27723af8e374bc595da26ea242dccb6ae497be26eea57e563ed517e90011010001889f0418010200090502533a52bc021b0c000a0910a42704b92866382afa1403ff70284c2de8a043ff51d8d29772602fa98009b7861c540535f874f2c230af8caf5638151a636b21f8255003997ccd29747fdd06777bb24f9593bd7d98a3e887689bf902f999915fcc94625ae487e5d13e6616f89090ebc4fdc7eb5cad8943e4056995bb61c6af37f8043016876a958ec7ebf39c43d20d53b7f546cfa83e8d2604b88d04533b8283010400c0b529316dbdf58b4c54461e7e669dc11c09eb7f73819f178ccd4177b9182b91d138605fcf1e463262fabefa73f94a52b5e15d1904635541c7ea540f07050ce0fb51b73e6f88644cec86e91107c957a114f69554548a85295d2b70bd0b203992f76eb5d493d86d9eabcaa7ef3fc7db7e458438db3fcdb0ca1cc97c638439a9170011010001889f0418010200090502533b8283021b0c000a0910a42704b92866382adc6d0400cfff6258485a21675adb7a811c3e19ebca18851533f75a7ba317950b9997fda8d1a4c8c76505c08c04b6c2cc31dc704d33da36a21273f2b388a1a706f7c3378b66d887197a525936ed9a69acb57fe7f718133da85ec742001c5d1864e9c6c8ea1b94f1c3759cebfd93b18606066c063a63be86085b7e37bdbc65f9a915bf084bb901a204533b85cd110400aed3d2c52af2b38b5b67904b0ef73d6dd7aef86adb770e2b153cd22489654dcc91730892087bb9856ae2d9f7ed1eb48f214243fe86bfe87b349ebd7c30e630e49c07b21fdabf78b7a95c8b7f969e97e3d33f2e074c63552ba64a2ded7badc05ce0ea2be6d53485f6900c7860c7aa76560376ce963d7271b9b54638a4028b573f00a0d8854bfcdb04986141568046202192263b9b67350400aaa1049dbc7943141ef590a70dcb028d730371d92ea4863de715f7f0f16d168bd3dc266c2450457d46dcbbf0b071547e5fbee7700a820c3750b236335d8d5848adb3c0da010e998908dfd93d961480084f3aea20b247034f8988eccb5546efaa35a92d0451df3aaf1aee5aa36a4c4d462c760ecd9cebcabfbe1412b1f21450f203fd126687cd486496e971a87fd9e1a8a765fe654baa219a6871ab97768596ab05c26c1aeea8f1a2c72395a58dbc12ef9640d2b95784e974a4d2d5a9b17c25fedacfe551bda52602de8f6d2e48443f5dd1a2a2a8e6a5e70ecdb88cd6e766ad9745c7ee91d78cc55c3d06536b49c3fee6c3d0b6ff0fb2bf13a314f57c953b8f4d93bf88e70418010200090502533b85cd021b0200520910a42704b92866382a47200419110200060502533b85cd000a091042ce2c64bc0ba99214b2009e26b26852c8b13b10c35768e40e78fbbb48bd084100a0c79d9ea0844fa5853dd3c85ff3ecae6f2c9dd6c557aa04008bbbc964cd65b9b8299d4ebf31f41cc7264b8cf33a00e82c5af022331fac79efc9563a822497ba012953cefe2629f1242fcdcb911dbb2315985bab060bfd58261ace3c654bdbbe2e8ed27a46e836490145c86dc7bae15c011f7e1ffc33730109b9338cd9f483e7cef3d2f396aab5bd80efb6646d7e778270ee99d934d187dd98"
|
||||
const revokedKeyHex = "988d045331ce82010400c4fdf7b40a5477f206e6ee278eaef888ca73bf9128a9eef9f2f1ddb8b7b71a4c07cfa241f028a04edb405e4d916c61d6beabc333813dc7b484d2b3c52ee233c6a79b1eea4e9cc51596ba9cd5ac5aeb9df62d86ea051055b79d03f8a4fa9f38386f5bd17529138f3325d46801514ea9047977e0829ed728e68636802796801be10011010001889f04200102000905025331d0e3021d03000a0910a401d9f09a34f7c042aa040086631196405b7e6af71026b88e98012eab44aa9849f6ef3fa930c7c9f23deaedba9db1538830f8652fb7648ec3fcade8dbcbf9eaf428e83c6cbcc272201bfe2fbb90d41963397a7c0637a1a9d9448ce695d9790db2dc95433ad7be19eb3de72dacf1d6db82c3644c13eae2a3d072b99bb341debba012c5ce4006a7d34a1f4b94b444526567205265766f6b657220283c52656727732022424d204261726973746122204b657920262530305c303e5c29203c72656740626d626172697374612e636f2e61753e88b704130102002205025331ce82021b03060b090807030206150802090a0b0416020301021e01021780000a0910a401d9f09a34f7c0019c03f75edfbeb6a73e7225ad3cc52724e2872e04260d7daf0d693c170d8c4b243b8767bc7785763533febc62ec2600c30603c433c095453ede59ff2fcabeb84ce32e0ed9d5cf15ffcbc816202b64370d4d77c1e9077d74e94a16fb4fa2e5bec23a56d7a73cf275f91691ae1801a976fcde09e981a2f6327ac27ea1fecf3185df0d56889c04100102000605025331cfb5000a0910fe9645554e8266b64b4303fc084075396674fb6f778d302ac07cef6bc0b5d07b66b2004c44aef711cbac79617ef06d836b4957522d8772dd94bf41a2f4ac8b1ee6d70c57503f837445a74765a076d07b829b8111fc2a918423ddb817ead7ca2a613ef0bfb9c6b3562aec6c3cf3c75ef3031d81d95f6563e4cdcc9960bcb386c5d757b104fcca5fe11fc709df884604101102000605025331cfe7000a09107b15a67f0b3ddc0317f6009e360beea58f29c1d963a22b962b80788c3fa6c84e009d148cfde6b351469b8eae91187eff07ad9d08fcaab88d045331ce820104009f25e20a42b904f3fa555530fe5c46737cf7bd076c35a2a0d22b11f7e0b61a69320b768f4a80fe13980ce380d1cfc4a0cd8fbe2d2e2ef85416668b77208baa65bf973fe8e500e78cc310d7c8705cdb34328bf80e24f0385fce5845c33bc7943cf6b11b02348a23da0bf6428e57c05135f2dc6bd7c1ce325d666d5a5fd2fd5e410011010001889f04180102000905025331ce82021b0c000a0910a401d9f09a34f7c0418003fe34feafcbeaef348a800a0d908a7a6809cc7304017d820f70f0474d5e23cb17e38b67dc6dca282c6ca00961f4ec9edf2738d0f087b1d81e4871ef08e1798010863afb4eac4c44a376cb343be929c5be66a78cfd4456ae9ec6a99d97f4e1c3ff3583351db2147a65c0acef5c003fb544ab3a2e2dc4d43646f58b811a6c3a369d1f"
|
||||
|
||||
+8
-1
@@ -42,12 +42,18 @@ func (e *EncryptedKey) parse(r io.Reader) (err error) {
|
||||
switch e.Algo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
|
||||
e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case PubKeyAlgoElGamal:
|
||||
e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
e.encryptedMPI2.bytes, e.encryptedMPI2.bitLength, err = readMPI(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
_, err = consumeAll(r)
|
||||
return
|
||||
@@ -72,7 +78,8 @@ func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error {
|
||||
// padding oracle attacks.
|
||||
switch priv.PubKeyAlgo {
|
||||
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:
|
||||
c1 := new(big.Int).SetBytes(e.encryptedMPI1.bytes)
|
||||
c2 := new(big.Int).SetBytes(e.encryptedMPI2.bytes)
|
||||
|
||||
+34
-29
@@ -39,39 +39,44 @@ var encryptedKeyPriv = &PrivateKey{
|
||||
}
|
||||
|
||||
func TestDecryptingEncryptedKey(t *testing.T) {
|
||||
const encryptedKeyHex = "c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8"
|
||||
const expectedKeyHex = "d930363f7e0308c333b9618617ea728963d8df993665ae7be1092d4926fd864b"
|
||||
for i, encryptedKeyHex := range []string{
|
||||
"c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8",
|
||||
// MPI can be shorter than the length of the key.
|
||||
"c18b032a67d68660df41c70103f8e520c52ae9807183c669ce26e772e482dc5d8cf60e6f59316e145be14d2e5221ee69550db1d5618a8cb002a719f1f0b9345bde21536d410ec90ba86cac37748dec7933eb7f9873873b2d61d3321d1cd44535014f6df58f7bc0c7afb5edc38e1a974428997d2f747f9a173bea9ca53079b409517d332df62d805564cffc9be6",
|
||||
} {
|
||||
const expectedKeyHex = "d930363f7e0308c333b9618617ea728963d8df993665ae7be1092d4926fd864b"
|
||||
|
||||
p, err := Read(readerFromHex(encryptedKeyHex))
|
||||
if err != nil {
|
||||
t.Errorf("error from Read: %s", err)
|
||||
return
|
||||
}
|
||||
ek, ok := p.(*EncryptedKey)
|
||||
if !ok {
|
||||
t.Errorf("didn't parse an EncryptedKey, got %#v", p)
|
||||
return
|
||||
}
|
||||
p, err := Read(readerFromHex(encryptedKeyHex))
|
||||
if err != nil {
|
||||
t.Errorf("#%d: error from Read: %s", i, err)
|
||||
return
|
||||
}
|
||||
ek, ok := p.(*EncryptedKey)
|
||||
if !ok {
|
||||
t.Errorf("#%d: didn't parse an EncryptedKey, got %#v", i, p)
|
||||
return
|
||||
}
|
||||
|
||||
if ek.KeyId != 0x2a67d68660df41c7 || ek.Algo != PubKeyAlgoRSA {
|
||||
t.Errorf("unexpected EncryptedKey contents: %#v", ek)
|
||||
return
|
||||
}
|
||||
if ek.KeyId != 0x2a67d68660df41c7 || ek.Algo != PubKeyAlgoRSA {
|
||||
t.Errorf("#%d: unexpected EncryptedKey contents: %#v", i, ek)
|
||||
return
|
||||
}
|
||||
|
||||
err = ek.Decrypt(encryptedKeyPriv, nil)
|
||||
if err != nil {
|
||||
t.Errorf("error from Decrypt: %s", err)
|
||||
return
|
||||
}
|
||||
err = ek.Decrypt(encryptedKeyPriv, nil)
|
||||
if err != nil {
|
||||
t.Errorf("#%d: error from Decrypt: %s", i, err)
|
||||
return
|
||||
}
|
||||
|
||||
if ek.CipherFunc != CipherAES256 {
|
||||
t.Errorf("unexpected EncryptedKey contents: %#v", ek)
|
||||
return
|
||||
}
|
||||
if ek.CipherFunc != CipherAES256 {
|
||||
t.Errorf("#%d: unexpected EncryptedKey contents: %#v", i, ek)
|
||||
return
|
||||
}
|
||||
|
||||
keyHex := fmt.Sprintf("%x", ek.Key)
|
||||
if keyHex != expectedKeyHex {
|
||||
t.Errorf("bad key, got %s want %x", keyHex, expectedKeyHex)
|
||||
keyHex := fmt.Sprintf("%x", ek.Key)
|
||||
if 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)
|
||||
if keyHex != expectedKeyHex {
|
||||
t.Errorf("bad key, got %s want %x", keyHex, expectedKeyHex)
|
||||
t.Errorf("bad key, got %s want %s", keyHex, expectedKeyHex)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+22
-10
@@ -11,10 +11,12 @@ import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"golang.org/x/crypto/cast5"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"crypto/rsa"
|
||||
"io"
|
||||
"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
|
||||
@@ -500,19 +502,17 @@ func readMPI(r io.Reader) (mpi []byte, bitLength uint16, err error) {
|
||||
numBytes := (int(bitLength) + 7) / 8
|
||||
mpi = make([]byte, numBytes)
|
||||
_, err = readFull(r, mpi)
|
||||
return
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
return
|
||||
}
|
||||
|
||||
// writeMPI serializes a big integer to w.
|
||||
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)})
|
||||
if err == nil {
|
||||
_, 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())
|
||||
}
|
||||
|
||||
// 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
|
||||
// supported by OpenPGP (except for BZIP2, which is not currently
|
||||
// supported). See Section 9.3 of RFC 4880.
|
||||
|
||||
+8
-3
@@ -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.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()
|
||||
return pk
|
||||
@@ -515,7 +520,7 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err erro
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
|
||||
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 {
|
||||
return errors.SignatureError("RSA verification failure")
|
||||
}
|
||||
@@ -566,7 +571,7 @@ func (pk *PublicKey) VerifySignatureV3(signed hash.Hash, sig *SignatureV3) (err
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
|
||||
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
|
||||
|
||||
+26
@@ -6,7 +6,10 @@ package packet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"testing"
|
||||
"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 rsaPkDataHex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001"
|
||||
|
||||
+13
-5
@@ -50,15 +50,23 @@ func TestVectors(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMillionA(t *testing.T) {
|
||||
func millionA() string {
|
||||
md := New()
|
||||
for i := 0; i < 100000; i++ {
|
||||
io.WriteString(md, "aaaaaaaaaa")
|
||||
}
|
||||
out := "52783243c1697bdbe16d37f97f68f08325dc1528"
|
||||
s := fmt.Sprintf("%x", md.Sum(nil))
|
||||
if s != out {
|
||||
return fmt.Sprintf("%x", md.Sum(nil))
|
||||
}
|
||||
|
||||
func TestMillionA(t *testing.T) {
|
||||
const out = "52783243c1697bdbe16d37f97f68f08325dc1528"
|
||||
if s := millionA(); s != out {
|
||||
t.Fatalf("RIPEMD-160 (1 million 'a') = %s, expected %s", s, out)
|
||||
}
|
||||
md.Reset()
|
||||
}
|
||||
|
||||
func BenchmarkMillionA(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
millionA()
|
||||
}
|
||||
}
|
||||
|
||||
+34
-30
@@ -8,6 +8,10 @@
|
||||
|
||||
package ripemd160
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// work buffer indices and roll amounts for one line
|
||||
var _n = [80]uint{
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
@@ -59,16 +63,16 @@ func _Block(md *digest, p []byte) int {
|
||||
i := 0
|
||||
for i < 16 {
|
||||
alpha = a + (b ^ c ^ d) + x[_n[i]]
|
||||
s := _r[i]
|
||||
alpha = (alpha<<s | alpha>>(32-s)) + e
|
||||
beta = c<<10 | c>>22
|
||||
s := int(_r[i])
|
||||
alpha = bits.RotateLeft32(alpha, s) + e
|
||||
beta = bits.RotateLeft32(c, 10)
|
||||
a, b, c, d, e = e, alpha, b, beta, d
|
||||
|
||||
// parallel line
|
||||
alpha = aa + (bb ^ (cc | ^dd)) + x[n_[i]] + 0x50a28be6
|
||||
s = r_[i]
|
||||
alpha = (alpha<<s | alpha>>(32-s)) + ee
|
||||
beta = cc<<10 | cc>>22
|
||||
s = int(r_[i])
|
||||
alpha = bits.RotateLeft32(alpha, s) + ee
|
||||
beta = bits.RotateLeft32(cc, 10)
|
||||
aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd
|
||||
|
||||
i++
|
||||
@@ -77,16 +81,16 @@ func _Block(md *digest, p []byte) int {
|
||||
// round 2
|
||||
for i < 32 {
|
||||
alpha = a + (b&c | ^b&d) + x[_n[i]] + 0x5a827999
|
||||
s := _r[i]
|
||||
alpha = (alpha<<s | alpha>>(32-s)) + e
|
||||
beta = c<<10 | c>>22
|
||||
s := int(_r[i])
|
||||
alpha = bits.RotateLeft32(alpha, s) + e
|
||||
beta = bits.RotateLeft32(c, 10)
|
||||
a, b, c, d, e = e, alpha, b, beta, d
|
||||
|
||||
// parallel line
|
||||
alpha = aa + (bb&dd | cc&^dd) + x[n_[i]] + 0x5c4dd124
|
||||
s = r_[i]
|
||||
alpha = (alpha<<s | alpha>>(32-s)) + ee
|
||||
beta = cc<<10 | cc>>22
|
||||
s = int(r_[i])
|
||||
alpha = bits.RotateLeft32(alpha, s) + ee
|
||||
beta = bits.RotateLeft32(cc, 10)
|
||||
aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd
|
||||
|
||||
i++
|
||||
@@ -95,16 +99,16 @@ func _Block(md *digest, p []byte) int {
|
||||
// round 3
|
||||
for i < 48 {
|
||||
alpha = a + (b | ^c ^ d) + x[_n[i]] + 0x6ed9eba1
|
||||
s := _r[i]
|
||||
alpha = (alpha<<s | alpha>>(32-s)) + e
|
||||
beta = c<<10 | c>>22
|
||||
s := int(_r[i])
|
||||
alpha = bits.RotateLeft32(alpha, s) + e
|
||||
beta = bits.RotateLeft32(c, 10)
|
||||
a, b, c, d, e = e, alpha, b, beta, d
|
||||
|
||||
// parallel line
|
||||
alpha = aa + (bb | ^cc ^ dd) + x[n_[i]] + 0x6d703ef3
|
||||
s = r_[i]
|
||||
alpha = (alpha<<s | alpha>>(32-s)) + ee
|
||||
beta = cc<<10 | cc>>22
|
||||
s = int(r_[i])
|
||||
alpha = bits.RotateLeft32(alpha, s) + ee
|
||||
beta = bits.RotateLeft32(cc, 10)
|
||||
aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd
|
||||
|
||||
i++
|
||||
@@ -113,16 +117,16 @@ func _Block(md *digest, p []byte) int {
|
||||
// round 4
|
||||
for i < 64 {
|
||||
alpha = a + (b&d | c&^d) + x[_n[i]] + 0x8f1bbcdc
|
||||
s := _r[i]
|
||||
alpha = (alpha<<s | alpha>>(32-s)) + e
|
||||
beta = c<<10 | c>>22
|
||||
s := int(_r[i])
|
||||
alpha = bits.RotateLeft32(alpha, s) + e
|
||||
beta = bits.RotateLeft32(c, 10)
|
||||
a, b, c, d, e = e, alpha, b, beta, d
|
||||
|
||||
// parallel line
|
||||
alpha = aa + (bb&cc | ^bb&dd) + x[n_[i]] + 0x7a6d76e9
|
||||
s = r_[i]
|
||||
alpha = (alpha<<s | alpha>>(32-s)) + ee
|
||||
beta = cc<<10 | cc>>22
|
||||
s = int(r_[i])
|
||||
alpha = bits.RotateLeft32(alpha, s) + ee
|
||||
beta = bits.RotateLeft32(cc, 10)
|
||||
aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd
|
||||
|
||||
i++
|
||||
@@ -131,16 +135,16 @@ func _Block(md *digest, p []byte) int {
|
||||
// round 5
|
||||
for i < 80 {
|
||||
alpha = a + (b ^ (c | ^d)) + x[_n[i]] + 0xa953fd4e
|
||||
s := _r[i]
|
||||
alpha = (alpha<<s | alpha>>(32-s)) + e
|
||||
beta = c<<10 | c>>22
|
||||
s := int(_r[i])
|
||||
alpha = bits.RotateLeft32(alpha, s) + e
|
||||
beta = bits.RotateLeft32(c, 10)
|
||||
a, b, c, d, e = e, alpha, b, beta, d
|
||||
|
||||
// parallel line
|
||||
alpha = aa + (bb ^ cc ^ dd) + x[n_[i]]
|
||||
s = r_[i]
|
||||
alpha = (alpha<<s | alpha>>(32-s)) + ee
|
||||
beta = cc<<10 | cc>>22
|
||||
s = int(r_[i])
|
||||
alpha = bits.RotateLeft32(alpha, s) + ee
|
||||
beta = bits.RotateLeft32(cc, 10)
|
||||
aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd
|
||||
|
||||
i++
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ func (d *state) Clone() ShakeHash {
|
||||
// least 32 bytes of its output are used.
|
||||
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
|
||||
// at least 64 bytes of its output are used.
|
||||
func NewShake256() ShakeHash { return &state{rate: 136, dsbyte: 0x1f} }
|
||||
|
||||
+3
-1
@@ -44,7 +44,9 @@ type Signature struct {
|
||||
const CertTimeInfinity = 1<<64 - 1
|
||||
|
||||
// An Certificate represents an OpenSSH certificate as defined in
|
||||
// [PROTOCOL.certkeys]?rev=1.8.
|
||||
// [PROTOCOL.certkeys]?rev=1.8. The Certificate type implements the
|
||||
// PublicKey interface, so it can be unmarshaled using
|
||||
// ParsePublicKey.
|
||||
type Certificate struct {
|
||||
Nonce []byte
|
||||
Key PublicKey
|
||||
|
||||
+193
-51
@@ -16,6 +16,9 @@ import (
|
||||
"hash"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"golang.org/x/crypto/internal/chacha20"
|
||||
"golang.org/x/crypto/poly1305"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -53,78 +56,78 @@ func newRC4(key, iv []byte) (cipher.Stream, error) {
|
||||
return rc4.NewCipher(key)
|
||||
}
|
||||
|
||||
type streamCipherMode struct {
|
||||
keySize int
|
||||
ivSize int
|
||||
skip int
|
||||
createFunc func(key, iv []byte) (cipher.Stream, error)
|
||||
type cipherMode struct {
|
||||
keySize int
|
||||
ivSize int
|
||||
create func(key, iv []byte, macKey []byte, algs directionAlgorithms) (packetCipher, error)
|
||||
}
|
||||
|
||||
func (c *streamCipherMode) createStream(key, iv []byte) (cipher.Stream, error) {
|
||||
if len(key) < c.keySize {
|
||||
panic("ssh: key length too small for cipher")
|
||||
}
|
||||
if len(iv) < c.ivSize {
|
||||
panic("ssh: iv too small for cipher")
|
||||
}
|
||||
|
||||
stream, err := c.createFunc(key[:c.keySize], iv[:c.ivSize])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var streamDump []byte
|
||||
if c.skip > 0 {
|
||||
streamDump = make([]byte, 512)
|
||||
}
|
||||
|
||||
for remainingToDump := c.skip; remainingToDump > 0; {
|
||||
dumpThisTime := remainingToDump
|
||||
if dumpThisTime > len(streamDump) {
|
||||
dumpThisTime = len(streamDump)
|
||||
func streamCipherMode(skip int, createFunc func(key, iv []byte) (cipher.Stream, error)) func(key, iv []byte, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
||||
return func(key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
||||
stream, err := createFunc(key, iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime])
|
||||
remainingToDump -= dumpThisTime
|
||||
}
|
||||
|
||||
return stream, nil
|
||||
var streamDump []byte
|
||||
if skip > 0 {
|
||||
streamDump = make([]byte, 512)
|
||||
}
|
||||
|
||||
for remainingToDump := skip; remainingToDump > 0; {
|
||||
dumpThisTime := remainingToDump
|
||||
if dumpThisTime > len(streamDump) {
|
||||
dumpThisTime = len(streamDump)
|
||||
}
|
||||
stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime])
|
||||
remainingToDump -= dumpThisTime
|
||||
}
|
||||
|
||||
mac := macModes[algs.MAC].new(macKey)
|
||||
return &streamPacketCipher{
|
||||
mac: mac,
|
||||
etm: macModes[algs.MAC].etm,
|
||||
macResult: make([]byte, mac.Size()),
|
||||
cipher: stream,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// cipherModes documents properties of supported ciphers. Ciphers not included
|
||||
// are not supported and will not be negotiated, even if explicitly requested in
|
||||
// ClientConfig.Crypto.Ciphers.
|
||||
var cipherModes = map[string]*streamCipherMode{
|
||||
var cipherModes = map[string]*cipherMode{
|
||||
// Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms
|
||||
// are defined in the order specified in the RFC.
|
||||
"aes128-ctr": {16, aes.BlockSize, 0, newAESCTR},
|
||||
"aes192-ctr": {24, aes.BlockSize, 0, newAESCTR},
|
||||
"aes256-ctr": {32, aes.BlockSize, 0, newAESCTR},
|
||||
"aes128-ctr": {16, aes.BlockSize, streamCipherMode(0, newAESCTR)},
|
||||
"aes192-ctr": {24, aes.BlockSize, streamCipherMode(0, newAESCTR)},
|
||||
"aes256-ctr": {32, aes.BlockSize, streamCipherMode(0, newAESCTR)},
|
||||
|
||||
// Ciphers from RFC4345, which introduces security-improved arcfour ciphers.
|
||||
// They are defined in the order specified in the RFC.
|
||||
"arcfour128": {16, 0, 1536, newRC4},
|
||||
"arcfour256": {32, 0, 1536, newRC4},
|
||||
"arcfour128": {16, 0, streamCipherMode(1536, newRC4)},
|
||||
"arcfour256": {32, 0, streamCipherMode(1536, newRC4)},
|
||||
|
||||
// Cipher defined in RFC 4253, which describes SSH Transport Layer Protocol.
|
||||
// Note that this cipher is not safe, as stated in RFC 4253: "Arcfour (and
|
||||
// RC4) has problems with weak keys, and should be used with caution."
|
||||
// RFC4345 introduces improved versions of Arcfour.
|
||||
"arcfour": {16, 0, 0, newRC4},
|
||||
"arcfour": {16, 0, streamCipherMode(0, newRC4)},
|
||||
|
||||
// AES-GCM is not a stream cipher, so it is constructed with a
|
||||
// special case. If we add any more non-stream ciphers, we
|
||||
// should invest a cleaner way to do this.
|
||||
gcmCipherID: {16, 12, 0, nil},
|
||||
// AEAD ciphers
|
||||
gcmCipherID: {16, 12, newGCMCipher},
|
||||
chacha20Poly1305ID: {64, 0, newChaCha20Cipher},
|
||||
|
||||
// CBC mode is insecure and so is not included in the default config.
|
||||
// (See http://www.isg.rhul.ac.uk/~kp/SandPfinal.pdf). If absolutely
|
||||
// needed, it's possible to specify a custom Config to enable it.
|
||||
// You should expect that an active attacker can recover plaintext if
|
||||
// you do.
|
||||
aes128cbcID: {16, aes.BlockSize, 0, nil},
|
||||
aes128cbcID: {16, aes.BlockSize, newAESCBCCipher},
|
||||
|
||||
// 3des-cbc is insecure and is disabled by default.
|
||||
tripledescbcID: {24, des.BlockSize, 0, nil},
|
||||
// 3des-cbc is insecure and is not included in the default
|
||||
// config.
|
||||
tripledescbcID: {24, des.BlockSize, newTripleDESCBCCipher},
|
||||
}
|
||||
|
||||
// prefixLen is the length of the packet prefix that contains the packet length
|
||||
@@ -304,7 +307,7 @@ type gcmCipher struct {
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func newGCMCipher(iv, key []byte) (packetCipher, error) {
|
||||
func newGCMCipher(key, iv, unusedMacKey []byte, unusedAlgs directionAlgorithms) (packetCipher, error) {
|
||||
c, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -422,7 +425,7 @@ type cbcCipher struct {
|
||||
oracleCamouflage uint32
|
||||
}
|
||||
|
||||
func newCBCCipher(c cipher.Block, iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
||||
func newCBCCipher(c cipher.Block, key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
||||
cbc := &cbcCipher{
|
||||
mac: macModes[algs.MAC].new(macKey),
|
||||
decrypter: cipher.NewCBCDecrypter(c, iv),
|
||||
@@ -436,13 +439,13 @@ func newCBCCipher(c cipher.Block, iv, key, macKey []byte, algs directionAlgorith
|
||||
return cbc, nil
|
||||
}
|
||||
|
||||
func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
||||
func newAESCBCCipher(key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
||||
c, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cbc, err := newCBCCipher(c, iv, key, macKey, algs)
|
||||
cbc, err := newCBCCipher(c, key, iv, macKey, algs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -450,13 +453,13 @@ func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCi
|
||||
return cbc, nil
|
||||
}
|
||||
|
||||
func newTripleDESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
||||
func newTripleDESCBCCipher(key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
||||
c, err := des.NewTripleDESCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cbc, err := newCBCCipher(c, iv, key, macKey, algs)
|
||||
cbc, err := newCBCCipher(c, key, iv, macKey, algs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -627,3 +630,142 @@ func (c *cbcCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, pack
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const chacha20Poly1305ID = "chacha20-poly1305@openssh.com"
|
||||
|
||||
// chacha20Poly1305Cipher implements the chacha20-poly1305@openssh.com
|
||||
// AEAD, which is described here:
|
||||
//
|
||||
// https://tools.ietf.org/html/draft-josefsson-ssh-chacha20-poly1305-openssh-00
|
||||
//
|
||||
// the methods here also implement padding, which RFC4253 Section 6
|
||||
// also requires of stream ciphers.
|
||||
type chacha20Poly1305Cipher struct {
|
||||
lengthKey [32]byte
|
||||
contentKey [32]byte
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func newChaCha20Cipher(key, unusedIV, unusedMACKey []byte, unusedAlgs directionAlgorithms) (packetCipher, error) {
|
||||
if len(key) != 64 {
|
||||
panic(len(key))
|
||||
}
|
||||
|
||||
c := &chacha20Poly1305Cipher{
|
||||
buf: make([]byte, 256),
|
||||
}
|
||||
|
||||
copy(c.contentKey[:], key[:32])
|
||||
copy(c.lengthKey[:], key[32:])
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// The Poly1305 key is obtained by encrypting 32 0-bytes.
|
||||
var chacha20PolyKeyInput [32]byte
|
||||
|
||||
func (c *chacha20Poly1305Cipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
|
||||
var counter [16]byte
|
||||
binary.BigEndian.PutUint64(counter[8:], uint64(seqNum))
|
||||
|
||||
var polyKey [32]byte
|
||||
chacha20.XORKeyStream(polyKey[:], chacha20PolyKeyInput[:], &counter, &c.contentKey)
|
||||
|
||||
encryptedLength := c.buf[:4]
|
||||
if _, err := io.ReadFull(r, encryptedLength); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var lenBytes [4]byte
|
||||
chacha20.XORKeyStream(lenBytes[:], encryptedLength, &counter, &c.lengthKey)
|
||||
|
||||
length := binary.BigEndian.Uint32(lenBytes[:])
|
||||
if length > maxPacket {
|
||||
return nil, errors.New("ssh: invalid packet length, packet too large")
|
||||
}
|
||||
|
||||
contentEnd := 4 + length
|
||||
packetEnd := contentEnd + poly1305.TagSize
|
||||
if uint32(cap(c.buf)) < packetEnd {
|
||||
c.buf = make([]byte, packetEnd)
|
||||
copy(c.buf[:], encryptedLength)
|
||||
} else {
|
||||
c.buf = c.buf[:packetEnd]
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(r, c.buf[4:packetEnd]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var mac [poly1305.TagSize]byte
|
||||
copy(mac[:], c.buf[contentEnd:packetEnd])
|
||||
if !poly1305.Verify(&mac, c.buf[:contentEnd], &polyKey) {
|
||||
return nil, errors.New("ssh: MAC failure")
|
||||
}
|
||||
|
||||
counter[0] = 1
|
||||
|
||||
plain := c.buf[4:contentEnd]
|
||||
chacha20.XORKeyStream(plain, plain, &counter, &c.contentKey)
|
||||
|
||||
padding := plain[0]
|
||||
if padding < 4 {
|
||||
// padding is a byte, so it automatically satisfies
|
||||
// the maximum size, which is 255.
|
||||
return nil, fmt.Errorf("ssh: illegal padding %d", padding)
|
||||
}
|
||||
|
||||
if int(padding)+1 >= len(plain) {
|
||||
return nil, fmt.Errorf("ssh: padding %d too large", padding)
|
||||
}
|
||||
|
||||
plain = plain[1 : len(plain)-int(padding)]
|
||||
|
||||
return plain, nil
|
||||
}
|
||||
|
||||
func (c *chacha20Poly1305Cipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, payload []byte) error {
|
||||
var counter [16]byte
|
||||
binary.BigEndian.PutUint64(counter[8:], uint64(seqNum))
|
||||
|
||||
var polyKey [32]byte
|
||||
chacha20.XORKeyStream(polyKey[:], chacha20PolyKeyInput[:], &counter, &c.contentKey)
|
||||
|
||||
// There is no blocksize, so fall back to multiple of 8 byte
|
||||
// padding, as described in RFC 4253, Sec 6.
|
||||
const packetSizeMultiple = 8
|
||||
|
||||
padding := packetSizeMultiple - (1+len(payload))%packetSizeMultiple
|
||||
if padding < 4 {
|
||||
padding += packetSizeMultiple
|
||||
}
|
||||
|
||||
// size (4 bytes), padding (1), payload, padding, tag.
|
||||
totalLength := 4 + 1 + len(payload) + padding + poly1305.TagSize
|
||||
if cap(c.buf) < totalLength {
|
||||
c.buf = make([]byte, totalLength)
|
||||
} else {
|
||||
c.buf = c.buf[:totalLength]
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint32(c.buf, uint32(1+len(payload)+padding))
|
||||
chacha20.XORKeyStream(c.buf, c.buf[:4], &counter, &c.lengthKey)
|
||||
c.buf[4] = byte(padding)
|
||||
copy(c.buf[5:], payload)
|
||||
packetEnd := 5 + len(payload) + padding
|
||||
if _, err := io.ReadFull(rand, c.buf[5+len(payload):packetEnd]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
counter[0] = 1
|
||||
chacha20.XORKeyStream(c.buf[4:], c.buf[4:packetEnd], &counter, &c.contentKey)
|
||||
|
||||
var mac [poly1305.TagSize]byte
|
||||
poly1305.Sum(&mac, c.buf[:packetEnd], &polyKey)
|
||||
|
||||
copy(c.buf[packetEnd:], mac[:])
|
||||
|
||||
if _, err := w.Write(c.buf); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+6
-5
@@ -7,7 +7,6 @@ package ssh
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/aes"
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
)
|
||||
@@ -15,7 +14,12 @@ import (
|
||||
func TestDefaultCiphersExist(t *testing.T) {
|
||||
for _, cipherAlgo := range supportedCiphers {
|
||||
if _, ok := cipherModes[cipherAlgo]; !ok {
|
||||
t.Errorf("default cipher %q is unknown", cipherAlgo)
|
||||
t.Errorf("supported cipher %q is unknown", cipherAlgo)
|
||||
}
|
||||
}
|
||||
for _, cipherAlgo := range preferredCiphers {
|
||||
if _, ok := cipherModes[cipherAlgo]; !ok {
|
||||
t.Errorf("preferred cipher %q is unknown", cipherAlgo)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,9 +71,6 @@ func testPacketCipher(t *testing.T, cipher, mac string) {
|
||||
}
|
||||
|
||||
func TestCBCOracleCounterMeasure(t *testing.T) {
|
||||
cipherModes[aes128cbcID] = &streamCipherMode{16, aes.BlockSize, 0, nil}
|
||||
defer delete(cipherModes, aes128cbcID)
|
||||
|
||||
kr := &kexResult{Hash: crypto.SHA1}
|
||||
algs := directionAlgorithms{
|
||||
Cipher: aes128cbcID,
|
||||
|
||||
+55
-40
@@ -11,6 +11,14 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
type authResult int
|
||||
|
||||
const (
|
||||
authFailure authResult = iota
|
||||
authPartialSuccess
|
||||
authSuccess
|
||||
)
|
||||
|
||||
// clientAuthenticate authenticates with the remote server. See RFC 4252.
|
||||
func (c *connection) clientAuthenticate(config *ClientConfig) error {
|
||||
// initiate user auth session
|
||||
@@ -37,11 +45,12 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok {
|
||||
if ok == authSuccess {
|
||||
// success
|
||||
return nil
|
||||
} else if ok == authFailure {
|
||||
tried[auth.method()] = true
|
||||
}
|
||||
tried[auth.method()] = true
|
||||
if methods == nil {
|
||||
methods = lastMethods
|
||||
}
|
||||
@@ -82,7 +91,7 @@ type AuthMethod interface {
|
||||
// If authentication is not successful, a []string of alternative
|
||||
// method names is returned. If the slice is nil, it will be ignored
|
||||
// and the previous set of possible methods will be reused.
|
||||
auth(session []byte, user string, p packetConn, rand io.Reader) (bool, []string, error)
|
||||
auth(session []byte, user string, p packetConn, rand io.Reader) (authResult, []string, error)
|
||||
|
||||
// method returns the RFC 4252 method name.
|
||||
method() string
|
||||
@@ -91,13 +100,13 @@ type AuthMethod interface {
|
||||
// "none" authentication, RFC 4252 section 5.2.
|
||||
type noneAuth int
|
||||
|
||||
func (n *noneAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) {
|
||||
func (n *noneAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) {
|
||||
if err := c.writePacket(Marshal(&userAuthRequestMsg{
|
||||
User: user,
|
||||
Service: serviceSSH,
|
||||
Method: "none",
|
||||
})); err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
|
||||
return handleAuthResponse(c)
|
||||
@@ -111,7 +120,7 @@ func (n *noneAuth) method() string {
|
||||
// a function call, e.g. by prompting the user.
|
||||
type passwordCallback func() (password string, err error)
|
||||
|
||||
func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) {
|
||||
func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) {
|
||||
type passwordAuthMsg struct {
|
||||
User string `sshtype:"50"`
|
||||
Service string
|
||||
@@ -125,7 +134,7 @@ func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand
|
||||
// The program may only find out that the user doesn't have a password
|
||||
// when prompting.
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
|
||||
if err := c.writePacket(Marshal(&passwordAuthMsg{
|
||||
@@ -135,7 +144,7 @@ func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand
|
||||
Reply: false,
|
||||
Password: pw,
|
||||
})); err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
|
||||
return handleAuthResponse(c)
|
||||
@@ -178,7 +187,7 @@ func (cb publicKeyCallback) method() string {
|
||||
return "publickey"
|
||||
}
|
||||
|
||||
func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) {
|
||||
func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) {
|
||||
// Authentication is performed by sending an enquiry to test if a key is
|
||||
// acceptable to the remote. If the key is acceptable, the client will
|
||||
// attempt to authenticate with the valid key. If not the client will repeat
|
||||
@@ -186,13 +195,13 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
|
||||
|
||||
signers, err := cb()
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
var methods []string
|
||||
for _, signer := range signers {
|
||||
ok, err := validateKey(signer.PublicKey(), user, c)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
if !ok {
|
||||
continue
|
||||
@@ -206,7 +215,7 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
|
||||
Method: cb.method(),
|
||||
}, []byte(pub.Type()), pubKey))
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
|
||||
// manually wrap the serialized signature in a string
|
||||
@@ -224,24 +233,24 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
|
||||
}
|
||||
p := Marshal(&msg)
|
||||
if err := c.writePacket(p); err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
var success bool
|
||||
var success authResult
|
||||
success, methods, err = handleAuthResponse(c)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
|
||||
// If authentication succeeds or the list of available methods does not
|
||||
// contain the "publickey" method, do not attempt to authenticate with any
|
||||
// other keys. According to RFC 4252 Section 7, the latter can occur when
|
||||
// additional authentication methods are required.
|
||||
if success || !containsMethod(methods, cb.method()) {
|
||||
if success == authSuccess || !containsMethod(methods, cb.method()) {
|
||||
return success, methods, err
|
||||
}
|
||||
}
|
||||
|
||||
return false, methods, nil
|
||||
return authFailure, methods, nil
|
||||
}
|
||||
|
||||
func containsMethod(methods []string, method string) bool {
|
||||
@@ -318,28 +327,31 @@ func PublicKeysCallback(getSigners func() (signers []Signer, err error)) AuthMet
|
||||
// handleAuthResponse returns whether the preceding authentication request succeeded
|
||||
// along with a list of remaining authentication methods to try next and
|
||||
// an error if an unexpected response was received.
|
||||
func handleAuthResponse(c packetConn) (bool, []string, error) {
|
||||
func handleAuthResponse(c packetConn) (authResult, []string, error) {
|
||||
for {
|
||||
packet, err := c.readPacket()
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
|
||||
switch packet[0] {
|
||||
case msgUserAuthBanner:
|
||||
if err := handleBannerResponse(c, packet); err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
case msgUserAuthFailure:
|
||||
var msg userAuthFailureMsg
|
||||
if err := Unmarshal(packet, &msg); err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
return false, msg.Methods, nil
|
||||
if msg.PartialSuccess {
|
||||
return authPartialSuccess, msg.Methods, nil
|
||||
}
|
||||
return authFailure, msg.Methods, nil
|
||||
case msgUserAuthSuccess:
|
||||
return true, nil, nil
|
||||
return authSuccess, nil, nil
|
||||
default:
|
||||
return false, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0])
|
||||
return authFailure, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -381,7 +393,7 @@ func (cb KeyboardInteractiveChallenge) method() string {
|
||||
return "keyboard-interactive"
|
||||
}
|
||||
|
||||
func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) {
|
||||
func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) {
|
||||
type initiateMsg struct {
|
||||
User string `sshtype:"50"`
|
||||
Service string
|
||||
@@ -395,20 +407,20 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
|
||||
Service: serviceSSH,
|
||||
Method: "keyboard-interactive",
|
||||
})); err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
|
||||
for {
|
||||
packet, err := c.readPacket()
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
|
||||
// like handleAuthResponse, but with less options.
|
||||
switch packet[0] {
|
||||
case msgUserAuthBanner:
|
||||
if err := handleBannerResponse(c, packet); err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
continue
|
||||
case msgUserAuthInfoRequest:
|
||||
@@ -416,18 +428,21 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
|
||||
case msgUserAuthFailure:
|
||||
var msg userAuthFailureMsg
|
||||
if err := Unmarshal(packet, &msg); err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
return false, msg.Methods, nil
|
||||
if msg.PartialSuccess {
|
||||
return authPartialSuccess, msg.Methods, nil
|
||||
}
|
||||
return authFailure, msg.Methods, nil
|
||||
case msgUserAuthSuccess:
|
||||
return true, nil, nil
|
||||
return authSuccess, nil, nil
|
||||
default:
|
||||
return false, nil, unexpectedMessageError(msgUserAuthInfoRequest, packet[0])
|
||||
return authFailure, nil, unexpectedMessageError(msgUserAuthInfoRequest, packet[0])
|
||||
}
|
||||
|
||||
var msg userAuthInfoRequestMsg
|
||||
if err := Unmarshal(packet, &msg); err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
|
||||
// Manually unpack the prompt/echo pairs.
|
||||
@@ -437,7 +452,7 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
|
||||
for i := 0; i < int(msg.NumPrompts); i++ {
|
||||
prompt, r, ok := parseString(rest)
|
||||
if !ok || len(r) == 0 {
|
||||
return false, nil, errors.New("ssh: prompt format error")
|
||||
return authFailure, nil, errors.New("ssh: prompt format error")
|
||||
}
|
||||
prompts = append(prompts, string(prompt))
|
||||
echos = append(echos, r[0] != 0)
|
||||
@@ -445,16 +460,16 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
|
||||
}
|
||||
|
||||
if len(rest) != 0 {
|
||||
return false, nil, errors.New("ssh: extra data following keyboard-interactive pairs")
|
||||
return authFailure, nil, errors.New("ssh: extra data following keyboard-interactive pairs")
|
||||
}
|
||||
|
||||
answers, err := cb(msg.User, msg.Instruction, prompts, echos)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
|
||||
if len(answers) != len(prompts) {
|
||||
return false, nil, errors.New("ssh: not enough answers from keyboard-interactive callback")
|
||||
return authFailure, nil, errors.New("ssh: not enough answers from keyboard-interactive callback")
|
||||
}
|
||||
responseLength := 1 + 4
|
||||
for _, a := range answers {
|
||||
@@ -470,7 +485,7 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
|
||||
}
|
||||
|
||||
if err := c.writePacket(serialized); err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -480,10 +495,10 @@ type retryableAuthMethod struct {
|
||||
maxTries int
|
||||
}
|
||||
|
||||
func (r *retryableAuthMethod) auth(session []byte, user string, c packetConn, rand io.Reader) (ok bool, methods []string, err error) {
|
||||
func (r *retryableAuthMethod) auth(session []byte, user string, c packetConn, rand io.Reader) (ok authResult, methods []string, err error) {
|
||||
for i := 0; r.maxTries <= 0 || i < r.maxTries; i++ {
|
||||
ok, methods, err = r.authMethod.auth(session, user, c, rand)
|
||||
if ok || err != nil { // either success or error terminate
|
||||
if ok != authFailure || err != nil { // either success, partial success or error terminate
|
||||
return ok, methods, err
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -614,8 +614,8 @@ func TestClientAuthErrorList(t *testing.T) {
|
||||
for i, e := range authErrs.Errors {
|
||||
switch i {
|
||||
case 0:
|
||||
if e.Error() != "no auth passed yet" {
|
||||
t.Fatalf("errors: got %v, want no auth passed yet", e.Error())
|
||||
if e != ErrNoAuth {
|
||||
t.Fatalf("errors: got error %v, want ErrNoAuth", e)
|
||||
}
|
||||
case 1:
|
||||
if e != publicKeyErr {
|
||||
|
||||
+13
-3
@@ -24,11 +24,21 @@ const (
|
||||
serviceSSH = "ssh-connection"
|
||||
)
|
||||
|
||||
// supportedCiphers specifies the supported ciphers in preference order.
|
||||
// supportedCiphers lists ciphers we support but might not recommend.
|
||||
var supportedCiphers = []string{
|
||||
"aes128-ctr", "aes192-ctr", "aes256-ctr",
|
||||
"aes128-gcm@openssh.com",
|
||||
"arcfour256", "arcfour128",
|
||||
chacha20Poly1305ID,
|
||||
"arcfour256", "arcfour128", "arcfour",
|
||||
aes128cbcID,
|
||||
tripledescbcID,
|
||||
}
|
||||
|
||||
// preferredCiphers specifies the default preference for ciphers.
|
||||
var preferredCiphers = []string{
|
||||
"aes128-gcm@openssh.com",
|
||||
chacha20Poly1305ID,
|
||||
"aes128-ctr", "aes192-ctr", "aes256-ctr",
|
||||
}
|
||||
|
||||
// supportedKexAlgos specifies the supported key-exchange algorithms in
|
||||
@@ -211,7 +221,7 @@ func (c *Config) SetDefaults() {
|
||||
c.Rand = rand.Reader
|
||||
}
|
||||
if c.Ciphers == nil {
|
||||
c.Ciphers = supportedCiphers
|
||||
c.Ciphers = preferredCiphers
|
||||
}
|
||||
var ciphers []string
|
||||
for _, c := range c.Ciphers {
|
||||
|
||||
+2
-1
@@ -276,7 +276,8 @@ type PublicKey interface {
|
||||
Type() string
|
||||
|
||||
// Marshal returns the serialized key data in SSH wire format,
|
||||
// with the name prefix.
|
||||
// with the name prefix. To unmarshal the returned data, use
|
||||
// the ParsePublicKey function.
|
||||
Marshal() []byte
|
||||
|
||||
// Verify that sig is a signature on the given data using this
|
||||
|
||||
+12
-12
@@ -234,7 +234,7 @@ func TestMarshalParsePublicKey(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type authResult struct {
|
||||
type testAuthResult struct {
|
||||
pubKey PublicKey
|
||||
options []string
|
||||
comments string
|
||||
@@ -242,11 +242,11 @@ type authResult struct {
|
||||
ok bool
|
||||
}
|
||||
|
||||
func testAuthorizedKeys(t *testing.T, authKeys []byte, expected []authResult) {
|
||||
func testAuthorizedKeys(t *testing.T, authKeys []byte, expected []testAuthResult) {
|
||||
rest := authKeys
|
||||
var values []authResult
|
||||
var values []testAuthResult
|
||||
for len(rest) > 0 {
|
||||
var r authResult
|
||||
var r testAuthResult
|
||||
var err error
|
||||
r.pubKey, r.comments, r.options, rest, err = ParseAuthorizedKey(rest)
|
||||
r.ok = (err == nil)
|
||||
@@ -264,7 +264,7 @@ func TestAuthorizedKeyBasic(t *testing.T) {
|
||||
pub, pubSerialized := getTestKey()
|
||||
line := "ssh-rsa " + pubSerialized + " user@host"
|
||||
testAuthorizedKeys(t, []byte(line),
|
||||
[]authResult{
|
||||
[]testAuthResult{
|
||||
{pub, nil, "user@host", "", true},
|
||||
})
|
||||
}
|
||||
@@ -286,7 +286,7 @@ func TestAuth(t *testing.T) {
|
||||
authOptions := strings.Join(authWithOptions, eol)
|
||||
rest2 := strings.Join(authWithOptions[3:], eol)
|
||||
rest3 := strings.Join(authWithOptions[6:], eol)
|
||||
testAuthorizedKeys(t, []byte(authOptions), []authResult{
|
||||
testAuthorizedKeys(t, []byte(authOptions), []testAuthResult{
|
||||
{pub, []string{`env="HOME=/home/root"`, "no-port-forwarding"}, "user@host", rest2, true},
|
||||
{pub, []string{`env="HOME=/home/root2"`}, "user2@host2", rest3, true},
|
||||
{nil, nil, "", "", false},
|
||||
@@ -297,7 +297,7 @@ func TestAuth(t *testing.T) {
|
||||
func TestAuthWithQuotedSpaceInEnv(t *testing.T) {
|
||||
pub, pubSerialized := getTestKey()
|
||||
authWithQuotedSpaceInEnv := []byte(`env="HOME=/home/root dir",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`)
|
||||
testAuthorizedKeys(t, []byte(authWithQuotedSpaceInEnv), []authResult{
|
||||
testAuthorizedKeys(t, []byte(authWithQuotedSpaceInEnv), []testAuthResult{
|
||||
{pub, []string{`env="HOME=/home/root dir"`, "no-port-forwarding"}, "user@host", "", true},
|
||||
})
|
||||
}
|
||||
@@ -305,7 +305,7 @@ func TestAuthWithQuotedSpaceInEnv(t *testing.T) {
|
||||
func TestAuthWithQuotedCommaInEnv(t *testing.T) {
|
||||
pub, pubSerialized := getTestKey()
|
||||
authWithQuotedCommaInEnv := []byte(`env="HOME=/home/root,dir",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`)
|
||||
testAuthorizedKeys(t, []byte(authWithQuotedCommaInEnv), []authResult{
|
||||
testAuthorizedKeys(t, []byte(authWithQuotedCommaInEnv), []testAuthResult{
|
||||
{pub, []string{`env="HOME=/home/root,dir"`, "no-port-forwarding"}, "user@host", "", true},
|
||||
})
|
||||
}
|
||||
@@ -314,11 +314,11 @@ func TestAuthWithQuotedQuoteInEnv(t *testing.T) {
|
||||
pub, pubSerialized := getTestKey()
|
||||
authWithQuotedQuoteInEnv := []byte(`env="HOME=/home/\"root dir",no-port-forwarding` + "\t" + `ssh-rsa` + "\t" + pubSerialized + ` user@host`)
|
||||
authWithDoubleQuotedQuote := []byte(`no-port-forwarding,env="HOME=/home/ \"root dir\"" ssh-rsa ` + pubSerialized + "\t" + `user@host`)
|
||||
testAuthorizedKeys(t, []byte(authWithQuotedQuoteInEnv), []authResult{
|
||||
testAuthorizedKeys(t, []byte(authWithQuotedQuoteInEnv), []testAuthResult{
|
||||
{pub, []string{`env="HOME=/home/\"root dir"`, "no-port-forwarding"}, "user@host", "", true},
|
||||
})
|
||||
|
||||
testAuthorizedKeys(t, []byte(authWithDoubleQuotedQuote), []authResult{
|
||||
testAuthorizedKeys(t, []byte(authWithDoubleQuotedQuote), []testAuthResult{
|
||||
{pub, []string{"no-port-forwarding", `env="HOME=/home/ \"root dir\""`}, "user@host", "", true},
|
||||
})
|
||||
}
|
||||
@@ -327,7 +327,7 @@ func TestAuthWithInvalidSpace(t *testing.T) {
|
||||
_, pubSerialized := getTestKey()
|
||||
authWithInvalidSpace := []byte(`env="HOME=/home/root dir", no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host
|
||||
#more to follow but still no valid keys`)
|
||||
testAuthorizedKeys(t, []byte(authWithInvalidSpace), []authResult{
|
||||
testAuthorizedKeys(t, []byte(authWithInvalidSpace), []testAuthResult{
|
||||
{nil, nil, "", "", false},
|
||||
})
|
||||
}
|
||||
@@ -337,7 +337,7 @@ func TestAuthWithMissingQuote(t *testing.T) {
|
||||
authWithMissingQuote := []byte(`env="HOME=/home/root,no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host
|
||||
env="HOME=/home/root",shared-control ssh-rsa ` + pubSerialized + ` user@host`)
|
||||
|
||||
testAuthorizedKeys(t, []byte(authWithMissingQuote), []authResult{
|
||||
testAuthorizedKeys(t, []byte(authWithMissingQuote), []testAuthResult{
|
||||
{pub, []string{`env="HOME=/home/root"`, `shared-control`}, "user@host", "", true},
|
||||
})
|
||||
}
|
||||
|
||||
+1
-1
@@ -414,7 +414,7 @@ func (db *hostKeyDB) Read(r io.Reader, filename string) error {
|
||||
|
||||
// New creates a host key callback from the given OpenSSH host key
|
||||
// files. The returned callback is for use in
|
||||
// ssh.ClientConfig.HostKeyCallback. Hashed hostnames are not supported.
|
||||
// ssh.ClientConfig.HostKeyCallback.
|
||||
func New(files ...string) (ssh.HostKeyCallback, error) {
|
||||
db := newHostKeyDB()
|
||||
for _, fn := range files {
|
||||
|
||||
+16
-5
@@ -166,6 +166,9 @@ type ServerConn struct {
|
||||
// unsuccessful, it closes the connection and returns an error. The
|
||||
// Request and NewChannel channels must be serviced, or the connection
|
||||
// will hang.
|
||||
//
|
||||
// The returned error may be of type *ServerAuthError for
|
||||
// authentication errors.
|
||||
func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewChannel, <-chan *Request, error) {
|
||||
fullConf := *config
|
||||
fullConf.SetDefaults()
|
||||
@@ -292,12 +295,13 @@ func checkSourceAddress(addr net.Addr, sourceAddrs string) error {
|
||||
return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr)
|
||||
}
|
||||
|
||||
// ServerAuthError implements the error interface. It appends any authentication
|
||||
// errors that may occur, and is returned if all of the authentication methods
|
||||
// provided by the user failed to authenticate.
|
||||
// ServerAuthError represents server authentication errors and is
|
||||
// sometimes returned by NewServerConn. It appends any authentication
|
||||
// errors that may occur, and is returned if all of the authentication
|
||||
// methods provided by the user failed to authenticate.
|
||||
type ServerAuthError struct {
|
||||
// Errors contains authentication errors returned by the authentication
|
||||
// callback methods.
|
||||
// callback methods. The first entry is typically ErrNoAuth.
|
||||
Errors []error
|
||||
}
|
||||
|
||||
@@ -309,6 +313,13 @@ func (l ServerAuthError) Error() string {
|
||||
return "[" + strings.Join(errs, ", ") + "]"
|
||||
}
|
||||
|
||||
// ErrNoAuth is the error value returned if no
|
||||
// authentication method has been passed yet. This happens as a normal
|
||||
// part of the authentication loop, since the client first tries
|
||||
// 'none' authentication to discover available methods.
|
||||
// It is returned in ServerAuthError.Errors from NewServerConn.
|
||||
var ErrNoAuth = errors.New("ssh: no auth passed yet")
|
||||
|
||||
func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) {
|
||||
sessionID := s.transport.getSessionID()
|
||||
var cache pubKeyCache
|
||||
@@ -363,7 +374,7 @@ userAuthLoop:
|
||||
}
|
||||
|
||||
perms = nil
|
||||
authErr := errors.New("no auth passed yet")
|
||||
authErr := ErrNoAuth
|
||||
|
||||
switch userAuthReq.Method {
|
||||
case "none":
|
||||
|
||||
+1
-3
@@ -108,9 +108,7 @@ func ReadPassword(fd int) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
unix.IoctlSetTermios(fd, ioctlWriteTermios, termios)
|
||||
}()
|
||||
defer unix.IoctlSetTermios(fd, ioctlWriteTermios, termios)
|
||||
|
||||
return readPasswordLine(passwordReader(fd))
|
||||
}
|
||||
|
||||
+16
-20
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
// State contains the state of a terminal.
|
||||
type State struct {
|
||||
state *unix.Termios
|
||||
termios unix.Termios
|
||||
}
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
@@ -75,47 +75,43 @@ func ReadPassword(fd int) ([]byte, error) {
|
||||
// restored.
|
||||
// see http://cr.illumos.org/~webrev/andy_js/1060/
|
||||
func MakeRaw(fd int) (*State, error) {
|
||||
oldTermiosPtr, err := unix.IoctlGetTermios(fd, unix.TCGETS)
|
||||
termios, err := unix.IoctlGetTermios(fd, unix.TCGETS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
oldTermios := *oldTermiosPtr
|
||||
|
||||
newTermios := oldTermios
|
||||
newTermios.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON
|
||||
newTermios.Oflag &^= syscall.OPOST
|
||||
newTermios.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN
|
||||
newTermios.Cflag &^= syscall.CSIZE | syscall.PARENB
|
||||
newTermios.Cflag |= syscall.CS8
|
||||
newTermios.Cc[unix.VMIN] = 1
|
||||
newTermios.Cc[unix.VTIME] = 0
|
||||
oldState := State{termios: *termios}
|
||||
|
||||
if err := unix.IoctlSetTermios(fd, unix.TCSETS, &newTermios); err != nil {
|
||||
termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
|
||||
termios.Oflag &^= unix.OPOST
|
||||
termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
|
||||
termios.Cflag &^= unix.CSIZE | unix.PARENB
|
||||
termios.Cflag |= unix.CS8
|
||||
termios.Cc[unix.VMIN] = 1
|
||||
termios.Cc[unix.VTIME] = 0
|
||||
|
||||
if err := unix.IoctlSetTermios(fd, unix.TCSETS, termios); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &State{
|
||||
state: oldTermiosPtr,
|
||||
}, nil
|
||||
return &oldState, nil
|
||||
}
|
||||
|
||||
// Restore restores the terminal connected to the given file descriptor to a
|
||||
// previous state.
|
||||
func Restore(fd int, oldState *State) error {
|
||||
return unix.IoctlSetTermios(fd, unix.TCSETS, oldState.state)
|
||||
return unix.IoctlSetTermios(fd, unix.TCSETS, &oldState.termios)
|
||||
}
|
||||
|
||||
// GetState returns the current state of a terminal which may be useful to
|
||||
// restore the terminal after a signal.
|
||||
func GetState(fd int) (*State, error) {
|
||||
oldTermiosPtr, err := unix.IoctlGetTermios(fd, unix.TCGETS)
|
||||
termios, err := unix.IoctlGetTermios(fd, unix.TCGETS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &State{
|
||||
state: oldTermiosPtr,
|
||||
}, nil
|
||||
return &State{termios: *termios}, nil
|
||||
}
|
||||
|
||||
// GetSize returns the dimensions of the given terminal.
|
||||
|
||||
+10
-4
@@ -89,9 +89,15 @@ func ReadPassword(fd int) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
windows.SetConsoleMode(windows.Handle(fd), old)
|
||||
}()
|
||||
defer windows.SetConsoleMode(windows.Handle(fd), old)
|
||||
|
||||
return readPasswordLine(os.NewFile(uintptr(fd), "stdin"))
|
||||
var h windows.Handle
|
||||
p, _ := windows.GetCurrentProcess()
|
||||
if err := windows.DuplicateHandle(p, windows.Handle(fd), p, &h, 0, false, windows.DUPLICATE_SAME_ACCESS); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f := os.NewFile(uintptr(h), "stdin")
|
||||
defer f.Close()
|
||||
return readPasswordLine(f)
|
||||
}
|
||||
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Tests for ssh client multi-auth
|
||||
//
|
||||
// These tests run a simple go ssh client against OpenSSH server
|
||||
// over unix domain sockets. The tests use multiple combinations
|
||||
// of password, keyboard-interactive and publickey authentication
|
||||
// methods.
|
||||
//
|
||||
// A wrapper library for making sshd PAM authentication use test
|
||||
// passwords is required in ./sshd_test_pw.so. If the library does
|
||||
// not exist these tests will be skipped. See compile instructions
|
||||
// (for linux) in file ./sshd_test_pw.c.
|
||||
|
||||
// +build linux
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// test cases
|
||||
type multiAuthTestCase struct {
|
||||
authMethods []string
|
||||
expectedPasswordCbs int
|
||||
expectedKbdIntCbs int
|
||||
}
|
||||
|
||||
// test context
|
||||
type multiAuthTestCtx struct {
|
||||
password string
|
||||
numPasswordCbs int
|
||||
numKbdIntCbs int
|
||||
}
|
||||
|
||||
// create test context
|
||||
func newMultiAuthTestCtx(t *testing.T) *multiAuthTestCtx {
|
||||
password, err := randomPassword()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to generate random test password: %s", err.Error())
|
||||
}
|
||||
|
||||
return &multiAuthTestCtx{
|
||||
password: password,
|
||||
}
|
||||
}
|
||||
|
||||
// password callback
|
||||
func (ctx *multiAuthTestCtx) passwordCb() (secret string, err error) {
|
||||
ctx.numPasswordCbs++
|
||||
return ctx.password, nil
|
||||
}
|
||||
|
||||
// keyboard-interactive callback
|
||||
func (ctx *multiAuthTestCtx) kbdIntCb(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
|
||||
if len(questions) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
ctx.numKbdIntCbs++
|
||||
if len(questions) == 1 {
|
||||
return []string{ctx.password}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unsupported keyboard-interactive flow")
|
||||
}
|
||||
|
||||
// TestMultiAuth runs several subtests for different combinations of password, keyboard-interactive and publickey authentication methods
|
||||
func TestMultiAuth(t *testing.T) {
|
||||
testCases := []multiAuthTestCase{
|
||||
// Test password,publickey authentication, assert that password callback is called 1 time
|
||||
multiAuthTestCase{
|
||||
authMethods: []string{"password", "publickey"},
|
||||
expectedPasswordCbs: 1,
|
||||
},
|
||||
// Test keyboard-interactive,publickey authentication, assert that keyboard-interactive callback is called 1 time
|
||||
multiAuthTestCase{
|
||||
authMethods: []string{"keyboard-interactive", "publickey"},
|
||||
expectedKbdIntCbs: 1,
|
||||
},
|
||||
// Test publickey,password authentication, assert that password callback is called 1 time
|
||||
multiAuthTestCase{
|
||||
authMethods: []string{"publickey", "password"},
|
||||
expectedPasswordCbs: 1,
|
||||
},
|
||||
// Test publickey,keyboard-interactive authentication, assert that keyboard-interactive callback is called 1 time
|
||||
multiAuthTestCase{
|
||||
authMethods: []string{"publickey", "keyboard-interactive"},
|
||||
expectedKbdIntCbs: 1,
|
||||
},
|
||||
// Test password,password authentication, assert that password callback is called 2 times
|
||||
multiAuthTestCase{
|
||||
authMethods: []string{"password", "password"},
|
||||
expectedPasswordCbs: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(strings.Join(testCase.authMethods, ","), func(t *testing.T) {
|
||||
ctx := newMultiAuthTestCtx(t)
|
||||
|
||||
server := newServerForConfig(t, "MultiAuth", map[string]string{"AuthMethods": strings.Join(testCase.authMethods, ",")})
|
||||
defer server.Shutdown()
|
||||
|
||||
clientConfig := clientConfig()
|
||||
server.setTestPassword(clientConfig.User, ctx.password)
|
||||
|
||||
publicKeyAuthMethod := clientConfig.Auth[0]
|
||||
clientConfig.Auth = nil
|
||||
for _, authMethod := range testCase.authMethods {
|
||||
switch authMethod {
|
||||
case "publickey":
|
||||
clientConfig.Auth = append(clientConfig.Auth, publicKeyAuthMethod)
|
||||
case "password":
|
||||
clientConfig.Auth = append(clientConfig.Auth,
|
||||
ssh.RetryableAuthMethod(ssh.PasswordCallback(ctx.passwordCb), 5))
|
||||
case "keyboard-interactive":
|
||||
clientConfig.Auth = append(clientConfig.Auth,
|
||||
ssh.RetryableAuthMethod(ssh.KeyboardInteractive(ctx.kbdIntCb), 5))
|
||||
default:
|
||||
t.Fatalf("Unknown authentication method %s", authMethod)
|
||||
}
|
||||
}
|
||||
|
||||
conn := server.Dial(clientConfig)
|
||||
defer conn.Close()
|
||||
|
||||
if ctx.numPasswordCbs != testCase.expectedPasswordCbs {
|
||||
t.Fatalf("passwordCallback was called %d times, expected %d times", ctx.numPasswordCbs, testCase.expectedPasswordCbs)
|
||||
}
|
||||
|
||||
if ctx.numKbdIntCbs != testCase.expectedKbdIntCbs {
|
||||
t.Fatalf("keyboardInteractiveCallback was called %d times, expected %d times", ctx.numKbdIntCbs, testCase.expectedKbdIntCbs)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+45
-16
@@ -11,6 +11,7 @@ package test
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -324,31 +325,59 @@ func TestWindowChange(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func testOneCipher(t *testing.T, cipher string, cipherOrder []string) {
|
||||
server := newServer(t)
|
||||
defer server.Shutdown()
|
||||
conf := clientConfig()
|
||||
conf.Ciphers = []string{cipher}
|
||||
// Don't fail if sshd doesn't have the cipher.
|
||||
conf.Ciphers = append(conf.Ciphers, cipherOrder...)
|
||||
conn, err := server.TryDial(conf)
|
||||
if err != nil {
|
||||
t.Fatalf("TryDial: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
numBytes := 4096
|
||||
|
||||
// Exercise sending data to the server
|
||||
if _, _, err := conn.Conn.SendRequest("drop-me", false, make([]byte, numBytes)); err != nil {
|
||||
t.Fatalf("SendRequest: %v", err)
|
||||
}
|
||||
|
||||
// Exercise receiving data from the server
|
||||
session, err := conn.NewSession()
|
||||
if err != nil {
|
||||
t.Fatalf("NewSession: %v", err)
|
||||
}
|
||||
|
||||
out, err := session.Output(fmt.Sprintf("dd if=/dev/zero of=/dev/stdout bs=%d count=1", numBytes))
|
||||
if err != nil {
|
||||
t.Fatalf("Output: %v", err)
|
||||
}
|
||||
|
||||
if len(out) != numBytes {
|
||||
t.Fatalf("got %d bytes, want %d bytes", len(out), numBytes)
|
||||
}
|
||||
}
|
||||
|
||||
var deprecatedCiphers = []string{
|
||||
"aes128-cbc", "3des-cbc",
|
||||
"arcfour128", "arcfour256",
|
||||
}
|
||||
|
||||
func TestCiphers(t *testing.T) {
|
||||
var config ssh.Config
|
||||
config.SetDefaults()
|
||||
cipherOrder := config.Ciphers
|
||||
// These ciphers will not be tested when commented out in cipher.go it will
|
||||
// fallback to the next available as per line 292.
|
||||
cipherOrder = append(cipherOrder, "aes128-cbc", "3des-cbc")
|
||||
cipherOrder := append(config.Ciphers, deprecatedCiphers...)
|
||||
|
||||
for _, ciph := range cipherOrder {
|
||||
t.Run(ciph, func(t *testing.T) {
|
||||
server := newServer(t)
|
||||
defer server.Shutdown()
|
||||
conf := clientConfig()
|
||||
conf.Ciphers = []string{ciph}
|
||||
// Don't fail if sshd doesn't have the cipher.
|
||||
conf.Ciphers = append(conf.Ciphers, cipherOrder...)
|
||||
conn, err := server.TryDial(conf)
|
||||
if err == nil {
|
||||
conn.Close()
|
||||
} else {
|
||||
t.Fatalf("failed for cipher %q", ciph)
|
||||
}
|
||||
testOneCipher(t, ciph, cipherOrder)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMACs(t *testing.T) {
|
||||
var config ssh.Config
|
||||
config.SetDefaults()
|
||||
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// sshd_test_pw.c
|
||||
// Wrapper to inject test password data for sshd PAM authentication
|
||||
//
|
||||
// This wrapper implements custom versions of getpwnam, getpwnam_r,
|
||||
// getspnam and getspnam_r. These functions first call their real
|
||||
// libc versions, then check if the requested user matches test user
|
||||
// specified in env variable TEST_USER and if so replace the password
|
||||
// with crypted() value of TEST_PASSWD env variable.
|
||||
//
|
||||
// Compile:
|
||||
// gcc -Wall -shared -o sshd_test_pw.so -fPIC sshd_test_pw.c
|
||||
//
|
||||
// Compile with debug:
|
||||
// gcc -DVERBOSE -Wall -shared -o sshd_test_pw.so -fPIC sshd_test_pw.c
|
||||
//
|
||||
// Run sshd:
|
||||
// LD_PRELOAD="sshd_test_pw.so" TEST_USER="..." TEST_PASSWD="..." sshd ...
|
||||
|
||||
// +build ignore
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <string.h>
|
||||
#include <pwd.h>
|
||||
#include <shadow.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef VERBOSE
|
||||
#define DEBUG(X...) fprintf(stderr, X)
|
||||
#else
|
||||
#define DEBUG(X...) while (0) { }
|
||||
#endif
|
||||
|
||||
/* crypt() password */
|
||||
static char *
|
||||
pwhash(char *passwd) {
|
||||
return strdup(crypt(passwd, "$6$"));
|
||||
}
|
||||
|
||||
/* Pointers to real functions in libc */
|
||||
static struct passwd * (*real_getpwnam)(const char *) = NULL;
|
||||
static int (*real_getpwnam_r)(const char *, struct passwd *, char *, size_t, struct passwd **) = NULL;
|
||||
static struct spwd * (*real_getspnam)(const char *) = NULL;
|
||||
static int (*real_getspnam_r)(const char *, struct spwd *, char *, size_t, struct spwd **) = NULL;
|
||||
|
||||
/* Cached test user and test password */
|
||||
static char *test_user = NULL;
|
||||
static char *test_passwd_hash = NULL;
|
||||
|
||||
static void
|
||||
init(void) {
|
||||
/* Fetch real libc function pointers */
|
||||
real_getpwnam = dlsym(RTLD_NEXT, "getpwnam");
|
||||
real_getpwnam_r = dlsym(RTLD_NEXT, "getpwnam_r");
|
||||
real_getspnam = dlsym(RTLD_NEXT, "getspnam");
|
||||
real_getspnam_r = dlsym(RTLD_NEXT, "getspnam_r");
|
||||
|
||||
/* abort if env variables are not defined */
|
||||
if (getenv("TEST_USER") == NULL || getenv("TEST_PASSWD") == NULL) {
|
||||
fprintf(stderr, "env variables TEST_USER and TEST_PASSWD are missing\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Fetch test user and test password from env */
|
||||
test_user = strdup(getenv("TEST_USER"));
|
||||
test_passwd_hash = pwhash(getenv("TEST_PASSWD"));
|
||||
|
||||
DEBUG("sshd_test_pw init():\n");
|
||||
DEBUG("\treal_getpwnam: %p\n", real_getpwnam);
|
||||
DEBUG("\treal_getpwnam_r: %p\n", real_getpwnam_r);
|
||||
DEBUG("\treal_getspnam: %p\n", real_getspnam);
|
||||
DEBUG("\treal_getspnam_r: %p\n", real_getspnam_r);
|
||||
DEBUG("\tTEST_USER: '%s'\n", test_user);
|
||||
DEBUG("\tTEST_PASSWD: '%s'\n", getenv("TEST_PASSWD"));
|
||||
DEBUG("\tTEST_PASSWD_HASH: '%s'\n", test_passwd_hash);
|
||||
}
|
||||
|
||||
static int
|
||||
is_test_user(const char *name) {
|
||||
if (test_user != NULL && strcmp(test_user, name) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* getpwnam */
|
||||
|
||||
struct passwd *
|
||||
getpwnam(const char *name) {
|
||||
struct passwd *pw;
|
||||
|
||||
DEBUG("sshd_test_pw getpwnam(%s)\n", name);
|
||||
|
||||
if (real_getpwnam == NULL)
|
||||
init();
|
||||
if ((pw = real_getpwnam(name)) == NULL)
|
||||
return NULL;
|
||||
|
||||
if (is_test_user(name))
|
||||
pw->pw_passwd = strdup(test_passwd_hash);
|
||||
|
||||
return pw;
|
||||
}
|
||||
|
||||
/* getpwnam_r */
|
||||
|
||||
int
|
||||
getpwnam_r(const char *name,
|
||||
struct passwd *pwd,
|
||||
char *buf,
|
||||
size_t buflen,
|
||||
struct passwd **result) {
|
||||
int r;
|
||||
|
||||
DEBUG("sshd_test_pw getpwnam_r(%s)\n", name);
|
||||
|
||||
if (real_getpwnam_r == NULL)
|
||||
init();
|
||||
if ((r = real_getpwnam_r(name, pwd, buf, buflen, result)) != 0 || *result == NULL)
|
||||
return r;
|
||||
|
||||
if (is_test_user(name))
|
||||
pwd->pw_passwd = strdup(test_passwd_hash);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* getspnam */
|
||||
|
||||
struct spwd *
|
||||
getspnam(const char *name) {
|
||||
struct spwd *sp;
|
||||
|
||||
DEBUG("sshd_test_pw getspnam(%s)\n", name);
|
||||
|
||||
if (real_getspnam == NULL)
|
||||
init();
|
||||
if ((sp = real_getspnam(name)) == NULL)
|
||||
return NULL;
|
||||
|
||||
if (is_test_user(name))
|
||||
sp->sp_pwdp = strdup(test_passwd_hash);
|
||||
|
||||
return sp;
|
||||
}
|
||||
|
||||
/* getspnam_r */
|
||||
|
||||
int
|
||||
getspnam_r(const char *name,
|
||||
struct spwd *spbuf,
|
||||
char *buf,
|
||||
size_t buflen,
|
||||
struct spwd **spbufp) {
|
||||
int r;
|
||||
|
||||
DEBUG("sshd_test_pw getspnam_r(%s)\n", name);
|
||||
|
||||
if (real_getspnam_r == NULL)
|
||||
init();
|
||||
if ((r = real_getspnam_r(name, spbuf, buf, buflen, spbufp)) != 0)
|
||||
return r;
|
||||
|
||||
if (is_test_user(name))
|
||||
spbuf->sp_pwdp = strdup(test_passwd_hash);
|
||||
|
||||
return r;
|
||||
}
|
||||
+68
-5
@@ -10,6 +10,8 @@ package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
@@ -25,7 +27,8 @@ import (
|
||||
"golang.org/x/crypto/ssh/testdata"
|
||||
)
|
||||
|
||||
const sshdConfig = `
|
||||
const (
|
||||
defaultSshdConfig = `
|
||||
Protocol 2
|
||||
Banner {{.Dir}}/banner
|
||||
HostKey {{.Dir}}/id_rsa
|
||||
@@ -50,8 +53,17 @@ RhostsRSAAuthentication no
|
||||
HostbasedAuthentication no
|
||||
PubkeyAcceptedKeyTypes=*
|
||||
`
|
||||
multiAuthSshdConfigTail = `
|
||||
UsePAM yes
|
||||
PasswordAuthentication yes
|
||||
ChallengeResponseAuthentication yes
|
||||
AuthenticationMethods {{.AuthMethods}}
|
||||
`
|
||||
)
|
||||
|
||||
var configTmpl = template.Must(template.New("").Parse(sshdConfig))
|
||||
var configTmpl = map[string]*template.Template{
|
||||
"default": template.Must(template.New("").Parse(defaultSshdConfig)),
|
||||
"MultiAuth": template.Must(template.New("").Parse(defaultSshdConfig + multiAuthSshdConfigTail))}
|
||||
|
||||
type server struct {
|
||||
t *testing.T
|
||||
@@ -60,6 +72,10 @@ type server struct {
|
||||
cmd *exec.Cmd
|
||||
output bytes.Buffer // holds stderr from sshd process
|
||||
|
||||
testUser string // test username for sshd
|
||||
testPasswd string // test password for sshd
|
||||
sshdTestPwSo string // dynamic library to inject a custom password into sshd
|
||||
|
||||
// Client half of the network connection.
|
||||
clientConn net.Conn
|
||||
}
|
||||
@@ -186,6 +202,20 @@ func (s *server) TryDialWithAddr(config *ssh.ClientConfig, addr string) (*ssh.Cl
|
||||
s.cmd.Stdin = f
|
||||
s.cmd.Stdout = f
|
||||
s.cmd.Stderr = &s.output
|
||||
|
||||
if s.sshdTestPwSo != "" {
|
||||
if s.testUser == "" {
|
||||
s.t.Fatal("user missing from sshd_test_pw.so config")
|
||||
}
|
||||
if s.testPasswd == "" {
|
||||
s.t.Fatal("password missing from sshd_test_pw.so config")
|
||||
}
|
||||
s.cmd.Env = append(os.Environ(),
|
||||
fmt.Sprintf("LD_PRELOAD=%s", s.sshdTestPwSo),
|
||||
fmt.Sprintf("TEST_USER=%s", s.testUser),
|
||||
fmt.Sprintf("TEST_PASSWD=%s", s.testPasswd))
|
||||
}
|
||||
|
||||
if err := s.cmd.Start(); err != nil {
|
||||
s.t.Fail()
|
||||
s.Shutdown()
|
||||
@@ -236,8 +266,39 @@ func writeFile(path string, contents []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
// generate random password
|
||||
func randomPassword() (string, error) {
|
||||
b := make([]byte, 12)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.RawURLEncoding.EncodeToString(b), nil
|
||||
}
|
||||
|
||||
// setTestPassword is used for setting user and password data for sshd_test_pw.so
|
||||
// This function also checks that ./sshd_test_pw.so exists and if not calls s.t.Skip()
|
||||
func (s *server) setTestPassword(user, passwd string) error {
|
||||
wd, _ := os.Getwd()
|
||||
wrapper := filepath.Join(wd, "sshd_test_pw.so")
|
||||
if _, err := os.Stat(wrapper); err != nil {
|
||||
s.t.Skip(fmt.Errorf("sshd_test_pw.so is not available"))
|
||||
return err
|
||||
}
|
||||
|
||||
s.sshdTestPwSo = wrapper
|
||||
s.testUser = user
|
||||
s.testPasswd = passwd
|
||||
return nil
|
||||
}
|
||||
|
||||
// newServer returns a new mock ssh server.
|
||||
func newServer(t *testing.T) *server {
|
||||
return newServerForConfig(t, "default", map[string]string{})
|
||||
}
|
||||
|
||||
// newServerForConfig returns a new mock ssh server.
|
||||
func newServerForConfig(t *testing.T, config string, configVars map[string]string) *server {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test due to -short")
|
||||
}
|
||||
@@ -249,9 +310,11 @@ func newServer(t *testing.T) *server {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = configTmpl.Execute(f, map[string]string{
|
||||
"Dir": dir,
|
||||
})
|
||||
if _, ok := configTmpl[config]; ok == false {
|
||||
t.Fatal(fmt.Errorf("Invalid server config '%s'", config))
|
||||
}
|
||||
configVars["Dir"] = dir
|
||||
err = configTmpl[config].Execute(f, configVars)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
+9
-39
@@ -233,52 +233,22 @@ var (
|
||||
clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}}
|
||||
)
|
||||
|
||||
// generateKeys generates key material for IV, MAC and encryption.
|
||||
func generateKeys(d direction, algs directionAlgorithms, kex *kexResult) (iv, key, macKey []byte) {
|
||||
cipherMode := cipherModes[algs.Cipher]
|
||||
macMode := macModes[algs.MAC]
|
||||
|
||||
iv = make([]byte, cipherMode.ivSize)
|
||||
key = make([]byte, cipherMode.keySize)
|
||||
macKey = make([]byte, macMode.keySize)
|
||||
|
||||
generateKeyMaterial(iv, d.ivTag, kex)
|
||||
generateKeyMaterial(key, d.keyTag, kex)
|
||||
generateKeyMaterial(macKey, d.macKeyTag, kex)
|
||||
return
|
||||
}
|
||||
|
||||
// setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as
|
||||
// described in RFC 4253, section 6.4. direction should either be serverKeys
|
||||
// (to setup server->client keys) or clientKeys (for client->server keys).
|
||||
func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (packetCipher, error) {
|
||||
iv, key, macKey := generateKeys(d, algs, kex)
|
||||
cipherMode := cipherModes[algs.Cipher]
|
||||
macMode := macModes[algs.MAC]
|
||||
|
||||
if algs.Cipher == gcmCipherID {
|
||||
return newGCMCipher(iv, key)
|
||||
}
|
||||
iv := make([]byte, cipherMode.ivSize)
|
||||
key := make([]byte, cipherMode.keySize)
|
||||
macKey := make([]byte, macMode.keySize)
|
||||
|
||||
if algs.Cipher == aes128cbcID {
|
||||
return newAESCBCCipher(iv, key, macKey, algs)
|
||||
}
|
||||
generateKeyMaterial(iv, d.ivTag, kex)
|
||||
generateKeyMaterial(key, d.keyTag, kex)
|
||||
generateKeyMaterial(macKey, d.macKeyTag, kex)
|
||||
|
||||
if algs.Cipher == tripledescbcID {
|
||||
return newTripleDESCBCCipher(iv, key, macKey, algs)
|
||||
}
|
||||
|
||||
c := &streamPacketCipher{
|
||||
mac: macModes[algs.MAC].new(macKey),
|
||||
etm: macModes[algs.MAC].etm,
|
||||
}
|
||||
c.macResult = make([]byte, c.mac.Size())
|
||||
|
||||
var err error
|
||||
c.cipher, err = cipherModes[algs.Cipher].createStream(key, iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
return cipherModes[algs.Cipher].create(key, iv, macKey, algs)
|
||||
}
|
||||
|
||||
// generateKeyMaterial fills out with key material generated from tag, K, H
|
||||
|
||||
+5
-10
@@ -4,16 +4,15 @@ Go is an open source project.
|
||||
|
||||
It is the work of hundreds of contributors. We appreciate your help!
|
||||
|
||||
|
||||
## Filing issues
|
||||
|
||||
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`)?
|
||||
2. What operating system and processor architecture are you using?
|
||||
3. What did you do?
|
||||
4. What did you expect to see?
|
||||
5. What did you see instead?
|
||||
1. What version of Go are you using (`go version`)?
|
||||
2. What operating system and processor architecture are you using?
|
||||
3. What did you do?
|
||||
4. What did you expect to see?
|
||||
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.
|
||||
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)
|
||||
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
|
||||
the BSD-style license found in the LICENSE file.
|
||||
|
||||
|
||||
+16
-16
@@ -198,7 +198,7 @@ func (a LoadConstant) Assemble() (RawInstruction, error) {
|
||||
return assembleLoad(a.Dst, 4, opAddrModeImmediate, a.Val)
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a LoadConstant) String() string {
|
||||
switch a.Dst {
|
||||
case RegA:
|
||||
@@ -224,7 +224,7 @@ func (a LoadScratch) Assemble() (RawInstruction, error) {
|
||||
return assembleLoad(a.Dst, 4, opAddrModeScratch, uint32(a.N))
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a LoadScratch) String() string {
|
||||
switch a.Dst {
|
||||
case RegA:
|
||||
@@ -248,7 +248,7 @@ func (a LoadAbsolute) Assemble() (RawInstruction, error) {
|
||||
return assembleLoad(RegA, a.Size, opAddrModeAbsolute, a.Off)
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a LoadAbsolute) String() string {
|
||||
switch a.Size {
|
||||
case 1: // byte
|
||||
@@ -277,7 +277,7 @@ func (a LoadIndirect) Assemble() (RawInstruction, error) {
|
||||
return assembleLoad(RegA, a.Size, opAddrModeIndirect, a.Off)
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a LoadIndirect) String() string {
|
||||
switch a.Size {
|
||||
case 1: // byte
|
||||
@@ -306,7 +306,7 @@ func (a LoadMemShift) Assemble() (RawInstruction, error) {
|
||||
return assembleLoad(RegX, 1, opAddrModeMemShift, a.Off)
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a LoadMemShift) String() string {
|
||||
return fmt.Sprintf("ldx 4*([%d]&0xf)", a.Off)
|
||||
}
|
||||
@@ -325,7 +325,7 @@ func (a LoadExtension) Assemble() (RawInstruction, error) {
|
||||
return assembleLoad(RegA, 4, opAddrModeAbsolute, uint32(extOffset+a.Num))
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a LoadExtension) String() string {
|
||||
switch a.Num {
|
||||
case ExtLen:
|
||||
@@ -392,7 +392,7 @@ func (a StoreScratch) Assemble() (RawInstruction, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a StoreScratch) String() string {
|
||||
switch a.Src {
|
||||
case RegA:
|
||||
@@ -418,7 +418,7 @@ func (a ALUOpConstant) Assemble() (RawInstruction, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a ALUOpConstant) String() string {
|
||||
switch a.Op {
|
||||
case ALUOpAdd:
|
||||
@@ -458,7 +458,7 @@ func (a ALUOpX) Assemble() (RawInstruction, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a ALUOpX) String() string {
|
||||
switch a.Op {
|
||||
case ALUOpAdd:
|
||||
@@ -496,7 +496,7 @@ func (a NegateA) Assemble() (RawInstruction, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a NegateA) String() string {
|
||||
return fmt.Sprintf("neg")
|
||||
}
|
||||
@@ -514,7 +514,7 @@ func (a Jump) Assemble() (RawInstruction, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a Jump) String() string {
|
||||
return fmt.Sprintf("ja %d", a.Skip)
|
||||
}
|
||||
@@ -566,7 +566,7 @@ func (a JumpIf) Assemble() (RawInstruction, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a JumpIf) String() string {
|
||||
switch a.Cond {
|
||||
// K == A
|
||||
@@ -621,7 +621,7 @@ func (a RetA) Assemble() (RawInstruction, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a RetA) String() string {
|
||||
return fmt.Sprintf("ret a")
|
||||
}
|
||||
@@ -639,7 +639,7 @@ func (a RetConstant) Assemble() (RawInstruction, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a RetConstant) String() string {
|
||||
return fmt.Sprintf("ret #%d", a.Val)
|
||||
}
|
||||
@@ -654,7 +654,7 @@ func (a TXA) Assemble() (RawInstruction, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a TXA) String() string {
|
||||
return fmt.Sprintf("txa")
|
||||
}
|
||||
@@ -669,7 +669,7 @@ func (a TAX) Assemble() (RawInstruction, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the the instruction in assembler notation.
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a TAX) String() string {
|
||||
return fmt.Sprintf("tax")
|
||||
}
|
||||
|
||||
+4
-4
@@ -37,20 +37,20 @@ func ExampleParser() {
|
||||
},
|
||||
Answers: []dnsmessage.Resource{
|
||||
{
|
||||
dnsmessage.ResourceHeader{
|
||||
Header: dnsmessage.ResourceHeader{
|
||||
Name: mustNewName("foo.bar.example.com."),
|
||||
Type: dnsmessage.TypeA,
|
||||
Class: dnsmessage.ClassINET,
|
||||
},
|
||||
&dnsmessage.AResource{[4]byte{127, 0, 0, 1}},
|
||||
Body: &dnsmessage.AResource{A: [4]byte{127, 0, 0, 1}},
|
||||
},
|
||||
{
|
||||
dnsmessage.ResourceHeader{
|
||||
Header: dnsmessage.ResourceHeader{
|
||||
Name: mustNewName("bar.example.com."),
|
||||
Type: dnsmessage.TypeA,
|
||||
Class: dnsmessage.ClassINET,
|
||||
},
|
||||
&dnsmessage.AResource{[4]byte{127, 0, 0, 2}},
|
||||
Body: &dnsmessage.AResource{A: [4]byte{127, 0, 0, 2}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
+197
-95
@@ -90,6 +90,8 @@ var (
|
||||
errTooManyAuthorities = errors.New("too many Authorities 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 .)")
|
||||
errStringTooLong = errors.New("character string exceeds maximum length (255)")
|
||||
errCompressedSRV = errors.New("compressed name in SRV resource data")
|
||||
)
|
||||
|
||||
// Internal constants.
|
||||
@@ -218,6 +220,7 @@ func (h *header) count(sec section) uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// pack appends the wire format of the header to msg.
|
||||
func (h *header) pack(msg []byte) []byte {
|
||||
msg = packUint16(msg, h.id)
|
||||
msg = packUint16(msg, h.bits)
|
||||
@@ -273,25 +276,26 @@ type Resource struct {
|
||||
// A ResourceBody is a DNS resource record minus the header.
|
||||
type ResourceBody interface {
|
||||
// pack packs a Resource except for its header.
|
||||
pack(msg []byte, compression map[string]int) ([]byte, error)
|
||||
pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error)
|
||||
|
||||
// realType returns the actual type of the Resource. This is used to
|
||||
// fill in the header Type field.
|
||||
realType() Type
|
||||
}
|
||||
|
||||
func (r *Resource) pack(msg []byte, compression map[string]int) ([]byte, error) {
|
||||
// pack appends the wire format of the Resource to msg.
|
||||
func (r *Resource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
|
||||
if r.Body == nil {
|
||||
return msg, errNilResouceBody
|
||||
}
|
||||
oldMsg := msg
|
||||
r.Header.Type = r.Body.realType()
|
||||
msg, length, err := r.Header.pack(msg, compression)
|
||||
msg, length, err := r.Header.pack(msg, compression, compressionOff)
|
||||
if err != nil {
|
||||
return msg, &nestedError{"ResourceHeader", err}
|
||||
}
|
||||
preLen := len(msg)
|
||||
msg, err = r.Body.pack(msg, compression)
|
||||
msg, err = r.Body.pack(msg, compression, compressionOff)
|
||||
if err != nil {
|
||||
return msg, &nestedError{"content", err}
|
||||
}
|
||||
@@ -436,7 +440,13 @@ func (p *Parser) Question() (Question, error) {
|
||||
|
||||
// AllQuestions parses all Questions.
|
||||
func (p *Parser) AllQuestions() ([]Question, error) {
|
||||
qs := make([]Question, 0, p.header.questions)
|
||||
// Multiple questions are valid according to the spec,
|
||||
// but servers don't actually support them. There will
|
||||
// be at most one question here.
|
||||
//
|
||||
// Do not pre-allocate based on info in p.header, since
|
||||
// the data is untrusted.
|
||||
qs := []Question{}
|
||||
for {
|
||||
q, err := p.Question()
|
||||
if err == ErrSectionDone {
|
||||
@@ -492,7 +502,16 @@ func (p *Parser) Answer() (Resource, error) {
|
||||
|
||||
// AllAnswers parses all Answer Resources.
|
||||
func (p *Parser) AllAnswers() ([]Resource, error) {
|
||||
as := make([]Resource, 0, p.header.answers)
|
||||
// The most common query is for A/AAAA, which usually returns
|
||||
// a handful of IPs.
|
||||
//
|
||||
// Pre-allocate up to a certain limit, since p.header is
|
||||
// untrusted data.
|
||||
n := int(p.header.answers)
|
||||
if n > 20 {
|
||||
n = 20
|
||||
}
|
||||
as := make([]Resource, 0, n)
|
||||
for {
|
||||
a, err := p.Answer()
|
||||
if err == ErrSectionDone {
|
||||
@@ -533,7 +552,16 @@ func (p *Parser) Authority() (Resource, error) {
|
||||
|
||||
// AllAuthorities parses all Authority Resources.
|
||||
func (p *Parser) AllAuthorities() ([]Resource, error) {
|
||||
as := make([]Resource, 0, p.header.authorities)
|
||||
// Authorities contains SOA in case of NXDOMAIN and friends,
|
||||
// otherwise it is empty.
|
||||
//
|
||||
// Pre-allocate up to a certain limit, since p.header is
|
||||
// untrusted data.
|
||||
n := int(p.header.authorities)
|
||||
if n > 10 {
|
||||
n = 10
|
||||
}
|
||||
as := make([]Resource, 0, n)
|
||||
for {
|
||||
a, err := p.Authority()
|
||||
if err == ErrSectionDone {
|
||||
@@ -574,7 +602,16 @@ func (p *Parser) Additional() (Resource, error) {
|
||||
|
||||
// AllAdditionals parses all Additional Resources.
|
||||
func (p *Parser) AllAdditionals() ([]Resource, error) {
|
||||
as := make([]Resource, 0, p.header.additionals)
|
||||
// Additionals usually contain OPT, and sometimes A/AAAA
|
||||
// glue records.
|
||||
//
|
||||
// Pre-allocate up to a certain limit, since p.header is
|
||||
// untrusted data.
|
||||
n := int(p.header.additionals)
|
||||
if n > 10 {
|
||||
n = 10
|
||||
}
|
||||
as := make([]Resource, 0, n)
|
||||
for {
|
||||
a, err := p.Additional()
|
||||
if err == ErrSectionDone {
|
||||
@@ -819,6 +856,7 @@ func (m *Message) AppendPack(b []byte) ([]byte, error) {
|
||||
h.authorities = uint16(len(m.Authorities))
|
||||
h.additionals = uint16(len(m.Additionals))
|
||||
|
||||
compressionOff := len(b)
|
||||
msg := h.pack(b)
|
||||
|
||||
// RFC 1035 allows (but does not require) compression for packing. RFC
|
||||
@@ -833,25 +871,25 @@ func (m *Message) AppendPack(b []byte) ([]byte, error) {
|
||||
|
||||
for i := range m.Questions {
|
||||
var err error
|
||||
if msg, err = m.Questions[i].pack(msg, compression); err != nil {
|
||||
if msg, err = m.Questions[i].pack(msg, compression, compressionOff); err != nil {
|
||||
return nil, &nestedError{"packing Question", err}
|
||||
}
|
||||
}
|
||||
for i := range m.Answers {
|
||||
var err error
|
||||
if msg, err = m.Answers[i].pack(msg, compression); err != nil {
|
||||
if msg, err = m.Answers[i].pack(msg, compression, compressionOff); err != nil {
|
||||
return nil, &nestedError{"packing Answer", err}
|
||||
}
|
||||
}
|
||||
for i := range m.Authorities {
|
||||
var err error
|
||||
if msg, err = m.Authorities[i].pack(msg, compression); err != nil {
|
||||
if msg, err = m.Authorities[i].pack(msg, compression, compressionOff); err != nil {
|
||||
return nil, &nestedError{"packing Authority", err}
|
||||
}
|
||||
}
|
||||
for i := range m.Additionals {
|
||||
var err error
|
||||
if msg, err = m.Additionals[i].pack(msg, compression); err != nil {
|
||||
if msg, err = m.Additionals[i].pack(msg, compression, compressionOff); err != nil {
|
||||
return nil, &nestedError{"packing Additional", err}
|
||||
}
|
||||
}
|
||||
@@ -860,36 +898,69 @@ func (m *Message) AppendPack(b []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
// A Builder allows incrementally packing a DNS message.
|
||||
//
|
||||
// Example usage:
|
||||
// buf := make([]byte, 2, 514)
|
||||
// b := NewBuilder(buf, Header{...})
|
||||
// b.EnableCompression()
|
||||
// // Optionally start a section and add things to that section.
|
||||
// // Repeat adding sections as necessary.
|
||||
// buf, err := b.Finish()
|
||||
// // If err is nil, buf[2:] will contain the built bytes.
|
||||
type Builder struct {
|
||||
msg []byte
|
||||
header header
|
||||
section section
|
||||
// msg is the storage for the message being built.
|
||||
msg []byte
|
||||
|
||||
// section keeps track of the current section being built.
|
||||
section section
|
||||
|
||||
// header keeps track of what should go in the header when Finish is
|
||||
// called.
|
||||
header header
|
||||
|
||||
// start is the starting index of the bytes allocated in msg for header.
|
||||
start int
|
||||
|
||||
// compression is a mapping from name suffixes to their starting index
|
||||
// in msg.
|
||||
compression map[string]int
|
||||
}
|
||||
|
||||
// Start initializes the builder.
|
||||
// NewBuilder creates a new builder with compression disabled.
|
||||
//
|
||||
// buf is optional (nil is fine), but if provided, Start takes ownership of buf.
|
||||
func (b *Builder) Start(buf []byte, h Header) {
|
||||
b.StartWithoutCompression(buf, h)
|
||||
b.compression = map[string]int{}
|
||||
// Note: Most users will want to immediately enable compression with the
|
||||
// EnableCompression method. See that method's comment for why you may or may
|
||||
// not want to enable compression.
|
||||
//
|
||||
// The DNS message is appended to the provided initial buffer buf (which may be
|
||||
// nil) as it is built. The final message is returned by the (*Builder).Finish
|
||||
// method, which may return the same underlying array if there was sufficient
|
||||
// capacity in the slice.
|
||||
func NewBuilder(buf []byte, h Header) Builder {
|
||||
if buf == nil {
|
||||
buf = make([]byte, 0, packStartingCap)
|
||||
}
|
||||
b := Builder{msg: buf, start: len(buf)}
|
||||
b.header.id, b.header.bits = h.pack()
|
||||
var hb [headerLen]byte
|
||||
b.msg = append(b.msg, hb[:]...)
|
||||
b.section = sectionHeader
|
||||
return b
|
||||
}
|
||||
|
||||
// StartWithoutCompression initializes the builder with compression disabled.
|
||||
// EnableCompression enables compression in the Builder.
|
||||
//
|
||||
// This avoids compression related allocations, but can result in larger message
|
||||
// sizes. Be careful with this mode as it can cause messages to exceed the UDP
|
||||
// size limit.
|
||||
// Leaving compression disabled avoids compression related allocations, but can
|
||||
// result in larger message sizes. Be careful with this mode as it can cause
|
||||
// messages to exceed the UDP size limit.
|
||||
//
|
||||
// buf is optional (nil is fine), but if provided, Start takes ownership of buf.
|
||||
func (b *Builder) StartWithoutCompression(buf []byte, h Header) {
|
||||
*b = Builder{msg: buf}
|
||||
b.header.id, b.header.bits = h.pack()
|
||||
if cap(b.msg) < headerLen {
|
||||
b.msg = make([]byte, 0, packStartingCap)
|
||||
}
|
||||
b.msg = b.msg[:headerLen]
|
||||
b.section = sectionHeader
|
||||
// According to RFC 1035, section 4.1.4, the use of compression is optional, but
|
||||
// all implementations must accept both compressed and uncompressed DNS
|
||||
// messages.
|
||||
//
|
||||
// Compression should be enabled before any sections are added for best results.
|
||||
func (b *Builder) EnableCompression() {
|
||||
b.compression = map[string]int{}
|
||||
}
|
||||
|
||||
func (b *Builder) startCheck(s section) error {
|
||||
@@ -970,7 +1041,7 @@ func (b *Builder) Question(q Question) error {
|
||||
if b.section > sectionQuestions {
|
||||
return ErrSectionDone
|
||||
}
|
||||
msg, err := q.pack(b.msg, b.compression)
|
||||
msg, err := q.pack(b.msg, b.compression, b.start)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -997,12 +1068,12 @@ func (b *Builder) CNAMEResource(h ResourceHeader, r CNAMEResource) error {
|
||||
return err
|
||||
}
|
||||
h.Type = r.realType()
|
||||
msg, length, err := h.pack(b.msg, b.compression)
|
||||
msg, length, err := h.pack(b.msg, b.compression, b.start)
|
||||
if err != nil {
|
||||
return &nestedError{"ResourceHeader", err}
|
||||
}
|
||||
preLen := len(msg)
|
||||
if msg, err = r.pack(msg, b.compression); err != nil {
|
||||
if msg, err = r.pack(msg, b.compression, b.start); err != nil {
|
||||
return &nestedError{"CNAMEResource body", err}
|
||||
}
|
||||
if err := h.fixLen(msg, length, preLen); err != nil {
|
||||
@@ -1021,12 +1092,12 @@ func (b *Builder) MXResource(h ResourceHeader, r MXResource) error {
|
||||
return err
|
||||
}
|
||||
h.Type = r.realType()
|
||||
msg, length, err := h.pack(b.msg, b.compression)
|
||||
msg, length, err := h.pack(b.msg, b.compression, b.start)
|
||||
if err != nil {
|
||||
return &nestedError{"ResourceHeader", err}
|
||||
}
|
||||
preLen := len(msg)
|
||||
if msg, err = r.pack(msg, b.compression); err != nil {
|
||||
if msg, err = r.pack(msg, b.compression, b.start); err != nil {
|
||||
return &nestedError{"MXResource body", err}
|
||||
}
|
||||
if err := h.fixLen(msg, length, preLen); err != nil {
|
||||
@@ -1045,12 +1116,12 @@ func (b *Builder) NSResource(h ResourceHeader, r NSResource) error {
|
||||
return err
|
||||
}
|
||||
h.Type = r.realType()
|
||||
msg, length, err := h.pack(b.msg, b.compression)
|
||||
msg, length, err := h.pack(b.msg, b.compression, b.start)
|
||||
if err != nil {
|
||||
return &nestedError{"ResourceHeader", err}
|
||||
}
|
||||
preLen := len(msg)
|
||||
if msg, err = r.pack(msg, b.compression); err != nil {
|
||||
if msg, err = r.pack(msg, b.compression, b.start); err != nil {
|
||||
return &nestedError{"NSResource body", err}
|
||||
}
|
||||
if err := h.fixLen(msg, length, preLen); err != nil {
|
||||
@@ -1069,12 +1140,12 @@ func (b *Builder) PTRResource(h ResourceHeader, r PTRResource) error {
|
||||
return err
|
||||
}
|
||||
h.Type = r.realType()
|
||||
msg, length, err := h.pack(b.msg, b.compression)
|
||||
msg, length, err := h.pack(b.msg, b.compression, b.start)
|
||||
if err != nil {
|
||||
return &nestedError{"ResourceHeader", err}
|
||||
}
|
||||
preLen := len(msg)
|
||||
if msg, err = r.pack(msg, b.compression); err != nil {
|
||||
if msg, err = r.pack(msg, b.compression, b.start); err != nil {
|
||||
return &nestedError{"PTRResource body", err}
|
||||
}
|
||||
if err := h.fixLen(msg, length, preLen); err != nil {
|
||||
@@ -1093,12 +1164,12 @@ func (b *Builder) SOAResource(h ResourceHeader, r SOAResource) error {
|
||||
return err
|
||||
}
|
||||
h.Type = r.realType()
|
||||
msg, length, err := h.pack(b.msg, b.compression)
|
||||
msg, length, err := h.pack(b.msg, b.compression, b.start)
|
||||
if err != nil {
|
||||
return &nestedError{"ResourceHeader", err}
|
||||
}
|
||||
preLen := len(msg)
|
||||
if msg, err = r.pack(msg, b.compression); err != nil {
|
||||
if msg, err = r.pack(msg, b.compression, b.start); err != nil {
|
||||
return &nestedError{"SOAResource body", err}
|
||||
}
|
||||
if err := h.fixLen(msg, length, preLen); err != nil {
|
||||
@@ -1117,12 +1188,12 @@ func (b *Builder) TXTResource(h ResourceHeader, r TXTResource) error {
|
||||
return err
|
||||
}
|
||||
h.Type = r.realType()
|
||||
msg, length, err := h.pack(b.msg, b.compression)
|
||||
msg, length, err := h.pack(b.msg, b.compression, b.start)
|
||||
if err != nil {
|
||||
return &nestedError{"ResourceHeader", err}
|
||||
}
|
||||
preLen := len(msg)
|
||||
if msg, err = r.pack(msg, b.compression); err != nil {
|
||||
if msg, err = r.pack(msg, b.compression, b.start); err != nil {
|
||||
return &nestedError{"TXTResource body", err}
|
||||
}
|
||||
if err := h.fixLen(msg, length, preLen); err != nil {
|
||||
@@ -1141,12 +1212,12 @@ func (b *Builder) SRVResource(h ResourceHeader, r SRVResource) error {
|
||||
return err
|
||||
}
|
||||
h.Type = r.realType()
|
||||
msg, length, err := h.pack(b.msg, b.compression)
|
||||
msg, length, err := h.pack(b.msg, b.compression, b.start)
|
||||
if err != nil {
|
||||
return &nestedError{"ResourceHeader", err}
|
||||
}
|
||||
preLen := len(msg)
|
||||
if msg, err = r.pack(msg, b.compression); err != nil {
|
||||
if msg, err = r.pack(msg, b.compression, b.start); err != nil {
|
||||
return &nestedError{"SRVResource body", err}
|
||||
}
|
||||
if err := h.fixLen(msg, length, preLen); err != nil {
|
||||
@@ -1165,12 +1236,12 @@ func (b *Builder) AResource(h ResourceHeader, r AResource) error {
|
||||
return err
|
||||
}
|
||||
h.Type = r.realType()
|
||||
msg, length, err := h.pack(b.msg, b.compression)
|
||||
msg, length, err := h.pack(b.msg, b.compression, b.start)
|
||||
if err != nil {
|
||||
return &nestedError{"ResourceHeader", err}
|
||||
}
|
||||
preLen := len(msg)
|
||||
if msg, err = r.pack(msg, b.compression); err != nil {
|
||||
if msg, err = r.pack(msg, b.compression, b.start); err != nil {
|
||||
return &nestedError{"AResource body", err}
|
||||
}
|
||||
if err := h.fixLen(msg, length, preLen); err != nil {
|
||||
@@ -1189,12 +1260,12 @@ func (b *Builder) AAAAResource(h ResourceHeader, r AAAAResource) error {
|
||||
return err
|
||||
}
|
||||
h.Type = r.realType()
|
||||
msg, length, err := h.pack(b.msg, b.compression)
|
||||
msg, length, err := h.pack(b.msg, b.compression, b.start)
|
||||
if err != nil {
|
||||
return &nestedError{"ResourceHeader", err}
|
||||
}
|
||||
preLen := len(msg)
|
||||
if msg, err = r.pack(msg, b.compression); err != nil {
|
||||
if msg, err = r.pack(msg, b.compression, b.start); err != nil {
|
||||
return &nestedError{"AAAAResource body", err}
|
||||
}
|
||||
if err := h.fixLen(msg, length, preLen); err != nil {
|
||||
@@ -1213,7 +1284,8 @@ func (b *Builder) Finish() ([]byte, error) {
|
||||
return nil, ErrNotStarted
|
||||
}
|
||||
b.section = sectionDone
|
||||
b.header.pack(b.msg[:0])
|
||||
// Space for the header was allocated in NewBuilder.
|
||||
b.header.pack(b.msg[b.start:b.start])
|
||||
return b.msg, nil
|
||||
}
|
||||
|
||||
@@ -1243,12 +1315,13 @@ type ResourceHeader struct {
|
||||
Length uint16
|
||||
}
|
||||
|
||||
// pack packs all of the fields in a ResourceHeader except for the length. The
|
||||
// length bytes are returned as a slice so they can be filled in after the rest
|
||||
// of the Resource has been packed.
|
||||
func (h *ResourceHeader) pack(oldMsg []byte, compression map[string]int) (msg []byte, length []byte, err error) {
|
||||
// pack appends the wire format of the ResourceHeader to oldMsg.
|
||||
//
|
||||
// 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) {
|
||||
msg = oldMsg
|
||||
if msg, err = h.Name.pack(msg, compression); err != nil {
|
||||
if msg, err = h.Name.pack(msg, compression, compressionOff); err != nil {
|
||||
return oldMsg, nil, &nestedError{"Name", err}
|
||||
}
|
||||
msg = packType(msg, h.Type)
|
||||
@@ -1317,6 +1390,7 @@ func skipResource(msg []byte, off int) (int, error) {
|
||||
return newOff, nil
|
||||
}
|
||||
|
||||
// packUint16 appends the wire format of field to msg.
|
||||
func packUint16(msg []byte, field uint16) []byte {
|
||||
return append(msg, byte(field>>8), byte(field))
|
||||
}
|
||||
@@ -1335,6 +1409,7 @@ func skipUint16(msg []byte, off int) (int, error) {
|
||||
return off + uint16Len, nil
|
||||
}
|
||||
|
||||
// packType appends the wire format of field to msg.
|
||||
func packType(msg []byte, field Type) []byte {
|
||||
return packUint16(msg, uint16(field))
|
||||
}
|
||||
@@ -1348,6 +1423,7 @@ func skipType(msg []byte, off int) (int, error) {
|
||||
return skipUint16(msg, off)
|
||||
}
|
||||
|
||||
// packClass appends the wire format of field to msg.
|
||||
func packClass(msg []byte, field Class) []byte {
|
||||
return packUint16(msg, uint16(field))
|
||||
}
|
||||
@@ -1361,6 +1437,7 @@ func skipClass(msg []byte, off int) (int, error) {
|
||||
return skipUint16(msg, off)
|
||||
}
|
||||
|
||||
// packUint32 appends the wire format of field to msg.
|
||||
func packUint32(msg []byte, field uint32) []byte {
|
||||
return append(
|
||||
msg,
|
||||
@@ -1386,17 +1463,16 @@ func skipUint32(msg []byte, off int) (int, error) {
|
||||
return off + uint32Len, nil
|
||||
}
|
||||
|
||||
func packText(msg []byte, field string) []byte {
|
||||
for len(field) > 0 {
|
||||
l := len(field)
|
||||
if l > 255 {
|
||||
l = 255
|
||||
}
|
||||
msg = append(msg, byte(l))
|
||||
msg = append(msg, field[:l]...)
|
||||
field = field[l:]
|
||||
// packText appends the wire format of field to msg.
|
||||
func packText(msg []byte, field string) ([]byte, error) {
|
||||
l := len(field)
|
||||
if l > 255 {
|
||||
return nil, errStringTooLong
|
||||
}
|
||||
return msg
|
||||
msg = append(msg, byte(l))
|
||||
msg = append(msg, field...)
|
||||
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func unpackText(msg []byte, off int) (string, int, error) {
|
||||
@@ -1422,6 +1498,7 @@ func skipText(msg []byte, off int) (int, error) {
|
||||
return endOff, nil
|
||||
}
|
||||
|
||||
// packBytes appends the wire format of field to msg.
|
||||
func packBytes(msg []byte, field []byte) []byte {
|
||||
return append(msg, field...)
|
||||
}
|
||||
@@ -1466,14 +1543,14 @@ func (n Name) String() string {
|
||||
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
|
||||
// with a zero-length string. Compression can be used to reuse domain suffixes.
|
||||
//
|
||||
// The compression map will be updated with new domain suffixes. If compression
|
||||
// is nil, compression will not be used.
|
||||
func (n *Name) pack(msg []byte, compression map[string]int) ([]byte, error) {
|
||||
func (n *Name) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
|
||||
oldMsg := msg
|
||||
|
||||
// Add a trailing dot to canonicalize name.
|
||||
@@ -1525,7 +1602,7 @@ func (n *Name) pack(msg []byte, compression map[string]int) ([]byte, error) {
|
||||
// Miss. Add the suffix to the compression table if the
|
||||
// offset can be stored in the available 14 bytes.
|
||||
if len(msg) <= int(^uint16(0)>>2) {
|
||||
compression[string(n.Data[i:])] = len(msg)
|
||||
compression[string(n.Data[i:])] = len(msg) - compressionOff
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1534,6 +1611,10 @@ func (n *Name) pack(msg []byte, compression map[string]int) ([]byte, error) {
|
||||
|
||||
// unpack unpacks a domain name.
|
||||
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 := off
|
||||
|
||||
@@ -1569,6 +1650,9 @@ Loop:
|
||||
name = append(name, '.')
|
||||
currOff = endOff
|
||||
case 0xC0: // Pointer
|
||||
if !allowCompression {
|
||||
return off, errCompressedSRV
|
||||
}
|
||||
if currOff >= len(msg) {
|
||||
return off, errInvalidPtr
|
||||
}
|
||||
@@ -1648,8 +1732,9 @@ type Question struct {
|
||||
Class Class
|
||||
}
|
||||
|
||||
func (q *Question) pack(msg []byte, compression map[string]int) ([]byte, error) {
|
||||
msg, err := q.Name.pack(msg, compression)
|
||||
// pack appends the wire format of the Question to msg.
|
||||
func (q *Question) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
|
||||
msg, err := q.Name.pack(msg, compression, compressionOff)
|
||||
if err != nil {
|
||||
return msg, &nestedError{"Name", err}
|
||||
}
|
||||
@@ -1728,8 +1813,9 @@ func (r *CNAMEResource) realType() Type {
|
||||
return TypeCNAME
|
||||
}
|
||||
|
||||
func (r *CNAMEResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
|
||||
return r.CNAME.pack(msg, compression)
|
||||
// pack appends the wire format of the CNAMEResource to msg.
|
||||
func (r *CNAMEResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
|
||||
return r.CNAME.pack(msg, compression, compressionOff)
|
||||
}
|
||||
|
||||
func unpackCNAMEResource(msg []byte, off int) (CNAMEResource, error) {
|
||||
@@ -1750,10 +1836,11 @@ func (r *MXResource) realType() Type {
|
||||
return TypeMX
|
||||
}
|
||||
|
||||
func (r *MXResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
|
||||
// pack appends the wire format of the MXResource to msg.
|
||||
func (r *MXResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
|
||||
oldMsg := msg
|
||||
msg = packUint16(msg, r.Pref)
|
||||
msg, err := r.MX.pack(msg, compression)
|
||||
msg, err := r.MX.pack(msg, compression, compressionOff)
|
||||
if err != nil {
|
||||
return oldMsg, &nestedError{"MXResource.MX", err}
|
||||
}
|
||||
@@ -1781,8 +1868,9 @@ func (r *NSResource) realType() Type {
|
||||
return TypeNS
|
||||
}
|
||||
|
||||
func (r *NSResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
|
||||
return r.NS.pack(msg, compression)
|
||||
// pack appends the wire format of the NSResource to msg.
|
||||
func (r *NSResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
|
||||
return r.NS.pack(msg, compression, compressionOff)
|
||||
}
|
||||
|
||||
func unpackNSResource(msg []byte, off int) (NSResource, error) {
|
||||
@@ -1802,8 +1890,9 @@ func (r *PTRResource) realType() Type {
|
||||
return TypePTR
|
||||
}
|
||||
|
||||
func (r *PTRResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
|
||||
return r.PTR.pack(msg, compression)
|
||||
// pack appends the wire format of the PTRResource to msg.
|
||||
func (r *PTRResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
|
||||
return r.PTR.pack(msg, compression, compressionOff)
|
||||
}
|
||||
|
||||
func unpackPTRResource(msg []byte, off int) (PTRResource, error) {
|
||||
@@ -1833,13 +1922,14 @@ func (r *SOAResource) realType() Type {
|
||||
return TypeSOA
|
||||
}
|
||||
|
||||
func (r *SOAResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
|
||||
// pack appends the wire format of the SOAResource to msg.
|
||||
func (r *SOAResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
|
||||
oldMsg := msg
|
||||
msg, err := r.NS.pack(msg, compression)
|
||||
msg, err := r.NS.pack(msg, compression, compressionOff)
|
||||
if err != nil {
|
||||
return oldMsg, &nestedError{"SOAResource.NS", err}
|
||||
}
|
||||
msg, err = r.MBox.pack(msg, compression)
|
||||
msg, err = r.MBox.pack(msg, compression, compressionOff)
|
||||
if err != nil {
|
||||
return oldMsg, &nestedError{"SOAResource.MBox", err}
|
||||
}
|
||||
@@ -1885,19 +1975,28 @@ func unpackSOAResource(msg []byte, off int) (SOAResource, error) {
|
||||
|
||||
// A TXTResource is a TXT Resource record.
|
||||
type TXTResource struct {
|
||||
Txt string // Not a domain name.
|
||||
TXT []string
|
||||
}
|
||||
|
||||
func (r *TXTResource) realType() Type {
|
||||
return TypeTXT
|
||||
}
|
||||
|
||||
func (r *TXTResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
|
||||
return packText(msg, r.Txt), nil
|
||||
// pack appends the wire format of the TXTResource to msg.
|
||||
func (r *TXTResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
|
||||
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) {
|
||||
var txt string
|
||||
txts := make([]string, 0, 1)
|
||||
for n := uint16(0); n < length; {
|
||||
var t string
|
||||
var err error
|
||||
@@ -1909,9 +2008,9 @@ func unpackTXTResource(msg []byte, off int, length uint16) (TXTResource, error)
|
||||
return TXTResource{}, errCalcLen
|
||||
}
|
||||
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.
|
||||
@@ -1926,12 +2025,13 @@ func (r *SRVResource) realType() Type {
|
||||
return TypeSRV
|
||||
}
|
||||
|
||||
func (r *SRVResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
|
||||
// pack appends the wire format of the SRVResource to msg.
|
||||
func (r *SRVResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
|
||||
oldMsg := msg
|
||||
msg = packUint16(msg, r.Priority)
|
||||
msg = packUint16(msg, r.Weight)
|
||||
msg = packUint16(msg, r.Port)
|
||||
msg, err := r.Target.pack(msg, nil)
|
||||
msg, err := r.Target.pack(msg, nil, compressionOff)
|
||||
if err != nil {
|
||||
return oldMsg, &nestedError{"SRVResource.Target", err}
|
||||
}
|
||||
@@ -1952,7 +2052,7 @@ func unpackSRVResource(msg []byte, off int) (SRVResource, error) {
|
||||
return SRVResource{}, &nestedError{"Port", err}
|
||||
}
|
||||
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{priority, weight, port, target}, nil
|
||||
@@ -1967,7 +2067,8 @@ func (r *AResource) realType() Type {
|
||||
return TypeA
|
||||
}
|
||||
|
||||
func (r *AResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
|
||||
// pack appends the wire format of the AResource to msg.
|
||||
func (r *AResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
|
||||
return packBytes(msg, r.A[:]), nil
|
||||
}
|
||||
|
||||
@@ -1988,7 +2089,8 @@ func (r *AAAAResource) realType() Type {
|
||||
return TypeAAAA
|
||||
}
|
||||
|
||||
func (r *AAAAResource) pack(msg []byte, compression map[string]int) ([]byte, error) {
|
||||
// pack appends the wire format of the AAAAResource to msg.
|
||||
func (r *AAAAResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
|
||||
return packBytes(msg, r.AAAA[:]), nil
|
||||
}
|
||||
|
||||
|
||||
+225
-229
@@ -8,6 +8,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -62,7 +63,7 @@ func TestQuestionPackUnpack(t *testing.T) {
|
||||
Type: TypeA,
|
||||
Class: ClassINET,
|
||||
}
|
||||
buf, err := want.pack(make([]byte, 1, 50), map[string]int{})
|
||||
buf, err := want.pack(make([]byte, 1, 50), map[string]int{}, 1)
|
||||
if err != nil {
|
||||
t.Fatal("Packing failed:", err)
|
||||
}
|
||||
@@ -129,7 +130,7 @@ func TestNamePackUnpack(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
in := mustNewName(test.in)
|
||||
want := mustNewName(test.want)
|
||||
buf, err := in.pack(make([]byte, 0, 30), map[string]int{})
|
||||
buf, err := in.pack(make([]byte, 0, 30), map[string]int{}, 0)
|
||||
if err != test.err {
|
||||
t.Errorf("Packing of %q: got err = %v, want err = %v", test.in, err, test.err)
|
||||
continue
|
||||
@@ -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 {
|
||||
e, ok := err.(*nestedError)
|
||||
return ok && e.s == prefix
|
||||
@@ -248,6 +271,40 @@ func TestDNSPackUnpack(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDNSAppendPackUnpack(t *testing.T) {
|
||||
wants := []Message{
|
||||
{
|
||||
Questions: []Question{
|
||||
{
|
||||
Name: mustNewName("."),
|
||||
Type: TypeAAAA,
|
||||
Class: ClassINET,
|
||||
},
|
||||
},
|
||||
Answers: []Resource{},
|
||||
Authorities: []Resource{},
|
||||
Additionals: []Resource{},
|
||||
},
|
||||
largeTestMsg(),
|
||||
}
|
||||
for i, want := range wants {
|
||||
b := make([]byte, 2, 514)
|
||||
b, err := want.AppendPack(b)
|
||||
if err != nil {
|
||||
t.Fatalf("%d: packing failed: %v", i, err)
|
||||
}
|
||||
b = b[2:]
|
||||
var got Message
|
||||
err = got.Unpack(b)
|
||||
if err != nil {
|
||||
t.Fatalf("%d: unpacking failed: %v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%d: got = %+v, want = %+v", i, &got, &want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSkipAll(t *testing.T) {
|
||||
msg := largeTestMsg()
|
||||
buf, err := msg.Pack()
|
||||
@@ -410,9 +467,17 @@ func TestVeryLongTxt(t *testing.T) {
|
||||
Type: TypeTXT,
|
||||
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{})
|
||||
buf, err := want.pack(make([]byte, 0, 8000), map[string]int{}, 0)
|
||||
if err != nil {
|
||||
t.Fatal("Packing failed:", err)
|
||||
}
|
||||
@@ -434,6 +499,33 @@ 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) {
|
||||
buf := make([]byte, 2, 514)
|
||||
wantBuf := []byte{4, 44}
|
||||
copy(buf, wantBuf)
|
||||
|
||||
b := NewBuilder(buf, Header{})
|
||||
b.EnableCompression()
|
||||
|
||||
buf, err := b.Finish()
|
||||
if err != nil {
|
||||
t.Fatal("Building failed:", err)
|
||||
}
|
||||
if got, want := len(buf), headerLen+2; got != want {
|
||||
t.Errorf("Got len(buf} = %d, want = %d", got, want)
|
||||
}
|
||||
if string(buf[:2]) != string(wantBuf) {
|
||||
t.Errorf("Original data not preserved, got = %v, want = %v", buf[:2], wantBuf)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartError(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -514,8 +606,8 @@ func TestBuilder(t *testing.T) {
|
||||
t.Fatal("Packing without builder:", err)
|
||||
}
|
||||
|
||||
var b Builder
|
||||
b.Start(nil, msg.Header)
|
||||
b := NewBuilder(nil, msg.Header)
|
||||
b.EnableCompression()
|
||||
|
||||
if err := b.StartQuestions(); err != nil {
|
||||
t.Fatal("b.StartQuestions():", err)
|
||||
@@ -653,9 +745,7 @@ func TestResourcePack(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParsing(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
|
||||
func benchmarkParsingSetup() ([]byte, error) {
|
||||
name := mustNewName("foo.bar.example.com.")
|
||||
msg := Message{
|
||||
Header: Header{Response: true, Authoritative: true},
|
||||
@@ -700,111 +790,148 @@ func BenchmarkParsing(b *testing.B) {
|
||||
|
||||
buf, err := msg.Pack()
|
||||
if err != nil {
|
||||
b.Fatal("msg.Pack():", err)
|
||||
return nil, fmt.Errorf("msg.Pack(): %v", err)
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func benchmarkParsing(tb testing.TB, buf []byte) {
|
||||
var p Parser
|
||||
if _, err := p.Start(buf); err != nil {
|
||||
tb.Fatal("p.Start(buf):", err)
|
||||
}
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
var p Parser
|
||||
if _, err := p.Start(buf); err != nil {
|
||||
b.Fatal("p.Start(buf):", err)
|
||||
for {
|
||||
_, err := p.Question()
|
||||
if err == ErrSectionDone {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
tb.Fatal("p.Question():", err)
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
h, err := p.AnswerHeader()
|
||||
if err == ErrSectionDone {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for {
|
||||
_, err := p.Question()
|
||||
if err == ErrSectionDone {
|
||||
break
|
||||
switch h.Type {
|
||||
case TypeA:
|
||||
if _, err := p.AResource(); err != nil {
|
||||
tb.Fatal("p.AResource():", err)
|
||||
}
|
||||
if err != nil {
|
||||
b.Fatal("p.Question():", err)
|
||||
case TypeAAAA:
|
||||
if _, err := p.AAAAResource(); err != nil {
|
||||
tb.Fatal("p.AAAAResource():", err)
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
h, err := p.AnswerHeader()
|
||||
if err == ErrSectionDone {
|
||||
break
|
||||
case TypeCNAME:
|
||||
if _, err := p.CNAMEResource(); err != nil {
|
||||
tb.Fatal("p.CNAMEResource():", err)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
switch h.Type {
|
||||
case TypeA:
|
||||
if _, err := p.AResource(); err != nil {
|
||||
b.Fatal("p.AResource():", err)
|
||||
}
|
||||
case TypeAAAA:
|
||||
if _, err := p.AAAAResource(); err != nil {
|
||||
b.Fatal("p.AAAAResource():", err)
|
||||
}
|
||||
case TypeCNAME:
|
||||
if _, err := p.CNAMEResource(); err != nil {
|
||||
b.Fatal("p.CNAMEResource():", err)
|
||||
}
|
||||
case TypeNS:
|
||||
if _, err := p.NSResource(); err != nil {
|
||||
b.Fatal("p.NSResource():", err)
|
||||
}
|
||||
default:
|
||||
b.Fatalf("unknown type: %T", h)
|
||||
case TypeNS:
|
||||
if _, err := p.NSResource(); err != nil {
|
||||
tb.Fatal("p.NSResource():", err)
|
||||
}
|
||||
default:
|
||||
tb.Fatalf("unknown type: %T", h)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBuilding(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
func BenchmarkParsing(b *testing.B) {
|
||||
buf, err := benchmarkParsingSetup()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchmarkParsing(b, buf)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsingAllocs(t *testing.T) {
|
||||
buf, err := benchmarkParsingSetup()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if allocs := testing.AllocsPerRun(100, func() { benchmarkParsing(t, buf) }); allocs > 0.5 {
|
||||
t.Errorf("Allocations during parsing: got = %f, want ~0", allocs)
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkBuildingSetup() (Name, []byte) {
|
||||
name := mustNewName("foo.bar.example.com.")
|
||||
buf := make([]byte, 0, packStartingCap)
|
||||
return name, buf
|
||||
}
|
||||
|
||||
func benchmarkBuilding(tb testing.TB, name Name, buf []byte) {
|
||||
bld := NewBuilder(buf, Header{Response: true, Authoritative: true})
|
||||
|
||||
if err := bld.StartQuestions(); err != nil {
|
||||
tb.Fatal("bld.StartQuestions():", err)
|
||||
}
|
||||
q := Question{
|
||||
Name: name,
|
||||
Type: TypeA,
|
||||
Class: ClassINET,
|
||||
}
|
||||
if err := bld.Question(q); err != nil {
|
||||
tb.Fatalf("bld.Question(%+v): %v", q, err)
|
||||
}
|
||||
|
||||
hdr := ResourceHeader{
|
||||
Name: name,
|
||||
Class: ClassINET,
|
||||
}
|
||||
if err := bld.StartAnswers(); err != nil {
|
||||
tb.Fatal("bld.StartQuestions():", err)
|
||||
}
|
||||
|
||||
ar := AResource{[4]byte{}}
|
||||
if err := bld.AResource(hdr, ar); err != nil {
|
||||
tb.Fatalf("bld.AResource(%+v, %+v): %v", hdr, ar, err)
|
||||
}
|
||||
|
||||
aaar := AAAAResource{[16]byte{}}
|
||||
if err := bld.AAAAResource(hdr, aaar); err != nil {
|
||||
tb.Fatalf("bld.AAAAResource(%+v, %+v): %v", hdr, aaar, err)
|
||||
}
|
||||
|
||||
cnr := CNAMEResource{name}
|
||||
if err := bld.CNAMEResource(hdr, cnr); err != nil {
|
||||
tb.Fatalf("bld.CNAMEResource(%+v, %+v): %v", hdr, cnr, err)
|
||||
}
|
||||
|
||||
nsr := NSResource{name}
|
||||
if err := bld.NSResource(hdr, nsr); err != nil {
|
||||
tb.Fatalf("bld.NSResource(%+v, %+v): %v", hdr, nsr, err)
|
||||
}
|
||||
|
||||
if _, err := bld.Finish(); err != nil {
|
||||
tb.Fatal("bld.Finish():", err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBuilding(b *testing.B) {
|
||||
name, buf := benchmarkBuildingSetup()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
var bld Builder
|
||||
bld.StartWithoutCompression(buf, Header{Response: true, Authoritative: true})
|
||||
benchmarkBuilding(b, name, buf)
|
||||
}
|
||||
}
|
||||
|
||||
if err := bld.StartQuestions(); err != nil {
|
||||
b.Fatal("bld.StartQuestions():", err)
|
||||
}
|
||||
q := Question{
|
||||
Name: name,
|
||||
Type: TypeA,
|
||||
Class: ClassINET,
|
||||
}
|
||||
if err := bld.Question(q); err != nil {
|
||||
b.Fatalf("bld.Question(%+v): %v", q, err)
|
||||
}
|
||||
|
||||
hdr := ResourceHeader{
|
||||
Name: name,
|
||||
Class: ClassINET,
|
||||
}
|
||||
if err := bld.StartAnswers(); err != nil {
|
||||
b.Fatal("bld.StartQuestions():", err)
|
||||
}
|
||||
|
||||
ar := AResource{[4]byte{}}
|
||||
if err := bld.AResource(hdr, ar); err != nil {
|
||||
b.Fatalf("bld.AResource(%+v, %+v): %v", hdr, ar, err)
|
||||
}
|
||||
|
||||
aaar := AAAAResource{[16]byte{}}
|
||||
if err := bld.AAAAResource(hdr, aaar); err != nil {
|
||||
b.Fatalf("bld.AAAAResource(%+v, %+v): %v", hdr, aaar, err)
|
||||
}
|
||||
|
||||
cnr := CNAMEResource{name}
|
||||
if err := bld.CNAMEResource(hdr, cnr); err != nil {
|
||||
b.Fatalf("bld.CNAMEResource(%+v, %+v): %v", hdr, cnr, err)
|
||||
}
|
||||
|
||||
nsr := NSResource{name}
|
||||
if err := bld.NSResource(hdr, nsr); err != nil {
|
||||
b.Fatalf("bld.NSResource(%+v, %+v): %v", hdr, nsr, err)
|
||||
}
|
||||
|
||||
if _, err := bld.Finish(); err != nil {
|
||||
b.Fatal("bld.Finish():", err)
|
||||
}
|
||||
func TestBuildingAllocs(t *testing.T) {
|
||||
name, buf := benchmarkBuildingSetup()
|
||||
if allocs := testing.AllocsPerRun(100, func() { benchmarkBuilding(t, name, buf) }); allocs > 0.5 {
|
||||
t.Errorf("Allocations during building: got = %f, want ~0", allocs)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -995,7 +1122,7 @@ func largeTestMsg() Message {
|
||||
Type: TypeTXT,
|
||||
Class: ClassINET,
|
||||
},
|
||||
&TXTResource{"So Long, and Thanks for All the Fish"},
|
||||
&TXTResource{[]string{"So Long, and Thanks for All the Fish"}},
|
||||
},
|
||||
{
|
||||
ResourceHeader{
|
||||
@@ -1003,139 +1130,8 @@ func largeTestMsg() Message {
|
||||
Type: TypeTXT,
|
||||
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.
|
||||
`
|
||||
|
||||
+1
@@ -665,6 +665,7 @@ var eventHandlers = []string{
|
||||
|
||||
// extra are ad-hoc values not covered by any of the lists above.
|
||||
var extra = []string{
|
||||
"acronym",
|
||||
"align",
|
||||
"annotation",
|
||||
"annotation-xml",
|
||||
|
||||
+684
-682
File diff suppressed because it is too large
Load Diff
+1
@@ -10,6 +10,7 @@ var testAtomList = []string{
|
||||
"accept",
|
||||
"accept-charset",
|
||||
"accesskey",
|
||||
"acronym",
|
||||
"action",
|
||||
"address",
|
||||
"align",
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
|
||||
package html
|
||||
|
||||
// Section 12.2.3.2 of the HTML5 specification says "The following elements
|
||||
// Section 12.2.4.2 of the HTML5 specification says "The following elements
|
||||
// have varying levels of special parsing rules".
|
||||
// https://html.spec.whatwg.org/multipage/syntax.html#the-stack-of-open-elements
|
||||
var isSpecialElementMap = map[string]bool{
|
||||
|
||||
+3
-3
@@ -67,7 +67,7 @@ func mathMLTextIntegrationPoint(n *Node) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.5.
|
||||
// Section 12.2.6.5.
|
||||
var breakout = map[string]bool{
|
||||
"b": true,
|
||||
"big": true,
|
||||
@@ -115,7 +115,7 @@ var breakout = map[string]bool{
|
||||
"var": true,
|
||||
}
|
||||
|
||||
// Section 12.2.5.5.
|
||||
// Section 12.2.6.5.
|
||||
var svgTagNameAdjustments = map[string]string{
|
||||
"altglyph": "altGlyph",
|
||||
"altglyphdef": "altGlyphDef",
|
||||
@@ -155,7 +155,7 @@ var svgTagNameAdjustments = map[string]string{
|
||||
"textpath": "textPath",
|
||||
}
|
||||
|
||||
// Section 12.2.5.1
|
||||
// Section 12.2.6.1
|
||||
var mathMLAttributeAdjustments = map[string]string{
|
||||
"definitionurl": "definitionURL",
|
||||
}
|
||||
|
||||
+4
-3
@@ -21,9 +21,10 @@ const (
|
||||
scopeMarkerNode
|
||||
)
|
||||
|
||||
// Section 12.2.3.3 says "scope markers are inserted when entering applet
|
||||
// elements, buttons, object elements, marquees, table cells, and table
|
||||
// captions, and are used to prevent formatting from 'leaking'".
|
||||
// Section 12.2.4.3 says "The markers are inserted when entering applet,
|
||||
// object, marquee, template, td, th, and caption elements, and are used
|
||||
// to prevent formatting from "leaking" into applet, object, marquee,
|
||||
// template, td, th, and caption elements".
|
||||
var scopeMarker = Node{Type: scopeMarkerNode}
|
||||
|
||||
// A Node consists of a NodeType and some Data (tag name for element nodes,
|
||||
|
||||
+38
-38
@@ -25,12 +25,12 @@ type parser struct {
|
||||
hasSelfClosingToken bool
|
||||
// doc is the document root element.
|
||||
doc *Node
|
||||
// The stack of open elements (section 12.2.3.2) and active formatting
|
||||
// elements (section 12.2.3.3).
|
||||
// The stack of open elements (section 12.2.4.2) and active formatting
|
||||
// elements (section 12.2.4.3).
|
||||
oe, afe nodeStack
|
||||
// Element pointers (section 12.2.3.4).
|
||||
// Element pointers (section 12.2.4.4).
|
||||
head, form *Node
|
||||
// Other parsing state flags (section 12.2.3.5).
|
||||
// Other parsing state flags (section 12.2.4.5).
|
||||
scripting, framesetOK bool
|
||||
// im is the current insertion mode.
|
||||
im insertionMode
|
||||
@@ -38,7 +38,7 @@ type parser struct {
|
||||
// or inTableText insertion mode.
|
||||
originalIM insertionMode
|
||||
// fosterParenting is whether new elements should be inserted according to
|
||||
// the foster parenting rules (section 12.2.5.3).
|
||||
// the foster parenting rules (section 12.2.6.1).
|
||||
fosterParenting bool
|
||||
// quirks is whether the parser is operating in "quirks mode."
|
||||
quirks bool
|
||||
@@ -56,7 +56,7 @@ func (p *parser) top() *Node {
|
||||
return p.doc
|
||||
}
|
||||
|
||||
// Stop tags for use in popUntil. These come from section 12.2.3.2.
|
||||
// Stop tags for use in popUntil. These come from section 12.2.4.2.
|
||||
var (
|
||||
defaultScopeStopTags = map[string][]a.Atom{
|
||||
"": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template},
|
||||
@@ -79,7 +79,7 @@ const (
|
||||
|
||||
// popUntil pops the stack of open elements at the highest element whose tag
|
||||
// is in matchTags, provided there is no higher element in the scope's stop
|
||||
// tags (as defined in section 12.2.3.2). It returns whether or not there was
|
||||
// tags (as defined in section 12.2.4.2). It returns whether or not there was
|
||||
// such an element. If there was not, popUntil leaves the stack unchanged.
|
||||
//
|
||||
// For example, the set of stop tags for table scope is: "html", "table". If
|
||||
@@ -234,7 +234,7 @@ func (p *parser) shouldFosterParent() bool {
|
||||
}
|
||||
|
||||
// fosterParent adds a child node according to the foster parenting rules.
|
||||
// Section 12.2.5.3, "foster parenting".
|
||||
// Section 12.2.6.1, "foster parenting".
|
||||
func (p *parser) fosterParent(n *Node) {
|
||||
var table, parent, prev *Node
|
||||
var i int
|
||||
@@ -304,7 +304,7 @@ func (p *parser) addElement() {
|
||||
})
|
||||
}
|
||||
|
||||
// Section 12.2.3.3.
|
||||
// Section 12.2.4.3.
|
||||
func (p *parser) addFormattingElement() {
|
||||
tagAtom, attr := p.tok.DataAtom, p.tok.Attr
|
||||
p.addElement()
|
||||
@@ -351,7 +351,7 @@ findIdenticalElements:
|
||||
p.afe = append(p.afe, p.top())
|
||||
}
|
||||
|
||||
// Section 12.2.3.3.
|
||||
// Section 12.2.4.3.
|
||||
func (p *parser) clearActiveFormattingElements() {
|
||||
for {
|
||||
n := p.afe.pop()
|
||||
@@ -361,7 +361,7 @@ func (p *parser) clearActiveFormattingElements() {
|
||||
}
|
||||
}
|
||||
|
||||
// Section 12.2.3.3.
|
||||
// Section 12.2.4.3.
|
||||
func (p *parser) reconstructActiveFormattingElements() {
|
||||
n := p.afe.top()
|
||||
if n == nil {
|
||||
@@ -390,12 +390,12 @@ func (p *parser) reconstructActiveFormattingElements() {
|
||||
}
|
||||
}
|
||||
|
||||
// Section 12.2.4.
|
||||
// Section 12.2.5.
|
||||
func (p *parser) acknowledgeSelfClosingTag() {
|
||||
p.hasSelfClosingToken = false
|
||||
}
|
||||
|
||||
// An insertion mode (section 12.2.3.1) is the state transition function from
|
||||
// An insertion mode (section 12.2.4.1) is the state transition function from
|
||||
// a particular state in the HTML5 parser's state machine. It updates the
|
||||
// parser's fields depending on parser.tok (where ErrorToken means EOF).
|
||||
// It returns whether the token was consumed.
|
||||
@@ -403,7 +403,7 @@ type insertionMode func(*parser) bool
|
||||
|
||||
// setOriginalIM sets the insertion mode to return to after completing a text or
|
||||
// inTableText insertion mode.
|
||||
// Section 12.2.3.1, "using the rules for".
|
||||
// Section 12.2.4.1, "using the rules for".
|
||||
func (p *parser) setOriginalIM() {
|
||||
if p.originalIM != nil {
|
||||
panic("html: bad parser state: originalIM was set twice")
|
||||
@@ -411,7 +411,7 @@ func (p *parser) setOriginalIM() {
|
||||
p.originalIM = p.im
|
||||
}
|
||||
|
||||
// Section 12.2.3.1, "reset the insertion mode".
|
||||
// Section 12.2.4.1, "reset the insertion mode".
|
||||
func (p *parser) resetInsertionMode() {
|
||||
for i := len(p.oe) - 1; i >= 0; i-- {
|
||||
n := p.oe[i]
|
||||
@@ -452,7 +452,7 @@ func (p *parser) resetInsertionMode() {
|
||||
|
||||
const whitespace = " \t\r\n\f"
|
||||
|
||||
// Section 12.2.5.4.1.
|
||||
// Section 12.2.6.4.1.
|
||||
func initialIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken:
|
||||
@@ -479,7 +479,7 @@ func initialIM(p *parser) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.2.
|
||||
// Section 12.2.6.4.2.
|
||||
func beforeHTMLIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case DoctypeToken:
|
||||
@@ -517,7 +517,7 @@ func beforeHTMLIM(p *parser) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.3.
|
||||
// Section 12.2.6.4.3.
|
||||
func beforeHeadIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken:
|
||||
@@ -560,7 +560,7 @@ func beforeHeadIM(p *parser) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.4.
|
||||
// Section 12.2.6.4.4.
|
||||
func inHeadIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken:
|
||||
@@ -622,7 +622,7 @@ func inHeadIM(p *parser) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.6.
|
||||
// Section 12.2.6.4.6.
|
||||
func afterHeadIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken:
|
||||
@@ -697,7 +697,7 @@ func copyAttributes(dst *Node, src Token) {
|
||||
}
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.7.
|
||||
// Section 12.2.6.4.7.
|
||||
func inBodyIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken:
|
||||
@@ -1160,7 +1160,7 @@ func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) {
|
||||
}
|
||||
|
||||
// inBodyEndTagOther performs the "any other end tag" algorithm for inBodyIM.
|
||||
// "Any other end tag" handling from 12.2.5.5 The rules for parsing tokens in foreign content
|
||||
// "Any other end tag" handling from 12.2.6.5 The rules for parsing tokens in foreign content
|
||||
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inforeign
|
||||
func (p *parser) inBodyEndTagOther(tagAtom a.Atom) {
|
||||
for i := len(p.oe) - 1; i >= 0; i-- {
|
||||
@@ -1174,7 +1174,7 @@ func (p *parser) inBodyEndTagOther(tagAtom a.Atom) {
|
||||
}
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.8.
|
||||
// Section 12.2.6.4.8.
|
||||
func textIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case ErrorToken:
|
||||
@@ -1203,7 +1203,7 @@ func textIM(p *parser) bool {
|
||||
return p.tok.Type == EndTagToken
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.9.
|
||||
// Section 12.2.6.4.9.
|
||||
func inTableIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case ErrorToken:
|
||||
@@ -1309,7 +1309,7 @@ func inTableIM(p *parser) bool {
|
||||
return inBodyIM(p)
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.11.
|
||||
// Section 12.2.6.4.11.
|
||||
func inCaptionIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case StartTagToken:
|
||||
@@ -1355,7 +1355,7 @@ func inCaptionIM(p *parser) bool {
|
||||
return inBodyIM(p)
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.12.
|
||||
// Section 12.2.6.4.12.
|
||||
func inColumnGroupIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken:
|
||||
@@ -1408,7 +1408,7 @@ func inColumnGroupIM(p *parser) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.13.
|
||||
// Section 12.2.6.4.13.
|
||||
func inTableBodyIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case StartTagToken:
|
||||
@@ -1460,7 +1460,7 @@ func inTableBodyIM(p *parser) bool {
|
||||
return inTableIM(p)
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.14.
|
||||
// Section 12.2.6.4.14.
|
||||
func inRowIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case StartTagToken:
|
||||
@@ -1511,7 +1511,7 @@ func inRowIM(p *parser) bool {
|
||||
return inTableIM(p)
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.15.
|
||||
// Section 12.2.6.4.15.
|
||||
func inCellIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case StartTagToken:
|
||||
@@ -1560,7 +1560,7 @@ func inCellIM(p *parser) bool {
|
||||
return inBodyIM(p)
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.16.
|
||||
// Section 12.2.6.4.16.
|
||||
func inSelectIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case ErrorToken:
|
||||
@@ -1632,7 +1632,7 @@ func inSelectIM(p *parser) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.17.
|
||||
// Section 12.2.6.4.17.
|
||||
func inSelectInTableIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case StartTagToken, EndTagToken:
|
||||
@@ -1650,7 +1650,7 @@ func inSelectInTableIM(p *parser) bool {
|
||||
return inSelectIM(p)
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.18.
|
||||
// Section 12.2.6.4.19.
|
||||
func afterBodyIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case ErrorToken:
|
||||
@@ -1688,7 +1688,7 @@ func afterBodyIM(p *parser) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.19.
|
||||
// Section 12.2.6.4.20.
|
||||
func inFramesetIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case CommentToken:
|
||||
@@ -1738,7 +1738,7 @@ func inFramesetIM(p *parser) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.20.
|
||||
// Section 12.2.6.4.21.
|
||||
func afterFramesetIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case CommentToken:
|
||||
@@ -1777,7 +1777,7 @@ func afterFramesetIM(p *parser) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.21.
|
||||
// Section 12.2.6.4.22.
|
||||
func afterAfterBodyIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case ErrorToken:
|
||||
@@ -1806,7 +1806,7 @@ func afterAfterBodyIM(p *parser) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.22.
|
||||
// Section 12.2.6.4.23.
|
||||
func afterAfterFramesetIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case CommentToken:
|
||||
@@ -1844,7 +1844,7 @@ func afterAfterFramesetIM(p *parser) bool {
|
||||
|
||||
const whitespaceOrNUL = whitespace + "\x00"
|
||||
|
||||
// Section 12.2.5.5.
|
||||
// Section 12.2.6.5
|
||||
func parseForeignContent(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken:
|
||||
@@ -1924,7 +1924,7 @@ func parseForeignContent(p *parser) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Section 12.2.5.
|
||||
// Section 12.2.6.
|
||||
func (p *parser) inForeignContent() bool {
|
||||
if len(p.oe) == 0 {
|
||||
return false
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
package http2
|
||||
|
||||
// A list of the possible cipher suite ids. Taken from
|
||||
// http://www.iana.org/assignments/tls-parameters/tls-parameters.txt
|
||||
// https://www.iana.org/assignments/tls-parameters/tls-parameters.txt
|
||||
|
||||
const (
|
||||
cipher_TLS_NULL_WITH_NULL_NULL uint16 = 0x0000
|
||||
|
||||
+1
@@ -3,3 +3,4 @@ h2demo.linux
|
||||
client-id.dat
|
||||
client-secret.dat
|
||||
token.dat
|
||||
ca-certificates.crt
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
# 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.
|
||||
|
||||
FROM scratch
|
||||
LABEL maintainer "golang-dev@googlegroups.com"
|
||||
|
||||
COPY ca-certificates.crt /etc/ssl/certs/
|
||||
COPY h2demo /
|
||||
ENTRYPOINT ["/h2demo", "-prod"]
|
||||
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
# 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.
|
||||
|
||||
FROM golang:1.9
|
||||
LABEL maintainer "golang-dev@googlegroups.com"
|
||||
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
# BEGIN deps (run `make update-deps` to update)
|
||||
|
||||
# Repo cloud.google.com/go at 1d0c2da (2018-01-30)
|
||||
ENV REV=1d0c2da40456a9b47f5376165f275424acc15c09
|
||||
RUN go get -d cloud.google.com/go/compute/metadata `#and 6 other pkgs` &&\
|
||||
(cd /go/src/cloud.google.com/go && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
|
||||
|
||||
# Repo github.com/golang/protobuf at 9255415 (2018-01-25)
|
||||
ENV REV=925541529c1fa6821df4e44ce2723319eb2be768
|
||||
RUN go get -d github.com/golang/protobuf/proto `#and 6 other pkgs` &&\
|
||||
(cd /go/src/github.com/golang/protobuf && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
|
||||
|
||||
# Repo github.com/googleapis/gax-go at 317e000 (2017-09-15)
|
||||
ENV REV=317e0006254c44a0ac427cc52a0e083ff0b9622f
|
||||
RUN go get -d github.com/googleapis/gax-go &&\
|
||||
(cd /go/src/github.com/googleapis/gax-go && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
|
||||
|
||||
# Repo go4.org at 034d17a (2017-05-25)
|
||||
ENV REV=034d17a462f7b2dcd1a4a73553ec5357ff6e6c6e
|
||||
RUN go get -d go4.org/syncutil/singleflight &&\
|
||||
(cd /go/src/go4.org && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
|
||||
|
||||
# Repo golang.org/x/build at 8aa9ee0 (2018-02-01)
|
||||
ENV REV=8aa9ee0e557fd49c14113e5ba106e13a5b455460
|
||||
RUN go get -d golang.org/x/build/autocertcache &&\
|
||||
(cd /go/src/golang.org/x/build && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
|
||||
|
||||
# Repo golang.org/x/crypto at 1875d0a (2018-01-27)
|
||||
ENV REV=1875d0a70c90e57f11972aefd42276df65e895b9
|
||||
RUN go get -d golang.org/x/crypto/acme `#and 2 other pkgs` &&\
|
||||
(cd /go/src/golang.org/x/crypto && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
|
||||
|
||||
# Repo golang.org/x/oauth2 at 30785a2 (2018-01-04)
|
||||
ENV REV=30785a2c434e431ef7c507b54617d6a951d5f2b4
|
||||
RUN go get -d golang.org/x/oauth2 `#and 5 other pkgs` &&\
|
||||
(cd /go/src/golang.org/x/oauth2 && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
|
||||
|
||||
# Repo golang.org/x/text at e19ae14 (2017-12-27)
|
||||
ENV REV=e19ae1496984b1c655b8044a65c0300a3c878dd3
|
||||
RUN go get -d golang.org/x/text/secure/bidirule `#and 4 other pkgs` &&\
|
||||
(cd /go/src/golang.org/x/text && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
|
||||
|
||||
# Repo google.golang.org/api at 7d0e2d3 (2018-01-30)
|
||||
ENV REV=7d0e2d350555821bef5a5b8aecf0d12cc1def633
|
||||
RUN go get -d google.golang.org/api/gensupport `#and 9 other pkgs` &&\
|
||||
(cd /go/src/google.golang.org/api && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
|
||||
|
||||
# Repo google.golang.org/genproto at 4eb30f4 (2018-01-25)
|
||||
ENV REV=4eb30f4778eed4c258ba66527a0d4f9ec8a36c45
|
||||
RUN go get -d google.golang.org/genproto/googleapis/api/annotations `#and 3 other pkgs` &&\
|
||||
(cd /go/src/google.golang.org/genproto && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
|
||||
|
||||
# Repo google.golang.org/grpc at 0bd008f (2018-01-25)
|
||||
ENV REV=0bd008f5fadb62d228f12b18d016709e8139a7af
|
||||
RUN go get -d google.golang.org/grpc `#and 23 other pkgs` &&\
|
||||
(cd /go/src/google.golang.org/grpc && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
|
||||
|
||||
# Optimization to speed up iterative development, not necessary for correctness:
|
||||
RUN go install cloud.google.com/go/compute/metadata \
|
||||
cloud.google.com/go/iam \
|
||||
cloud.google.com/go/internal \
|
||||
cloud.google.com/go/internal/optional \
|
||||
cloud.google.com/go/internal/version \
|
||||
cloud.google.com/go/storage \
|
||||
github.com/golang/protobuf/proto \
|
||||
github.com/golang/protobuf/protoc-gen-go/descriptor \
|
||||
github.com/golang/protobuf/ptypes \
|
||||
github.com/golang/protobuf/ptypes/any \
|
||||
github.com/golang/protobuf/ptypes/duration \
|
||||
github.com/golang/protobuf/ptypes/timestamp \
|
||||
github.com/googleapis/gax-go \
|
||||
go4.org/syncutil/singleflight \
|
||||
golang.org/x/build/autocertcache \
|
||||
golang.org/x/crypto/acme \
|
||||
golang.org/x/crypto/acme/autocert \
|
||||
golang.org/x/oauth2 \
|
||||
golang.org/x/oauth2/google \
|
||||
golang.org/x/oauth2/internal \
|
||||
golang.org/x/oauth2/jws \
|
||||
golang.org/x/oauth2/jwt \
|
||||
golang.org/x/text/secure/bidirule \
|
||||
golang.org/x/text/transform \
|
||||
golang.org/x/text/unicode/bidi \
|
||||
golang.org/x/text/unicode/norm \
|
||||
google.golang.org/api/gensupport \
|
||||
google.golang.org/api/googleapi \
|
||||
google.golang.org/api/googleapi/internal/uritemplates \
|
||||
google.golang.org/api/googleapi/transport \
|
||||
google.golang.org/api/internal \
|
||||
google.golang.org/api/iterator \
|
||||
google.golang.org/api/option \
|
||||
google.golang.org/api/storage/v1 \
|
||||
google.golang.org/api/transport/http \
|
||||
google.golang.org/genproto/googleapis/api/annotations \
|
||||
google.golang.org/genproto/googleapis/iam/v1 \
|
||||
google.golang.org/genproto/googleapis/rpc/status \
|
||||
google.golang.org/grpc \
|
||||
google.golang.org/grpc/balancer \
|
||||
google.golang.org/grpc/balancer/base \
|
||||
google.golang.org/grpc/balancer/roundrobin \
|
||||
google.golang.org/grpc/codes \
|
||||
google.golang.org/grpc/connectivity \
|
||||
google.golang.org/grpc/credentials \
|
||||
google.golang.org/grpc/encoding \
|
||||
google.golang.org/grpc/encoding/proto \
|
||||
google.golang.org/grpc/grpclb/grpc_lb_v1/messages \
|
||||
google.golang.org/grpc/grpclog \
|
||||
google.golang.org/grpc/internal \
|
||||
google.golang.org/grpc/keepalive \
|
||||
google.golang.org/grpc/metadata \
|
||||
google.golang.org/grpc/naming \
|
||||
google.golang.org/grpc/peer \
|
||||
google.golang.org/grpc/resolver \
|
||||
google.golang.org/grpc/resolver/dns \
|
||||
google.golang.org/grpc/resolver/passthrough \
|
||||
google.golang.org/grpc/stats \
|
||||
google.golang.org/grpc/status \
|
||||
google.golang.org/grpc/tap \
|
||||
google.golang.org/grpc/transport
|
||||
# END deps
|
||||
|
||||
COPY . /go/src/golang.org/x/net/
|
||||
|
||||
RUN go install -tags "h2demo netgo" -ldflags "-linkmode=external -extldflags '-static -pthread'" golang.org/x/net/http2/h2demo
|
||||
|
||||
+53
-6
@@ -1,8 +1,55 @@
|
||||
h2demo.linux: h2demo.go
|
||||
GOOS=linux go build --tags=h2demo -o h2demo.linux .
|
||||
# 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.
|
||||
|
||||
MUTABLE_VERSION ?= latest
|
||||
VERSION ?= $(shell git rev-parse --short HEAD)
|
||||
|
||||
IMAGE_STAGING := gcr.io/go-dashboard-dev/h2demo
|
||||
IMAGE_PROD := gcr.io/symbolic-datum-552/h2demo
|
||||
|
||||
DOCKER_IMAGE_build0=build0/h2demo:latest
|
||||
DOCKER_CTR_build0=h2demo-build0
|
||||
|
||||
build0: *.go Dockerfile.0
|
||||
docker build --force-rm -f Dockerfile.0 --tag=$(DOCKER_IMAGE_build0) ../..
|
||||
|
||||
h2demo: build0
|
||||
docker create --name $(DOCKER_CTR_build0) $(DOCKER_IMAGE_build0)
|
||||
docker cp $(DOCKER_CTR_build0):/go/bin/$@ $@
|
||||
docker rm $(DOCKER_CTR_build0)
|
||||
|
||||
ca-certificates.crt:
|
||||
docker create --name $(DOCKER_CTR_build0) $(DOCKER_IMAGE_build0)
|
||||
docker cp $(DOCKER_CTR_build0):/etc/ssl/certs/$@ $@
|
||||
docker rm $(DOCKER_CTR_build0)
|
||||
|
||||
update-deps:
|
||||
go install golang.org/x/build/cmd/gitlock
|
||||
gitlock --update=Dockerfile.0 --ignore=golang.org/x/net --tags=h2demo golang.org/x/net/http2/h2demo
|
||||
|
||||
docker-prod: Dockerfile h2demo ca-certificates.crt
|
||||
docker build --force-rm --tag=$(IMAGE_PROD):$(VERSION) .
|
||||
docker tag $(IMAGE_PROD):$(VERSION) $(IMAGE_PROD):$(MUTABLE_VERSION)
|
||||
docker-staging: Dockerfile h2demo ca-certificates.crt
|
||||
docker build --force-rm --tag=$(IMAGE_STAGING):$(VERSION) .
|
||||
docker tag $(IMAGE_STAGING):$(VERSION) $(IMAGE_STAGING):$(MUTABLE_VERSION)
|
||||
|
||||
push-prod: docker-prod
|
||||
gcloud docker -- push $(IMAGE_PROD):$(MUTABLE_VERSION)
|
||||
gcloud docker -- push $(IMAGE_PROD):$(VERSION)
|
||||
push-staging: docker-staging
|
||||
gcloud docker -- push $(IMAGE_STAGING):$(MUTABLE_VERSION)
|
||||
gcloud docker -- push $(IMAGE_STAGING):$(VERSION)
|
||||
|
||||
deploy-prod: push-prod
|
||||
kubectl set image deployment/h2demo-deployment h2demo=$(IMAGE_PROD):$(VERSION)
|
||||
deploy-staging: push-staging
|
||||
kubectl set image deployment/h2demo-deployment h2demo=$(IMAGE_STAGING):$(VERSION)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
$(RM) h2demo
|
||||
$(RM) ca-certificates.crt
|
||||
|
||||
FORCE:
|
||||
|
||||
upload: FORCE
|
||||
go install golang.org/x/build/cmd/upload
|
||||
upload --verbose --osarch=linux-amd64 --tags=h2demo --file=go:golang.org/x/net/http2/h2demo --public http2-demo-server-tls/h2demo
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: h2demo-deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: h2demo
|
||||
annotations:
|
||||
container.seccomp.security.alpha.kubernetes.io/h2demo: docker/default
|
||||
container.apparmor.security.beta.kubernetes.io/h2demo: runtime/default
|
||||
spec:
|
||||
containers:
|
||||
- name: h2demo
|
||||
image: gcr.io/symbolic-datum-552/h2demo:latest
|
||||
imagePullPolicy: Always
|
||||
command: ["/h2demo", "-prod"]
|
||||
ports:
|
||||
- containerPort: 80
|
||||
- containerPort: 443
|
||||
resources:
|
||||
requests:
|
||||
cpu: "1"
|
||||
memory: "1Gi"
|
||||
limits:
|
||||
memory: "2Gi"
|
||||
+19
-14
@@ -8,6 +8,7 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
"fmt"
|
||||
@@ -19,7 +20,6 @@ import (
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"runtime"
|
||||
@@ -28,7 +28,9 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/storage"
|
||||
"go4.org/syncutil/singleflight"
|
||||
"golang.org/x/build/autocertcache"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
@@ -426,19 +428,10 @@ func httpHost() string {
|
||||
}
|
||||
}
|
||||
|
||||
func serveProdTLS() error {
|
||||
const cacheDir = "/var/cache/autocert"
|
||||
if err := os.MkdirAll(cacheDir, 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
m := autocert.Manager{
|
||||
Cache: autocert.DirCache(cacheDir),
|
||||
Prompt: autocert.AcceptTOS,
|
||||
HostPolicy: autocert.HostWhitelist("http2.golang.org"),
|
||||
}
|
||||
func serveProdTLS(autocertManager *autocert.Manager) error {
|
||||
srv := &http.Server{
|
||||
TLSConfig: &tls.Config{
|
||||
GetCertificate: m.GetCertificate,
|
||||
GetCertificate: autocertManager.GetCertificate,
|
||||
},
|
||||
}
|
||||
http2.ConfigureServer(srv, &http2.Server{
|
||||
@@ -468,9 +461,21 @@ func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
|
||||
}
|
||||
|
||||
func serveProd() error {
|
||||
log.Printf("running in production mode")
|
||||
|
||||
storageClient, err := storage.NewClient(context.Background())
|
||||
if err != nil {
|
||||
log.Fatalf("storage.NewClient: %v", err)
|
||||
}
|
||||
autocertManager := &autocert.Manager{
|
||||
Prompt: autocert.AcceptTOS,
|
||||
HostPolicy: autocert.HostWhitelist("http2.golang.org"),
|
||||
Cache: autocertcache.NewGoogleCloudStorageCache(storageClient, "golang-h2demo-autocert"),
|
||||
}
|
||||
|
||||
errc := make(chan error, 2)
|
||||
go func() { errc <- http.ListenAndServe(":80", nil) }()
|
||||
go func() { errc <- serveProdTLS() }()
|
||||
go func() { errc <- http.ListenAndServe(":80", autocertManager.HTTPHandler(http.DefaultServeMux)) }()
|
||||
go func() { errc <- serveProdTLS(autocertManager) }()
|
||||
return <-errc
|
||||
}
|
||||
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: h2demo
|
||||
spec:
|
||||
externalTrafficPolicy: Local
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
name: http
|
||||
- port: 443
|
||||
targetPort: 443
|
||||
name: https
|
||||
selector:
|
||||
app: h2demo
|
||||
type: LoadBalancer
|
||||
loadBalancerIP: 130.211.116.44
|
||||
+1
-1
@@ -206,7 +206,7 @@ func appendVarInt(dst []byte, n byte, i uint64) []byte {
|
||||
}
|
||||
|
||||
// appendHpackString appends s, as encoded in "String Literal"
|
||||
// representation, to dst and returns the the extended buffer.
|
||||
// representation, to dst and returns the extended buffer.
|
||||
//
|
||||
// s will be encoded in Huffman codes only when it produces strictly
|
||||
// shorter byte string.
|
||||
|
||||
+1
-1
@@ -312,7 +312,7 @@ func mustUint31(v int32) uint32 {
|
||||
}
|
||||
|
||||
// bodyAllowedForStatus reports whether a given response status code
|
||||
// permits a body. See RFC 2616, section 4.4.
|
||||
// permits a body. See RFC 7230, section 3.3.
|
||||
func bodyAllowedForStatus(status int) bool {
|
||||
switch {
|
||||
case status >= 100 && status <= 199:
|
||||
|
||||
+4
-4
@@ -406,7 +406,7 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
||||
// addresses during development.
|
||||
//
|
||||
// TODO: optionally enforce? Or enforce at the time we receive
|
||||
// a new request, and verify the the ServerName matches the :authority?
|
||||
// a new request, and verify the ServerName matches the :authority?
|
||||
// But that precludes proxy situations, perhaps.
|
||||
//
|
||||
// So for now, do nothing here again.
|
||||
@@ -2285,7 +2285,7 @@ func (rws *responseWriterState) hasTrailers() bool { return len(rws.trailers) !=
|
||||
func (rws *responseWriterState) declareTrailer(k string) {
|
||||
k = http.CanonicalHeaderKey(k)
|
||||
if !ValidTrailerHeader(k) {
|
||||
// Forbidden by RFC 2616 14.40.
|
||||
// Forbidden by RFC 7230, section 4.1.2.
|
||||
rws.conn.logf("ignoring invalid trailer %q", k)
|
||||
return
|
||||
}
|
||||
@@ -2406,7 +2406,7 @@ const TrailerPrefix = "Trailer:"
|
||||
// after the header has already been flushed. Because the Go
|
||||
// ResponseWriter interface has no way to set Trailers (only the
|
||||
// Header), and because we didn't want to expand the ResponseWriter
|
||||
// interface, and because nobody used trailers, and because RFC 2616
|
||||
// interface, and because nobody used trailers, and because RFC 7230
|
||||
// says you SHOULD (but not must) predeclare any trailers in the
|
||||
// header, the official ResponseWriter rules said trailers in Go must
|
||||
// be predeclared, and then we reuse the same ResponseWriter.Header()
|
||||
@@ -2790,7 +2790,7 @@ func (sc *serverConn) startPush(msg *startPushRequest) {
|
||||
}
|
||||
|
||||
// foreachHeaderElement splits v according to the "#rule" construction
|
||||
// in RFC 2616 section 2.1 and calls fn for each non-empty element.
|
||||
// in RFC 7230 section 7 and calls fn for each non-empty element.
|
||||
func foreachHeaderElement(v string, fn func(string)) {
|
||||
v = textproto.TrimString(v)
|
||||
if v == "" {
|
||||
|
||||
+7
-7
@@ -2877,9 +2877,9 @@ func testServerWritesTrailers(t *testing.T, withFlush bool) {
|
||||
w.Header().Set("Trailer:post-header-trailer2", "hi2")
|
||||
w.Header().Set("Trailer:Range", "invalid")
|
||||
w.Header().Set("Trailer:Foo\x01Bogus", "invalid")
|
||||
w.Header().Set("Transfer-Encoding", "should not be included; Forbidden by RFC 2616 14.40")
|
||||
w.Header().Set("Content-Length", "should not be included; Forbidden by RFC 2616 14.40")
|
||||
w.Header().Set("Trailer", "should not be included; Forbidden by RFC 2616 14.40")
|
||||
w.Header().Set("Transfer-Encoding", "should not be included; Forbidden by RFC 7230 4.1.2")
|
||||
w.Header().Set("Content-Length", "should not be included; Forbidden by RFC 7230 4.1.2")
|
||||
w.Header().Set("Trailer", "should not be included; Forbidden by RFC 7230 4.1.2")
|
||||
return nil
|
||||
}, func(st *serverTester) {
|
||||
getSlash(st)
|
||||
@@ -2971,7 +2971,7 @@ func BenchmarkServerGets(b *testing.B) {
|
||||
defer st.Close()
|
||||
st.greet()
|
||||
|
||||
// Give the server quota to reply. (plus it has the the 64KB)
|
||||
// Give the server quota to reply. (plus it has the 64KB)
|
||||
if err := st.fr.WriteWindowUpdate(0, uint32(b.N*len(msg))); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
@@ -3009,7 +3009,7 @@ func BenchmarkServerPosts(b *testing.B) {
|
||||
defer st.Close()
|
||||
st.greet()
|
||||
|
||||
// Give the server quota to reply. (plus it has the the 64KB)
|
||||
// Give the server quota to reply. (plus it has the 64KB)
|
||||
if err := st.fr.WriteWindowUpdate(0, uint32(b.N*len(msg))); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
@@ -3316,7 +3316,7 @@ func BenchmarkServer_GetRequest(b *testing.B) {
|
||||
defer st.Close()
|
||||
|
||||
st.greet()
|
||||
// Give the server quota to reply. (plus it has the the 64KB)
|
||||
// Give the server quota to reply. (plus it has the 64KB)
|
||||
if err := st.fr.WriteWindowUpdate(0, uint32(b.N*len(msg))); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
@@ -3347,7 +3347,7 @@ func BenchmarkServer_PostRequest(b *testing.B) {
|
||||
})
|
||||
defer st.Close()
|
||||
st.greet()
|
||||
// Give the server quota to reply. (plus it has the the 64KB)
|
||||
// Give the server quota to reply. (plus it has the 64KB)
|
||||
if err := st.fr.WriteWindowUpdate(0, uint32(b.N*len(msg))); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
+1
-1
@@ -1693,7 +1693,7 @@ func TestTransportChecksResponseHeaderListSize(t *testing.T) {
|
||||
ct.run()
|
||||
}
|
||||
|
||||
// Test that the the Transport returns a typed error from Response.Body.Read calls
|
||||
// Test that the Transport returns a typed error from Response.Body.Read calls
|
||||
// when the server sends an error. (here we use a panic, since that should generate
|
||||
// a stream error, but others like cancel should be similar)
|
||||
func TestTransportBodyReadErrorType(t *testing.T) {
|
||||
|
||||
Generated
Vendored
+108
-84
@@ -21,102 +21,102 @@ import (
|
||||
"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 {
|
||||
type diagTest struct {
|
||||
network, address string
|
||||
protocol int
|
||||
mtype icmp.Type
|
||||
m icmp.Message
|
||||
}
|
||||
|
||||
var nonPrivilegedPingTests = []pingTest{
|
||||
{"udp4", "0.0.0.0", iana.ProtocolICMP, ipv4.ICMPTypeEcho},
|
||||
|
||||
{"udp6", "::", iana.ProtocolIPv6ICMP, ipv6.ICMPTypeEchoRequest},
|
||||
}
|
||||
|
||||
func TestNonPrivilegedPing(t *testing.T) {
|
||||
func TestDiag(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)
|
||||
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"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
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)
|
||||
{
|
||||
"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.Log(m)
|
||||
return
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func doPing(tt pingTest, seq int) error {
|
||||
c, err := icmp.ListenPacket(tt.network, tt.address)
|
||||
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, tt.protocol)
|
||||
dst, err := googleAddr(c, dt.protocol)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if tt.network != "udp6" && tt.protocol == iana.ProtocolIPv6ICMP {
|
||||
if dt.network != "udp6" && dt.protocol == iana.ProtocolIPv6ICMP {
|
||||
var f ipv6.ICMPFilter
|
||||
f.SetAll(true)
|
||||
f.Accept(ipv6.ICMPTypeDestinationUnreachable)
|
||||
@@ -129,14 +129,11 @@ func doPing(tt pingTest, seq int) error {
|
||||
}
|
||||
}
|
||||
|
||||
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"),
|
||||
},
|
||||
switch m := dt.m.Body.(type) {
|
||||
case *icmp.Echo:
|
||||
m.Seq = 1 << uint(seq)
|
||||
}
|
||||
wb, err := wm.Marshal(nil)
|
||||
wb, err := dt.m.Marshal(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -154,18 +151,45 @@ func doPing(tt pingTest, seq int) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rm, err := icmp.ParseMessage(tt.protocol, rb[:n])
|
||||
rm, err := icmp.ParseMessage(dt.protocol, rb[:n])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch rm.Type {
|
||||
case ipv4.ICMPTypeEchoReply, ipv6.ICMPTypeEchoReply:
|
||||
switch {
|
||||
case dt.m.Type == ipv4.ICMPTypeEcho && rm.Type == ipv4.ICMPTypeEchoReply:
|
||||
fallthrough
|
||||
case dt.m.Type == ipv6.ICMPTypeEchoRequest && rm.Type == ipv6.ICMPTypeEchoReply:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("got %+v from %v; want 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")
|
||||
+188
-208
@@ -5,6 +5,7 @@
|
||||
package icmp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
@@ -12,185 +13,16 @@ import (
|
||||
"golang.org/x/net/internal/iana"
|
||||
)
|
||||
|
||||
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) {
|
||||
for i, tt := range marshalAndParseExtensionTests {
|
||||
for j, ext := range tt.exts {
|
||||
var err error
|
||||
var b []byte
|
||||
switch ext := ext.(type) {
|
||||
case *MPLSLabelStack:
|
||||
b, err = ext.Marshal(tt.proto)
|
||||
if err != nil {
|
||||
t.Errorf("#%v/%v: %v", i, j, err)
|
||||
continue
|
||||
}
|
||||
case *InterfaceInfo:
|
||||
b, err = ext.Marshal(tt.proto)
|
||||
if err != nil {
|
||||
t.Errorf("#%v/%v: %v", i, j, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if !reflect.DeepEqual(b, tt.obj) {
|
||||
t.Errorf("#%v/%v: got %#v; want %#v", i, j, b, tt.obj)
|
||||
continue
|
||||
}
|
||||
fn := func(t *testing.T, proto int, hdr, obj []byte, te Extension) error {
|
||||
b, err := te.Marshal(proto)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for j, wire := range []struct {
|
||||
if !reflect.DeepEqual(b, obj) {
|
||||
return fmt.Errorf("got %#v; want %#v", b, obj)
|
||||
}
|
||||
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
|
||||
@@ -203,55 +35,203 @@ func TestMarshalAndParseExtension(t *testing.T) {
|
||||
{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, 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(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},
|
||||
{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(wire.data, wire.inlattr)
|
||||
if err != wire.err {
|
||||
t.Errorf("#%v/%v: got %v; want %v", i, j, err, wire.err)
|
||||
continue
|
||||
return fmt.Errorf("#%d: got %v; want %v", i, err, wire.err)
|
||||
}
|
||||
if wire.err != nil {
|
||||
continue
|
||||
}
|
||||
if l != wire.outlattr {
|
||||
t.Errorf("#%v/%v: got %v; want %v", i, j, l, wire.outlattr)
|
||||
return fmt.Errorf("#%d: got %d; want %d", i, 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
|
||||
if !reflect.DeepEqual(exts, []Extension{te}) {
|
||||
return fmt.Errorf("#%d: got %#v; want %#v", i, exts[0], te)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var parseInterfaceNameTests = []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},
|
||||
t.Run("MPLSLabelStack", func(t *testing.T) {
|
||||
for _, et := range []struct {
|
||||
proto int
|
||||
hdr []byte
|
||||
obj []byte
|
||||
ext Extension
|
||||
}{
|
||||
// MPLS label stack with no label
|
||||
{
|
||||
proto: iana.ProtocolICMP,
|
||||
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,
|
||||
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,
|
||||
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.hdr, et.obj, et.ext); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
t.Run("InterfaceInfo", func(t *testing.T) {
|
||||
for _, et := range []struct {
|
||||
proto int
|
||||
hdr []byte
|
||||
obj []byte
|
||||
ext Extension
|
||||
}{
|
||||
// Interface information with no attribute
|
||||
{
|
||||
proto: iana.ProtocolICMP,
|
||||
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,
|
||||
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,
|
||||
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.hdr, et.obj, et.ext); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestParseInterfaceName(t *testing.T) {
|
||||
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 {
|
||||
t.Errorf("#%d: got %v; want %v", i, err, tt.error)
|
||||
}
|
||||
|
||||
+55
-63
@@ -15,69 +15,61 @@ import (
|
||||
"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) {
|
||||
tt := &ipv4HeaderLittleEndianTest
|
||||
if socket.NativeEndian != binary.LittleEndian {
|
||||
t.Skip("no test for non-little endian machine yet")
|
||||
}
|
||||
|
||||
var wh []byte
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
wh = tt.wireHeaderFromTradBSDKernel[:]
|
||||
case "freebsd":
|
||||
if freebsdVersion >= 1000000 {
|
||||
wh = tt.wireHeaderFromKernel[:]
|
||||
} else {
|
||||
wh = tt.wireHeaderFromTradBSDKernel[:]
|
||||
}
|
||||
default:
|
||||
wh = tt.wireHeaderFromKernel[:]
|
||||
}
|
||||
h, err := ParseIPv4Header(wh)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(h, tt.Header) {
|
||||
t.Fatalf("got %#v; want %#v", h, tt.Header)
|
||||
switch socket.NativeEndian {
|
||||
case binary.LittleEndian:
|
||||
t.Run("LittleEndian", func(t *testing.T) {
|
||||
// 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,
|
||||
}
|
||||
th := &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),
|
||||
}
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+109
-112
@@ -15,120 +15,117 @@ import (
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
var marshalAndParseMessageForIPv4Tests = []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.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)
|
||||
func TestMarshalAndParseMessage(t *testing.T) {
|
||||
fn := func(t *testing.T, proto int, tms []icmp.Message) {
|
||||
var pshs [][]byte
|
||||
switch proto {
|
||||
case iana.ProtocolICMP:
|
||||
pshs = [][]byte{nil}
|
||||
case iana.ProtocolIPv6ICMP:
|
||||
pshs = [][]byte{
|
||||
icmp.IPv6PseudoHeader(net.ParseIP("fe80::1"), net.ParseIP("ff02::1")),
|
||||
nil,
|
||||
}
|
||||
m, err := icmp.ParseMessage(iana.ProtocolIPv6ICMP, 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)
|
||||
}
|
||||
for i, tm := range tms {
|
||||
for _, psh := range pshs {
|
||||
b, err := tm.Marshal(psh)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m, err := icmp.ParseMessage(proto, b)
|
||||
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.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.ICMPTypeDuplicateAddressConfirmation,
|
||||
Body: &icmp.DefaultMessageBody{
|
||||
Data: []byte{0x80, 0x40, 0x20, 0x10},
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
+367
-366
@@ -5,6 +5,7 @@
|
||||
package icmp_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
@@ -16,425 +17,425 @@ import (
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
var marshalAndParseMultipartMessageForIPv4Tests = []icmp.Message{
|
||||
{
|
||||
Type: ipv4.ICMPTypeDestinationUnreachable, Code: 15,
|
||||
Body: &icmp.DstUnreach{
|
||||
Data: []byte("ERROR-INVOKING-PACKET"),
|
||||
Extensions: []icmp.Extension{
|
||||
&icmp.MPLSLabelStack{
|
||||
Class: 1,
|
||||
Type: 1,
|
||||
Labels: []icmp.MPLSLabel{
|
||||
{
|
||||
Label: 16014,
|
||||
TC: 0x4,
|
||||
S: true,
|
||||
TTL: 255,
|
||||
},
|
||||
},
|
||||
},
|
||||
&icmp.InterfaceInfo{
|
||||
Class: 2,
|
||||
Type: 0x0f,
|
||||
Interface: &net.Interface{
|
||||
Index: 15,
|
||||
Name: "en101",
|
||||
MTU: 8192,
|
||||
},
|
||||
Addr: &net.IPAddr{
|
||||
IP: net.IPv4(192, 168, 0, 1).To4(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: ipv4.ICMPTypeTimeExceeded, Code: 1,
|
||||
Body: &icmp.TimeExceeded{
|
||||
Data: []byte("ERROR-INVOKING-PACKET"),
|
||||
Extensions: []icmp.Extension{
|
||||
&icmp.InterfaceInfo{
|
||||
Class: 2,
|
||||
Type: 0x0f,
|
||||
Interface: &net.Interface{
|
||||
Index: 15,
|
||||
Name: "en101",
|
||||
MTU: 8192,
|
||||
},
|
||||
Addr: &net.IPAddr{
|
||||
IP: net.IPv4(192, 168, 0, 1).To4(),
|
||||
},
|
||||
},
|
||||
&icmp.MPLSLabelStack{
|
||||
Class: 1,
|
||||
Type: 1,
|
||||
Labels: []icmp.MPLSLabel{
|
||||
{
|
||||
Label: 16014,
|
||||
TC: 0x4,
|
||||
S: true,
|
||||
TTL: 255,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: ipv4.ICMPTypeParameterProblem, Code: 2,
|
||||
Body: &icmp.ParamProb{
|
||||
Pointer: 8,
|
||||
Data: []byte("ERROR-INVOKING-PACKET"),
|
||||
Extensions: []icmp.Extension{
|
||||
&icmp.MPLSLabelStack{
|
||||
Class: 1,
|
||||
Type: 1,
|
||||
Labels: []icmp.MPLSLabel{
|
||||
{
|
||||
Label: 16014,
|
||||
TC: 0x4,
|
||||
S: true,
|
||||
TTL: 255,
|
||||
},
|
||||
},
|
||||
},
|
||||
&icmp.InterfaceInfo{
|
||||
Class: 2,
|
||||
Type: 0x0f,
|
||||
Interface: &net.Interface{
|
||||
Index: 15,
|
||||
Name: "en101",
|
||||
MTU: 8192,
|
||||
},
|
||||
Addr: &net.IPAddr{
|
||||
IP: net.IPv4(192, 168, 0, 1).To4(),
|
||||
},
|
||||
},
|
||||
&icmp.InterfaceInfo{
|
||||
Class: 2,
|
||||
Type: 0x2f,
|
||||
Interface: &net.Interface{
|
||||
Index: 16,
|
||||
Name: "en102",
|
||||
MTU: 8192,
|
||||
},
|
||||
Addr: &net.IPAddr{
|
||||
IP: net.IPv4(192, 168, 0, 2).To4(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestMarshalAndParseMultipartMessageForIPv4(t *testing.T) {
|
||||
for i, tt := range marshalAndParseMultipartMessageForIPv4Tests {
|
||||
b, err := tt.Marshal(nil)
|
||||
func TestMarshalAndParseMultipartMessage(t *testing.T) {
|
||||
fn := func(t *testing.T, proto int, tm icmp.Message) error {
|
||||
b, err := tm.Marshal(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return err
|
||||
}
|
||||
if b[5] != 32 {
|
||||
t.Errorf("#%v: got %v; want 32", i, b[5])
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
return err
|
||||
}
|
||||
if m.Type != tt.Type || m.Code != tt.Code {
|
||||
t.Errorf("#%v: got %v; want %v", i, m, &tt)
|
||||
if m.Type != tm.Type || m.Code != tm.Code {
|
||||
return fmt.Errorf("got %v; want %v", m, &tm)
|
||||
}
|
||||
switch m.Type {
|
||||
case ipv4.ICMPTypeDestinationUnreachable:
|
||||
got, want := m.Body.(*icmp.DstUnreach), tt.Body.(*icmp.DstUnreach)
|
||||
got, want := m.Body.(*icmp.DstUnreach), tm.Body.(*icmp.DstUnreach)
|
||||
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 {
|
||||
t.Errorf("#%v: got %v; want 128", i, len(got.Data))
|
||||
return fmt.Errorf("got %d; want 128", len(got.Data))
|
||||
}
|
||||
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) {
|
||||
t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
|
||||
return errors.New(dumpExtensions(got.Extensions, want.Extensions))
|
||||
}
|
||||
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:
|
||||
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) {
|
||||
t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
|
||||
return errors.New(dumpExtensions(got.Extensions, want.Extensions))
|
||||
}
|
||||
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{
|
||||
{
|
||||
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,
|
||||
t.Run("IPv4", func(t *testing.T) {
|
||||
for i, tm := range []icmp.Message{
|
||||
{
|
||||
Type: ipv4.ICMPTypeDestinationUnreachable, Code: 15,
|
||||
Body: &icmp.DstUnreach{
|
||||
Data: []byte("ERROR-INVOKING-PACKET"),
|
||||
Extensions: []icmp.Extension{
|
||||
&icmp.MPLSLabelStack{
|
||||
Class: 1,
|
||||
Type: 1,
|
||||
Labels: []icmp.MPLSLabel{
|
||||
{
|
||||
Label: 16014,
|
||||
TC: 0x4,
|
||||
S: true,
|
||||
TTL: 255,
|
||||
},
|
||||
},
|
||||
},
|
||||
&icmp.InterfaceInfo{
|
||||
Class: 2,
|
||||
Type: 0x0f,
|
||||
Interface: &net.Interface{
|
||||
Index: 15,
|
||||
Name: "en101",
|
||||
MTU: 8192,
|
||||
},
|
||||
Addr: &net.IPAddr{
|
||||
IP: net.IPv4(192, 168, 0, 1).To4(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&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,
|
||||
{
|
||||
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,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&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: 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 TestMarshalAndParseMultipartMessageForIPv6(t *testing.T) {
|
||||
pshicmp := icmp.IPv6PseudoHeader(net.ParseIP("fe80::1"), net.ParseIP("ff02::1"))
|
||||
for i, tt := range marshalAndParseMultipartMessageForIPv6Tests {
|
||||
for _, psh := range [][]byte{pshicmp, nil} {
|
||||
b, err := tt.Marshal(psh)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b[4] != 16 {
|
||||
t.Errorf("#%v: got %v; want 16", i, b[4])
|
||||
}
|
||||
m, err := icmp.ParseMessage(iana.ProtocolIPv6ICMP, 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)
|
||||
}
|
||||
switch m.Type {
|
||||
case ipv6.ICMPTypeDestinationUnreachable:
|
||||
got, want := m.Body.(*icmp.DstUnreach), tt.Body.(*icmp.DstUnreach)
|
||||
if !reflect.DeepEqual(got.Extensions, want.Extensions) {
|
||||
t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
|
||||
}
|
||||
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)
|
||||
if !reflect.DeepEqual(got.Extensions, want.Extensions) {
|
||||
t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
|
||||
}
|
||||
if len(got.Data) != 128 {
|
||||
t.Errorf("#%v: got %v; want 128", i, len(got.Data))
|
||||
}
|
||||
} {
|
||||
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",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
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
|
||||
for j, got := range gotExts {
|
||||
for i, got := range gotExts {
|
||||
switch got := got.(type) {
|
||||
case *icmp.MPLSLabelStack:
|
||||
want := wantExts[j].(*icmp.MPLSLabelStack)
|
||||
want := wantExts[i].(*icmp.MPLSLabelStack)
|
||||
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:
|
||||
want := wantExts[j].(*icmp.InterfaceInfo)
|
||||
want := wantExts[i].(*icmp.InterfaceInfo)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(s) == 0 {
|
||||
return "<nil>"
|
||||
}
|
||||
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) {
|
||||
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
|
||||
},
|
||||
} {
|
||||
if out := tt.in.Len(tt.proto); out != tt.out {
|
||||
t.Errorf("#%d: got %d; want %d", i, out, tt.out)
|
||||
}
|
||||
|
||||
+49
-2
@@ -1,5 +1,5 @@
|
||||
// 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 // import "golang.org/x/net/internal/iana"
|
||||
@@ -38,7 +38,7 @@ const (
|
||||
CongestionExperienced = 0x3 // CE (Congestion Experienced)
|
||||
)
|
||||
|
||||
// Protocol Numbers, Updated: 2016-06-22
|
||||
// Protocol Numbers, Updated: 2017-10-13
|
||||
const (
|
||||
ProtocolIP = 0 // IPv4 encapsulation, pseudo protocol number
|
||||
ProtocolHOPOPT = 0 // IPv6 Hop-by-Hop Option
|
||||
@@ -178,3 +178,50 @@ const (
|
||||
ProtocolROHC = 142 // Robust Header Compression
|
||||
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
|
||||
)
|
||||
|
||||
+98
-4
@@ -28,23 +28,27 @@ var registries = []struct {
|
||||
parse func(io.Writer, io.Reader) error
|
||||
}{
|
||||
{
|
||||
"http://www.iana.org/assignments/dscp-registry/dscp-registry.xml",
|
||||
"https://www.iana.org/assignments/dscp-registry/dscp-registry.xml",
|
||||
parseDSCPRegistry,
|
||||
},
|
||||
{
|
||||
"http://www.iana.org/assignments/ipv4-tos-byte/ipv4-tos-byte.xml",
|
||||
"https://www.iana.org/assignments/ipv4-tos-byte/ipv4-tos-byte.xml",
|
||||
parseTOSTCByte,
|
||||
},
|
||||
{
|
||||
"http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml",
|
||||
"https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml",
|
||||
parseProtocolNumbers,
|
||||
},
|
||||
{
|
||||
"http://www.iana.org/assignments/address-family-numbers/address-family-numbers.xml",
|
||||
parseAddrFamilyNumbers,
|
||||
},
|
||||
}
|
||||
|
||||
func main() {
|
||||
var bb bytes.Buffer
|
||||
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 // import "golang.org/x/net/internal/iana"`+"\n\n")
|
||||
for _, r := range registries {
|
||||
@@ -291,3 +295,93 @@ func (pn *protocolNumbers) escape() []canonProtocolRecord {
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
+6
@@ -26,6 +26,11 @@ type msghdr struct {
|
||||
Flags int32
|
||||
}
|
||||
|
||||
type mmsghdr struct {
|
||||
Hdr msghdr
|
||||
Len uint32
|
||||
}
|
||||
|
||||
type cmsghdr struct {
|
||||
Len uint32
|
||||
Level int32
|
||||
@@ -52,6 +57,7 @@ type sockaddrInet6 struct {
|
||||
const (
|
||||
sizeofIovec = 0x8
|
||||
sizeofMsghdr = 0x1c
|
||||
sizeofMmsghdr = 0x20
|
||||
sizeofCmsghdr = 0xc
|
||||
|
||||
sizeofSockaddrInet = 0x10
|
||||
|
||||
+2
-2
@@ -72,7 +72,7 @@ var registries = []struct {
|
||||
parse func(io.Writer, io.Reader) error
|
||||
}{
|
||||
{
|
||||
"http://www.iana.org/assignments/icmp-parameters/icmp-parameters.xml",
|
||||
"https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xml",
|
||||
parseICMPv4Parameters,
|
||||
},
|
||||
}
|
||||
@@ -80,7 +80,7 @@ var registries = []struct {
|
||||
func geniana() error {
|
||||
var bb bytes.Buffer
|
||||
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")
|
||||
for _, r := range registries {
|
||||
resp, err := http.Get(r.url)
|
||||
|
||||
+7
-3
@@ -1,9 +1,9 @@
|
||||
// go generate gen.go
|
||||
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
// Code generated by the command above; DO NOT EDIT.
|
||||
|
||||
package ipv4
|
||||
|
||||
// Internet Control Message Protocol (ICMP) Parameters, Updated: 2013-04-19
|
||||
// Internet Control Message Protocol (ICMP) Parameters, Updated: 2018-02-26
|
||||
const (
|
||||
ICMPTypeEchoReply ICMPType = 0 // Echo Reply
|
||||
ICMPTypeDestinationUnreachable ICMPType = 3 // Destination Unreachable
|
||||
@@ -16,9 +16,11 @@ const (
|
||||
ICMPTypeTimestamp ICMPType = 13 // Timestamp
|
||||
ICMPTypeTimestampReply ICMPType = 14 // Timestamp Reply
|
||||
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{
|
||||
0: "echo reply",
|
||||
3: "destination unreachable",
|
||||
@@ -31,4 +33,6 @@ var icmpTypes = map[ICMPType]string{
|
||||
13: "timestamp",
|
||||
14: "timestamp reply",
|
||||
40: "photuris",
|
||||
42: "extended echo request",
|
||||
43: "extended echo reply",
|
||||
}
|
||||
|
||||
+2
-2
@@ -72,7 +72,7 @@ var registries = []struct {
|
||||
parse func(io.Writer, io.Reader) error
|
||||
}{
|
||||
{
|
||||
"http://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xml",
|
||||
"https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xml",
|
||||
parseICMPv6Parameters,
|
||||
},
|
||||
}
|
||||
@@ -80,7 +80,7 @@ var registries = []struct {
|
||||
func geniana() error {
|
||||
var bb bytes.Buffer
|
||||
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")
|
||||
for _, r := range registries {
|
||||
resp, err := http.Get(r.url)
|
||||
|
||||
+7
-3
@@ -1,9 +1,9 @@
|
||||
// go generate gen.go
|
||||
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
// Code generated by the command above; DO NOT EDIT.
|
||||
|
||||
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 (
|
||||
ICMPTypeDestinationUnreachable ICMPType = 1 // Destination Unreachable
|
||||
ICMPTypePacketTooBig ICMPType = 2 // Packet Too Big
|
||||
@@ -40,9 +40,11 @@ const (
|
||||
ICMPTypeDuplicateAddressRequest ICMPType = 157 // Duplicate Address Request
|
||||
ICMPTypeDuplicateAddressConfirmation ICMPType = 158 // Duplicate Address Confirmation
|
||||
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{
|
||||
1: "destination unreachable",
|
||||
2: "packet too big",
|
||||
@@ -79,4 +81,6 @@ var icmpTypes = map[ICMPType]string{
|
||||
157: "duplicate address request",
|
||||
158: "duplicate address confirmation",
|
||||
159: "mpl control message",
|
||||
160: "extended echo request",
|
||||
161: "extended echo reply",
|
||||
}
|
||||
|
||||
+9449
-9334
File diff suppressed because it is too large
Load Diff
+221
-18
@@ -302,32 +302,78 @@ var rules = [...]string{
|
||||
"bo",
|
||||
"com.bo",
|
||||
"edu.bo",
|
||||
"gov.bo",
|
||||
"gob.bo",
|
||||
"int.bo",
|
||||
"org.bo",
|
||||
"net.bo",
|
||||
"mil.bo",
|
||||
"tv.bo",
|
||||
"web.bo",
|
||||
"academia.bo",
|
||||
"agro.bo",
|
||||
"arte.bo",
|
||||
"blog.bo",
|
||||
"bolivia.bo",
|
||||
"ciencia.bo",
|
||||
"cooperativa.bo",
|
||||
"democracia.bo",
|
||||
"deporte.bo",
|
||||
"ecologia.bo",
|
||||
"economia.bo",
|
||||
"empresa.bo",
|
||||
"indigena.bo",
|
||||
"industria.bo",
|
||||
"info.bo",
|
||||
"medicina.bo",
|
||||
"movimiento.bo",
|
||||
"musica.bo",
|
||||
"natural.bo",
|
||||
"nombre.bo",
|
||||
"noticias.bo",
|
||||
"patria.bo",
|
||||
"politica.bo",
|
||||
"profesional.bo",
|
||||
"plurinacional.bo",
|
||||
"pueblo.bo",
|
||||
"revista.bo",
|
||||
"salud.bo",
|
||||
"tecnologia.bo",
|
||||
"tksat.bo",
|
||||
"transporte.bo",
|
||||
"wiki.bo",
|
||||
"br",
|
||||
"9guacu.br",
|
||||
"abc.br",
|
||||
"adm.br",
|
||||
"adv.br",
|
||||
"agr.br",
|
||||
"aju.br",
|
||||
"am.br",
|
||||
"anani.br",
|
||||
"aparecida.br",
|
||||
"arq.br",
|
||||
"art.br",
|
||||
"ato.br",
|
||||
"b.br",
|
||||
"belem.br",
|
||||
"bhz.br",
|
||||
"bio.br",
|
||||
"blog.br",
|
||||
"bmd.br",
|
||||
"boavista.br",
|
||||
"bsb.br",
|
||||
"campinagrande.br",
|
||||
"campinas.br",
|
||||
"caxias.br",
|
||||
"cim.br",
|
||||
"cng.br",
|
||||
"cnt.br",
|
||||
"com.br",
|
||||
"contagem.br",
|
||||
"coop.br",
|
||||
"cri.br",
|
||||
"cuiaba.br",
|
||||
"curitiba.br",
|
||||
"def.br",
|
||||
"ecn.br",
|
||||
"eco.br",
|
||||
@@ -338,14 +384,18 @@ var rules = [...]string{
|
||||
"etc.br",
|
||||
"eti.br",
|
||||
"far.br",
|
||||
"feira.br",
|
||||
"flog.br",
|
||||
"floripa.br",
|
||||
"fm.br",
|
||||
"fnd.br",
|
||||
"fortal.br",
|
||||
"fot.br",
|
||||
"foz.br",
|
||||
"fst.br",
|
||||
"g12.br",
|
||||
"ggf.br",
|
||||
"goiania.br",
|
||||
"gov.br",
|
||||
"ac.gov.br",
|
||||
"al.gov.br",
|
||||
@@ -374,42 +424,72 @@ var rules = [...]string{
|
||||
"se.gov.br",
|
||||
"sp.gov.br",
|
||||
"to.gov.br",
|
||||
"gru.br",
|
||||
"imb.br",
|
||||
"ind.br",
|
||||
"inf.br",
|
||||
"jab.br",
|
||||
"jampa.br",
|
||||
"jdf.br",
|
||||
"joinville.br",
|
||||
"jor.br",
|
||||
"jus.br",
|
||||
"leg.br",
|
||||
"lel.br",
|
||||
"londrina.br",
|
||||
"macapa.br",
|
||||
"maceio.br",
|
||||
"manaus.br",
|
||||
"maringa.br",
|
||||
"mat.br",
|
||||
"med.br",
|
||||
"mil.br",
|
||||
"morena.br",
|
||||
"mp.br",
|
||||
"mus.br",
|
||||
"natal.br",
|
||||
"net.br",
|
||||
"niteroi.br",
|
||||
"*.nom.br",
|
||||
"not.br",
|
||||
"ntr.br",
|
||||
"odo.br",
|
||||
"org.br",
|
||||
"osasco.br",
|
||||
"palmas.br",
|
||||
"poa.br",
|
||||
"ppg.br",
|
||||
"pro.br",
|
||||
"psc.br",
|
||||
"psi.br",
|
||||
"pvh.br",
|
||||
"qsl.br",
|
||||
"radio.br",
|
||||
"rec.br",
|
||||
"recife.br",
|
||||
"ribeirao.br",
|
||||
"rio.br",
|
||||
"riobranco.br",
|
||||
"riopreto.br",
|
||||
"salvador.br",
|
||||
"sampa.br",
|
||||
"santamaria.br",
|
||||
"santoandre.br",
|
||||
"saobernardo.br",
|
||||
"saogonca.br",
|
||||
"sjc.br",
|
||||
"slg.br",
|
||||
"slz.br",
|
||||
"sorocaba.br",
|
||||
"srv.br",
|
||||
"taxi.br",
|
||||
"teo.br",
|
||||
"the.br",
|
||||
"tmp.br",
|
||||
"trd.br",
|
||||
"tur.br",
|
||||
"tv.br",
|
||||
"udi.br",
|
||||
"vet.br",
|
||||
"vix.br",
|
||||
"vlog.br",
|
||||
@@ -3114,7 +3194,16 @@ var rules = [...]string{
|
||||
"uenohara.yamanashi.jp",
|
||||
"yamanakako.yamanashi.jp",
|
||||
"yamanashi.yamanashi.jp",
|
||||
"*.ke",
|
||||
"ke",
|
||||
"ac.ke",
|
||||
"co.ke",
|
||||
"go.ke",
|
||||
"info.ke",
|
||||
"me.ke",
|
||||
"mobi.ke",
|
||||
"ne.ke",
|
||||
"or.ke",
|
||||
"sc.ke",
|
||||
"kg",
|
||||
"org.kg",
|
||||
"net.kg",
|
||||
@@ -6168,7 +6257,6 @@ var rules = [...]string{
|
||||
"chat",
|
||||
"cheap",
|
||||
"chintai",
|
||||
"chloe",
|
||||
"christmas",
|
||||
"chrome",
|
||||
"chrysler",
|
||||
@@ -6459,7 +6547,6 @@ var rules = [...]string{
|
||||
"house",
|
||||
"how",
|
||||
"hsbc",
|
||||
"htc",
|
||||
"hughes",
|
||||
"hyatt",
|
||||
"hyundai",
|
||||
@@ -6611,8 +6698,6 @@ var rules = [...]string{
|
||||
"maserati",
|
||||
"mattel",
|
||||
"mba",
|
||||
"mcd",
|
||||
"mcdonalds",
|
||||
"mckinsey",
|
||||
"med",
|
||||
"media",
|
||||
@@ -6643,7 +6728,6 @@ var rules = [...]string{
|
||||
"monash",
|
||||
"money",
|
||||
"monster",
|
||||
"montblanc",
|
||||
"mopar",
|
||||
"mormon",
|
||||
"mortgage",
|
||||
@@ -6721,7 +6805,6 @@ var rules = [...]string{
|
||||
"ott",
|
||||
"ovh",
|
||||
"page",
|
||||
"pamperedchef",
|
||||
"panasonic",
|
||||
"panerai",
|
||||
"paris",
|
||||
@@ -7195,10 +7278,13 @@ var rules = [...]string{
|
||||
"cc.ua",
|
||||
"inf.ua",
|
||||
"ltd.ua",
|
||||
"1password.ca",
|
||||
"1password.com",
|
||||
"1password.eu",
|
||||
"beep.pl",
|
||||
"*.compute.estate",
|
||||
"*.alces.network",
|
||||
"*.alwaysdata.net",
|
||||
"alwaysdata.net",
|
||||
"cloudfront.net",
|
||||
"*.compute.amazonaws.com",
|
||||
"*.compute-1.amazonaws.com",
|
||||
@@ -7215,6 +7301,7 @@ var rules = [...]string{
|
||||
"eu-central-1.elasticbeanstalk.com",
|
||||
"eu-west-1.elasticbeanstalk.com",
|
||||
"eu-west-2.elasticbeanstalk.com",
|
||||
"eu-west-3.elasticbeanstalk.com",
|
||||
"sa-east-1.elasticbeanstalk.com",
|
||||
"us-east-1.elasticbeanstalk.com",
|
||||
"us-east-2.elasticbeanstalk.com",
|
||||
@@ -7233,6 +7320,7 @@ var rules = [...]string{
|
||||
"s3-eu-central-1.amazonaws.com",
|
||||
"s3-eu-west-1.amazonaws.com",
|
||||
"s3-eu-west-2.amazonaws.com",
|
||||
"s3-eu-west-3.amazonaws.com",
|
||||
"s3-external-1.amazonaws.com",
|
||||
"s3-fips-us-gov-west-1.amazonaws.com",
|
||||
"s3-sa-east-1.amazonaws.com",
|
||||
@@ -7246,6 +7334,7 @@ var rules = [...]string{
|
||||
"s3.ca-central-1.amazonaws.com",
|
||||
"s3.eu-central-1.amazonaws.com",
|
||||
"s3.eu-west-2.amazonaws.com",
|
||||
"s3.eu-west-3.amazonaws.com",
|
||||
"s3.us-east-2.amazonaws.com",
|
||||
"s3.dualstack.ap-northeast-1.amazonaws.com",
|
||||
"s3.dualstack.ap-northeast-2.amazonaws.com",
|
||||
@@ -7256,6 +7345,7 @@ var rules = [...]string{
|
||||
"s3.dualstack.eu-central-1.amazonaws.com",
|
||||
"s3.dualstack.eu-west-1.amazonaws.com",
|
||||
"s3.dualstack.eu-west-2.amazonaws.com",
|
||||
"s3.dualstack.eu-west-3.amazonaws.com",
|
||||
"s3.dualstack.sa-east-1.amazonaws.com",
|
||||
"s3.dualstack.us-east-1.amazonaws.com",
|
||||
"s3.dualstack.us-east-2.amazonaws.com",
|
||||
@@ -7272,6 +7362,7 @@ var rules = [...]string{
|
||||
"s3-website.ca-central-1.amazonaws.com",
|
||||
"s3-website.eu-central-1.amazonaws.com",
|
||||
"s3-website.eu-west-2.amazonaws.com",
|
||||
"s3-website.eu-west-3.amazonaws.com",
|
||||
"s3-website.us-east-2.amazonaws.com",
|
||||
"t3l3p0rt.net",
|
||||
"tele.amune.org",
|
||||
@@ -7363,6 +7454,8 @@ var rules = [...]string{
|
||||
"cloudns.us",
|
||||
"co.nl",
|
||||
"co.no",
|
||||
"webhosting.be",
|
||||
"hosting-cluster.nl",
|
||||
"dyn.cosidns.de",
|
||||
"dynamisches-dns.de",
|
||||
"dnsupdater.de",
|
||||
@@ -7863,6 +7956,8 @@ var rules = [...]string{
|
||||
"fedorainfracloud.org",
|
||||
"fedorapeople.org",
|
||||
"cloud.fedoraproject.org",
|
||||
"app.os.fedoraproject.org",
|
||||
"app.os.stg.fedoraproject.org",
|
||||
"filegear.me",
|
||||
"firebaseapp.com",
|
||||
"flynnhub.com",
|
||||
@@ -7873,7 +7968,6 @@ var rules = [...]string{
|
||||
"fbxos.fr",
|
||||
"freebox-os.fr",
|
||||
"freeboxos.fr",
|
||||
"myfusion.cloud",
|
||||
"*.futurecms.at",
|
||||
"futurehosting.at",
|
||||
"futuremailing.at",
|
||||
@@ -8049,6 +8143,7 @@ var rules = [...]string{
|
||||
"netlify.com",
|
||||
"4u.com",
|
||||
"ngrok.io",
|
||||
"nh-serv.co.uk",
|
||||
"nfshost.com",
|
||||
"nsupdate.info",
|
||||
"nerdpol.ovh",
|
||||
@@ -8214,6 +8309,8 @@ var rules = [...]string{
|
||||
"rackmaze.com",
|
||||
"rackmaze.net",
|
||||
"rhcloud.com",
|
||||
"resindevice.io",
|
||||
"devices.resinstaging.io",
|
||||
"hzc.io",
|
||||
"wellbeingzone.eu",
|
||||
"ptplus.fit",
|
||||
@@ -8221,6 +8318,7 @@ var rules = [...]string{
|
||||
"sandcats.io",
|
||||
"logoip.de",
|
||||
"logoip.com",
|
||||
"scrysec.com",
|
||||
"firewall-gateway.com",
|
||||
"firewall-gateway.de",
|
||||
"my-gateway.de",
|
||||
@@ -8231,6 +8329,7 @@ var rules = [...]string{
|
||||
"my-firewall.org",
|
||||
"myfirewall.org",
|
||||
"spdns.org",
|
||||
"*.s5y.io",
|
||||
"*.sensiosite.cloud",
|
||||
"biz.ua",
|
||||
"co.ua",
|
||||
@@ -8591,7 +8690,6 @@ var nodeLabels = [...]string{
|
||||
"chat",
|
||||
"cheap",
|
||||
"chintai",
|
||||
"chloe",
|
||||
"christmas",
|
||||
"chrome",
|
||||
"chrysler",
|
||||
@@ -8942,7 +9040,6 @@ var nodeLabels = [...]string{
|
||||
"hr",
|
||||
"hsbc",
|
||||
"ht",
|
||||
"htc",
|
||||
"hu",
|
||||
"hughes",
|
||||
"hyatt",
|
||||
@@ -9136,8 +9233,6 @@ var nodeLabels = [...]string{
|
||||
"mattel",
|
||||
"mba",
|
||||
"mc",
|
||||
"mcd",
|
||||
"mcdonalds",
|
||||
"mckinsey",
|
||||
"md",
|
||||
"me",
|
||||
@@ -9179,7 +9274,6 @@ var nodeLabels = [...]string{
|
||||
"monash",
|
||||
"money",
|
||||
"monster",
|
||||
"montblanc",
|
||||
"mopar",
|
||||
"mormon",
|
||||
"mortgage",
|
||||
@@ -9287,7 +9381,6 @@ var nodeLabels = [...]string{
|
||||
"ovh",
|
||||
"pa",
|
||||
"page",
|
||||
"pamperedchef",
|
||||
"panasonic",
|
||||
"panerai",
|
||||
"paris",
|
||||
@@ -10138,6 +10231,7 @@ var nodeLabels = [...]string{
|
||||
"ac",
|
||||
"blogspot",
|
||||
"transurl",
|
||||
"webhosting",
|
||||
"gov",
|
||||
"0",
|
||||
"1",
|
||||
@@ -10208,33 +10302,79 @@ var nodeLabels = [...]string{
|
||||
"gov",
|
||||
"net",
|
||||
"org",
|
||||
"academia",
|
||||
"agro",
|
||||
"arte",
|
||||
"blog",
|
||||
"bolivia",
|
||||
"ciencia",
|
||||
"com",
|
||||
"cooperativa",
|
||||
"democracia",
|
||||
"deporte",
|
||||
"ecologia",
|
||||
"economia",
|
||||
"edu",
|
||||
"empresa",
|
||||
"gob",
|
||||
"gov",
|
||||
"indigena",
|
||||
"industria",
|
||||
"info",
|
||||
"int",
|
||||
"medicina",
|
||||
"mil",
|
||||
"movimiento",
|
||||
"musica",
|
||||
"natural",
|
||||
"net",
|
||||
"nombre",
|
||||
"noticias",
|
||||
"org",
|
||||
"patria",
|
||||
"plurinacional",
|
||||
"politica",
|
||||
"profesional",
|
||||
"pueblo",
|
||||
"revista",
|
||||
"salud",
|
||||
"tecnologia",
|
||||
"tksat",
|
||||
"transporte",
|
||||
"tv",
|
||||
"web",
|
||||
"wiki",
|
||||
"9guacu",
|
||||
"abc",
|
||||
"adm",
|
||||
"adv",
|
||||
"agr",
|
||||
"aju",
|
||||
"am",
|
||||
"anani",
|
||||
"aparecida",
|
||||
"arq",
|
||||
"art",
|
||||
"ato",
|
||||
"b",
|
||||
"belem",
|
||||
"bhz",
|
||||
"bio",
|
||||
"blog",
|
||||
"bmd",
|
||||
"boavista",
|
||||
"bsb",
|
||||
"campinagrande",
|
||||
"campinas",
|
||||
"caxias",
|
||||
"cim",
|
||||
"cng",
|
||||
"cnt",
|
||||
"com",
|
||||
"contagem",
|
||||
"coop",
|
||||
"cri",
|
||||
"cuiaba",
|
||||
"curitiba",
|
||||
"def",
|
||||
"ecn",
|
||||
"eco",
|
||||
@@ -10245,51 +10385,85 @@ var nodeLabels = [...]string{
|
||||
"etc",
|
||||
"eti",
|
||||
"far",
|
||||
"feira",
|
||||
"flog",
|
||||
"floripa",
|
||||
"fm",
|
||||
"fnd",
|
||||
"fortal",
|
||||
"fot",
|
||||
"foz",
|
||||
"fst",
|
||||
"g12",
|
||||
"ggf",
|
||||
"goiania",
|
||||
"gov",
|
||||
"gru",
|
||||
"imb",
|
||||
"ind",
|
||||
"inf",
|
||||
"jab",
|
||||
"jampa",
|
||||
"jdf",
|
||||
"joinville",
|
||||
"jor",
|
||||
"jus",
|
||||
"leg",
|
||||
"lel",
|
||||
"londrina",
|
||||
"macapa",
|
||||
"maceio",
|
||||
"manaus",
|
||||
"maringa",
|
||||
"mat",
|
||||
"med",
|
||||
"mil",
|
||||
"morena",
|
||||
"mp",
|
||||
"mus",
|
||||
"natal",
|
||||
"net",
|
||||
"niteroi",
|
||||
"nom",
|
||||
"not",
|
||||
"ntr",
|
||||
"odo",
|
||||
"org",
|
||||
"osasco",
|
||||
"palmas",
|
||||
"poa",
|
||||
"ppg",
|
||||
"pro",
|
||||
"psc",
|
||||
"psi",
|
||||
"pvh",
|
||||
"qsl",
|
||||
"radio",
|
||||
"rec",
|
||||
"recife",
|
||||
"ribeirao",
|
||||
"rio",
|
||||
"riobranco",
|
||||
"riopreto",
|
||||
"salvador",
|
||||
"sampa",
|
||||
"santamaria",
|
||||
"santoandre",
|
||||
"saobernardo",
|
||||
"saogonca",
|
||||
"sjc",
|
||||
"slg",
|
||||
"slz",
|
||||
"sorocaba",
|
||||
"srv",
|
||||
"taxi",
|
||||
"teo",
|
||||
"the",
|
||||
"tmp",
|
||||
"trd",
|
||||
"tur",
|
||||
"tv",
|
||||
"udi",
|
||||
"vet",
|
||||
"vix",
|
||||
"vlog",
|
||||
@@ -10376,6 +10550,7 @@ var nodeLabels = [...]string{
|
||||
"nym",
|
||||
"org",
|
||||
"za",
|
||||
"1password",
|
||||
"ab",
|
||||
"awdev",
|
||||
"bc",
|
||||
@@ -10434,7 +10609,6 @@ var nodeLabels = [...]string{
|
||||
"mil",
|
||||
"nom",
|
||||
"magentosite",
|
||||
"myfusion",
|
||||
"sensiosite",
|
||||
"statics",
|
||||
"trafficplex",
|
||||
@@ -10512,6 +10686,7 @@ var nodeLabels = [...]string{
|
||||
"blogspot",
|
||||
"0emm",
|
||||
"1kapp",
|
||||
"1password",
|
||||
"3utilities",
|
||||
"4u",
|
||||
"africa",
|
||||
@@ -10759,6 +10934,7 @@ var nodeLabels = [...]string{
|
||||
"ru",
|
||||
"sa",
|
||||
"saves-the-whales",
|
||||
"scrysec",
|
||||
"se",
|
||||
"securitytactics",
|
||||
"selfip",
|
||||
@@ -10812,6 +10988,7 @@ var nodeLabels = [...]string{
|
||||
"eu-central-1",
|
||||
"eu-west-1",
|
||||
"eu-west-2",
|
||||
"eu-west-3",
|
||||
"s3",
|
||||
"s3-ap-northeast-1",
|
||||
"s3-ap-northeast-2",
|
||||
@@ -10822,6 +10999,7 @@ var nodeLabels = [...]string{
|
||||
"s3-eu-central-1",
|
||||
"s3-eu-west-1",
|
||||
"s3-eu-west-2",
|
||||
"s3-eu-west-3",
|
||||
"s3-external-1",
|
||||
"s3-fips-us-gov-west-1",
|
||||
"s3-sa-east-1",
|
||||
@@ -10870,6 +11048,10 @@ var nodeLabels = [...]string{
|
||||
"s3",
|
||||
"dualstack",
|
||||
"s3",
|
||||
"s3-website",
|
||||
"s3",
|
||||
"dualstack",
|
||||
"s3",
|
||||
"dualstack",
|
||||
"s3",
|
||||
"dualstack",
|
||||
@@ -10887,6 +11069,7 @@ var nodeLabels = [...]string{
|
||||
"eu-central-1",
|
||||
"eu-west-1",
|
||||
"eu-west-2",
|
||||
"eu-west-3",
|
||||
"sa-east-1",
|
||||
"us-east-1",
|
||||
"us-east-2",
|
||||
@@ -11077,6 +11260,7 @@ var nodeLabels = [...]string{
|
||||
"name",
|
||||
"net",
|
||||
"org",
|
||||
"1password",
|
||||
"barsy",
|
||||
"cloudns",
|
||||
"diskstation",
|
||||
@@ -11360,6 +11544,9 @@ var nodeLabels = [...]string{
|
||||
"nodum",
|
||||
"pantheonsite",
|
||||
"protonet",
|
||||
"resindevice",
|
||||
"resinstaging",
|
||||
"s5y",
|
||||
"sandcats",
|
||||
"shiftedit",
|
||||
"spacekit",
|
||||
@@ -11370,6 +11557,7 @@ var nodeLabels = [...]string{
|
||||
"customer",
|
||||
"apps",
|
||||
"stage",
|
||||
"devices",
|
||||
"dev",
|
||||
"disrec",
|
||||
"prod",
|
||||
@@ -13572,7 +13760,15 @@ var nodeLabels = [...]string{
|
||||
"yamanakako",
|
||||
"yamanashi",
|
||||
"city",
|
||||
"ac",
|
||||
"co",
|
||||
"go",
|
||||
"info",
|
||||
"me",
|
||||
"mobi",
|
||||
"ne",
|
||||
"or",
|
||||
"sc",
|
||||
"blogspot",
|
||||
"com",
|
||||
"edu",
|
||||
@@ -14590,6 +14786,7 @@ var nodeLabels = [...]string{
|
||||
"cistron",
|
||||
"co",
|
||||
"demon",
|
||||
"hosting-cluster",
|
||||
"transurl",
|
||||
"virtueeldomein",
|
||||
"aa",
|
||||
@@ -15560,6 +15757,11 @@ var nodeLabels = [...]string{
|
||||
"uk",
|
||||
"us",
|
||||
"cloud",
|
||||
"os",
|
||||
"stg",
|
||||
"app",
|
||||
"os",
|
||||
"app",
|
||||
"nerdpol",
|
||||
"abo",
|
||||
"ac",
|
||||
@@ -16393,6 +16595,7 @@ var nodeLabels = [...]string{
|
||||
"police",
|
||||
"sch",
|
||||
"blogspot",
|
||||
"nh-serv",
|
||||
"no-ip",
|
||||
"wellbeingzone",
|
||||
"homeoffice",
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ func sysctl(mib []int32, old *byte, oldlen *uintptr, new *byte, newlen uintptr)
|
||||
} else {
|
||||
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 {
|
||||
return error(errno)
|
||||
}
|
||||
|
||||
+40
-19
@@ -368,7 +368,11 @@ func New(family, title string) Trace {
|
||||
}
|
||||
|
||||
func (tr *trace) Finish() {
|
||||
tr.Elapsed = time.Now().Sub(tr.Start)
|
||||
elapsed := time.Now().Sub(tr.Start)
|
||||
tr.mu.Lock()
|
||||
tr.Elapsed = elapsed
|
||||
tr.mu.Unlock()
|
||||
|
||||
if DebugUseAfterFinish {
|
||||
buf := make([]byte, 4<<10) // 4 KB should be enough
|
||||
n := runtime.Stack(buf, false)
|
||||
@@ -381,14 +385,17 @@ func (tr *trace) Finish() {
|
||||
m.Remove(tr)
|
||||
|
||||
f := getFamily(tr.Family, true)
|
||||
tr.mu.RLock() // protects tr fields in Cond.match calls
|
||||
for _, b := range f.Buckets {
|
||||
if b.Cond.match(tr) {
|
||||
b.Add(tr)
|
||||
}
|
||||
}
|
||||
tr.mu.RUnlock()
|
||||
|
||||
// Add a sample of elapsed time as microseconds to the family's timeseries
|
||||
h := new(histogram)
|
||||
h.addMeasurement(tr.Elapsed.Nanoseconds() / 1e3)
|
||||
h.addMeasurement(elapsed.Nanoseconds() / 1e3)
|
||||
f.LatencyMu.Lock()
|
||||
f.Latency.Add(h)
|
||||
f.LatencyMu.Unlock()
|
||||
@@ -684,25 +691,20 @@ type trace struct {
|
||||
// Title is the title of this trace.
|
||||
Title string
|
||||
|
||||
// Timing information.
|
||||
Start time.Time
|
||||
Elapsed time.Duration // zero while active
|
||||
// Start time of the this trace.
|
||||
Start time.Time
|
||||
|
||||
// Trace information if non-zero.
|
||||
traceID uint64
|
||||
spanID uint64
|
||||
|
||||
// Whether this trace resulted in an error.
|
||||
IsError bool
|
||||
|
||||
// Append-only sequence of events (modulo discards).
|
||||
mu sync.RWMutex
|
||||
events []event
|
||||
events []event // Append-only sequence of events (modulo discards).
|
||||
maxEvents int
|
||||
recycler func(interface{})
|
||||
IsError bool // Whether this trace resulted in an error.
|
||||
Elapsed time.Duration // Elapsed time for this trace, zero while active.
|
||||
traceID uint64 // Trace information if non-zero.
|
||||
spanID uint64
|
||||
|
||||
refs int32 // how many buckets this is in
|
||||
recycler func(interface{})
|
||||
disc discarded // scratch space to avoid allocation
|
||||
refs int32 // how many buckets this is in
|
||||
disc discarded // scratch space to avoid allocation
|
||||
|
||||
finishStack []byte // where finish was called, if DebugUseAfterFinish is set
|
||||
|
||||
@@ -714,14 +716,18 @@ func (tr *trace) reset() {
|
||||
tr.Family = ""
|
||||
tr.Title = ""
|
||||
tr.Start = time.Time{}
|
||||
|
||||
tr.mu.Lock()
|
||||
tr.Elapsed = 0
|
||||
tr.traceID = 0
|
||||
tr.spanID = 0
|
||||
tr.IsError = false
|
||||
tr.maxEvents = 0
|
||||
tr.events = nil
|
||||
tr.refs = 0
|
||||
tr.recycler = nil
|
||||
tr.mu.Unlock()
|
||||
|
||||
tr.refs = 0
|
||||
tr.disc = 0
|
||||
tr.finishStack = nil
|
||||
for i := range tr.eventsBuf {
|
||||
@@ -801,21 +807,31 @@ func (tr *trace) LazyPrintf(format string, a ...interface{}) {
|
||||
tr.addEvent(&lazySprintf{format, a}, false, false)
|
||||
}
|
||||
|
||||
func (tr *trace) SetError() { tr.IsError = true }
|
||||
func (tr *trace) SetError() {
|
||||
tr.mu.Lock()
|
||||
tr.IsError = true
|
||||
tr.mu.Unlock()
|
||||
}
|
||||
|
||||
func (tr *trace) SetRecycler(f func(interface{})) {
|
||||
tr.mu.Lock()
|
||||
tr.recycler = f
|
||||
tr.mu.Unlock()
|
||||
}
|
||||
|
||||
func (tr *trace) SetTraceInfo(traceID, spanID uint64) {
|
||||
tr.mu.Lock()
|
||||
tr.traceID, tr.spanID = traceID, spanID
|
||||
tr.mu.Unlock()
|
||||
}
|
||||
|
||||
func (tr *trace) SetMaxEvents(m int) {
|
||||
tr.mu.Lock()
|
||||
// Always keep at least three events: first, discarded count, last.
|
||||
if len(tr.events) == 0 && m > 3 {
|
||||
tr.maxEvents = m
|
||||
}
|
||||
tr.mu.Unlock()
|
||||
}
|
||||
|
||||
func (tr *trace) ref() {
|
||||
@@ -824,6 +840,7 @@ func (tr *trace) ref() {
|
||||
|
||||
func (tr *trace) unref() {
|
||||
if atomic.AddInt32(&tr.refs, -1) == 0 {
|
||||
tr.mu.RLock()
|
||||
if tr.recycler != nil {
|
||||
// freeTrace clears tr, so we hold tr.recycler and tr.events here.
|
||||
go func(f func(interface{}), es []event) {
|
||||
@@ -834,6 +851,7 @@ func (tr *trace) unref() {
|
||||
}
|
||||
}(tr.recycler, tr.events)
|
||||
}
|
||||
tr.mu.RUnlock()
|
||||
|
||||
freeTrace(tr)
|
||||
}
|
||||
@@ -844,7 +862,10 @@ func (tr *trace) When() string {
|
||||
}
|
||||
|
||||
func (tr *trace) ElapsedTime() string {
|
||||
tr.mu.RLock()
|
||||
t := tr.Elapsed
|
||||
tr.mu.RUnlock()
|
||||
|
||||
if t == 0 {
|
||||
// Active trace.
|
||||
t = time.Since(tr.Start)
|
||||
|
||||
+5
-10
@@ -4,16 +4,15 @@ Go is an open source project.
|
||||
|
||||
It is the work of hundreds of contributors. We appreciate your help!
|
||||
|
||||
|
||||
## Filing issues
|
||||
|
||||
When [filing an issue](https://github.com/golang/oauth2/issues), make sure to answer these five questions:
|
||||
|
||||
1. What version of Go are you using (`go version`)?
|
||||
2. What operating system and processor architecture are you using?
|
||||
3. What did you do?
|
||||
4. What did you expect to see?
|
||||
5. What did you see instead?
|
||||
1. What version of Go are you using (`go version`)?
|
||||
2. What operating system and processor architecture are you using?
|
||||
3. What did you do?
|
||||
4. What did you expect to see?
|
||||
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.
|
||||
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)
|
||||
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
|
||||
the BSD-style license found in the LICENSE file.
|
||||
|
||||
|
||||
+23
-45
@@ -18,20 +18,6 @@ import (
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// DefaultCredentials holds "Application Default Credentials".
|
||||
// For more details, see:
|
||||
// https://developers.google.com/accounts/docs/application-default-credentials
|
||||
type DefaultCredentials struct {
|
||||
ProjectID string // may be empty
|
||||
TokenSource oauth2.TokenSource
|
||||
|
||||
// JSON contains the raw bytes from a JSON credentials file.
|
||||
// This field may be nil if authentication is provided by the
|
||||
// environment and not with a credentials file, e.g. when code is
|
||||
// running on Google Cloud Platform.
|
||||
JSON []byte
|
||||
}
|
||||
|
||||
// DefaultClient returns an HTTP Client that uses the
|
||||
// DefaultTokenSource to obtain authentication credentials.
|
||||
func DefaultClient(ctx context.Context, scope ...string) (*http.Client, error) {
|
||||
@@ -53,25 +39,12 @@ func DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSourc
|
||||
return creds.TokenSource, nil
|
||||
}
|
||||
|
||||
// FindDefaultCredentials searches for "Application Default Credentials".
|
||||
//
|
||||
// It looks for credentials in the following places,
|
||||
// preferring the first location found:
|
||||
//
|
||||
// 1. A JSON file whose path is specified by the
|
||||
// GOOGLE_APPLICATION_CREDENTIALS environment variable.
|
||||
// 2. A JSON file in a location known to the gcloud command-line tool.
|
||||
// On Windows, this is %APPDATA%/gcloud/application_default_credentials.json.
|
||||
// On other systems, $HOME/.config/gcloud/application_default_credentials.json.
|
||||
// 3. On Google App Engine it uses the appengine.AccessToken function.
|
||||
// 4. On Google Compute Engine and Google App Engine Managed VMs, it fetches
|
||||
// credentials from the metadata server.
|
||||
// (In this final case any provided scopes are ignored.)
|
||||
func FindDefaultCredentials(ctx context.Context, scope ...string) (*DefaultCredentials, error) {
|
||||
// Common implementation for FindDefaultCredentials.
|
||||
func findDefaultCredentials(ctx context.Context, scopes []string) (*DefaultCredentials, error) {
|
||||
// First, try the environment variable.
|
||||
const envVar = "GOOGLE_APPLICATION_CREDENTIALS"
|
||||
if filename := os.Getenv(envVar); filename != "" {
|
||||
creds, err := readCredentialsFile(ctx, filename, scope)
|
||||
creds, err := readCredentialsFile(ctx, filename, scopes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("google: error getting credentials using %v environment variable: %v", envVar, err)
|
||||
}
|
||||
@@ -80,7 +53,7 @@ func FindDefaultCredentials(ctx context.Context, scope ...string) (*DefaultCrede
|
||||
|
||||
// Second, try a well-known file.
|
||||
filename := wellKnownFile()
|
||||
if creds, err := readCredentialsFile(ctx, filename, scope); err == nil {
|
||||
if creds, err := readCredentialsFile(ctx, filename, scopes); err == nil {
|
||||
return creds, nil
|
||||
} else if !os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("google: error getting credentials using well-known file (%v): %v", filename, err)
|
||||
@@ -90,7 +63,7 @@ func FindDefaultCredentials(ctx context.Context, scope ...string) (*DefaultCrede
|
||||
if appengineTokenFunc != nil && !appengineFlex {
|
||||
return &DefaultCredentials{
|
||||
ProjectID: appengineAppIDFunc(ctx),
|
||||
TokenSource: AppEngineTokenSource(ctx, scope...),
|
||||
TokenSource: AppEngineTokenSource(ctx, scopes...),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -108,6 +81,23 @@ func FindDefaultCredentials(ctx context.Context, scope ...string) (*DefaultCrede
|
||||
return nil, fmt.Errorf("google: could not find default credentials. See %v for more information.", url)
|
||||
}
|
||||
|
||||
// Common implementation for CredentialsFromJSON.
|
||||
func credentialsFromJSON(ctx context.Context, jsonData []byte, scopes []string) (*DefaultCredentials, error) {
|
||||
var f credentialsFile
|
||||
if err := json.Unmarshal(jsonData, &f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ts, err := f.tokenSource(ctx, append([]string(nil), scopes...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &DefaultCredentials{
|
||||
ProjectID: f.ProjectID,
|
||||
TokenSource: ts,
|
||||
JSON: jsonData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func wellKnownFile() string {
|
||||
const f = "application_default_credentials.json"
|
||||
if runtime.GOOS == "windows" {
|
||||
@@ -121,17 +111,5 @@ func readCredentialsFile(ctx context.Context, filename string, scopes []string)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var f credentialsFile
|
||||
if err := json.Unmarshal(b, &f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ts, err := f.tokenSource(ctx, append([]string(nil), scopes...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &DefaultCredentials{
|
||||
ProjectID: f.ProjectID,
|
||||
TokenSource: ts,
|
||||
JSON: b,
|
||||
}, nil
|
||||
return CredentialsFromJSON(ctx, b, scopes...)
|
||||
}
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
// 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 go1.9
|
||||
|
||||
// Package google provides support for making OAuth2 authorized and authenticated
|
||||
// HTTP requests to Google APIs. It supports the Web server flow, client-side
|
||||
// credentials, service accounts, Google Compute Engine service accounts, and Google
|
||||
// App Engine service accounts.
|
||||
//
|
||||
// A brief overview of the package follows. For more information, please read
|
||||
// https://developers.google.com/accounts/docs/OAuth2
|
||||
// and
|
||||
// https://developers.google.com/accounts/docs/application-default-credentials.
|
||||
//
|
||||
// OAuth2 Configs
|
||||
//
|
||||
// Two functions in this package return golang.org/x/oauth2.Config values from Google credential
|
||||
// data. Google supports two JSON formats for OAuth2 credentials: one is handled by ConfigFromJSON,
|
||||
// the other by JWTConfigFromJSON. The returned Config can be used to obtain a TokenSource or
|
||||
// create an http.Client.
|
||||
//
|
||||
//
|
||||
// Credentials
|
||||
//
|
||||
// The Credentials type represents Google credentials, including Application Default
|
||||
// Credentials.
|
||||
//
|
||||
// Use FindDefaultCredentials to obtain Application Default Credentials.
|
||||
// FindDefaultCredentials looks in some well-known places for a credentials file, and
|
||||
// will call AppEngineTokenSource or ComputeTokenSource as needed.
|
||||
//
|
||||
// DefaultClient and DefaultTokenSource are convenience methods. They first call FindDefaultCredentials,
|
||||
// then use the credentials to construct an http.Client or an oauth2.TokenSource.
|
||||
//
|
||||
// Use CredentialsFromJSON to obtain credentials from either of the two JSON formats
|
||||
// described in OAuth2 Configs, above. The TokenSource in the returned value is the
|
||||
// same as the one obtained from the oauth2.Config returned from ConfigFromJSON or
|
||||
// JWTConfigFromJSON, but the Credentials may contain additional information
|
||||
// that is useful is some circumstances.
|
||||
package google // import "golang.org/x/oauth2/google"
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
// 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 !go1.9
|
||||
|
||||
// Package google provides support for making OAuth2 authorized and authenticated
|
||||
// HTTP requests to Google APIs. It supports the Web server flow, client-side
|
||||
// credentials, service accounts, Google Compute Engine service accounts, and Google
|
||||
// App Engine service accounts.
|
||||
//
|
||||
// A brief overview of the package follows. For more information, please read
|
||||
// https://developers.google.com/accounts/docs/OAuth2
|
||||
// and
|
||||
// https://developers.google.com/accounts/docs/application-default-credentials.
|
||||
//
|
||||
// OAuth2 Configs
|
||||
//
|
||||
// Two functions in this package return golang.org/x/oauth2.Config values from Google credential
|
||||
// data. Google supports two JSON formats for OAuth2 credentials: one is handled by ConfigFromJSON,
|
||||
// the other by JWTConfigFromJSON. The returned Config can be used to obtain a TokenSource or
|
||||
// create an http.Client.
|
||||
//
|
||||
//
|
||||
// Credentials
|
||||
//
|
||||
// The DefaultCredentials type represents Google Application Default Credentials, as
|
||||
// well as other forms of credential.
|
||||
//
|
||||
// Use FindDefaultCredentials to obtain Application Default Credentials.
|
||||
// FindDefaultCredentials looks in some well-known places for a credentials file, and
|
||||
// will call AppEngineTokenSource or ComputeTokenSource as needed.
|
||||
//
|
||||
// DefaultClient and DefaultTokenSource are convenience methods. They first call FindDefaultCredentials,
|
||||
// then use the credentials to construct an http.Client or an oauth2.TokenSource.
|
||||
//
|
||||
// Use CredentialsFromJSON to obtain credentials from either of the two JSON
|
||||
// formats described in OAuth2 Configs, above. (The DefaultCredentials returned may
|
||||
// not be "Application Default Credentials".) The TokenSource in the returned value
|
||||
// is the same as the one obtained from the oauth2.Config returned from
|
||||
// ConfigFromJSON or JWTConfigFromJSON, but the DefaultCredentials may contain
|
||||
// additional information that is useful is some circumstances.
|
||||
package google // import "golang.org/x/oauth2/google"
|
||||
+14
-2
@@ -2,8 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build appenginevm appengine
|
||||
|
||||
package google_test
|
||||
|
||||
import (
|
||||
@@ -12,6 +10,7 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"golang.org/x/oauth2/jwt"
|
||||
@@ -148,3 +147,16 @@ func ExampleComputeTokenSource() {
|
||||
}
|
||||
client.Get("...")
|
||||
}
|
||||
|
||||
func ExampleCredentialsFromJSON() {
|
||||
ctx := context.Background()
|
||||
data, err := ioutil.ReadFile("/path/to/key-file.json")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
creds, err := google.CredentialsFromJSON(ctx, data, "https://www.googleapis.com/auth/bigquery")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
_ = creds // TODO: Use creds.
|
||||
}
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
// 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 go1.9
|
||||
|
||||
package google
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// Credentials holds Google credentials, including "Application Default Credentials".
|
||||
// For more details, see:
|
||||
// https://developers.google.com/accounts/docs/application-default-credentials
|
||||
type Credentials struct {
|
||||
ProjectID string // may be empty
|
||||
TokenSource oauth2.TokenSource
|
||||
|
||||
// JSON contains the raw bytes from a JSON credentials file.
|
||||
// This field may be nil if authentication is provided by the
|
||||
// environment and not with a credentials file, e.g. when code is
|
||||
// running on Google Cloud Platform.
|
||||
JSON []byte
|
||||
}
|
||||
|
||||
// DefaultCredentials is the old name of Credentials.
|
||||
//
|
||||
// Deprecated: use Credentials instead.
|
||||
type DefaultCredentials = Credentials
|
||||
|
||||
// FindDefaultCredentials searches for "Application Default Credentials".
|
||||
//
|
||||
// It looks for credentials in the following places,
|
||||
// preferring the first location found:
|
||||
//
|
||||
// 1. A JSON file whose path is specified by the
|
||||
// GOOGLE_APPLICATION_CREDENTIALS environment variable.
|
||||
// 2. A JSON file in a location known to the gcloud command-line tool.
|
||||
// On Windows, this is %APPDATA%/gcloud/application_default_credentials.json.
|
||||
// On other systems, $HOME/.config/gcloud/application_default_credentials.json.
|
||||
// 3. On Google App Engine it uses the appengine.AccessToken function.
|
||||
// 4. On Google Compute Engine and Google App Engine Managed VMs, it fetches
|
||||
// credentials from the metadata server.
|
||||
// (In this final case any provided scopes are ignored.)
|
||||
func FindDefaultCredentials(ctx context.Context, scopes ...string) (*Credentials, error) {
|
||||
return findDefaultCredentials(ctx, scopes)
|
||||
}
|
||||
|
||||
// CredentialsFromJSON obtains Google credentials from a JSON value. The JSON can
|
||||
// represent either a Google Developers Console client_credentials.json file (as in
|
||||
// ConfigFromJSON) or a Google Developers service account key file (as in
|
||||
// JWTConfigFromJSON).
|
||||
func CredentialsFromJSON(ctx context.Context, jsonData []byte, scopes ...string) (*Credentials, error) {
|
||||
return credentialsFromJSON(ctx, jsonData, scopes)
|
||||
}
|
||||
+1
-11
@@ -2,17 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package google provides support for making OAuth2 authorized and
|
||||
// authenticated HTTP requests to Google APIs.
|
||||
// It supports the Web server flow, client-side credentials, service accounts,
|
||||
// Google Compute Engine service accounts, and Google App Engine service
|
||||
// accounts.
|
||||
//
|
||||
// For more information, please read
|
||||
// https://developers.google.com/accounts/docs/OAuth2
|
||||
// and
|
||||
// https://developers.google.com/accounts/docs/application-default-credentials.
|
||||
package google // import "golang.org/x/oauth2/google"
|
||||
package google
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
// 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 !go1.9
|
||||
|
||||
package google
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// DefaultCredentials holds Google credentials, including "Application Default Credentials".
|
||||
// For more details, see:
|
||||
// https://developers.google.com/accounts/docs/application-default-credentials
|
||||
type DefaultCredentials struct {
|
||||
ProjectID string // may be empty
|
||||
TokenSource oauth2.TokenSource
|
||||
|
||||
// JSON contains the raw bytes from a JSON credentials file.
|
||||
// This field may be nil if authentication is provided by the
|
||||
// environment and not with a credentials file, e.g. when code is
|
||||
// running on Google Cloud Platform.
|
||||
JSON []byte
|
||||
}
|
||||
|
||||
// FindDefaultCredentials searches for "Application Default Credentials".
|
||||
//
|
||||
// It looks for credentials in the following places,
|
||||
// preferring the first location found:
|
||||
//
|
||||
// 1. A JSON file whose path is specified by the
|
||||
// GOOGLE_APPLICATION_CREDENTIALS environment variable.
|
||||
// 2. A JSON file in a location known to the gcloud command-line tool.
|
||||
// On Windows, this is %APPDATA%/gcloud/application_default_credentials.json.
|
||||
// On other systems, $HOME/.config/gcloud/application_default_credentials.json.
|
||||
// 3. On Google App Engine it uses the appengine.AccessToken function.
|
||||
// 4. On Google Compute Engine and Google App Engine Managed VMs, it fetches
|
||||
// credentials from the metadata server.
|
||||
// (In this final case any provided scopes are ignored.)
|
||||
func FindDefaultCredentials(ctx context.Context, scopes ...string) (*DefaultCredentials, error) {
|
||||
return findDefaultCredentials(ctx, scopes)
|
||||
}
|
||||
|
||||
// CredentialsFromJSON obtains Google credentials from a JSON value. The JSON can
|
||||
// represent either a Google Developers Console client_credentials.json file (as in
|
||||
// ConfigFromJSON) or a Google Developers service account key file (as in
|
||||
// JWTConfigFromJSON).
|
||||
//
|
||||
// Note: despite the name, the returned credentials may not be Application Default Credentials.
|
||||
func CredentialsFromJSON(ctx context.Context, jsonData []byte, scopes ...string) (*DefaultCredentials, error) {
|
||||
return credentialsFromJSON(ctx, jsonData, scopes)
|
||||
}
|
||||
+4
-1
@@ -103,7 +103,7 @@ var brokenAuthHeaderProviders = []string{
|
||||
"https://api.twitch.tv/",
|
||||
"https://app.box.com/",
|
||||
"https://connect.stripe.com/",
|
||||
"https://graph.facebook.com", // see https://github.com/golang/oauth2/issues/214
|
||||
"https://login.mailchimp.com/",
|
||||
"https://login.microsoftonline.com/",
|
||||
"https://login.salesforce.com/",
|
||||
"https://login.windows.net",
|
||||
@@ -124,10 +124,13 @@ var brokenAuthHeaderProviders = []string{
|
||||
"https://api.patreon.com/",
|
||||
"https://sandbox.codeswholesale.com/oauth/token",
|
||||
"https://api.sipgate.com/v1/authorization/oauth",
|
||||
"https://api.medium.com/v1/tokens",
|
||||
"https://log.finalsurge.com/oauth/token",
|
||||
}
|
||||
|
||||
// brokenAuthHeaderDomains lists broken providers that issue dynamic endpoints.
|
||||
var brokenAuthHeaderDomains = []string{
|
||||
".auth0.com",
|
||||
".force.com",
|
||||
".myshopify.com",
|
||||
".okta.com",
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// 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 mailchimp provides constants for using OAuth2 to access MailChimp.
|
||||
package mailchimp // import "golang.org/x/oauth2/mailchimp"
|
||||
|
||||
import (
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// Endpoint is MailChimp's OAuth 2.0 endpoint.
|
||||
// See http://developer.mailchimp.com/documentation/mailchimp/guides/how-to-use-oauth2/
|
||||
var Endpoint = oauth2.Endpoint{
|
||||
AuthURL: "https://login.mailchimp.com/oauth2/authorize",
|
||||
TokenURL: "https://login.mailchimp.com/oauth2/token",
|
||||
}
|
||||
+1
-1
@@ -117,7 +117,7 @@ func SetAuthURLParam(key, value string) AuthCodeOption {
|
||||
// that asks for permissions for the required scopes explicitly.
|
||||
//
|
||||
// State is a token to protect the user from CSRF attacks. You must
|
||||
// always provide a non-zero string and validate that it matches the
|
||||
// always provide a non-empty string and validate that it matches the
|
||||
// the state query parameter on your redirect callback.
|
||||
// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info.
|
||||
//
|
||||
|
||||
+5
-10
@@ -4,16 +4,15 @@ Go is an open source project.
|
||||
|
||||
It is the work of hundreds of contributors. We appreciate your help!
|
||||
|
||||
|
||||
## Filing issues
|
||||
|
||||
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`)?
|
||||
2. What operating system and processor architecture are you using?
|
||||
3. What did you do?
|
||||
4. What did you expect to see?
|
||||
5. What did you see instead?
|
||||
1. What version of Go are you using (`go version`)?
|
||||
2. What operating system and processor architecture are you using?
|
||||
3. What did you do?
|
||||
4. What did you expect to see?
|
||||
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.
|
||||
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)
|
||||
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
|
||||
the BSD-style license found in the LICENSE file.
|
||||
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// System call support for plan9 on arm
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-32
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-44
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·seek(SB),NOSPLIT,$0-36
|
||||
JMP syscall·exit(SB)
|
||||
+3
@@ -11,11 +11,14 @@
|
||||
// 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
|
||||
// to freebsd and $GOARCH to arm.
|
||||
//
|
||||
// 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
|
||||
// those packages rather than this one if you can.
|
||||
//
|
||||
// For details of the functions and data types in this package consult
|
||||
// the manuals for the appropriate operating system.
|
||||
//
|
||||
// These calls return err == nil to indicate success; otherwise
|
||||
// err represents an operating system error describing the failure and
|
||||
// holds a value of type syscall.ErrorString.
|
||||
|
||||
+5
-5
@@ -12,6 +12,7 @@
|
||||
package plan9
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -50,12 +51,11 @@ func atoi(b []byte) (n uint) {
|
||||
}
|
||||
|
||||
func cstring(s []byte) string {
|
||||
for i := range s {
|
||||
if s[i] == 0 {
|
||||
return string(s[0:i])
|
||||
}
|
||||
i := bytes.IndexByte(s, 0)
|
||||
if i == -1 {
|
||||
i = len(s)
|
||||
}
|
||||
return string(s)
|
||||
return string(s[:i])
|
||||
}
|
||||
|
||||
func errstr() string {
|
||||
|
||||
+284
@@ -0,0 +1,284 @@
|
||||
// mksyscall.pl -l32 -plan9 -tags plan9,arm syscall_plan9.go
|
||||
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
|
||||
// +build plan9,arm
|
||||
|
||||
package plan9
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fd2path(fd int, buf []byte) (err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(buf) > 0 {
|
||||
_p0 = unsafe.Pointer(&buf[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf)))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func pipe(p *[2]int32) (err error) {
|
||||
r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func await(s []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(s) > 0 {
|
||||
_p0 = unsafe.Pointer(&s[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0)
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func open(path string, mode int) (fd int, err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
|
||||
fd = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func create(path string, mode int, perm uint32) (fd int, err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
|
||||
fd = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func remove(path string) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func stat(path string, edir []byte) (n int, err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 unsafe.Pointer
|
||||
if len(edir) > 0 {
|
||||
_p1 = unsafe.Pointer(&edir[0])
|
||||
} else {
|
||||
_p1 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func bind(name string, old string, flag int) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *byte
|
||||
_p1, err = BytePtrFromString(old)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func mount(fd int, afd int, old string, flag int, aname string) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(old)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *byte
|
||||
_p1, err = BytePtrFromString(aname)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func wstat(path string, edir []byte) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 unsafe.Pointer
|
||||
if len(edir) > 0 {
|
||||
_p1 = unsafe.Pointer(&edir[0])
|
||||
} else {
|
||||
_p1 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func chdir(path string) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Dup(oldfd int, newfd int) (fd int, err error) {
|
||||
r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0)
|
||||
fd = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pread(fd int, p []byte, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
_p0 = unsafe.Pointer(&p[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
_p0 = unsafe.Pointer(&p[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Close(fd int) (err error) {
|
||||
r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fstat(fd int, edir []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(edir) > 0 {
|
||||
_p0 = unsafe.Pointer(&edir[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fwstat(fd int, edir []byte) (err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(edir) > 0 {
|
||||
_p0 = unsafe.Pointer(&edir[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
+1
-1
@@ -16,7 +16,7 @@ const cpuSetSize = _CPU_SETSIZE / _NCPUBITS
|
||||
type CPUSet [cpuSetSize]cpuMask
|
||||
|
||||
func schedAffinity(trap uintptr, pid int, set *CPUSet) error {
|
||||
_, _, e := RawSyscall(trap, uintptr(pid), uintptr(unsafe.Sizeof(set)), uintptr(unsafe.Pointer(set)))
|
||||
_, _, e := RawSyscall(trap, uintptr(pid), uintptr(unsafe.Sizeof(*set)), uintptr(unsafe.Pointer(set)))
|
||||
if e != 0 {
|
||||
return errnoErr(e)
|
||||
}
|
||||
|
||||
+2
-87
@@ -6,97 +6,12 @@
|
||||
|
||||
package unix
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// readInt returns the size-bytes unsigned integer in native byte order at offset off.
|
||||
func readInt(b []byte, off, size uintptr) (u uint64, ok bool) {
|
||||
if len(b) < int(off+size) {
|
||||
return 0, false
|
||||
}
|
||||
if isBigEndian {
|
||||
return readIntBE(b[off:], size), true
|
||||
}
|
||||
return readIntLE(b[off:], size), true
|
||||
}
|
||||
|
||||
func readIntBE(b []byte, size uintptr) uint64 {
|
||||
switch size {
|
||||
case 1:
|
||||
return uint64(b[0])
|
||||
case 2:
|
||||
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint64(b[1]) | uint64(b[0])<<8
|
||||
case 4:
|
||||
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24
|
||||
case 8:
|
||||
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
|
||||
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
|
||||
default:
|
||||
panic("syscall: readInt with unsupported size")
|
||||
}
|
||||
}
|
||||
|
||||
func readIntLE(b []byte, size uintptr) uint64 {
|
||||
switch size {
|
||||
case 1:
|
||||
return uint64(b[0])
|
||||
case 2:
|
||||
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint64(b[0]) | uint64(b[1])<<8
|
||||
case 4:
|
||||
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24
|
||||
case 8:
|
||||
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
|
||||
uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
|
||||
default:
|
||||
panic("syscall: readInt with unsupported size")
|
||||
}
|
||||
}
|
||||
import "syscall"
|
||||
|
||||
// ParseDirent parses up to max directory entries in buf,
|
||||
// appending the names to names. It returns the number of
|
||||
// bytes consumed from buf, the number of entries added
|
||||
// to names, and the new names slice.
|
||||
func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) {
|
||||
origlen := len(buf)
|
||||
count = 0
|
||||
for max != 0 && len(buf) > 0 {
|
||||
reclen, ok := direntReclen(buf)
|
||||
if !ok || reclen > uint64(len(buf)) {
|
||||
return origlen, count, names
|
||||
}
|
||||
rec := buf[:reclen]
|
||||
buf = buf[reclen:]
|
||||
ino, ok := direntIno(rec)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if ino == 0 { // File absent in directory.
|
||||
continue
|
||||
}
|
||||
const namoff = uint64(unsafe.Offsetof(Dirent{}.Name))
|
||||
namlen, ok := direntNamlen(rec)
|
||||
if !ok || namoff+namlen > uint64(len(rec)) {
|
||||
break
|
||||
}
|
||||
name := rec[namoff : namoff+namlen]
|
||||
for i, c := range name {
|
||||
if c == 0 {
|
||||
name = name[:i]
|
||||
break
|
||||
}
|
||||
}
|
||||
// Check for useless names before allocating a string.
|
||||
if string(name) == "." || string(name) == ".." {
|
||||
continue
|
||||
}
|
||||
max--
|
||||
count++
|
||||
names = append(names, string(name))
|
||||
}
|
||||
return origlen - len(buf), count, names
|
||||
return syscall.ParseDirent(buf, max, names)
|
||||
}
|
||||
|
||||
+19
@@ -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)
|
||||
}
|
||||
+9
-12
@@ -1,7 +1,4 @@
|
||||
FROM ubuntu:16.04
|
||||
|
||||
# Use the most recent ubuntu sources
|
||||
RUN echo 'deb http://en.archive.ubuntu.com/ubuntu/ artful main universe' >> /etc/apt/sources.list
|
||||
FROM ubuntu:17.10
|
||||
|
||||
# Dependencies to get the git sources and go binaries
|
||||
RUN apt-get update && apt-get install -y \
|
||||
@@ -12,15 +9,15 @@ RUN apt-get update && apt-get install -y \
|
||||
# Get the git sources. If not cached, this takes O(5 minutes).
|
||||
WORKDIR /git
|
||||
RUN git config --global advice.detachedHead false
|
||||
# Linux Kernel: Released 03 Sep 2017
|
||||
RUN git clone --branch v4.13 --depth 1 https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux
|
||||
# GNU C library: Released 02 Aug 2017 (we should try to get a secure way to clone this)
|
||||
RUN git clone --branch glibc-2.26 --depth 1 git://sourceware.org/git/glibc.git
|
||||
# Linux Kernel: Released 28 Jan 2018
|
||||
RUN git clone --branch v4.15 --depth 1 https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux
|
||||
# GNU C library: Released 01 Feb 2018 (we should try to get a secure way to clone this)
|
||||
RUN git clone --branch glibc-2.27 --depth 1 git://sourceware.org/git/glibc.git
|
||||
|
||||
# Get Go 1.9.2
|
||||
ENV GOLANG_VERSION 1.9.2
|
||||
# Get Go 1.10
|
||||
ENV GOLANG_VERSION 1.10
|
||||
ENV GOLANG_DOWNLOAD_URL https://golang.org/dl/go$GOLANG_VERSION.linux-amd64.tar.gz
|
||||
ENV GOLANG_DOWNLOAD_SHA256 de874549d9a8d8d8062be05808509c09a88a248e77ec14eb77453530829ac02b
|
||||
ENV GOLANG_DOWNLOAD_SHA256 b5a64335f1490277b585832d1f6c7f8c6c11206cba5cd3f771dcb87b98ad1a33
|
||||
|
||||
RUN curl -fsSL "$GOLANG_DOWNLOAD_URL" -o golang.tar.gz \
|
||||
&& echo "$GOLANG_DOWNLOAD_SHA256 golang.tar.gz" | sha256sum -c - \
|
||||
@@ -31,7 +28,7 @@ ENV PATH /usr/local/go/bin:$PATH
|
||||
|
||||
# Linux and Glibc build dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
gawk make python \
|
||||
bison gawk make python \
|
||||
gcc gcc-multilib \
|
||||
gettext texinfo \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user