Stop vendoring go dns and context (#794)

* Stop vendoring go dns and x/net/context

* dep ensure run

* Fix Makefile

* Fix typo

* Delete context vendor dir
This commit is contained in:
Miek Gieben 2017-07-26 02:24:01 -07:00 committed by GitHub
parent 7d1b0cf2e1
commit f5fbfce876
179 changed files with 38 additions and 60610 deletions

46
Gopkg.lock generated
View file

@ -67,6 +67,12 @@
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
branch = "master"
name = "github.com/dnstap/golang-dnstap"
packages = ["."]
revision = "c32b266c26b040b00f522a0183527a9eb2a1cf8b"
[[projects]]
name = "github.com/docker/distribution"
packages = ["digest","reference"]
@ -97,6 +103,12 @@
revision = "777bb3f19bcafe2575ffb2a3e46af92509ae9594"
version = "v1.2"
[[projects]]
branch = "master"
name = "github.com/farsightsec/golang-framestream"
packages = ["."]
revision = "dec85654e8b8cf6712870afb14ee53d1c98cd5e2"
[[projects]]
name = "github.com/fsnotify/fsnotify"
packages = ["."]
@ -181,12 +193,6 @@
packages = ["go/otgrpc"]
revision = "6c130eed1e297e1aa4d415a50c90d0c81c52677e"
[[projects]]
branch = "master"
name = "github.com/hashicorp/golang-lru"
packages = [".","simplelru"]
revision = "0a025b7e63adc15a622f29b0b2c4c3848243bbf6"
[[projects]]
branch = "master"
name = "github.com/howeyc/gopass"
@ -229,12 +235,6 @@
revision = "3247c84500bff8d9fb6d579d800f20b3e091582c"
version = "v1.0.0"
[[projects]]
branch = "master"
name = "github.com/miekg/dns"
packages = ["."]
revision = "e78414ef75607394ad7d917824f07f381df2eafa"
[[projects]]
name = "github.com/opentracing/opentracing-go"
packages = [".","ext","log"]
@ -313,12 +313,6 @@
packages = ["ssh/terminal"]
revision = "e1a4589e7d3ea14a3352255d04b6f1a418845e5e"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = ["context","context/ctxhttp","http2","http2/hpack","idna","internal/timeseries","lex/httplex","trace"]
revision = "3da985ce5951d99de868be4385f21ea6c2b22f24"
[[projects]]
branch = "master"
name = "golang.org/x/oauth2"
@ -334,7 +328,7 @@
[[projects]]
branch = "master"
name = "golang.org/x/text"
packages = ["internal/gen","internal/triegen","internal/ucd","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable","width"]
packages = ["internal/gen","internal/triegen","internal/ucd","transform","unicode/cldr","unicode/norm","width"]
revision = "4ee4af566555f5fbe026368b75596286a312663a"
[[projects]]
@ -373,21 +367,9 @@
revision = "1195e3a8ee1a529d53eed7c624527a68555ddf1f"
version = "v1.5.1"
[[projects]]
branch = "master"
name = "github.com/farsightsec/golang-framestream"
packages = ["."]
revision = "dec85654e8b8cf6712870afb14ee53d1c98cd5e2"
[[projects]]
branch = "master"
name = "github.com/dnstap/golang-dnstap"
packages = ["."]
revision = "c32b266c26b040b00f522a0183527a9eb2a1cf8b"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "a59be89c7a92b81f951b47efd072c2af04fb66b4f2b7a780cadb7466559d0d6b"
inputs-digest = "0ddaab68018c7f38bccad1d9932df961ae52544dc56e3c0060ce1c28e5da0a47"
solver-name = "gps-cdcl"
solver-version = 1

View file

@ -1,52 +1,18 @@
ignored = [
"github.com/mholt/caddy",
"github.com/mholt/caddy/caddyfile",
"github.com/mholt/caddy/startupshutdown",
"github.com/miekg/dns",
"golang.org/x/net/context",
"golang.org/x/net/context/ctxhttp",
"golang.org/x/net/http2",
"golang.org/x/net/http2/hpack",
"golang.org/x/net/idna",
"golang.org/x/net/internal/timeseries",
"golang.org/x/net/lex/httplex",
"golang.org/x/net/trace",
]
## Gopkg.toml example (these lines may be deleted)
## "required" lists a set of packages (not projects) that must be included in
## Gopkg.lock. This list is merged with the set of packages imported by the current
## project. Use it when your project needs a package it doesn't explicitly import -
## including "main" packages.
# required = ["github.com/user/thing/cmd/thing"]
## "ignored" lists a set of packages (not projects) that are ignored when
## dep statically analyzes source code. Ignored packages can be in this project,
## or in a dependency.
ignored = ["github.com/mholt/caddy", "github.com/mholt/caddy/caddyfile", "github.com/mholt/caddy/startupshutdown"]
## Dependencies define constraints on dependent projects. They are respected by
## dep whether coming from the Gopkg.toml of the current project or a dependency.
# [[dependencies]]
## Required: the root import path of the project being constrained.
# name = "github.com/user/project"
#
## Recommended: the version constraint to enforce for the project.
## Only one of "branch", "version" or "revision" can be specified.
# version = "1.0.0"
# branch = "master"
# revision = "abc123"
#
## Optional: an alternate location (URL or import path) for the project's source.
# source = "https://github.com/myfork/package.git"
## Overrides have the same structure as [[dependencies]], but supercede all
## [[dependencies]] declarations from all projects. Only the current project's
## [[overrides]] are applied.
##
## Overrides are a sledgehammer. Use them only as a last resort.
# [[overrides]]
## Required: the root import path of the project being constrained.
# name = "github.com/user/project"
#
## Optional: specifying a version constraint override will cause all other
## constraints on this project to be ignored; only the overriden constraint
## need be satisfied.
## Again, only one of "branch", "version" or "revision" can be specified.
# version = "1.0.0"
# branch = "master"
# revision = "abc123"
#
## Optional: specifying an alternate source location as an override will
## enforce that the alternate location is used for that project, regardless of
## what source location any dependent projects specify.
# source = "https://github.com/myfork/package.git"
[[constraint]]
name = "github.com/openzipkin/zipkin-go-opentracing"
version = "v0.2.3"

View file

@ -7,11 +7,11 @@ all: coredns
# Phony this to ensure we always build the binary.
# TODO: Add .go file dependencies.
.PHONY: coredns
coredns: check caddy
coredns: check godeps
go build $(BUILD_VERBOSE) -ldflags="-s -w"
.PHONY: deps
deps: core/zmiddleware.go core/dnsserver/zdirectives.go caddy
deps: core/zmiddleware.go core/dnsserver/zdirectives.go godeps
go get -u github.com/golang/lint/golint
.PHONY: check
@ -25,9 +25,12 @@ test: check
testk8s: check
go test -race $(TEST_VERBOSE) -tags=k8s -run 'TestKubernetes' ./test ./middleware/kubernetes/...
.PHONY: caddy
caddy:
.PHONY: godeps
godeps:
go get github.com/mholt/caddy
go get github.com/miekg/dns
go get golang.org/x/net/context
go get golang.org/x/text
.PHONY: coverage
coverage: check

View file

@ -1,23 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test

View file

@ -1,212 +0,0 @@
package lru
import (
"fmt"
"sync"
"github.com/hashicorp/golang-lru/simplelru"
)
const (
// Default2QRecentRatio is the ratio of the 2Q cache dedicated
// to recently added entries that have only been accessed once.
Default2QRecentRatio = 0.25
// Default2QGhostEntries is the default ratio of ghost
// entries kept to track entries recently evicted
Default2QGhostEntries = 0.50
)
// TwoQueueCache is a thread-safe fixed size 2Q cache.
// 2Q is an enhancement over the standard LRU cache
// in that it tracks both frequently and recently used
// entries separately. This avoids a burst in access to new
// entries from evicting frequently used entries. It adds some
// additional tracking overhead to the standard LRU cache, and is
// computationally about 2x the cost, and adds some metadata over
// head. The ARCCache is similar, but does not require setting any
// parameters.
type TwoQueueCache struct {
size int
recentSize int
recent *simplelru.LRU
frequent *simplelru.LRU
recentEvict *simplelru.LRU
lock sync.RWMutex
}
// New2Q creates a new TwoQueueCache using the default
// values for the parameters.
func New2Q(size int) (*TwoQueueCache, error) {
return New2QParams(size, Default2QRecentRatio, Default2QGhostEntries)
}
// New2QParams creates a new TwoQueueCache using the provided
// parameter values.
func New2QParams(size int, recentRatio float64, ghostRatio float64) (*TwoQueueCache, error) {
if size <= 0 {
return nil, fmt.Errorf("invalid size")
}
if recentRatio < 0.0 || recentRatio > 1.0 {
return nil, fmt.Errorf("invalid recent ratio")
}
if ghostRatio < 0.0 || ghostRatio > 1.0 {
return nil, fmt.Errorf("invalid ghost ratio")
}
// Determine the sub-sizes
recentSize := int(float64(size) * recentRatio)
evictSize := int(float64(size) * ghostRatio)
// Allocate the LRUs
recent, err := simplelru.NewLRU(size, nil)
if err != nil {
return nil, err
}
frequent, err := simplelru.NewLRU(size, nil)
if err != nil {
return nil, err
}
recentEvict, err := simplelru.NewLRU(evictSize, nil)
if err != nil {
return nil, err
}
// Initialize the cache
c := &TwoQueueCache{
size: size,
recentSize: recentSize,
recent: recent,
frequent: frequent,
recentEvict: recentEvict,
}
return c, nil
}
func (c *TwoQueueCache) Get(key interface{}) (interface{}, bool) {
c.lock.Lock()
defer c.lock.Unlock()
// Check if this is a frequent value
if val, ok := c.frequent.Get(key); ok {
return val, ok
}
// If the value is contained in recent, then we
// promote it to frequent
if val, ok := c.recent.Peek(key); ok {
c.recent.Remove(key)
c.frequent.Add(key, val)
return val, ok
}
// No hit
return nil, false
}
func (c *TwoQueueCache) Add(key, value interface{}) {
c.lock.Lock()
defer c.lock.Unlock()
// Check if the value is frequently used already,
// and just update the value
if c.frequent.Contains(key) {
c.frequent.Add(key, value)
return
}
// Check if the value is recently used, and promote
// the value into the frequent list
if c.recent.Contains(key) {
c.recent.Remove(key)
c.frequent.Add(key, value)
return
}
// If the value was recently evicted, add it to the
// frequently used list
if c.recentEvict.Contains(key) {
c.ensureSpace(true)
c.recentEvict.Remove(key)
c.frequent.Add(key, value)
return
}
// Add to the recently seen list
c.ensureSpace(false)
c.recent.Add(key, value)
return
}
// ensureSpace is used to ensure we have space in the cache
func (c *TwoQueueCache) ensureSpace(recentEvict bool) {
// If we have space, nothing to do
recentLen := c.recent.Len()
freqLen := c.frequent.Len()
if recentLen+freqLen < c.size {
return
}
// If the recent buffer is larger than
// the target, evict from there
if recentLen > 0 && (recentLen > c.recentSize || (recentLen == c.recentSize && !recentEvict)) {
k, _, _ := c.recent.RemoveOldest()
c.recentEvict.Add(k, nil)
return
}
// Remove from the frequent list otherwise
c.frequent.RemoveOldest()
}
func (c *TwoQueueCache) Len() int {
c.lock.RLock()
defer c.lock.RUnlock()
return c.recent.Len() + c.frequent.Len()
}
func (c *TwoQueueCache) Keys() []interface{} {
c.lock.RLock()
defer c.lock.RUnlock()
k1 := c.frequent.Keys()
k2 := c.recent.Keys()
return append(k1, k2...)
}
func (c *TwoQueueCache) Remove(key interface{}) {
c.lock.Lock()
defer c.lock.Unlock()
if c.frequent.Remove(key) {
return
}
if c.recent.Remove(key) {
return
}
if c.recentEvict.Remove(key) {
return
}
}
func (c *TwoQueueCache) Purge() {
c.lock.Lock()
defer c.lock.Unlock()
c.recent.Purge()
c.frequent.Purge()
c.recentEvict.Purge()
}
func (c *TwoQueueCache) Contains(key interface{}) bool {
c.lock.RLock()
defer c.lock.RUnlock()
return c.frequent.Contains(key) || c.recent.Contains(key)
}
func (c *TwoQueueCache) Peek(key interface{}) (interface{}, bool) {
c.lock.RLock()
defer c.lock.RUnlock()
if val, ok := c.frequent.Peek(key); ok {
return val, ok
}
return c.recent.Peek(key)
}

View file

@ -1,306 +0,0 @@
package lru
import (
"math/rand"
"testing"
)
func Benchmark2Q_Rand(b *testing.B) {
l, err := New2Q(8192)
if err != nil {
b.Fatalf("err: %v", err)
}
trace := make([]int64, b.N*2)
for i := 0; i < b.N*2; i++ {
trace[i] = rand.Int63() % 32768
}
b.ResetTimer()
var hit, miss int
for i := 0; i < 2*b.N; i++ {
if i%2 == 0 {
l.Add(trace[i], trace[i])
} else {
_, ok := l.Get(trace[i])
if ok {
hit++
} else {
miss++
}
}
}
b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(miss))
}
func Benchmark2Q_Freq(b *testing.B) {
l, err := New2Q(8192)
if err != nil {
b.Fatalf("err: %v", err)
}
trace := make([]int64, b.N*2)
for i := 0; i < b.N*2; i++ {
if i%2 == 0 {
trace[i] = rand.Int63() % 16384
} else {
trace[i] = rand.Int63() % 32768
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
l.Add(trace[i], trace[i])
}
var hit, miss int
for i := 0; i < b.N; i++ {
_, ok := l.Get(trace[i])
if ok {
hit++
} else {
miss++
}
}
b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(miss))
}
func Test2Q_RandomOps(t *testing.T) {
size := 128
l, err := New2Q(128)
if err != nil {
t.Fatalf("err: %v", err)
}
n := 200000
for i := 0; i < n; i++ {
key := rand.Int63() % 512
r := rand.Int63()
switch r % 3 {
case 0:
l.Add(key, key)
case 1:
l.Get(key)
case 2:
l.Remove(key)
}
if l.recent.Len()+l.frequent.Len() > size {
t.Fatalf("bad: recent: %d freq: %d",
l.recent.Len(), l.frequent.Len())
}
}
}
func Test2Q_Get_RecentToFrequent(t *testing.T) {
l, err := New2Q(128)
if err != nil {
t.Fatalf("err: %v", err)
}
// Touch all the entries, should be in t1
for i := 0; i < 128; i++ {
l.Add(i, i)
}
if n := l.recent.Len(); n != 128 {
t.Fatalf("bad: %d", n)
}
if n := l.frequent.Len(); n != 0 {
t.Fatalf("bad: %d", n)
}
// Get should upgrade to t2
for i := 0; i < 128; i++ {
_, ok := l.Get(i)
if !ok {
t.Fatalf("missing: %d", i)
}
}
if n := l.recent.Len(); n != 0 {
t.Fatalf("bad: %d", n)
}
if n := l.frequent.Len(); n != 128 {
t.Fatalf("bad: %d", n)
}
// Get be from t2
for i := 0; i < 128; i++ {
_, ok := l.Get(i)
if !ok {
t.Fatalf("missing: %d", i)
}
}
if n := l.recent.Len(); n != 0 {
t.Fatalf("bad: %d", n)
}
if n := l.frequent.Len(); n != 128 {
t.Fatalf("bad: %d", n)
}
}
func Test2Q_Add_RecentToFrequent(t *testing.T) {
l, err := New2Q(128)
if err != nil {
t.Fatalf("err: %v", err)
}
// Add initially to recent
l.Add(1, 1)
if n := l.recent.Len(); n != 1 {
t.Fatalf("bad: %d", n)
}
if n := l.frequent.Len(); n != 0 {
t.Fatalf("bad: %d", n)
}
// Add should upgrade to frequent
l.Add(1, 1)
if n := l.recent.Len(); n != 0 {
t.Fatalf("bad: %d", n)
}
if n := l.frequent.Len(); n != 1 {
t.Fatalf("bad: %d", n)
}
// Add should remain in frequent
l.Add(1, 1)
if n := l.recent.Len(); n != 0 {
t.Fatalf("bad: %d", n)
}
if n := l.frequent.Len(); n != 1 {
t.Fatalf("bad: %d", n)
}
}
func Test2Q_Add_RecentEvict(t *testing.T) {
l, err := New2Q(4)
if err != nil {
t.Fatalf("err: %v", err)
}
// Add 1,2,3,4,5 -> Evict 1
l.Add(1, 1)
l.Add(2, 2)
l.Add(3, 3)
l.Add(4, 4)
l.Add(5, 5)
if n := l.recent.Len(); n != 4 {
t.Fatalf("bad: %d", n)
}
if n := l.recentEvict.Len(); n != 1 {
t.Fatalf("bad: %d", n)
}
if n := l.frequent.Len(); n != 0 {
t.Fatalf("bad: %d", n)
}
// Pull in the recently evicted
l.Add(1, 1)
if n := l.recent.Len(); n != 3 {
t.Fatalf("bad: %d", n)
}
if n := l.recentEvict.Len(); n != 1 {
t.Fatalf("bad: %d", n)
}
if n := l.frequent.Len(); n != 1 {
t.Fatalf("bad: %d", n)
}
// Add 6, should cause another recent evict
l.Add(6, 6)
if n := l.recent.Len(); n != 3 {
t.Fatalf("bad: %d", n)
}
if n := l.recentEvict.Len(); n != 2 {
t.Fatalf("bad: %d", n)
}
if n := l.frequent.Len(); n != 1 {
t.Fatalf("bad: %d", n)
}
}
func Test2Q(t *testing.T) {
l, err := New2Q(128)
if err != nil {
t.Fatalf("err: %v", err)
}
for i := 0; i < 256; i++ {
l.Add(i, i)
}
if l.Len() != 128 {
t.Fatalf("bad len: %v", l.Len())
}
for i, k := range l.Keys() {
if v, ok := l.Get(k); !ok || v != k || v != i+128 {
t.Fatalf("bad key: %v", k)
}
}
for i := 0; i < 128; i++ {
_, ok := l.Get(i)
if ok {
t.Fatalf("should be evicted")
}
}
for i := 128; i < 256; i++ {
_, ok := l.Get(i)
if !ok {
t.Fatalf("should not be evicted")
}
}
for i := 128; i < 192; i++ {
l.Remove(i)
_, ok := l.Get(i)
if ok {
t.Fatalf("should be deleted")
}
}
l.Purge()
if l.Len() != 0 {
t.Fatalf("bad len: %v", l.Len())
}
if _, ok := l.Get(200); ok {
t.Fatalf("should contain nothing")
}
}
// Test that Contains doesn't update recent-ness
func Test2Q_Contains(t *testing.T) {
l, err := New2Q(2)
if err != nil {
t.Fatalf("err: %v", err)
}
l.Add(1, 1)
l.Add(2, 2)
if !l.Contains(1) {
t.Errorf("1 should be contained")
}
l.Add(3, 3)
if l.Contains(1) {
t.Errorf("Contains should not have updated recent-ness of 1")
}
}
// Test that Peek doesn't update recent-ness
func Test2Q_Peek(t *testing.T) {
l, err := New2Q(2)
if err != nil {
t.Fatalf("err: %v", err)
}
l.Add(1, 1)
l.Add(2, 2)
if v, ok := l.Peek(1); !ok || v != 1 {
t.Errorf("1 should be set to 1: %v, %v", v, ok)
}
l.Add(3, 3)
if l.Contains(1) {
t.Errorf("should not have updated recent-ness of 1")
}
}

View file

@ -1,362 +0,0 @@
Mozilla Public License, version 2.0
1. Definitions
1.1. "Contributor"
means each individual or legal entity that creates, contributes to the
creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used by a
Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached the
notice in Exhibit A, the Executable Form of such Source Code Form, and
Modifications of such Source Code Form, in each case including portions
thereof.
1.5. "Incompatible With Secondary Licenses"
means
a. that the initial Contributor has attached the notice described in
Exhibit B to the Covered Software; or
b. that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the terms of
a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in a
separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible, whether
at the time of the initial grant or subsequently, any and all of the
rights conveyed by this License.
1.10. "Modifications"
means any of the following:
a. any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered Software; or
b. any new file in Source Code Form that contains any Covered Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the License,
by the making, using, selling, offering for sale, having made, import,
or transfer of either its Contributions or its Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU Lesser
General Public License, Version 2.1, the GNU Affero General Public
License, Version 3.0, or any later versions of those licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that controls, is
controlled by, or is under common control with You. For purposes of this
definition, "control" means (a) the power, direct or indirect, to cause
the direction or management of such entity, whether by contract or
otherwise, or (b) ownership of more than fifty percent (50%) of the
outstanding shares or beneficial ownership of such entity.
2. License Grants and Conditions
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
a. under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
b. under Patent Claims of such Contributor to make, use, sell, offer for
sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
a. for any code that a Contributor has removed from Covered Software; or
b. for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
c. under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights to
grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
Section 2.1.
3. Responsibilities
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
a. such Covered Software must also be made available in Source Code Form,
as described in Section 3.1, and You must inform recipients of the
Executable Form how they can obtain a copy of such Source Code Form by
reasonable means in a timely manner, at a charge no more than the cost
of distribution to the recipient; and
b. You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter the
recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty, or
limitations of liability) contained within the Source Code Form of the
Covered Software, except that You may alter any license notices to the
extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
If it is impossible for You to comply with any of the terms of this License
with respect to some or all of the Covered Software due to statute,
judicial order, or regulation then You must: (a) comply with the terms of
this License to the maximum extent possible; and (b) describe the
limitations and the code they affect. Such description must be placed in a
text file included with all distributions of the Covered Software under
this License. Except to the extent prohibited by statute or regulation,
such description must be sufficiently detailed for a recipient of ordinary
skill to be able to understand it.
5. Termination
5.1. The rights granted under this License will terminate automatically if You
fail to comply with any of its terms. However, if You become compliant,
then the rights granted under this License from a particular Contributor
are reinstated (a) provisionally, unless and until such Contributor
explicitly and finally terminates Your grants, and (b) on an ongoing
basis, if such Contributor fails to notify You of the non-compliance by
some reasonable means prior to 60 days after You have come back into
compliance. Moreover, Your grants from a particular Contributor are
reinstated on an ongoing basis if such Contributor notifies You of the
non-compliance by some reasonable means, this is the first time You have
received notice of non-compliance with this License from such
Contributor, and You become compliant prior to 30 days after Your receipt
of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
license agreements (excluding distributors and resellers) which have been
validly granted by You or Your distributors under this License prior to
termination shall survive termination.
6. Disclaimer of Warranty
Covered Software is provided under this License on an "as is" basis,
without warranty of any kind, either expressed, implied, or statutory,
including, without limitation, warranties that the Covered Software is free
of defects, merchantable, fit for a particular purpose or non-infringing.
The entire risk as to the quality and performance of the Covered Software
is with You. Should any Covered Software prove defective in any respect,
You (not any Contributor) assume the cost of any necessary servicing,
repair, or correction. This disclaimer of warranty constitutes an essential
part of this License. No use of any Covered Software is authorized under
this License except under this disclaimer.
7. Limitation of Liability
Under no circumstances and under no legal theory, whether tort (including
negligence), contract, or otherwise, shall any Contributor, or anyone who
distributes Covered Software as permitted above, be liable to You for any
direct, indirect, special, incidental, or consequential damages of any
character including, without limitation, damages for lost profits, loss of
goodwill, work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses, even if such party shall have been
informed of the possibility of such damages. This limitation of liability
shall not apply to liability for death or personal injury resulting from
such party's negligence to the extent applicable law prohibits such
limitation. Some jurisdictions do not allow the exclusion or limitation of
incidental or consequential damages, so this exclusion and limitation may
not apply to You.
8. Litigation
Any litigation relating to this License may be brought only in the courts
of a jurisdiction where the defendant maintains its principal place of
business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions. Nothing
in this Section shall prevent a party's ability to bring cross-claims or
counter-claims.
9. Miscellaneous
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides that
the language of a contract shall be construed against the drafter shall not
be used to construe this License against a Contributor.
10. Versions of the License
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses If You choose to distribute Source Code Form that is
Incompatible With Secondary Licenses under the terms of this version of
the License, the notice described in Exhibit B of this License must be
attached.
Exhibit A - Source Code Form License Notice
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular file,
then You may include the notice in a location (such as a LICENSE file in a
relevant directory) where a recipient would be likely to look for such a
notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
This Source Code Form is "Incompatible
With Secondary Licenses", as defined by
the Mozilla Public License, v. 2.0.

View file

@ -1,25 +0,0 @@
golang-lru
==========
This provides the `lru` package which implements a fixed-size
thread safe LRU cache. It is based on the cache in Groupcache.
Documentation
=============
Full docs are available on [Godoc](http://godoc.org/github.com/hashicorp/golang-lru)
Example
=======
Using the LRU is very simple:
```go
l, _ := New(128)
for i := 0; i < 256; i++ {
l.Add(i, nil)
}
if l.Len() != 128 {
panic(fmt.Sprintf("bad len: %v", l.Len()))
}
```

View file

@ -1,257 +0,0 @@
package lru
import (
"sync"
"github.com/hashicorp/golang-lru/simplelru"
)
// ARCCache is a thread-safe fixed size Adaptive Replacement Cache (ARC).
// ARC is an enhancement over the standard LRU cache in that tracks both
// frequency and recency of use. This avoids a burst in access to new
// entries from evicting the frequently used older entries. It adds some
// additional tracking overhead to a standard LRU cache, computationally
// it is roughly 2x the cost, and the extra memory overhead is linear
// with the size of the cache. ARC has been patented by IBM, but is
// similar to the TwoQueueCache (2Q) which requires setting parameters.
type ARCCache struct {
size int // Size is the total capacity of the cache
p int // P is the dynamic preference towards T1 or T2
t1 *simplelru.LRU // T1 is the LRU for recently accessed items
b1 *simplelru.LRU // B1 is the LRU for evictions from t1
t2 *simplelru.LRU // T2 is the LRU for frequently accessed items
b2 *simplelru.LRU // B2 is the LRU for evictions from t2
lock sync.RWMutex
}
// NewARC creates an ARC of the given size
func NewARC(size int) (*ARCCache, error) {
// Create the sub LRUs
b1, err := simplelru.NewLRU(size, nil)
if err != nil {
return nil, err
}
b2, err := simplelru.NewLRU(size, nil)
if err != nil {
return nil, err
}
t1, err := simplelru.NewLRU(size, nil)
if err != nil {
return nil, err
}
t2, err := simplelru.NewLRU(size, nil)
if err != nil {
return nil, err
}
// Initialize the ARC
c := &ARCCache{
size: size,
p: 0,
t1: t1,
b1: b1,
t2: t2,
b2: b2,
}
return c, nil
}
// Get looks up a key's value from the cache.
func (c *ARCCache) Get(key interface{}) (interface{}, bool) {
c.lock.Lock()
defer c.lock.Unlock()
// Ff the value is contained in T1 (recent), then
// promote it to T2 (frequent)
if val, ok := c.t1.Peek(key); ok {
c.t1.Remove(key)
c.t2.Add(key, val)
return val, ok
}
// Check if the value is contained in T2 (frequent)
if val, ok := c.t2.Get(key); ok {
return val, ok
}
// No hit
return nil, false
}
// Add adds a value to the cache.
func (c *ARCCache) Add(key, value interface{}) {
c.lock.Lock()
defer c.lock.Unlock()
// Check if the value is contained in T1 (recent), and potentially
// promote it to frequent T2
if c.t1.Contains(key) {
c.t1.Remove(key)
c.t2.Add(key, value)
return
}
// Check if the value is already in T2 (frequent) and update it
if c.t2.Contains(key) {
c.t2.Add(key, value)
return
}
// Check if this value was recently evicted as part of the
// recently used list
if c.b1.Contains(key) {
// T1 set is too small, increase P appropriately
delta := 1
b1Len := c.b1.Len()
b2Len := c.b2.Len()
if b2Len > b1Len {
delta = b2Len / b1Len
}
if c.p+delta >= c.size {
c.p = c.size
} else {
c.p += delta
}
// Potentially need to make room in the cache
if c.t1.Len()+c.t2.Len() >= c.size {
c.replace(false)
}
// Remove from B1
c.b1.Remove(key)
// Add the key to the frequently used list
c.t2.Add(key, value)
return
}
// Check if this value was recently evicted as part of the
// frequently used list
if c.b2.Contains(key) {
// T2 set is too small, decrease P appropriately
delta := 1
b1Len := c.b1.Len()
b2Len := c.b2.Len()
if b1Len > b2Len {
delta = b1Len / b2Len
}
if delta >= c.p {
c.p = 0
} else {
c.p -= delta
}
// Potentially need to make room in the cache
if c.t1.Len()+c.t2.Len() >= c.size {
c.replace(true)
}
// Remove from B2
c.b2.Remove(key)
// Add the key to the frequntly used list
c.t2.Add(key, value)
return
}
// Potentially need to make room in the cache
if c.t1.Len()+c.t2.Len() >= c.size {
c.replace(false)
}
// Keep the size of the ghost buffers trim
if c.b1.Len() > c.size-c.p {
c.b1.RemoveOldest()
}
if c.b2.Len() > c.p {
c.b2.RemoveOldest()
}
// Add to the recently seen list
c.t1.Add(key, value)
return
}
// replace is used to adaptively evict from either T1 or T2
// based on the current learned value of P
func (c *ARCCache) replace(b2ContainsKey bool) {
t1Len := c.t1.Len()
if t1Len > 0 && (t1Len > c.p || (t1Len == c.p && b2ContainsKey)) {
k, _, ok := c.t1.RemoveOldest()
if ok {
c.b1.Add(k, nil)
}
} else {
k, _, ok := c.t2.RemoveOldest()
if ok {
c.b2.Add(k, nil)
}
}
}
// Len returns the number of cached entries
func (c *ARCCache) Len() int {
c.lock.RLock()
defer c.lock.RUnlock()
return c.t1.Len() + c.t2.Len()
}
// Keys returns all the cached keys
func (c *ARCCache) Keys() []interface{} {
c.lock.RLock()
defer c.lock.RUnlock()
k1 := c.t1.Keys()
k2 := c.t2.Keys()
return append(k1, k2...)
}
// Remove is used to purge a key from the cache
func (c *ARCCache) Remove(key interface{}) {
c.lock.Lock()
defer c.lock.Unlock()
if c.t1.Remove(key) {
return
}
if c.t2.Remove(key) {
return
}
if c.b1.Remove(key) {
return
}
if c.b2.Remove(key) {
return
}
}
// Purge is used to clear the cache
func (c *ARCCache) Purge() {
c.lock.Lock()
defer c.lock.Unlock()
c.t1.Purge()
c.t2.Purge()
c.b1.Purge()
c.b2.Purge()
}
// Contains is used to check if the cache contains a key
// without updating recency or frequency.
func (c *ARCCache) Contains(key interface{}) bool {
c.lock.RLock()
defer c.lock.RUnlock()
return c.t1.Contains(key) || c.t2.Contains(key)
}
// Peek is used to inspect the cache value of a key
// without updating recency or frequency.
func (c *ARCCache) Peek(key interface{}) (interface{}, bool) {
c.lock.RLock()
defer c.lock.RUnlock()
if val, ok := c.t1.Peek(key); ok {
return val, ok
}
return c.t2.Peek(key)
}

View file

@ -1,377 +0,0 @@
package lru
import (
"math/rand"
"testing"
"time"
)
func init() {
rand.Seed(time.Now().Unix())
}
func BenchmarkARC_Rand(b *testing.B) {
l, err := NewARC(8192)
if err != nil {
b.Fatalf("err: %v", err)
}
trace := make([]int64, b.N*2)
for i := 0; i < b.N*2; i++ {
trace[i] = rand.Int63() % 32768
}
b.ResetTimer()
var hit, miss int
for i := 0; i < 2*b.N; i++ {
if i%2 == 0 {
l.Add(trace[i], trace[i])
} else {
_, ok := l.Get(trace[i])
if ok {
hit++
} else {
miss++
}
}
}
b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(miss))
}
func BenchmarkARC_Freq(b *testing.B) {
l, err := NewARC(8192)
if err != nil {
b.Fatalf("err: %v", err)
}
trace := make([]int64, b.N*2)
for i := 0; i < b.N*2; i++ {
if i%2 == 0 {
trace[i] = rand.Int63() % 16384
} else {
trace[i] = rand.Int63() % 32768
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
l.Add(trace[i], trace[i])
}
var hit, miss int
for i := 0; i < b.N; i++ {
_, ok := l.Get(trace[i])
if ok {
hit++
} else {
miss++
}
}
b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(miss))
}
func TestARC_RandomOps(t *testing.T) {
size := 128
l, err := NewARC(128)
if err != nil {
t.Fatalf("err: %v", err)
}
n := 200000
for i := 0; i < n; i++ {
key := rand.Int63() % 512
r := rand.Int63()
switch r % 3 {
case 0:
l.Add(key, key)
case 1:
l.Get(key)
case 2:
l.Remove(key)
}
if l.t1.Len()+l.t2.Len() > size {
t.Fatalf("bad: t1: %d t2: %d b1: %d b2: %d p: %d",
l.t1.Len(), l.t2.Len(), l.b1.Len(), l.b2.Len(), l.p)
}
if l.b1.Len()+l.b2.Len() > size {
t.Fatalf("bad: t1: %d t2: %d b1: %d b2: %d p: %d",
l.t1.Len(), l.t2.Len(), l.b1.Len(), l.b2.Len(), l.p)
}
}
}
func TestARC_Get_RecentToFrequent(t *testing.T) {
l, err := NewARC(128)
if err != nil {
t.Fatalf("err: %v", err)
}
// Touch all the entries, should be in t1
for i := 0; i < 128; i++ {
l.Add(i, i)
}
if n := l.t1.Len(); n != 128 {
t.Fatalf("bad: %d", n)
}
if n := l.t2.Len(); n != 0 {
t.Fatalf("bad: %d", n)
}
// Get should upgrade to t2
for i := 0; i < 128; i++ {
_, ok := l.Get(i)
if !ok {
t.Fatalf("missing: %d", i)
}
}
if n := l.t1.Len(); n != 0 {
t.Fatalf("bad: %d", n)
}
if n := l.t2.Len(); n != 128 {
t.Fatalf("bad: %d", n)
}
// Get be from t2
for i := 0; i < 128; i++ {
_, ok := l.Get(i)
if !ok {
t.Fatalf("missing: %d", i)
}
}
if n := l.t1.Len(); n != 0 {
t.Fatalf("bad: %d", n)
}
if n := l.t2.Len(); n != 128 {
t.Fatalf("bad: %d", n)
}
}
func TestARC_Add_RecentToFrequent(t *testing.T) {
l, err := NewARC(128)
if err != nil {
t.Fatalf("err: %v", err)
}
// Add initially to t1
l.Add(1, 1)
if n := l.t1.Len(); n != 1 {
t.Fatalf("bad: %d", n)
}
if n := l.t2.Len(); n != 0 {
t.Fatalf("bad: %d", n)
}
// Add should upgrade to t2
l.Add(1, 1)
if n := l.t1.Len(); n != 0 {
t.Fatalf("bad: %d", n)
}
if n := l.t2.Len(); n != 1 {
t.Fatalf("bad: %d", n)
}
// Add should remain in t2
l.Add(1, 1)
if n := l.t1.Len(); n != 0 {
t.Fatalf("bad: %d", n)
}
if n := l.t2.Len(); n != 1 {
t.Fatalf("bad: %d", n)
}
}
func TestARC_Adaptive(t *testing.T) {
l, err := NewARC(4)
if err != nil {
t.Fatalf("err: %v", err)
}
// Fill t1
for i := 0; i < 4; i++ {
l.Add(i, i)
}
if n := l.t1.Len(); n != 4 {
t.Fatalf("bad: %d", n)
}
// Move to t2
l.Get(0)
l.Get(1)
if n := l.t2.Len(); n != 2 {
t.Fatalf("bad: %d", n)
}
// Evict from t1
l.Add(4, 4)
if n := l.b1.Len(); n != 1 {
t.Fatalf("bad: %d", n)
}
// Current state
// t1 : (MRU) [4, 3] (LRU)
// t2 : (MRU) [1, 0] (LRU)
// b1 : (MRU) [2] (LRU)
// b2 : (MRU) [] (LRU)
// Add 2, should cause hit on b1
l.Add(2, 2)
if n := l.b1.Len(); n != 1 {
t.Fatalf("bad: %d", n)
}
if l.p != 1 {
t.Fatalf("bad: %d", l.p)
}
if n := l.t2.Len(); n != 3 {
t.Fatalf("bad: %d", n)
}
// Current state
// t1 : (MRU) [4] (LRU)
// t2 : (MRU) [2, 1, 0] (LRU)
// b1 : (MRU) [3] (LRU)
// b2 : (MRU) [] (LRU)
// Add 4, should migrate to t2
l.Add(4, 4)
if n := l.t1.Len(); n != 0 {
t.Fatalf("bad: %d", n)
}
if n := l.t2.Len(); n != 4 {
t.Fatalf("bad: %d", n)
}
// Current state
// t1 : (MRU) [] (LRU)
// t2 : (MRU) [4, 2, 1, 0] (LRU)
// b1 : (MRU) [3] (LRU)
// b2 : (MRU) [] (LRU)
// Add 4, should evict to b2
l.Add(5, 5)
if n := l.t1.Len(); n != 1 {
t.Fatalf("bad: %d", n)
}
if n := l.t2.Len(); n != 3 {
t.Fatalf("bad: %d", n)
}
if n := l.b2.Len(); n != 1 {
t.Fatalf("bad: %d", n)
}
// Current state
// t1 : (MRU) [5] (LRU)
// t2 : (MRU) [4, 2, 1] (LRU)
// b1 : (MRU) [3] (LRU)
// b2 : (MRU) [0] (LRU)
// Add 0, should decrease p
l.Add(0, 0)
if n := l.t1.Len(); n != 0 {
t.Fatalf("bad: %d", n)
}
if n := l.t2.Len(); n != 4 {
t.Fatalf("bad: %d", n)
}
if n := l.b1.Len(); n != 2 {
t.Fatalf("bad: %d", n)
}
if n := l.b2.Len(); n != 0 {
t.Fatalf("bad: %d", n)
}
if l.p != 0 {
t.Fatalf("bad: %d", l.p)
}
// Current state
// t1 : (MRU) [] (LRU)
// t2 : (MRU) [0, 4, 2, 1] (LRU)
// b1 : (MRU) [5, 3] (LRU)
// b2 : (MRU) [0] (LRU)
}
func TestARC(t *testing.T) {
l, err := NewARC(128)
if err != nil {
t.Fatalf("err: %v", err)
}
for i := 0; i < 256; i++ {
l.Add(i, i)
}
if l.Len() != 128 {
t.Fatalf("bad len: %v", l.Len())
}
for i, k := range l.Keys() {
if v, ok := l.Get(k); !ok || v != k || v != i+128 {
t.Fatalf("bad key: %v", k)
}
}
for i := 0; i < 128; i++ {
_, ok := l.Get(i)
if ok {
t.Fatalf("should be evicted")
}
}
for i := 128; i < 256; i++ {
_, ok := l.Get(i)
if !ok {
t.Fatalf("should not be evicted")
}
}
for i := 128; i < 192; i++ {
l.Remove(i)
_, ok := l.Get(i)
if ok {
t.Fatalf("should be deleted")
}
}
l.Purge()
if l.Len() != 0 {
t.Fatalf("bad len: %v", l.Len())
}
if _, ok := l.Get(200); ok {
t.Fatalf("should contain nothing")
}
}
// Test that Contains doesn't update recent-ness
func TestARC_Contains(t *testing.T) {
l, err := NewARC(2)
if err != nil {
t.Fatalf("err: %v", err)
}
l.Add(1, 1)
l.Add(2, 2)
if !l.Contains(1) {
t.Errorf("1 should be contained")
}
l.Add(3, 3)
if l.Contains(1) {
t.Errorf("Contains should not have updated recent-ness of 1")
}
}
// Test that Peek doesn't update recent-ness
func TestARC_Peek(t *testing.T) {
l, err := NewARC(2)
if err != nil {
t.Fatalf("err: %v", err)
}
l.Add(1, 1)
l.Add(2, 2)
if v, ok := l.Peek(1); !ok || v != 1 {
t.Errorf("1 should be set to 1: %v, %v", v, ok)
}
l.Add(3, 3)
if l.Contains(1) {
t.Errorf("should not have updated recent-ness of 1")
}
}

View file

@ -1,114 +0,0 @@
// This package provides a simple LRU cache. It is based on the
// LRU implementation in groupcache:
// https://github.com/golang/groupcache/tree/master/lru
package lru
import (
"sync"
"github.com/hashicorp/golang-lru/simplelru"
)
// Cache is a thread-safe fixed size LRU cache.
type Cache struct {
lru *simplelru.LRU
lock sync.RWMutex
}
// New creates an LRU of the given size
func New(size int) (*Cache, error) {
return NewWithEvict(size, nil)
}
// NewWithEvict constructs a fixed size cache with the given eviction
// callback.
func NewWithEvict(size int, onEvicted func(key interface{}, value interface{})) (*Cache, error) {
lru, err := simplelru.NewLRU(size, simplelru.EvictCallback(onEvicted))
if err != nil {
return nil, err
}
c := &Cache{
lru: lru,
}
return c, nil
}
// Purge is used to completely clear the cache
func (c *Cache) Purge() {
c.lock.Lock()
c.lru.Purge()
c.lock.Unlock()
}
// Add adds a value to the cache. Returns true if an eviction occurred.
func (c *Cache) Add(key, value interface{}) bool {
c.lock.Lock()
defer c.lock.Unlock()
return c.lru.Add(key, value)
}
// Get looks up a key's value from the cache.
func (c *Cache) Get(key interface{}) (interface{}, bool) {
c.lock.Lock()
defer c.lock.Unlock()
return c.lru.Get(key)
}
// Check if a key is in the cache, without updating the recent-ness
// or deleting it for being stale.
func (c *Cache) Contains(key interface{}) bool {
c.lock.RLock()
defer c.lock.RUnlock()
return c.lru.Contains(key)
}
// Returns the key value (or undefined if not found) without updating
// the "recently used"-ness of the key.
func (c *Cache) Peek(key interface{}) (interface{}, bool) {
c.lock.RLock()
defer c.lock.RUnlock()
return c.lru.Peek(key)
}
// ContainsOrAdd checks if a key is in the cache without updating the
// recent-ness or deleting it for being stale, and if not, adds the value.
// Returns whether found and whether an eviction occurred.
func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evict bool) {
c.lock.Lock()
defer c.lock.Unlock()
if c.lru.Contains(key) {
return true, false
} else {
evict := c.lru.Add(key, value)
return false, evict
}
}
// Remove removes the provided key from the cache.
func (c *Cache) Remove(key interface{}) {
c.lock.Lock()
c.lru.Remove(key)
c.lock.Unlock()
}
// RemoveOldest removes the oldest item from the cache.
func (c *Cache) RemoveOldest() {
c.lock.Lock()
c.lru.RemoveOldest()
c.lock.Unlock()
}
// Keys returns a slice of the keys in the cache, from oldest to newest.
func (c *Cache) Keys() []interface{} {
c.lock.RLock()
defer c.lock.RUnlock()
return c.lru.Keys()
}
// Len returns the number of items in the cache.
func (c *Cache) Len() int {
c.lock.RLock()
defer c.lock.RUnlock()
return c.lru.Len()
}

View file

@ -1,221 +0,0 @@
package lru
import (
"math/rand"
"testing"
)
func BenchmarkLRU_Rand(b *testing.B) {
l, err := New(8192)
if err != nil {
b.Fatalf("err: %v", err)
}
trace := make([]int64, b.N*2)
for i := 0; i < b.N*2; i++ {
trace[i] = rand.Int63() % 32768
}
b.ResetTimer()
var hit, miss int
for i := 0; i < 2*b.N; i++ {
if i%2 == 0 {
l.Add(trace[i], trace[i])
} else {
_, ok := l.Get(trace[i])
if ok {
hit++
} else {
miss++
}
}
}
b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(miss))
}
func BenchmarkLRU_Freq(b *testing.B) {
l, err := New(8192)
if err != nil {
b.Fatalf("err: %v", err)
}
trace := make([]int64, b.N*2)
for i := 0; i < b.N*2; i++ {
if i%2 == 0 {
trace[i] = rand.Int63() % 16384
} else {
trace[i] = rand.Int63() % 32768
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
l.Add(trace[i], trace[i])
}
var hit, miss int
for i := 0; i < b.N; i++ {
_, ok := l.Get(trace[i])
if ok {
hit++
} else {
miss++
}
}
b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(miss))
}
func TestLRU(t *testing.T) {
evictCounter := 0
onEvicted := func(k interface{}, v interface{}) {
if k != v {
t.Fatalf("Evict values not equal (%v!=%v)", k, v)
}
evictCounter += 1
}
l, err := NewWithEvict(128, onEvicted)
if err != nil {
t.Fatalf("err: %v", err)
}
for i := 0; i < 256; i++ {
l.Add(i, i)
}
if l.Len() != 128 {
t.Fatalf("bad len: %v", l.Len())
}
if evictCounter != 128 {
t.Fatalf("bad evict count: %v", evictCounter)
}
for i, k := range l.Keys() {
if v, ok := l.Get(k); !ok || v != k || v != i+128 {
t.Fatalf("bad key: %v", k)
}
}
for i := 0; i < 128; i++ {
_, ok := l.Get(i)
if ok {
t.Fatalf("should be evicted")
}
}
for i := 128; i < 256; i++ {
_, ok := l.Get(i)
if !ok {
t.Fatalf("should not be evicted")
}
}
for i := 128; i < 192; i++ {
l.Remove(i)
_, ok := l.Get(i)
if ok {
t.Fatalf("should be deleted")
}
}
l.Get(192) // expect 192 to be last key in l.Keys()
for i, k := range l.Keys() {
if (i < 63 && k != i+193) || (i == 63 && k != 192) {
t.Fatalf("out of order key: %v", k)
}
}
l.Purge()
if l.Len() != 0 {
t.Fatalf("bad len: %v", l.Len())
}
if _, ok := l.Get(200); ok {
t.Fatalf("should contain nothing")
}
}
// test that Add returns true/false if an eviction occurred
func TestLRUAdd(t *testing.T) {
evictCounter := 0
onEvicted := func(k interface{}, v interface{}) {
evictCounter += 1
}
l, err := NewWithEvict(1, onEvicted)
if err != nil {
t.Fatalf("err: %v", err)
}
if l.Add(1, 1) == true || evictCounter != 0 {
t.Errorf("should not have an eviction")
}
if l.Add(2, 2) == false || evictCounter != 1 {
t.Errorf("should have an eviction")
}
}
// test that Contains doesn't update recent-ness
func TestLRUContains(t *testing.T) {
l, err := New(2)
if err != nil {
t.Fatalf("err: %v", err)
}
l.Add(1, 1)
l.Add(2, 2)
if !l.Contains(1) {
t.Errorf("1 should be contained")
}
l.Add(3, 3)
if l.Contains(1) {
t.Errorf("Contains should not have updated recent-ness of 1")
}
}
// test that Contains doesn't update recent-ness
func TestLRUContainsOrAdd(t *testing.T) {
l, err := New(2)
if err != nil {
t.Fatalf("err: %v", err)
}
l.Add(1, 1)
l.Add(2, 2)
contains, evict := l.ContainsOrAdd(1, 1)
if !contains {
t.Errorf("1 should be contained")
}
if evict {
t.Errorf("nothing should be evicted here")
}
l.Add(3, 3)
contains, evict = l.ContainsOrAdd(1, 1)
if contains {
t.Errorf("1 should not have been contained")
}
if !evict {
t.Errorf("an eviction should have occurred")
}
if !l.Contains(1) {
t.Errorf("now 1 should be contained")
}
}
// test that Peek doesn't update recent-ness
func TestLRUPeek(t *testing.T) {
l, err := New(2)
if err != nil {
t.Fatalf("err: %v", err)
}
l.Add(1, 1)
l.Add(2, 2)
if v, ok := l.Peek(1); !ok || v != 1 {
t.Errorf("1 should be set to 1: %v, %v", v, ok)
}
l.Add(3, 3)
if l.Contains(1) {
t.Errorf("should not have updated recent-ness of 1")
}
}

View file

@ -1,160 +0,0 @@
package simplelru
import (
"container/list"
"errors"
)
// EvictCallback is used to get a callback when a cache entry is evicted
type EvictCallback func(key interface{}, value interface{})
// LRU implements a non-thread safe fixed size LRU cache
type LRU struct {
size int
evictList *list.List
items map[interface{}]*list.Element
onEvict EvictCallback
}
// entry is used to hold a value in the evictList
type entry struct {
key interface{}
value interface{}
}
// NewLRU constructs an LRU of the given size
func NewLRU(size int, onEvict EvictCallback) (*LRU, error) {
if size <= 0 {
return nil, errors.New("Must provide a positive size")
}
c := &LRU{
size: size,
evictList: list.New(),
items: make(map[interface{}]*list.Element),
onEvict: onEvict,
}
return c, nil
}
// Purge is used to completely clear the cache
func (c *LRU) Purge() {
for k, v := range c.items {
if c.onEvict != nil {
c.onEvict(k, v.Value.(*entry).value)
}
delete(c.items, k)
}
c.evictList.Init()
}
// Add adds a value to the cache. Returns true if an eviction occurred.
func (c *LRU) Add(key, value interface{}) bool {
// Check for existing item
if ent, ok := c.items[key]; ok {
c.evictList.MoveToFront(ent)
ent.Value.(*entry).value = value
return false
}
// Add new item
ent := &entry{key, value}
entry := c.evictList.PushFront(ent)
c.items[key] = entry
evict := c.evictList.Len() > c.size
// Verify size not exceeded
if evict {
c.removeOldest()
}
return evict
}
// Get looks up a key's value from the cache.
func (c *LRU) Get(key interface{}) (value interface{}, ok bool) {
if ent, ok := c.items[key]; ok {
c.evictList.MoveToFront(ent)
return ent.Value.(*entry).value, true
}
return
}
// Check if a key is in the cache, without updating the recent-ness
// or deleting it for being stale.
func (c *LRU) Contains(key interface{}) (ok bool) {
_, ok = c.items[key]
return ok
}
// Returns the key value (or undefined if not found) without updating
// the "recently used"-ness of the key.
func (c *LRU) Peek(key interface{}) (value interface{}, ok bool) {
if ent, ok := c.items[key]; ok {
return ent.Value.(*entry).value, true
}
return nil, ok
}
// Remove removes the provided key from the cache, returning if the
// key was contained.
func (c *LRU) Remove(key interface{}) bool {
if ent, ok := c.items[key]; ok {
c.removeElement(ent)
return true
}
return false
}
// RemoveOldest removes the oldest item from the cache.
func (c *LRU) RemoveOldest() (interface{}, interface{}, bool) {
ent := c.evictList.Back()
if ent != nil {
c.removeElement(ent)
kv := ent.Value.(*entry)
return kv.key, kv.value, true
}
return nil, nil, false
}
// GetOldest returns the oldest entry
func (c *LRU) GetOldest() (interface{}, interface{}, bool) {
ent := c.evictList.Back()
if ent != nil {
kv := ent.Value.(*entry)
return kv.key, kv.value, true
}
return nil, nil, false
}
// Keys returns a slice of the keys in the cache, from oldest to newest.
func (c *LRU) Keys() []interface{} {
keys := make([]interface{}, len(c.items))
i := 0
for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() {
keys[i] = ent.Value.(*entry).key
i++
}
return keys
}
// Len returns the number of items in the cache.
func (c *LRU) Len() int {
return c.evictList.Len()
}
// removeOldest removes the oldest item from the cache.
func (c *LRU) removeOldest() {
ent := c.evictList.Back()
if ent != nil {
c.removeElement(ent)
}
}
// removeElement is used to remove a given list element from the cache
func (c *LRU) removeElement(e *list.Element) {
c.evictList.Remove(e)
kv := e.Value.(*entry)
delete(c.items, kv.key)
if c.onEvict != nil {
c.onEvict(kv.key, kv.value)
}
}

View file

@ -1,167 +0,0 @@
package simplelru
import "testing"
func TestLRU(t *testing.T) {
evictCounter := 0
onEvicted := func(k interface{}, v interface{}) {
if k != v {
t.Fatalf("Evict values not equal (%v!=%v)", k, v)
}
evictCounter += 1
}
l, err := NewLRU(128, onEvicted)
if err != nil {
t.Fatalf("err: %v", err)
}
for i := 0; i < 256; i++ {
l.Add(i, i)
}
if l.Len() != 128 {
t.Fatalf("bad len: %v", l.Len())
}
if evictCounter != 128 {
t.Fatalf("bad evict count: %v", evictCounter)
}
for i, k := range l.Keys() {
if v, ok := l.Get(k); !ok || v != k || v != i+128 {
t.Fatalf("bad key: %v", k)
}
}
for i := 0; i < 128; i++ {
_, ok := l.Get(i)
if ok {
t.Fatalf("should be evicted")
}
}
for i := 128; i < 256; i++ {
_, ok := l.Get(i)
if !ok {
t.Fatalf("should not be evicted")
}
}
for i := 128; i < 192; i++ {
ok := l.Remove(i)
if !ok {
t.Fatalf("should be contained")
}
ok = l.Remove(i)
if ok {
t.Fatalf("should not be contained")
}
_, ok = l.Get(i)
if ok {
t.Fatalf("should be deleted")
}
}
l.Get(192) // expect 192 to be last key in l.Keys()
for i, k := range l.Keys() {
if (i < 63 && k != i+193) || (i == 63 && k != 192) {
t.Fatalf("out of order key: %v", k)
}
}
l.Purge()
if l.Len() != 0 {
t.Fatalf("bad len: %v", l.Len())
}
if _, ok := l.Get(200); ok {
t.Fatalf("should contain nothing")
}
}
func TestLRU_GetOldest_RemoveOldest(t *testing.T) {
l, err := NewLRU(128, nil)
if err != nil {
t.Fatalf("err: %v", err)
}
for i := 0; i < 256; i++ {
l.Add(i, i)
}
k, _, ok := l.GetOldest()
if !ok {
t.Fatalf("missing")
}
if k.(int) != 128 {
t.Fatalf("bad: %v", k)
}
k, _, ok = l.RemoveOldest()
if !ok {
t.Fatalf("missing")
}
if k.(int) != 128 {
t.Fatalf("bad: %v", k)
}
k, _, ok = l.RemoveOldest()
if !ok {
t.Fatalf("missing")
}
if k.(int) != 129 {
t.Fatalf("bad: %v", k)
}
}
// Test that Add returns true/false if an eviction occurred
func TestLRU_Add(t *testing.T) {
evictCounter := 0
onEvicted := func(k interface{}, v interface{}) {
evictCounter += 1
}
l, err := NewLRU(1, onEvicted)
if err != nil {
t.Fatalf("err: %v", err)
}
if l.Add(1, 1) == true || evictCounter != 0 {
t.Errorf("should not have an eviction")
}
if l.Add(2, 2) == false || evictCounter != 1 {
t.Errorf("should have an eviction")
}
}
// Test that Contains doesn't update recent-ness
func TestLRU_Contains(t *testing.T) {
l, err := NewLRU(2, nil)
if err != nil {
t.Fatalf("err: %v", err)
}
l.Add(1, 1)
l.Add(2, 2)
if !l.Contains(1) {
t.Errorf("1 should be contained")
}
l.Add(3, 3)
if l.Contains(1) {
t.Errorf("Contains should not have updated recent-ness of 1")
}
}
// Test that Peek doesn't update recent-ness
func TestLRU_Peek(t *testing.T) {
l, err := NewLRU(2, nil)
if err != nil {
t.Fatalf("err: %v", err)
}
l.Add(1, 1)
l.Add(2, 2)
if v, ok := l.Peek(1); !ok || v != 1 {
t.Errorf("1 should be set to 1: %v, %v", v, ok)
}
l.Add(3, 3)
if l.Contains(1) {
t.Errorf("should not have updated recent-ness of 1")
}
}

View file

@ -1,4 +0,0 @@
*.6
tags
test.out
a.out

View file

@ -1,14 +0,0 @@
language: go
sudo: false
go:
- 1.7.x
- 1.8.x
- tip
before_install:
# don't use the miekg/dns when testing forks
- mkdir -p $GOPATH/src/github.com/miekg
- ln -s $TRAVIS_BUILD_DIR $GOPATH/src/github.com/miekg/ || true
script:
- go test -race -v -bench=.

View file

@ -1 +0,0 @@
Miek Gieben <miek@miek.nl>

View file

@ -1,9 +0,0 @@
Alex A. Skinner
Andrew Tunnell-Jones
Ask Bjørn Hansen
Dave Cheney
Dusty Wilson
Marek Majkowski
Peter van Dijk
Omri Bahumi
Alex Sergeyev

View file

@ -1,9 +0,0 @@
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.
Extensions of the original work are copyright (c) 2011 Miek Gieben
Copyright 2011 Miek Gieben. All rights reserved. Use of this source code is
governed by a BSD-style license that can be found in the LICENSE file.
Copyright 2014 CloudFlare. All rights reserved. Use of this source code is
governed by a BSD-style license that can be found in the LICENSE file.

32
vendor/github.com/miekg/dns/LICENSE generated vendored
View file

@ -1,32 +0,0 @@
Extensions of the original work are copyright (c) 2011 Miek Gieben
As this is fork of the official Go code the same license applies:
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

162
vendor/github.com/miekg/dns/README.md generated vendored
View file

@ -1,162 +0,0 @@
[![Build Status](https://travis-ci.org/miekg/dns.svg?branch=master)](https://travis-ci.org/miekg/dns)
[![](https://godoc.org/github.com/miekg/dns?status.svg)](https://godoc.org/github.com/miekg/dns)
# Alternative (more granular) approach to a DNS library
> Less is more.
Complete and usable DNS library. All widely used Resource Records are
supported, including the DNSSEC types. It follows a lean and mean philosophy.
If there is stuff you should know as a DNS programmer there isn't a convenience
function for it. Server side and client side programming is supported, i.e. you
can build servers and resolvers with it.
We try to keep the "master" branch as sane as possible and at the bleeding edge
of standards, avoiding breaking changes wherever reasonable. We support the last
two versions of Go, currently: 1.7 and 1.8.
# Goals
* KISS;
* Fast;
* Small API. If it's easy to code in Go, don't make a function for it.
# Users
A not-so-up-to-date-list-that-may-be-actually-current:
* https://github.com/coredns/coredns
* https://cloudflare.com
* https://github.com/abh/geodns
* http://www.statdns.com/
* http://www.dnsinspect.com/
* https://github.com/chuangbo/jianbing-dictionary-dns
* http://www.dns-lg.com/
* https://github.com/fcambus/rrda
* https://github.com/kenshinx/godns
* https://github.com/skynetservices/skydns
* https://github.com/hashicorp/consul
* https://github.com/DevelopersPL/godnsagent
* https://github.com/duedil-ltd/discodns
* https://github.com/StalkR/dns-reverse-proxy
* https://github.com/tianon/rawdns
* https://mesosphere.github.io/mesos-dns/
* https://pulse.turbobytes.com/
* https://play.google.com/store/apps/details?id=com.turbobytes.dig
* https://github.com/fcambus/statzone
* https://github.com/benschw/dns-clb-go
* https://github.com/corny/dnscheck for http://public-dns.info/
* https://namesmith.io
* https://github.com/miekg/unbound
* https://github.com/miekg/exdns
* https://dnslookup.org
* https://github.com/looterz/grimd
* https://github.com/phamhongviet/serf-dns
* https://github.com/mehrdadrad/mylg
* https://github.com/bamarni/dockness
* https://github.com/fffaraz/microdns
* http://quilt.io
* https://github.com/ipdcode/hades (JD.COM)
* https://github.com/StackExchange/dnscontrol/
* https://www.dnsperf.com/
* https://dnssectest.net/
Send pull request if you want to be listed here.
# Features
* UDP/TCP queries, IPv4 and IPv6;
* RFC 1035 zone file parsing ($INCLUDE, $ORIGIN, $TTL and $GENERATE (for all record types) are supported;
* Fast:
* Reply speed around ~ 80K qps (faster hardware results in more qps);
* Parsing RRs ~ 100K RR/s, that's 5M records in about 50 seconds;
* Server side programming (mimicking the net/http package);
* Client side programming;
* DNSSEC: signing, validating and key generation for DSA, RSA and ECDSA;
* EDNS0, NSID, Cookies;
* AXFR/IXFR;
* TSIG, SIG(0);
* DNS over TLS: optional encrypted connection between client and server;
* DNS name compression;
* Depends only on the standard library.
Have fun!
Miek Gieben - 2010-2012 - <miek@miek.nl>
# Building
Building is done with the `go` tool. If you have setup your GOPATH
correctly, the following should work:
go get github.com/miekg/dns
go build github.com/miekg/dns
## Examples
A short "how to use the API" is at the beginning of doc.go (this also will show
when you call `godoc github.com/miekg/dns`).
Example programs can be found in the `github.com/miekg/exdns` repository.
## Supported RFCs
*all of them*
* 103{4,5} - DNS standard
* 1348 - NSAP record (removed the record)
* 1982 - Serial Arithmetic
* 1876 - LOC record
* 1995 - IXFR
* 1996 - DNS notify
* 2136 - DNS Update (dynamic updates)
* 2181 - RRset definition - there is no RRset type though, just []RR
* 2537 - RSAMD5 DNS keys
* 2065 - DNSSEC (updated in later RFCs)
* 2671 - EDNS record
* 2782 - SRV record
* 2845 - TSIG record
* 2915 - NAPTR record
* 2929 - DNS IANA Considerations
* 3110 - RSASHA1 DNS keys
* 3225 - DO bit (DNSSEC OK)
* 340{1,2,3} - NAPTR record
* 3445 - Limiting the scope of (DNS)KEY
* 3597 - Unknown RRs
* 403{3,4,5} - DNSSEC + validation functions
* 4255 - SSHFP record
* 4343 - Case insensitivity
* 4408 - SPF record
* 4509 - SHA256 Hash in DS
* 4592 - Wildcards in the DNS
* 4635 - HMAC SHA TSIG
* 4701 - DHCID
* 4892 - id.server
* 5001 - NSID
* 5155 - NSEC3 record
* 5205 - HIP record
* 5702 - SHA2 in the DNS
* 5936 - AXFR
* 5966 - TCP implementation recommendations
* 6605 - ECDSA
* 6725 - IANA Registry Update
* 6742 - ILNP DNS
* 6840 - Clarifications and Implementation Notes for DNS Security
* 6844 - CAA record
* 6891 - EDNS0 update
* 6895 - DNS IANA considerations
* 6975 - Algorithm Understanding in DNSSEC
* 7043 - EUI48/EUI64 records
* 7314 - DNS (EDNS) EXPIRE Option
* 7828 - edns-tcp-keepalive EDNS0 Option
* 7553 - URI record
* 7858 - DNS over TLS: Initiation and Performance Considerations (draft)
* 7873 - Domain Name System (DNS) Cookies (draft-ietf-dnsop-cookies)
* xxxx - EDNS0 DNS Update Lease (draft)
## Loosely based upon
* `ldns`
* `NSD`
* `Net::DNS`
* `GRONG`

467
vendor/github.com/miekg/dns/client.go generated vendored
View file

@ -1,467 +0,0 @@
package dns
// A client implementation.
import (
"bytes"
"crypto/tls"
"encoding/binary"
"io"
"net"
"time"
)
const dnsTimeout time.Duration = 2 * time.Second
const tcpIdleTimeout time.Duration = 8 * time.Second
// A Conn represents a connection to a DNS server.
type Conn struct {
net.Conn // a net.Conn holding the connection
UDPSize uint16 // minimum receive buffer for UDP messages
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be fully qualified
rtt time.Duration
t time.Time
tsigRequestMAC string
}
// A Client defines parameters for a DNS client.
type Client struct {
Net string // if "tcp" or "tcp-tls" (DNS over TLS) a TCP query will be initiated, otherwise an UDP one (default is "" for UDP)
UDPSize uint16 // minimum receive buffer for UDP messages
TLSConfig *tls.Config // TLS connection configuration
Timeout time.Duration // a cumulative timeout for dial, write and read, defaults to 0 (disabled) - overrides DialTimeout, ReadTimeout and WriteTimeout when non-zero
DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds - overridden by Timeout when that value is non-zero
ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be fully qualified
SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass
group singleflight
}
// Exchange performs a synchronous UDP query. It sends the message m to the address
// contained in a and waits for a reply. Exchange does not retry a failed query, nor
// will it fall back to TCP in case of truncation.
// See client.Exchange for more information on setting larger buffer sizes.
func Exchange(m *Msg, a string) (r *Msg, err error) {
var co *Conn
co, err = DialTimeout("udp", a, dnsTimeout)
if err != nil {
return nil, err
}
defer co.Close()
opt := m.IsEdns0()
// If EDNS0 is used use that for size.
if opt != nil && opt.UDPSize() >= MinMsgSize {
co.UDPSize = opt.UDPSize()
}
co.SetWriteDeadline(time.Now().Add(dnsTimeout))
if err = co.WriteMsg(m); err != nil {
return nil, err
}
co.SetReadDeadline(time.Now().Add(dnsTimeout))
r, err = co.ReadMsg()
if err == nil && r.Id != m.Id {
err = ErrId
}
return r, err
}
// ExchangeConn performs a synchronous query. It sends the message m via the connection
// c and waits for a reply. The connection c is not closed by ExchangeConn.
// This function is going away, but can easily be mimicked:
//
// co := &dns.Conn{Conn: c} // c is your net.Conn
// co.WriteMsg(m)
// in, _ := co.ReadMsg()
// co.Close()
//
func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) {
println("dns: this function is deprecated")
co := new(Conn)
co.Conn = c
if err = co.WriteMsg(m); err != nil {
return nil, err
}
r, err = co.ReadMsg()
if err == nil && r.Id != m.Id {
err = ErrId
}
return r, err
}
// Exchange performs a synchronous query. It sends the message m to the address
// contained in a and waits for a reply. Basic use pattern with a *dns.Client:
//
// c := new(dns.Client)
// in, rtt, err := c.Exchange(message, "127.0.0.1:53")
//
// Exchange does not retry a failed query, nor will it fall back to TCP in
// case of truncation.
// It is up to the caller to create a message that allows for larger responses to be
// returned. Specifically this means adding an EDNS0 OPT RR that will advertise a larger
// buffer, see SetEdns0. Messages without an OPT RR will fallback to the historic limit
// of 512 bytes.
func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
if !c.SingleInflight {
return c.exchange(m, a)
}
// This adds a bunch of garbage, TODO(miek).
t := "nop"
if t1, ok := TypeToString[m.Question[0].Qtype]; ok {
t = t1
}
cl := "nop"
if cl1, ok := ClassToString[m.Question[0].Qclass]; ok {
cl = cl1
}
r, rtt, err, shared := c.group.Do(m.Question[0].Name+t+cl, func() (*Msg, time.Duration, error) {
return c.exchange(m, a)
})
if r != nil && shared {
r = r.Copy()
}
if err != nil {
return r, rtt, err
}
return r, rtt, nil
}
func (c *Client) dialTimeout() time.Duration {
if c.Timeout != 0 {
return c.Timeout
}
if c.DialTimeout != 0 {
return c.DialTimeout
}
return dnsTimeout
}
func (c *Client) readTimeout() time.Duration {
if c.ReadTimeout != 0 {
return c.ReadTimeout
}
return dnsTimeout
}
func (c *Client) writeTimeout() time.Duration {
if c.WriteTimeout != 0 {
return c.WriteTimeout
}
return dnsTimeout
}
func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
var co *Conn
network := "udp"
tls := false
switch c.Net {
case "tcp-tls":
network = "tcp"
tls = true
case "tcp4-tls":
network = "tcp4"
tls = true
case "tcp6-tls":
network = "tcp6"
tls = true
default:
if c.Net != "" {
network = c.Net
}
}
var deadline time.Time
if c.Timeout != 0 {
deadline = time.Now().Add(c.Timeout)
}
if tls {
co, err = DialTimeoutWithTLS(network, a, c.TLSConfig, c.dialTimeout())
} else {
co, err = DialTimeout(network, a, c.dialTimeout())
}
if err != nil {
return nil, 0, err
}
defer co.Close()
opt := m.IsEdns0()
// If EDNS0 is used use that for size.
if opt != nil && opt.UDPSize() >= MinMsgSize {
co.UDPSize = opt.UDPSize()
}
// Otherwise use the client's configured UDP size.
if opt == nil && c.UDPSize >= MinMsgSize {
co.UDPSize = c.UDPSize
}
co.TsigSecret = c.TsigSecret
co.SetWriteDeadline(deadlineOrTimeout(deadline, c.writeTimeout()))
if err = co.WriteMsg(m); err != nil {
return nil, 0, err
}
co.SetReadDeadline(deadlineOrTimeout(deadline, c.readTimeout()))
r, err = co.ReadMsg()
if err == nil && r.Id != m.Id {
err = ErrId
}
return r, co.rtt, err
}
// ReadMsg reads a message from the connection co.
// If the received message contains a TSIG record the transaction
// signature is verified.
func (co *Conn) ReadMsg() (*Msg, error) {
p, err := co.ReadMsgHeader(nil)
if err != nil {
return nil, err
}
m := new(Msg)
if err := m.Unpack(p); err != nil {
// If ErrTruncated was returned, we still want to allow the user to use
// the message, but naively they can just check err if they don't want
// to use a truncated message
if err == ErrTruncated {
return m, err
}
return nil, err
}
if t := m.IsTsig(); t != nil {
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
return m, ErrSecret
}
// Need to work on the original message p, as that was used to calculate the tsig.
err = TsigVerify(p, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
}
return m, err
}
// ReadMsgHeader reads a DNS message, parses and populates hdr (when hdr is not nil).
// Returns message as a byte slice to be parsed with Msg.Unpack later on.
// Note that error handling on the message body is not possible as only the header is parsed.
func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) {
var (
p []byte
n int
err error
)
switch t := co.Conn.(type) {
case *net.TCPConn, *tls.Conn:
r := t.(io.Reader)
// First two bytes specify the length of the entire message.
l, err := tcpMsgLen(r)
if err != nil {
return nil, err
}
p = make([]byte, l)
n, err = tcpRead(r, p)
co.rtt = time.Since(co.t)
default:
if co.UDPSize > MinMsgSize {
p = make([]byte, co.UDPSize)
} else {
p = make([]byte, MinMsgSize)
}
n, err = co.Read(p)
co.rtt = time.Since(co.t)
}
if err != nil {
return nil, err
} else if n < headerSize {
return nil, ErrShortRead
}
p = p[:n]
if hdr != nil {
dh, _, err := unpackMsgHdr(p, 0)
if err != nil {
return nil, err
}
*hdr = dh
}
return p, err
}
// tcpMsgLen is a helper func to read first two bytes of stream as uint16 packet length.
func tcpMsgLen(t io.Reader) (int, error) {
p := []byte{0, 0}
n, err := t.Read(p)
if err != nil {
return 0, err
}
// As seen with my local router/switch, retursn 1 byte on the above read,
// resulting a a ShortRead. Just write it out (instead of loop) and read the
// other byte.
if n == 1 {
n1, err := t.Read(p[1:])
if err != nil {
return 0, err
}
n += n1
}
if n != 2 {
return 0, ErrShortRead
}
l := binary.BigEndian.Uint16(p)
if l == 0 {
return 0, ErrShortRead
}
return int(l), nil
}
// tcpRead calls TCPConn.Read enough times to fill allocated buffer.
func tcpRead(t io.Reader, p []byte) (int, error) {
n, err := t.Read(p)
if err != nil {
return n, err
}
for n < len(p) {
j, err := t.Read(p[n:])
if err != nil {
return n, err
}
n += j
}
return n, err
}
// Read implements the net.Conn read method.
func (co *Conn) Read(p []byte) (n int, err error) {
if co.Conn == nil {
return 0, ErrConnEmpty
}
if len(p) < 2 {
return 0, io.ErrShortBuffer
}
switch t := co.Conn.(type) {
case *net.TCPConn, *tls.Conn:
r := t.(io.Reader)
l, err := tcpMsgLen(r)
if err != nil {
return 0, err
}
if l > len(p) {
return int(l), io.ErrShortBuffer
}
return tcpRead(r, p[:l])
}
// UDP connection
n, err = co.Conn.Read(p)
if err != nil {
return n, err
}
return n, err
}
// WriteMsg sends a message through the connection co.
// If the message m contains a TSIG record the transaction
// signature is calculated.
func (co *Conn) WriteMsg(m *Msg) (err error) {
var out []byte
if t := m.IsTsig(); t != nil {
mac := ""
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
return ErrSecret
}
out, mac, err = TsigGenerate(m, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
// Set for the next read, although only used in zone transfers
co.tsigRequestMAC = mac
} else {
out, err = m.Pack()
}
if err != nil {
return err
}
co.t = time.Now()
if _, err = co.Write(out); err != nil {
return err
}
return nil
}
// Write implements the net.Conn Write method.
func (co *Conn) Write(p []byte) (n int, err error) {
switch t := co.Conn.(type) {
case *net.TCPConn, *tls.Conn:
w := t.(io.Writer)
lp := len(p)
if lp < 2 {
return 0, io.ErrShortBuffer
}
if lp > MaxMsgSize {
return 0, &Error{err: "message too large"}
}
l := make([]byte, 2, lp+2)
binary.BigEndian.PutUint16(l, uint16(lp))
p = append(l, p...)
n, err := io.Copy(w, bytes.NewReader(p))
return int(n), err
}
n, err = co.Conn.Write(p)
return n, err
}
// Dial connects to the address on the named network.
func Dial(network, address string) (conn *Conn, err error) {
conn = new(Conn)
conn.Conn, err = net.Dial(network, address)
if err != nil {
return nil, err
}
return conn, nil
}
// DialTimeout acts like Dial but takes a timeout.
func DialTimeout(network, address string, timeout time.Duration) (conn *Conn, err error) {
conn = new(Conn)
conn.Conn, err = net.DialTimeout(network, address, timeout)
if err != nil {
return nil, err
}
return conn, nil
}
// DialWithTLS connects to the address on the named network with TLS.
func DialWithTLS(network, address string, tlsConfig *tls.Config) (conn *Conn, err error) {
conn = new(Conn)
conn.Conn, err = tls.Dial(network, address, tlsConfig)
if err != nil {
return nil, err
}
return conn, nil
}
// DialTimeoutWithTLS acts like DialWithTLS but takes a timeout.
func DialTimeoutWithTLS(network, address string, tlsConfig *tls.Config, timeout time.Duration) (conn *Conn, err error) {
var dialer net.Dialer
dialer.Timeout = timeout
conn = new(Conn)
conn.Conn, err = tls.DialWithDialer(&dialer, network, address, tlsConfig)
if err != nil {
return nil, err
}
return conn, nil
}
func deadlineOrTimeout(deadline time.Time, timeout time.Duration) time.Time {
if deadline.IsZero() {
return time.Now().Add(timeout)
}
return deadline
}

View file

@ -1,511 +0,0 @@
package dns
import (
"crypto/tls"
"fmt"
"net"
"strconv"
"sync"
"testing"
"time"
)
func TestClientSync(t *testing.T) {
HandleFunc("miek.nl.", HelloServer)
defer HandleRemove("miek.nl.")
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("unable to run test server: %v", err)
}
defer s.Shutdown()
m := new(Msg)
m.SetQuestion("miek.nl.", TypeSOA)
c := new(Client)
r, _, err := c.Exchange(m, addrstr)
if err != nil {
t.Errorf("failed to exchange: %v", err)
}
if r != nil && r.Rcode != RcodeSuccess {
t.Errorf("failed to get an valid answer\n%v", r)
}
// And now with plain Exchange().
r, err = Exchange(m, addrstr)
if err != nil {
t.Errorf("failed to exchange: %v", err)
}
if r == nil || r.Rcode != RcodeSuccess {
t.Errorf("failed to get an valid answer\n%v", r)
}
}
func TestClientTLSSync(t *testing.T) {
HandleFunc("miek.nl.", HelloServer)
defer HandleRemove("miek.nl.")
cert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock)
if err != nil {
t.Fatalf("unable to build certificate: %v", err)
}
config := tls.Config{
Certificates: []tls.Certificate{cert},
}
s, addrstr, err := RunLocalTLSServer("127.0.0.1:0", &config)
if err != nil {
t.Fatalf("unable to run test server: %v", err)
}
defer s.Shutdown()
m := new(Msg)
m.SetQuestion("miek.nl.", TypeSOA)
c := new(Client)
c.Net = "tcp-tls"
c.TLSConfig = &tls.Config{
InsecureSkipVerify: true,
}
r, _, err := c.Exchange(m, addrstr)
if err != nil {
t.Errorf("failed to exchange: %v", err)
}
if r != nil && r.Rcode != RcodeSuccess {
t.Errorf("failed to get an valid answer\n%v", r)
}
}
func TestClientSyncBadID(t *testing.T) {
HandleFunc("miek.nl.", HelloServerBadID)
defer HandleRemove("miek.nl.")
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("unable to run test server: %v", err)
}
defer s.Shutdown()
m := new(Msg)
m.SetQuestion("miek.nl.", TypeSOA)
c := new(Client)
if _, _, err := c.Exchange(m, addrstr); err != ErrId {
t.Errorf("did not find a bad Id")
}
// And now with plain Exchange().
if _, err := Exchange(m, addrstr); err != ErrId {
t.Errorf("did not find a bad Id")
}
}
func TestClientEDNS0(t *testing.T) {
HandleFunc("miek.nl.", HelloServer)
defer HandleRemove("miek.nl.")
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("unable to run test server: %v", err)
}
defer s.Shutdown()
m := new(Msg)
m.SetQuestion("miek.nl.", TypeDNSKEY)
m.SetEdns0(2048, true)
c := new(Client)
r, _, err := c.Exchange(m, addrstr)
if err != nil {
t.Errorf("failed to exchange: %v", err)
}
if r != nil && r.Rcode != RcodeSuccess {
t.Errorf("failed to get an valid answer\n%v", r)
}
}
// Validates the transmission and parsing of local EDNS0 options.
func TestClientEDNS0Local(t *testing.T) {
optStr1 := "1979:0x0707"
optStr2 := strconv.Itoa(EDNS0LOCALSTART) + ":0x0601"
handler := func(w ResponseWriter, req *Msg) {
m := new(Msg)
m.SetReply(req)
m.Extra = make([]RR, 1, 2)
m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello local edns"}}
// If the local options are what we expect, then reflect them back.
ec1 := req.Extra[0].(*OPT).Option[0].(*EDNS0_LOCAL).String()
ec2 := req.Extra[0].(*OPT).Option[1].(*EDNS0_LOCAL).String()
if ec1 == optStr1 && ec2 == optStr2 {
m.Extra = append(m.Extra, req.Extra[0])
}
w.WriteMsg(m)
}
HandleFunc("miek.nl.", handler)
defer HandleRemove("miek.nl.")
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("unable to run test server: %s", err)
}
defer s.Shutdown()
m := new(Msg)
m.SetQuestion("miek.nl.", TypeTXT)
// Add two local edns options to the query.
ec1 := &EDNS0_LOCAL{Code: 1979, Data: []byte{7, 7}}
ec2 := &EDNS0_LOCAL{Code: EDNS0LOCALSTART, Data: []byte{6, 1}}
o := &OPT{Hdr: RR_Header{Name: ".", Rrtype: TypeOPT}, Option: []EDNS0{ec1, ec2}}
m.Extra = append(m.Extra, o)
c := new(Client)
r, _, err := c.Exchange(m, addrstr)
if err != nil {
t.Errorf("failed to exchange: %s", err)
}
if r != nil && r.Rcode != RcodeSuccess {
t.Error("failed to get a valid answer")
t.Logf("%v\n", r)
}
txt := r.Extra[0].(*TXT).Txt[0]
if txt != "Hello local edns" {
t.Error("Unexpected result for miek.nl", txt, "!= Hello local edns")
}
// Validate the local options in the reply.
got := r.Extra[1].(*OPT).Option[0].(*EDNS0_LOCAL).String()
if got != optStr1 {
t.Errorf("failed to get local edns0 answer; got %s, expected %s", got, optStr1)
t.Logf("%v\n", r)
}
got = r.Extra[1].(*OPT).Option[1].(*EDNS0_LOCAL).String()
if got != optStr2 {
t.Errorf("failed to get local edns0 answer; got %s, expected %s", got, optStr2)
t.Logf("%v\n", r)
}
}
// ExampleTsigSecret_updateLeaseTSIG shows how to update a lease signed with TSIG
func ExampleTsigSecret_updateLeaseTSIG() {
m := new(Msg)
m.SetUpdate("t.local.ip6.io.")
rr, _ := NewRR("t.local.ip6.io. 30 A 127.0.0.1")
rrs := make([]RR, 1)
rrs[0] = rr
m.Insert(rrs)
leaseRr := new(OPT)
leaseRr.Hdr.Name = "."
leaseRr.Hdr.Rrtype = TypeOPT
e := new(EDNS0_UL)
e.Code = EDNS0UL
e.Lease = 120
leaseRr.Option = append(leaseRr.Option, e)
m.Extra = append(m.Extra, leaseRr)
c := new(Client)
m.SetTsig("polvi.", HmacMD5, 300, time.Now().Unix())
c.TsigSecret = map[string]string{"polvi.": "pRZgBrBvI4NAHZYhxmhs/Q=="}
_, _, err := c.Exchange(m, "127.0.0.1:53")
if err != nil {
panic(err)
}
}
func TestClientConn(t *testing.T) {
HandleFunc("miek.nl.", HelloServer)
defer HandleRemove("miek.nl.")
// This uses TCP just to make it slightly different than TestClientSync
s, addrstr, err := RunLocalTCPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("unable to run test server: %v", err)
}
defer s.Shutdown()
m := new(Msg)
m.SetQuestion("miek.nl.", TypeSOA)
cn, err := Dial("tcp", addrstr)
if err != nil {
t.Errorf("failed to dial %s: %v", addrstr, err)
}
err = cn.WriteMsg(m)
if err != nil {
t.Errorf("failed to exchange: %v", err)
}
r, err := cn.ReadMsg()
if r == nil || r.Rcode != RcodeSuccess {
t.Errorf("failed to get an valid answer\n%v", r)
}
err = cn.WriteMsg(m)
if err != nil {
t.Errorf("failed to exchange: %v", err)
}
h := new(Header)
buf, err := cn.ReadMsgHeader(h)
if buf == nil {
t.Errorf("failed to get an valid answer\n%v", r)
}
if int(h.Bits&0xF) != RcodeSuccess {
t.Errorf("failed to get an valid answer in ReadMsgHeader\n%v", r)
}
if h.Ancount != 0 || h.Qdcount != 1 || h.Nscount != 0 || h.Arcount != 1 {
t.Errorf("expected to have question and additional in response; got something else: %+v", h)
}
if err = r.Unpack(buf); err != nil {
t.Errorf("unable to unpack message fully: %v", err)
}
}
func TestTruncatedMsg(t *testing.T) {
m := new(Msg)
m.SetQuestion("miek.nl.", TypeSRV)
cnt := 10
for i := 0; i < cnt; i++ {
r := &SRV{
Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeSRV, Class: ClassINET, Ttl: 0},
Port: uint16(i + 8000),
Target: "target.miek.nl.",
}
m.Answer = append(m.Answer, r)
re := &A{
Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeA, Class: ClassINET, Ttl: 0},
A: net.ParseIP(fmt.Sprintf("127.0.0.%d", i)).To4(),
}
m.Extra = append(m.Extra, re)
}
buf, err := m.Pack()
if err != nil {
t.Errorf("failed to pack: %v", err)
}
r := new(Msg)
if err = r.Unpack(buf); err != nil {
t.Errorf("unable to unpack message: %v", err)
}
if len(r.Answer) != cnt {
t.Errorf("answer count after regular unpack doesn't match: %d", len(r.Answer))
}
if len(r.Extra) != cnt {
t.Errorf("extra count after regular unpack doesn't match: %d", len(r.Extra))
}
m.Truncated = true
buf, err = m.Pack()
if err != nil {
t.Errorf("failed to pack truncated: %v", err)
}
r = new(Msg)
if err = r.Unpack(buf); err != nil && err != ErrTruncated {
t.Errorf("unable to unpack truncated message: %v", err)
}
if !r.Truncated {
t.Errorf("truncated message wasn't unpacked as truncated")
}
if len(r.Answer) != cnt {
t.Errorf("answer count after truncated unpack doesn't match: %d", len(r.Answer))
}
if len(r.Extra) != cnt {
t.Errorf("extra count after truncated unpack doesn't match: %d", len(r.Extra))
}
// Now we want to remove almost all of the extra records
// We're going to loop over the extra to get the count of the size of all
// of them
off := 0
buf1 := make([]byte, m.Len())
for i := 0; i < len(m.Extra); i++ {
off, err = PackRR(m.Extra[i], buf1, off, nil, m.Compress)
if err != nil {
t.Errorf("failed to pack extra: %v", err)
}
}
// Remove all of the extra bytes but 10 bytes from the end of buf
off -= 10
buf1 = buf[:len(buf)-off]
r = new(Msg)
if err = r.Unpack(buf1); err != nil && err != ErrTruncated {
t.Errorf("unable to unpack cutoff message: %v", err)
}
if !r.Truncated {
t.Error("truncated cutoff message wasn't unpacked as truncated")
}
if len(r.Answer) != cnt {
t.Errorf("answer count after cutoff unpack doesn't match: %d", len(r.Answer))
}
if len(r.Extra) != 0 {
t.Errorf("extra count after cutoff unpack is not zero: %d", len(r.Extra))
}
// Now we want to remove almost all of the answer records too
buf1 = make([]byte, m.Len())
as := 0
for i := 0; i < len(m.Extra); i++ {
off1 := off
off, err = PackRR(m.Extra[i], buf1, off, nil, m.Compress)
as = off - off1
if err != nil {
t.Errorf("failed to pack extra: %v", err)
}
}
// Keep exactly one answer left
// This should still cause Answer to be nil
off -= as
buf1 = buf[:len(buf)-off]
r = new(Msg)
if err = r.Unpack(buf1); err != nil && err != ErrTruncated {
t.Errorf("unable to unpack cutoff message: %v", err)
}
if !r.Truncated {
t.Error("truncated cutoff message wasn't unpacked as truncated")
}
if len(r.Answer) != 0 {
t.Errorf("answer count after second cutoff unpack is not zero: %d", len(r.Answer))
}
// Now leave only 1 byte of the question
// Since the header is always 12 bytes, we just need to keep 13
buf1 = buf[:13]
r = new(Msg)
err = r.Unpack(buf1)
if err == nil || err == ErrTruncated {
t.Errorf("error should not be ErrTruncated from question cutoff unpack: %v", err)
}
// Finally, if we only have the header, we should still return an error
buf1 = buf[:12]
r = new(Msg)
if err = r.Unpack(buf1); err == nil || err != ErrTruncated {
t.Errorf("error not ErrTruncated from header-only unpack: %v", err)
}
}
func TestTimeout(t *testing.T) {
// Set up a dummy UDP server that won't respond
addr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
if err != nil {
t.Fatalf("unable to resolve local udp address: %v", err)
}
conn, err := net.ListenUDP("udp", addr)
if err != nil {
t.Fatalf("unable to run test server: %v", err)
}
defer conn.Close()
addrstr := conn.LocalAddr().String()
// Message to send
m := new(Msg)
m.SetQuestion("miek.nl.", TypeTXT)
// Use a channel + timeout to ensure we don't get stuck if the
// Client Timeout is not working properly
done := make(chan struct{})
timeout := time.Millisecond
allowable := timeout + (10 * time.Millisecond)
abortAfter := timeout + (100 * time.Millisecond)
start := time.Now()
go func() {
c := &Client{Timeout: timeout}
_, _, err := c.Exchange(m, addrstr)
if err == nil {
t.Error("no timeout using Client")
}
done <- struct{}{}
}()
select {
case <-done:
case <-time.After(abortAfter):
}
length := time.Since(start)
if length > allowable {
t.Errorf("exchange took longer (%v) than specified Timeout (%v)", length, timeout)
}
}
// Check that responses from deduplicated requests aren't shared between callers
func TestConcurrentExchanges(t *testing.T) {
cases := make([]*Msg, 2)
cases[0] = new(Msg)
cases[1] = new(Msg)
cases[1].Truncated = true
for _, m := range cases {
block := make(chan struct{})
waiting := make(chan struct{})
handler := func(w ResponseWriter, req *Msg) {
r := m.Copy()
r.SetReply(req)
waiting <- struct{}{}
<-block
w.WriteMsg(r)
}
HandleFunc("miek.nl.", handler)
defer HandleRemove("miek.nl.")
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("unable to run test server: %s", err)
}
defer s.Shutdown()
m := new(Msg)
m.SetQuestion("miek.nl.", TypeSRV)
c := &Client{
SingleInflight: true,
}
r := make([]*Msg, 2)
var wg sync.WaitGroup
wg.Add(len(r))
for i := 0; i < len(r); i++ {
go func(i int) {
r[i], _, _ = c.Exchange(m.Copy(), addrstr)
wg.Done()
}(i)
}
select {
case <-waiting:
case <-time.After(time.Second):
t.FailNow()
}
close(block)
wg.Wait()
if r[0] == r[1] {
t.Log("Got same response object, expected non-shared responses")
t.Fail()
}
}
}

View file

@ -1,131 +0,0 @@
package dns
import (
"bufio"
"os"
"strconv"
"strings"
)
// ClientConfig wraps the contents of the /etc/resolv.conf file.
type ClientConfig struct {
Servers []string // servers to use
Search []string // suffixes to append to local name
Port string // what port to use
Ndots int // number of dots in name to trigger absolute lookup
Timeout int // seconds before giving up on packet
Attempts int // lost packets before giving up on server, not used in the package dns
}
// ClientConfigFromFile parses a resolv.conf(5) like file and returns
// a *ClientConfig.
func ClientConfigFromFile(resolvconf string) (*ClientConfig, error) {
file, err := os.Open(resolvconf)
if err != nil {
return nil, err
}
defer file.Close()
c := new(ClientConfig)
scanner := bufio.NewScanner(file)
c.Servers = make([]string, 0)
c.Search = make([]string, 0)
c.Port = "53"
c.Ndots = 1
c.Timeout = 5
c.Attempts = 2
for scanner.Scan() {
if err := scanner.Err(); err != nil {
return nil, err
}
line := scanner.Text()
f := strings.Fields(line)
if len(f) < 1 {
continue
}
switch f[0] {
case "nameserver": // add one name server
if len(f) > 1 {
// One more check: make sure server name is
// just an IP address. Otherwise we need DNS
// to look it up.
name := f[1]
c.Servers = append(c.Servers, name)
}
case "domain": // set search path to just this domain
if len(f) > 1 {
c.Search = make([]string, 1)
c.Search[0] = f[1]
} else {
c.Search = make([]string, 0)
}
case "search": // set search path to given servers
c.Search = make([]string, len(f)-1)
for i := 0; i < len(c.Search); i++ {
c.Search[i] = f[i+1]
}
case "options": // magic options
for i := 1; i < len(f); i++ {
s := f[i]
switch {
case len(s) >= 6 && s[:6] == "ndots:":
n, _ := strconv.Atoi(s[6:])
if n < 1 {
n = 1
}
c.Ndots = n
case len(s) >= 8 && s[:8] == "timeout:":
n, _ := strconv.Atoi(s[8:])
if n < 1 {
n = 1
}
c.Timeout = n
case len(s) >= 8 && s[:9] == "attempts:":
n, _ := strconv.Atoi(s[9:])
if n < 1 {
n = 1
}
c.Attempts = n
case s == "rotate":
/* not imp */
}
}
}
}
return c, nil
}
// NameList returns all of the names that should be queried based on the
// config. It is based off of go's net/dns name building, but it does not
// check the length of the resulting names.
func (c *ClientConfig) NameList(name string) []string {
// if this domain is already fully qualified, no append needed.
if IsFqdn(name) {
return []string{name}
}
// Check to see if the name has more labels than Ndots. Do this before making
// the domain fully qualified.
hasNdots := CountLabel(name) > c.Ndots
// Make the domain fully qualified.
name = Fqdn(name)
// Make a list of names based off search.
names := []string{}
// If name has enough dots, try that first.
if hasNdots {
names = append(names, name)
}
for _, s := range c.Search {
names = append(names, Fqdn(name+s))
}
// If we didn't have enough dots, try after suffixes.
if !hasNdots {
names = append(names, name)
}
return names
}

View file

@ -1,87 +0,0 @@
package dns
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
)
const normal string = `
# Comment
domain somedomain.com
nameserver 10.28.10.2
nameserver 11.28.10.1
`
const missingNewline string = `
domain somedomain.com
nameserver 10.28.10.2
nameserver 11.28.10.1` // <- NOTE: NO newline.
func testConfig(t *testing.T, data string) {
tempDir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("tempDir: %v", err)
}
defer os.RemoveAll(tempDir)
path := filepath.Join(tempDir, "resolv.conf")
if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil {
t.Fatalf("writeFile: %v", err)
}
cc, err := ClientConfigFromFile(path)
if err != nil {
t.Errorf("error parsing resolv.conf: %v", err)
}
if l := len(cc.Servers); l != 2 {
t.Errorf("incorrect number of nameservers detected: %d", l)
}
if l := len(cc.Search); l != 1 {
t.Errorf("domain directive not parsed correctly: %v", cc.Search)
} else {
if cc.Search[0] != "somedomain.com" {
t.Errorf("domain is unexpected: %v", cc.Search[0])
}
}
}
func TestNameserver(t *testing.T) { testConfig(t, normal) }
func TestMissingFinalNewLine(t *testing.T) { testConfig(t, missingNewline) }
func TestNameList(t *testing.T) {
cfg := ClientConfig{
Ndots: 1,
}
// fqdn should be only result returned
names := cfg.NameList("miek.nl.")
if len(names) != 1 {
t.Errorf("NameList returned != 1 names: %v", names)
} else if names[0] != "miek.nl." {
t.Errorf("NameList didn't return sent fqdn domain: %v", names[0])
}
cfg.Search = []string{
"test",
}
// Sent domain has NDots and search
names = cfg.NameList("miek.nl")
if len(names) != 2 {
t.Errorf("NameList returned != 2 names: %v", names)
} else if names[0] != "miek.nl." {
t.Errorf("NameList didn't return sent domain first: %v", names[0])
} else if names[1] != "miek.nl.test." {
t.Errorf("NameList didn't return search last: %v", names[1])
}
cfg.Ndots = 2
// Sent domain has less than NDots and search
names = cfg.NameList("miek.nl")
if len(names) != 2 {
t.Errorf("NameList returned != 2 names: %v", names)
} else if names[0] != "miek.nl.test." {
t.Errorf("NameList didn't return search first: %v", names[0])
} else if names[1] != "miek.nl." {
t.Errorf("NameList didn't return sent domain last: %v", names[1])
}
}

View file

@ -1,184 +0,0 @@
//+build ignore
// compression_generate.go is meant to run with go generate. It will use
// go/{importer,types} to track down all the RR struct types. Then for each type
// it will look to see if there are (compressible) names, if so it will add that
// type to compressionLenHelperType and comressionLenSearchType which "fake" the
// compression so that Len() is fast.
package main
import (
"bytes"
"fmt"
"go/format"
"go/importer"
"go/types"
"log"
"os"
)
var packageHdr = `
// *** DO NOT MODIFY ***
// AUTOGENERATED BY go generate from compress_generate.go
package dns
`
// getTypeStruct will take a type and the package scope, and return the
// (innermost) struct if the type is considered a RR type (currently defined as
// those structs beginning with a RR_Header, could be redefined as implementing
// the RR interface). The bool return value indicates if embedded structs were
// resolved.
func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) {
st, ok := t.Underlying().(*types.Struct)
if !ok {
return nil, false
}
if st.Field(0).Type() == scope.Lookup("RR_Header").Type() {
return st, false
}
if st.Field(0).Anonymous() {
st, _ := getTypeStruct(st.Field(0).Type(), scope)
return st, true
}
return nil, false
}
func main() {
// Import and type-check the package
pkg, err := importer.Default().Import("github.com/miekg/dns")
fatalIfErr(err)
scope := pkg.Scope()
domainTypes := map[string]bool{} // Types that have a domain name in them (either comressible or not).
cdomainTypes := map[string]bool{} // Types that have a compressible domain name in them (subset of domainType)
for _, name := range scope.Names() {
o := scope.Lookup(name)
if o == nil || !o.Exported() {
continue
}
st, _ := getTypeStruct(o.Type(), scope)
if st == nil {
continue
}
if name == "PrivateRR" {
continue
}
if scope.Lookup("Type"+o.Name()) == nil && o.Name() != "RFC3597" {
log.Fatalf("Constant Type%s does not exist.", o.Name())
}
for i := 1; i < st.NumFields(); i++ {
if _, ok := st.Field(i).Type().(*types.Slice); ok {
if st.Tag(i) == `dns:"domain-name"` {
domainTypes[o.Name()] = true
}
if st.Tag(i) == `dns:"cdomain-name"` {
cdomainTypes[o.Name()] = true
domainTypes[o.Name()] = true
}
continue
}
switch {
case st.Tag(i) == `dns:"domain-name"`:
domainTypes[o.Name()] = true
case st.Tag(i) == `dns:"cdomain-name"`:
cdomainTypes[o.Name()] = true
domainTypes[o.Name()] = true
}
}
}
b := &bytes.Buffer{}
b.WriteString(packageHdr)
// compressionLenHelperType - all types that have domain-name/cdomain-name can be used for compressing names
fmt.Fprint(b, "func compressionLenHelperType(c map[string]int, r RR) {\n")
fmt.Fprint(b, "switch x := r.(type) {\n")
for name, _ := range domainTypes {
o := scope.Lookup(name)
st, _ := getTypeStruct(o.Type(), scope)
fmt.Fprintf(b, "case *%s:\n", name)
for i := 1; i < st.NumFields(); i++ {
out := func(s string) { fmt.Fprintf(b, "compressionLenHelper(c, x.%s)\n", st.Field(i).Name()) }
if _, ok := st.Field(i).Type().(*types.Slice); ok {
switch st.Tag(i) {
case `dns:"domain-name"`:
fallthrough
case `dns:"cdomain-name"`:
// For HIP we need to slice over the elements in this slice.
fmt.Fprintf(b, `for i := range x.%s {
compressionLenHelper(c, x.%s[i])
}
`, st.Field(i).Name(), st.Field(i).Name())
}
continue
}
switch {
case st.Tag(i) == `dns:"cdomain-name"`:
fallthrough
case st.Tag(i) == `dns:"domain-name"`:
out(st.Field(i).Name())
}
}
}
fmt.Fprintln(b, "}\n}\n\n")
// compressionLenSearchType - search cdomain-tags types for compressible names.
fmt.Fprint(b, "func compressionLenSearchType(c map[string]int, r RR) (int, bool) {\n")
fmt.Fprint(b, "switch x := r.(type) {\n")
for name, _ := range cdomainTypes {
o := scope.Lookup(name)
st, _ := getTypeStruct(o.Type(), scope)
fmt.Fprintf(b, "case *%s:\n", name)
j := 1
for i := 1; i < st.NumFields(); i++ {
out := func(s string, j int) {
fmt.Fprintf(b, "k%d, ok%d := compressionLenSearch(c, x.%s)\n", j, j, st.Field(i).Name())
}
// There are no slice types with names that can be compressed.
switch {
case st.Tag(i) == `dns:"cdomain-name"`:
out(st.Field(i).Name(), j)
j++
}
}
k := "k1"
ok := "ok1"
for i := 2; i < j; i++ {
k += fmt.Sprintf(" + k%d", i)
ok += fmt.Sprintf(" && ok%d", i)
}
fmt.Fprintf(b, "return %s, %s\n", k, ok)
}
fmt.Fprintln(b, "}\nreturn 0, false\n}\n\n")
// gofmt
res, err := format.Source(b.Bytes())
if err != nil {
b.WriteTo(os.Stderr)
log.Fatal(err)
}
f, err := os.Create("zcompress.go")
fatalIfErr(err)
defer f.Close()
f.Write(res)
}
func fatalIfErr(err error) {
if err != nil {
log.Fatal(err)
}
}

43
vendor/github.com/miekg/dns/dane.go generated vendored
View file

@ -1,43 +0,0 @@
package dns
import (
"crypto/sha256"
"crypto/sha512"
"crypto/x509"
"encoding/hex"
"errors"
)
// CertificateToDANE converts a certificate to a hex string as used in the TLSA or SMIMEA records.
func CertificateToDANE(selector, matchingType uint8, cert *x509.Certificate) (string, error) {
switch matchingType {
case 0:
switch selector {
case 0:
return hex.EncodeToString(cert.Raw), nil
case 1:
return hex.EncodeToString(cert.RawSubjectPublicKeyInfo), nil
}
case 1:
h := sha256.New()
switch selector {
case 0:
h.Write(cert.Raw)
return hex.EncodeToString(h.Sum(nil)), nil
case 1:
h.Write(cert.RawSubjectPublicKeyInfo)
return hex.EncodeToString(h.Sum(nil)), nil
}
case 2:
h := sha512.New()
switch selector {
case 0:
h.Write(cert.Raw)
return hex.EncodeToString(h.Sum(nil)), nil
case 1:
h.Write(cert.RawSubjectPublicKeyInfo)
return hex.EncodeToString(h.Sum(nil)), nil
}
}
return "", errors.New("dns: bad MatchingType or Selector")
}

View file

@ -1,285 +0,0 @@
package dns
import (
"errors"
"net"
"strconv"
)
const hexDigit = "0123456789abcdef"
// Everything is assumed in ClassINET.
// SetReply creates a reply message from a request message.
func (dns *Msg) SetReply(request *Msg) *Msg {
dns.Id = request.Id
dns.Response = true
dns.Opcode = request.Opcode
if dns.Opcode == OpcodeQuery {
dns.RecursionDesired = request.RecursionDesired // Copy rd bit
dns.CheckingDisabled = request.CheckingDisabled // Copy cd bit
}
dns.Rcode = RcodeSuccess
if len(request.Question) > 0 {
dns.Question = make([]Question, 1)
dns.Question[0] = request.Question[0]
}
return dns
}
// SetQuestion creates a question message, it sets the Question
// section, generates an Id and sets the RecursionDesired (RD)
// bit to true.
func (dns *Msg) SetQuestion(z string, t uint16) *Msg {
dns.Id = Id()
dns.RecursionDesired = true
dns.Question = make([]Question, 1)
dns.Question[0] = Question{z, t, ClassINET}
return dns
}
// SetNotify creates a notify message, it sets the Question
// section, generates an Id and sets the Authoritative (AA)
// bit to true.
func (dns *Msg) SetNotify(z string) *Msg {
dns.Opcode = OpcodeNotify
dns.Authoritative = true
dns.Id = Id()
dns.Question = make([]Question, 1)
dns.Question[0] = Question{z, TypeSOA, ClassINET}
return dns
}
// SetRcode creates an error message suitable for the request.
func (dns *Msg) SetRcode(request *Msg, rcode int) *Msg {
dns.SetReply(request)
dns.Rcode = rcode
return dns
}
// SetRcodeFormatError creates a message with FormError set.
func (dns *Msg) SetRcodeFormatError(request *Msg) *Msg {
dns.Rcode = RcodeFormatError
dns.Opcode = OpcodeQuery
dns.Response = true
dns.Authoritative = false
dns.Id = request.Id
return dns
}
// SetUpdate makes the message a dynamic update message. It
// sets the ZONE section to: z, TypeSOA, ClassINET.
func (dns *Msg) SetUpdate(z string) *Msg {
dns.Id = Id()
dns.Response = false
dns.Opcode = OpcodeUpdate
dns.Compress = false // BIND9 cannot handle compression
dns.Question = make([]Question, 1)
dns.Question[0] = Question{z, TypeSOA, ClassINET}
return dns
}
// SetIxfr creates message for requesting an IXFR.
func (dns *Msg) SetIxfr(z string, serial uint32, ns, mbox string) *Msg {
dns.Id = Id()
dns.Question = make([]Question, 1)
dns.Ns = make([]RR, 1)
s := new(SOA)
s.Hdr = RR_Header{z, TypeSOA, ClassINET, defaultTtl, 0}
s.Serial = serial
s.Ns = ns
s.Mbox = mbox
dns.Question[0] = Question{z, TypeIXFR, ClassINET}
dns.Ns[0] = s
return dns
}
// SetAxfr creates message for requesting an AXFR.
func (dns *Msg) SetAxfr(z string) *Msg {
dns.Id = Id()
dns.Question = make([]Question, 1)
dns.Question[0] = Question{z, TypeAXFR, ClassINET}
return dns
}
// SetTsig appends a TSIG RR to the message.
// This is only a skeleton TSIG RR that is added as the last RR in the
// additional section. The Tsig is calculated when the message is being send.
func (dns *Msg) SetTsig(z, algo string, fudge uint16, timesigned int64) *Msg {
t := new(TSIG)
t.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0}
t.Algorithm = algo
t.Fudge = fudge
t.TimeSigned = uint64(timesigned)
t.OrigId = dns.Id
dns.Extra = append(dns.Extra, t)
return dns
}
// SetEdns0 appends a EDNS0 OPT RR to the message.
// TSIG should always the last RR in a message.
func (dns *Msg) SetEdns0(udpsize uint16, do bool) *Msg {
e := new(OPT)
e.Hdr.Name = "."
e.Hdr.Rrtype = TypeOPT
e.SetUDPSize(udpsize)
if do {
e.SetDo()
}
dns.Extra = append(dns.Extra, e)
return dns
}
// IsTsig checks if the message has a TSIG record as the last record
// in the additional section. It returns the TSIG record found or nil.
func (dns *Msg) IsTsig() *TSIG {
if len(dns.Extra) > 0 {
if dns.Extra[len(dns.Extra)-1].Header().Rrtype == TypeTSIG {
return dns.Extra[len(dns.Extra)-1].(*TSIG)
}
}
return nil
}
// IsEdns0 checks if the message has a EDNS0 (OPT) record, any EDNS0
// record in the additional section will do. It returns the OPT record
// found or nil.
func (dns *Msg) IsEdns0() *OPT {
// EDNS0 is at the end of the additional section, start there.
// We might want to change this to *only* look at the last two
// records. So we see TSIG and/or OPT - this a slightly bigger
// change though.
for i := len(dns.Extra) - 1; i >= 0; i-- {
if dns.Extra[i].Header().Rrtype == TypeOPT {
return dns.Extra[i].(*OPT)
}
}
return nil
}
// IsDomainName checks if s is a valid domain name, it returns the number of
// labels and true, when a domain name is valid. Note that non fully qualified
// domain name is considered valid, in this case the last label is counted in
// the number of labels. When false is returned the number of labels is not
// defined. Also note that this function is extremely liberal; almost any
// string is a valid domain name as the DNS is 8 bit protocol. It checks if each
// label fits in 63 characters, but there is no length check for the entire
// string s. I.e. a domain name longer than 255 characters is considered valid.
func IsDomainName(s string) (labels int, ok bool) {
_, labels, err := packDomainName(s, nil, 0, nil, false)
return labels, err == nil
}
// IsSubDomain checks if child is indeed a child of the parent. If child and parent
// are the same domain true is returned as well.
func IsSubDomain(parent, child string) bool {
// Entire child is contained in parent
return CompareDomainName(parent, child) == CountLabel(parent)
}
// IsMsg sanity checks buf and returns an error if it isn't a valid DNS packet.
// The checking is performed on the binary payload.
func IsMsg(buf []byte) error {
// Header
if len(buf) < 12 {
return errors.New("dns: bad message header")
}
// Header: Opcode
// TODO(miek): more checks here, e.g. check all header bits.
return nil
}
// IsFqdn checks if a domain name is fully qualified.
func IsFqdn(s string) bool {
l := len(s)
if l == 0 {
return false
}
return s[l-1] == '.'
}
// IsRRset checks if a set of RRs is a valid RRset as defined by RFC 2181.
// This means the RRs need to have the same type, name, and class. Returns true
// if the RR set is valid, otherwise false.
func IsRRset(rrset []RR) bool {
if len(rrset) == 0 {
return false
}
if len(rrset) == 1 {
return true
}
rrHeader := rrset[0].Header()
rrType := rrHeader.Rrtype
rrClass := rrHeader.Class
rrName := rrHeader.Name
for _, rr := range rrset[1:] {
curRRHeader := rr.Header()
if curRRHeader.Rrtype != rrType || curRRHeader.Class != rrClass || curRRHeader.Name != rrName {
// Mismatch between the records, so this is not a valid rrset for
//signing/verifying
return false
}
}
return true
}
// Fqdn return the fully qualified domain name from s.
// If s is already fully qualified, it behaves as the identity function.
func Fqdn(s string) string {
if IsFqdn(s) {
return s
}
return s + "."
}
// Copied from the official Go code.
// ReverseAddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP
// address suitable for reverse DNS (PTR) record lookups or an error if it fails
// to parse the IP address.
func ReverseAddr(addr string) (arpa string, err error) {
ip := net.ParseIP(addr)
if ip == nil {
return "", &Error{err: "unrecognized address: " + addr}
}
if ip.To4() != nil {
return strconv.Itoa(int(ip[15])) + "." + strconv.Itoa(int(ip[14])) + "." + strconv.Itoa(int(ip[13])) + "." +
strconv.Itoa(int(ip[12])) + ".in-addr.arpa.", nil
}
// Must be IPv6
buf := make([]byte, 0, len(ip)*4+len("ip6.arpa."))
// Add it, in reverse, to the buffer
for i := len(ip) - 1; i >= 0; i-- {
v := ip[i]
buf = append(buf, hexDigit[v&0xF])
buf = append(buf, '.')
buf = append(buf, hexDigit[v>>4])
buf = append(buf, '.')
}
// Append "ip6.arpa." and return (buf already has the final .)
buf = append(buf, "ip6.arpa."...)
return string(buf), nil
}
// String returns the string representation for the type t.
func (t Type) String() string {
if t1, ok := TypeToString[uint16(t)]; ok {
return t1
}
return "TYPE" + strconv.Itoa(int(t))
}
// String returns the string representation for the class c.
func (c Class) String() string {
if c1, ok := ClassToString[uint16(c)]; ok {
return c1
}
return "CLASS" + strconv.Itoa(int(c))
}
// String returns the string representation for the name n.
func (n Name) String() string {
return sprintName(string(n))
}

104
vendor/github.com/miekg/dns/dns.go generated vendored
View file

@ -1,104 +0,0 @@
package dns
import "strconv"
const (
year68 = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits.
defaultTtl = 3600 // Default internal TTL.
DefaultMsgSize = 4096 // DefaultMsgSize is the standard default for messages larger than 512 bytes.
MinMsgSize = 512 // MinMsgSize is the minimal size of a DNS packet.
MaxMsgSize = 65535 // MaxMsgSize is the largest possible DNS packet.
)
// Error represents a DNS error.
type Error struct{ err string }
func (e *Error) Error() string {
if e == nil {
return "dns: <nil>"
}
return "dns: " + e.err
}
// An RR represents a resource record.
type RR interface {
// Header returns the header of an resource record. The header contains
// everything up to the rdata.
Header() *RR_Header
// String returns the text representation of the resource record.
String() string
// copy returns a copy of the RR
copy() RR
// len returns the length (in octets) of the uncompressed RR in wire format.
len() int
// pack packs an RR into wire format.
pack([]byte, int, map[string]int, bool) (int, error)
}
// RR_Header is the header all DNS resource records share.
type RR_Header struct {
Name string `dns:"cdomain-name"`
Rrtype uint16
Class uint16
Ttl uint32
Rdlength uint16 // Length of data after header.
}
// Header returns itself. This is here to make RR_Header implements the RR interface.
func (h *RR_Header) Header() *RR_Header { return h }
// Just to implement the RR interface.
func (h *RR_Header) copy() RR { return nil }
func (h *RR_Header) copyHeader() *RR_Header {
r := new(RR_Header)
r.Name = h.Name
r.Rrtype = h.Rrtype
r.Class = h.Class
r.Ttl = h.Ttl
r.Rdlength = h.Rdlength
return r
}
func (h *RR_Header) String() string {
var s string
if h.Rrtype == TypeOPT {
s = ";"
// and maybe other things
}
s += sprintName(h.Name) + "\t"
s += strconv.FormatInt(int64(h.Ttl), 10) + "\t"
s += Class(h.Class).String() + "\t"
s += Type(h.Rrtype).String() + "\t"
return s
}
func (h *RR_Header) len() int {
l := len(h.Name) + 1
l += 10 // rrtype(2) + class(2) + ttl(4) + rdlength(2)
return l
}
// ToRFC3597 converts a known RR to the unknown RR representation from RFC 3597.
func (rr *RFC3597) ToRFC3597(r RR) error {
buf := make([]byte, r.len()*2)
off, err := PackRR(r, buf, 0, nil, false)
if err != nil {
return err
}
buf = buf[:off]
if int(r.Header().Rdlength) > off {
return ErrBuf
}
rfc3597, _, err := unpackRFC3597(*r.Header(), buf, off-int(r.Header().Rdlength))
if err != nil {
return err
}
*rr = *rfc3597.(*RFC3597)
return nil
}

View file

@ -1,211 +0,0 @@
package dns
import (
"net"
"testing"
)
func BenchmarkMsgLength(b *testing.B) {
b.StopTimer()
makeMsg := func(question string, ans, ns, e []RR) *Msg {
msg := new(Msg)
msg.SetQuestion(Fqdn(question), TypeANY)
msg.Answer = append(msg.Answer, ans...)
msg.Ns = append(msg.Ns, ns...)
msg.Extra = append(msg.Extra, e...)
msg.Compress = true
return msg
}
name1 := "12345678901234567890123456789012345.12345678.123."
rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1)
msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
b.StartTimer()
for i := 0; i < b.N; i++ {
msg.Len()
}
}
func BenchmarkMsgLengthPack(b *testing.B) {
makeMsg := func(question string, ans, ns, e []RR) *Msg {
msg := new(Msg)
msg.SetQuestion(Fqdn(question), TypeANY)
msg.Answer = append(msg.Answer, ans...)
msg.Ns = append(msg.Ns, ns...)
msg.Extra = append(msg.Extra, e...)
msg.Compress = true
return msg
}
name1 := "12345678901234567890123456789012345.12345678.123."
rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1)
msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = msg.Pack()
}
}
func BenchmarkPackDomainName(b *testing.B) {
name1 := "12345678901234567890123456789012345.12345678.123."
buf := make([]byte, len(name1)+1)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = PackDomainName(name1, buf, 0, nil, false)
}
}
func BenchmarkUnpackDomainName(b *testing.B) {
name1 := "12345678901234567890123456789012345.12345678.123."
buf := make([]byte, len(name1)+1)
_, _ = PackDomainName(name1, buf, 0, nil, false)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, _ = UnpackDomainName(buf, 0)
}
}
func BenchmarkUnpackDomainNameUnprintable(b *testing.B) {
name1 := "\x02\x02\x02\x025\x02\x02\x02\x02.12345678.123."
buf := make([]byte, len(name1)+1)
_, _ = PackDomainName(name1, buf, 0, nil, false)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, _ = UnpackDomainName(buf, 0)
}
}
func BenchmarkCopy(b *testing.B) {
b.ReportAllocs()
m := new(Msg)
m.SetQuestion("miek.nl.", TypeA)
rr, _ := NewRR("miek.nl. 2311 IN A 127.0.0.1")
m.Answer = []RR{rr}
rr, _ = NewRR("miek.nl. 2311 IN NS 127.0.0.1")
m.Ns = []RR{rr}
rr, _ = NewRR("miek.nl. 2311 IN A 127.0.0.1")
m.Extra = []RR{rr}
b.ResetTimer()
for i := 0; i < b.N; i++ {
m.Copy()
}
}
func BenchmarkPackA(b *testing.B) {
a := &A{Hdr: RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY}, A: net.IPv4(127, 0, 0, 1)}
buf := make([]byte, a.len())
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = PackRR(a, buf, 0, nil, false)
}
}
func BenchmarkUnpackA(b *testing.B) {
a := &A{Hdr: RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY}, A: net.IPv4(127, 0, 0, 1)}
buf := make([]byte, a.len())
PackRR(a, buf, 0, nil, false)
a = nil
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, _ = UnpackRR(buf, 0)
}
}
func BenchmarkPackMX(b *testing.B) {
m := &MX{Hdr: RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY}, Mx: "mx.miek.nl."}
buf := make([]byte, m.len())
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = PackRR(m, buf, 0, nil, false)
}
}
func BenchmarkUnpackMX(b *testing.B) {
m := &MX{Hdr: RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY}, Mx: "mx.miek.nl."}
buf := make([]byte, m.len())
PackRR(m, buf, 0, nil, false)
m = nil
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, _ = UnpackRR(buf, 0)
}
}
func BenchmarkPackAAAAA(b *testing.B) {
aaaa, _ := NewRR(". IN A ::1")
buf := make([]byte, aaaa.len())
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = PackRR(aaaa, buf, 0, nil, false)
}
}
func BenchmarkUnpackAAAA(b *testing.B) {
aaaa, _ := NewRR(". IN A ::1")
buf := make([]byte, aaaa.len())
PackRR(aaaa, buf, 0, nil, false)
aaaa = nil
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, _ = UnpackRR(buf, 0)
}
}
func BenchmarkPackMsg(b *testing.B) {
makeMsg := func(question string, ans, ns, e []RR) *Msg {
msg := new(Msg)
msg.SetQuestion(Fqdn(question), TypeANY)
msg.Answer = append(msg.Answer, ans...)
msg.Ns = append(msg.Ns, ns...)
msg.Extra = append(msg.Extra, e...)
msg.Compress = true
return msg
}
name1 := "12345678901234567890123456789012345.12345678.123."
rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1)
msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
buf := make([]byte, 512)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = msg.PackBuffer(buf)
}
}
func BenchmarkUnpackMsg(b *testing.B) {
makeMsg := func(question string, ans, ns, e []RR) *Msg {
msg := new(Msg)
msg.SetQuestion(Fqdn(question), TypeANY)
msg.Answer = append(msg.Answer, ans...)
msg.Ns = append(msg.Ns, ns...)
msg.Extra = append(msg.Extra, e...)
msg.Compress = true
return msg
}
name1 := "12345678901234567890123456789012345.12345678.123."
rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1)
msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
msgBuf, _ := msg.Pack()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = msg.Unpack(msgBuf)
}
}
func BenchmarkIdGeneration(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = id()
}
}

View file

@ -1,450 +0,0 @@
package dns
import (
"encoding/hex"
"net"
"testing"
)
func TestPackUnpack(t *testing.T) {
out := new(Msg)
out.Answer = make([]RR, 1)
key := new(DNSKEY)
key = &DNSKEY{Flags: 257, Protocol: 3, Algorithm: RSASHA1}
key.Hdr = RR_Header{Name: "miek.nl.", Rrtype: TypeDNSKEY, Class: ClassINET, Ttl: 3600}
key.PublicKey = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"
out.Answer[0] = key
msg, err := out.Pack()
if err != nil {
t.Error("failed to pack msg with DNSKEY")
}
in := new(Msg)
if in.Unpack(msg) != nil {
t.Error("failed to unpack msg with DNSKEY")
}
sig := new(RRSIG)
sig = &RRSIG{TypeCovered: TypeDNSKEY, Algorithm: RSASHA1, Labels: 2,
OrigTtl: 3600, Expiration: 4000, Inception: 4000, KeyTag: 34641, SignerName: "miek.nl.",
Signature: "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"}
sig.Hdr = RR_Header{Name: "miek.nl.", Rrtype: TypeRRSIG, Class: ClassINET, Ttl: 3600}
out.Answer[0] = sig
msg, err = out.Pack()
if err != nil {
t.Error("failed to pack msg with RRSIG")
}
if in.Unpack(msg) != nil {
t.Error("failed to unpack msg with RRSIG")
}
}
func TestPackUnpack2(t *testing.T) {
m := new(Msg)
m.Extra = make([]RR, 1)
m.Answer = make([]RR, 1)
dom := "miek.nl."
rr := new(A)
rr.Hdr = RR_Header{Name: dom, Rrtype: TypeA, Class: ClassINET, Ttl: 0}
rr.A = net.IPv4(127, 0, 0, 1)
x := new(TXT)
x.Hdr = RR_Header{Name: dom, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}
x.Txt = []string{"heelalaollo"}
m.Extra[0] = x
m.Answer[0] = rr
_, err := m.Pack()
if err != nil {
t.Error("Packing failed: ", err)
return
}
}
func TestPackUnpack3(t *testing.T) {
m := new(Msg)
m.Extra = make([]RR, 2)
m.Answer = make([]RR, 1)
dom := "miek.nl."
rr := new(A)
rr.Hdr = RR_Header{Name: dom, Rrtype: TypeA, Class: ClassINET, Ttl: 0}
rr.A = net.IPv4(127, 0, 0, 1)
x1 := new(TXT)
x1.Hdr = RR_Header{Name: dom, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}
x1.Txt = []string{}
x2 := new(TXT)
x2.Hdr = RR_Header{Name: dom, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}
x2.Txt = []string{"heelalaollo"}
m.Extra[0] = x1
m.Extra[1] = x2
m.Answer[0] = rr
b, err := m.Pack()
if err != nil {
t.Error("packing failed: ", err)
return
}
var unpackMsg Msg
err = unpackMsg.Unpack(b)
if err != nil {
t.Error("unpacking failed")
return
}
}
func TestBailiwick(t *testing.T) {
yes := map[string]string{
"miek1.nl": "miek1.nl",
"miek.nl": "ns.miek.nl",
".": "miek.nl",
}
for parent, child := range yes {
if !IsSubDomain(parent, child) {
t.Errorf("%s should be child of %s", child, parent)
t.Errorf("comparelabels %d", CompareDomainName(parent, child))
t.Errorf("lenlabels %d %d", CountLabel(parent), CountLabel(child))
}
}
no := map[string]string{
"www.miek.nl": "ns.miek.nl",
"m\\.iek.nl": "ns.miek.nl",
"w\\.iek.nl": "w.iek.nl",
"p\\\\.iek.nl": "ns.p.iek.nl", // p\\.iek.nl , literal \ in domain name
"miek.nl": ".",
}
for parent, child := range no {
if IsSubDomain(parent, child) {
t.Errorf("%s should not be child of %s", child, parent)
t.Errorf("comparelabels %d", CompareDomainName(parent, child))
t.Errorf("lenlabels %d %d", CountLabel(parent), CountLabel(child))
}
}
}
func TestPack(t *testing.T) {
rr := []string{"US. 86400 IN NSEC 0-.us. NS SOA RRSIG NSEC DNSKEY TYPE65534"}
m := new(Msg)
var err error
m.Answer = make([]RR, 1)
for _, r := range rr {
m.Answer[0], err = NewRR(r)
if err != nil {
t.Errorf("failed to create RR: %v", err)
continue
}
if _, err := m.Pack(); err != nil {
t.Errorf("packing failed: %v", err)
}
}
x := new(Msg)
ns, _ := NewRR("pool.ntp.org. 390 IN NS a.ntpns.org")
ns.(*NS).Ns = "a.ntpns.org"
x.Ns = append(m.Ns, ns)
x.Ns = append(m.Ns, ns)
x.Ns = append(m.Ns, ns)
// This crashes due to the fact the a.ntpns.org isn't a FQDN
// How to recover() from a remove panic()?
if _, err := x.Pack(); err == nil {
t.Error("packing should fail")
}
x.Answer = make([]RR, 1)
x.Answer[0], err = NewRR(rr[0])
if _, err := x.Pack(); err == nil {
t.Error("packing should fail")
}
x.Question = make([]Question, 1)
x.Question[0] = Question{";sd#edddds鍛↙赏‘℅∥↙xzztsestxssweewwsssstx@s@Z嵌e@cn.pool.ntp.org.", TypeA, ClassINET}
if _, err := x.Pack(); err == nil {
t.Error("packing should fail")
}
}
func TestPackNAPTR(t *testing.T) {
for _, n := range []string{
`apple.com. IN NAPTR 100 50 "se" "SIP+D2U" "" _sip._udp.apple.com.`,
`apple.com. IN NAPTR 90 50 "se" "SIP+D2T" "" _sip._tcp.apple.com.`,
`apple.com. IN NAPTR 50 50 "se" "SIPS+D2T" "" _sips._tcp.apple.com.`,
} {
rr, _ := NewRR(n)
msg := make([]byte, rr.len())
if off, err := PackRR(rr, msg, 0, nil, false); err != nil {
t.Errorf("packing failed: %v", err)
t.Errorf("length %d, need more than %d", rr.len(), off)
} else {
t.Logf("buf size needed: %d", off)
}
}
}
func TestCompressLength(t *testing.T) {
m := new(Msg)
m.SetQuestion("miek.nl", TypeMX)
ul := m.Len()
m.Compress = true
if ul != m.Len() {
t.Fatalf("should be equal")
}
}
// Does the predicted length match final packed length?
func TestMsgCompressLength(t *testing.T) {
makeMsg := func(question string, ans, ns, e []RR) *Msg {
msg := new(Msg)
msg.SetQuestion(Fqdn(question), TypeANY)
msg.Answer = append(msg.Answer, ans...)
msg.Ns = append(msg.Ns, ns...)
msg.Extra = append(msg.Extra, e...)
msg.Compress = true
return msg
}
name1 := "12345678901234567890123456789012345.12345678.123."
rrA, _ := NewRR(name1 + " 3600 IN A 192.0.2.1")
rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1)
tests := []*Msg{
makeMsg(name1, []RR{rrA}, nil, nil),
makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)}
for _, msg := range tests {
predicted := msg.Len()
buf, err := msg.Pack()
if err != nil {
t.Error(err)
}
if predicted < len(buf) {
t.Errorf("predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d",
msg.Question[0].Name, len(msg.Answer), predicted, len(buf))
}
}
}
func TestMsgLength(t *testing.T) {
makeMsg := func(question string, ans, ns, e []RR) *Msg {
msg := new(Msg)
msg.SetQuestion(Fqdn(question), TypeANY)
msg.Answer = append(msg.Answer, ans...)
msg.Ns = append(msg.Ns, ns...)
msg.Extra = append(msg.Extra, e...)
return msg
}
name1 := "12345678901234567890123456789012345.12345678.123."
rrA, _ := NewRR(name1 + " 3600 IN A 192.0.2.1")
rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1)
tests := []*Msg{
makeMsg(name1, []RR{rrA}, nil, nil),
makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)}
for _, msg := range tests {
predicted := msg.Len()
buf, err := msg.Pack()
if err != nil {
t.Error(err)
}
if predicted < len(buf) {
t.Errorf("predicted length is wrong: predicted %s (len=%d), actual %d",
msg.Question[0].Name, predicted, len(buf))
}
}
}
func TestMsgLength2(t *testing.T) {
// Serialized replies
var testMessages = []string{
// google.com. IN A?
"064e81800001000b0004000506676f6f676c6503636f6d0000010001c00c00010001000000050004adc22986c00c00010001000000050004adc22987c00c00010001000000050004adc22988c00c00010001000000050004adc22989c00c00010001000000050004adc2298ec00c00010001000000050004adc22980c00c00010001000000050004adc22981c00c00010001000000050004adc22982c00c00010001000000050004adc22983c00c00010001000000050004adc22984c00c00010001000000050004adc22985c00c00020001000000050006036e7331c00cc00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc0d800010001000000050004d8ef200ac0ea00010001000000050004d8ef220ac0fc00010001000000050004d8ef240ac10e00010001000000050004d8ef260a0000290500000000050000",
// amazon.com. IN A? (reply has no EDNS0 record)
// TODO(miek): this one is off-by-one, need to find out why
//"6de1818000010004000a000806616d617a6f6e03636f6d0000010001c00c000100010000000500044815c2d4c00c000100010000000500044815d7e8c00c00010001000000050004b02062a6c00c00010001000000050004cdfbf236c00c000200010000000500140570646e733408756c747261646e73036f726700c00c000200010000000500150570646e733508756c747261646e7304696e666f00c00c000200010000000500160570646e733608756c747261646e7302636f02756b00c00c00020001000000050014036e7331037033310664796e656374036e657400c00c00020001000000050006036e7332c0cfc00c00020001000000050006036e7333c0cfc00c00020001000000050006036e7334c0cfc00c000200010000000500110570646e733108756c747261646e73c0dac00c000200010000000500080570646e7332c127c00c000200010000000500080570646e7333c06ec0cb00010001000000050004d04e461fc0eb00010001000000050004cc0dfa1fc0fd00010001000000050004d04e471fc10f00010001000000050004cc0dfb1fc12100010001000000050004cc4a6c01c121001c000100000005001020010502f3ff00000000000000000001c13e00010001000000050004cc4a6d01c13e001c0001000000050010261000a1101400000000000000000001",
// yahoo.com. IN A?
"fc2d81800001000300070008057961686f6f03636f6d0000010001c00c00010001000000050004628afd6dc00c00010001000000050004628bb718c00c00010001000000050004cebe242dc00c00020001000000050006036e7336c00cc00c00020001000000050006036e7338c00cc00c00020001000000050006036e7331c00cc00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc00c00020001000000050006036e7335c00cc07b0001000100000005000444b48310c08d00010001000000050004448eff10c09f00010001000000050004cb54dd35c0b100010001000000050004628a0b9dc0c30001000100000005000477a0f77cc05700010001000000050004ca2bdfaac06900010001000000050004caa568160000290500000000050000",
// microsoft.com. IN A?
"f4368180000100020005000b096d6963726f736f667403636f6d0000010001c00c0001000100000005000440040b25c00c0001000100000005000441373ac9c00c0002000100000005000e036e7331046d736674036e657400c00c00020001000000050006036e7332c04fc00c00020001000000050006036e7333c04fc00c00020001000000050006036e7334c04fc00c00020001000000050006036e7335c04fc04b000100010000000500044137253ec04b001c00010000000500102a010111200500000000000000010001c0650001000100000005000440043badc065001c00010000000500102a010111200600060000000000010001c07700010001000000050004d5c7b435c077001c00010000000500102a010111202000000000000000010001c08900010001000000050004cf2e4bfec089001c00010000000500102404f800200300000000000000010001c09b000100010000000500044137e28cc09b001c00010000000500102a010111200f000100000000000100010000290500000000050000",
// google.com. IN MX?
"724b8180000100050004000b06676f6f676c6503636f6d00000f0001c00c000f000100000005000c000a056173706d78016cc00cc00c000f0001000000050009001404616c7431c02ac00c000f0001000000050009001e04616c7432c02ac00c000f0001000000050009002804616c7433c02ac00c000f0001000000050009003204616c7434c02ac00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc00c00020001000000050006036e7331c00cc02a00010001000000050004adc2421bc02a001c00010000000500102a00145040080c01000000000000001bc04200010001000000050004adc2461bc05700010001000000050004adc2451bc06c000100010000000500044a7d8f1bc081000100010000000500044a7d191bc0ca00010001000000050004d8ef200ac09400010001000000050004d8ef220ac0a600010001000000050004d8ef240ac0b800010001000000050004d8ef260a0000290500000000050000",
// reddit.com. IN A?
"12b98180000100080000000c0672656464697403636f6d0000020001c00c0002000100000005000f046175733204616b616d036e657400c00c000200010000000500070475736534c02dc00c000200010000000500070475737733c02dc00c000200010000000500070475737735c02dc00c00020001000000050008056173696131c02dc00c00020001000000050008056173696139c02dc00c00020001000000050008056e73312d31c02dc00c0002000100000005000a076e73312d313935c02dc02800010001000000050004c30a242ec04300010001000000050004451f1d39c05600010001000000050004451f3bc7c0690001000100000005000460073240c07c000100010000000500046007fb81c090000100010000000500047c283484c090001c00010000000500102a0226f0006700000000000000000064c0a400010001000000050004c16c5b01c0a4001c000100000005001026001401000200000000000000000001c0b800010001000000050004c16c5bc3c0b8001c0001000000050010260014010002000000000000000000c30000290500000000050000",
}
for i, hexData := range testMessages {
// we won't fail the decoding of the hex
input, _ := hex.DecodeString(hexData)
m := new(Msg)
m.Unpack(input)
m.Compress = true
lenComp := m.Len()
b, _ := m.Pack()
pacComp := len(b)
m.Compress = false
lenUnComp := m.Len()
b, _ = m.Pack()
pacUnComp := len(b)
if pacComp+1 != lenComp {
t.Errorf("msg.Len(compressed)=%d actual=%d for test %d", lenComp, pacComp, i)
}
if pacUnComp+1 != lenUnComp {
t.Errorf("msg.Len(uncompressed)=%d actual=%d for test %d", lenUnComp, pacUnComp, i)
}
}
}
func TestMsgLengthCompressionMalformed(t *testing.T) {
// SOA with empty hostmaster, which is illegal
soa := &SOA{Hdr: RR_Header{Name: ".", Rrtype: TypeSOA, Class: ClassINET, Ttl: 12345},
Ns: ".",
Mbox: "",
Serial: 0,
Refresh: 28800,
Retry: 7200,
Expire: 604800,
Minttl: 60}
m := new(Msg)
m.Compress = true
m.Ns = []RR{soa}
m.Len() // Should not crash.
}
func TestMsgCompressLength2(t *testing.T) {
msg := new(Msg)
msg.Compress = true
msg.SetQuestion(Fqdn("bliep."), TypeANY)
msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "blaat.", Rrtype: 0x21, Class: 0x1, Ttl: 0x3c}, Port: 0x4c57, Target: "foo.bar."})
msg.Extra = append(msg.Extra, &A{Hdr: RR_Header{Name: "foo.bar.", Rrtype: 0x1, Class: 0x1, Ttl: 0x3c}, A: net.IP{0xac, 0x11, 0x0, 0x3}})
predicted := msg.Len()
buf, err := msg.Pack()
if err != nil {
t.Error(err)
}
if predicted != len(buf) {
t.Errorf("predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d",
msg.Question[0].Name, len(msg.Answer), predicted, len(buf))
}
}
func TestToRFC3597(t *testing.T) {
a, _ := NewRR("miek.nl. IN A 10.0.1.1")
x := new(RFC3597)
x.ToRFC3597(a)
if x.String() != `miek.nl. 3600 CLASS1 TYPE1 \# 4 0a000101` {
t.Errorf("string mismatch, got: %s", x)
}
b, _ := NewRR("miek.nl. IN MX 10 mx.miek.nl.")
x.ToRFC3597(b)
if x.String() != `miek.nl. 3600 CLASS1 TYPE15 \# 14 000a026d78046d69656b026e6c00` {
t.Errorf("string mismatch, got: %s", x)
}
}
func TestNoRdataPack(t *testing.T) {
data := make([]byte, 1024)
for typ, fn := range TypeToRR {
r := fn()
*r.Header() = RR_Header{Name: "miek.nl.", Rrtype: typ, Class: ClassINET, Ttl: 16}
_, err := PackRR(r, data, 0, nil, false)
if err != nil {
t.Errorf("failed to pack RR with zero rdata: %s: %v", TypeToString[typ], err)
}
}
}
func TestNoRdataUnpack(t *testing.T) {
data := make([]byte, 1024)
for typ, fn := range TypeToRR {
if typ == TypeSOA || typ == TypeTSIG {
// SOA, TSIG will not be seen (like this) in dyn. updates?
continue
}
r := fn()
*r.Header() = RR_Header{Name: "miek.nl.", Rrtype: typ, Class: ClassINET, Ttl: 16}
off, err := PackRR(r, data, 0, nil, false)
if err != nil {
// Should always works, TestNoDataPack should have caught this
t.Errorf("failed to pack RR: %v", err)
continue
}
rr, _, err := UnpackRR(data[:off], 0)
if err != nil {
t.Errorf("failed to unpack RR with zero rdata: %s: %v", TypeToString[typ], err)
}
t.Log(rr)
}
}
func TestRdataOverflow(t *testing.T) {
rr := new(RFC3597)
rr.Hdr.Name = "."
rr.Hdr.Class = ClassINET
rr.Hdr.Rrtype = 65280
rr.Rdata = hex.EncodeToString(make([]byte, 0xFFFF))
buf := make([]byte, 0xFFFF*2)
if _, err := PackRR(rr, buf, 0, nil, false); err != nil {
t.Fatalf("maximum size rrdata pack failed: %v", err)
}
rr.Rdata += "00"
if _, err := PackRR(rr, buf, 0, nil, false); err != ErrRdata {
t.Fatalf("oversize rrdata pack didn't return ErrRdata - instead: %v", err)
}
}
func TestCopy(t *testing.T) {
rr, _ := NewRR("miek.nl. 2311 IN A 127.0.0.1") // Weird TTL to avoid catching TTL
rr1 := Copy(rr)
if rr.String() != rr1.String() {
t.Fatalf("Copy() failed %s != %s", rr.String(), rr1.String())
}
}
func TestMsgCopy(t *testing.T) {
m := new(Msg)
m.SetQuestion("miek.nl.", TypeA)
rr, _ := NewRR("miek.nl. 2311 IN A 127.0.0.1")
m.Answer = []RR{rr}
rr, _ = NewRR("miek.nl. 2311 IN NS 127.0.0.1")
m.Ns = []RR{rr}
m1 := m.Copy()
if m.String() != m1.String() {
t.Fatalf("Msg.Copy() failed %s != %s", m.String(), m1.String())
}
m1.Answer[0], _ = NewRR("somethingelse.nl. 2311 IN A 127.0.0.1")
if m.String() == m1.String() {
t.Fatalf("Msg.Copy() failed; change to copy changed template %s", m.String())
}
rr, _ = NewRR("miek.nl. 2311 IN A 127.0.0.2")
m1.Answer = append(m1.Answer, rr)
if m1.Ns[0].String() == m1.Answer[1].String() {
t.Fatalf("Msg.Copy() failed; append changed underlying array %s", m1.Ns[0].String())
}
}
func TestMsgPackBuffer(t *testing.T) {
var testMessages = []string{
// news.ycombinator.com.in.escapemg.com. IN A, response
"586285830001000000010000046e6577730b79636f6d62696e61746f7203636f6d02696e086573636170656d6703636f6d0000010001c0210006000100000e10002c036e7332c02103646e730b67726f6f7665736861726bc02d77ed50e600002a3000000e1000093a8000000e10",
// news.ycombinator.com.in.escapemg.com. IN A, question
"586201000001000000000000046e6577730b79636f6d62696e61746f7203636f6d02696e086573636170656d6703636f6d0000010001",
"398781020001000000000000046e6577730b79636f6d62696e61746f7203636f6d0000010001",
}
for i, hexData := range testMessages {
// we won't fail the decoding of the hex
input, _ := hex.DecodeString(hexData)
m := new(Msg)
if err := m.Unpack(input); err != nil {
t.Errorf("packet %d failed to unpack", i)
continue
}
t.Logf("packet %d %s", i, m.String())
}
}

720
vendor/github.com/miekg/dns/dnssec.go generated vendored
View file

@ -1,720 +0,0 @@
package dns
import (
"bytes"
"crypto"
"crypto/dsa"
"crypto/ecdsa"
"crypto/elliptic"
_ "crypto/md5"
"crypto/rand"
"crypto/rsa"
_ "crypto/sha1"
_ "crypto/sha256"
_ "crypto/sha512"
"encoding/asn1"
"encoding/binary"
"encoding/hex"
"math/big"
"sort"
"strings"
"time"
)
// DNSSEC encryption algorithm codes.
const (
_ uint8 = iota
RSAMD5
DH
DSA
_ // Skip 4, RFC 6725, section 2.1
RSASHA1
DSANSEC3SHA1
RSASHA1NSEC3SHA1
RSASHA256
_ // Skip 9, RFC 6725, section 2.1
RSASHA512
_ // Skip 11, RFC 6725, section 2.1
ECCGOST
ECDSAP256SHA256
ECDSAP384SHA384
INDIRECT uint8 = 252
PRIVATEDNS uint8 = 253 // Private (experimental keys)
PRIVATEOID uint8 = 254
)
// AlgorithmToString is a map of algorithm IDs to algorithm names.
var AlgorithmToString = map[uint8]string{
RSAMD5: "RSAMD5",
DH: "DH",
DSA: "DSA",
RSASHA1: "RSASHA1",
DSANSEC3SHA1: "DSA-NSEC3-SHA1",
RSASHA1NSEC3SHA1: "RSASHA1-NSEC3-SHA1",
RSASHA256: "RSASHA256",
RSASHA512: "RSASHA512",
ECCGOST: "ECC-GOST",
ECDSAP256SHA256: "ECDSAP256SHA256",
ECDSAP384SHA384: "ECDSAP384SHA384",
INDIRECT: "INDIRECT",
PRIVATEDNS: "PRIVATEDNS",
PRIVATEOID: "PRIVATEOID",
}
// StringToAlgorithm is the reverse of AlgorithmToString.
var StringToAlgorithm = reverseInt8(AlgorithmToString)
// AlgorithmToHash is a map of algorithm crypto hash IDs to crypto.Hash's.
var AlgorithmToHash = map[uint8]crypto.Hash{
RSAMD5: crypto.MD5, // Deprecated in RFC 6725
RSASHA1: crypto.SHA1,
RSASHA1NSEC3SHA1: crypto.SHA1,
RSASHA256: crypto.SHA256,
ECDSAP256SHA256: crypto.SHA256,
ECDSAP384SHA384: crypto.SHA384,
RSASHA512: crypto.SHA512,
}
// DNSSEC hashing algorithm codes.
const (
_ uint8 = iota
SHA1 // RFC 4034
SHA256 // RFC 4509
GOST94 // RFC 5933
SHA384 // Experimental
SHA512 // Experimental
)
// HashToString is a map of hash IDs to names.
var HashToString = map[uint8]string{
SHA1: "SHA1",
SHA256: "SHA256",
GOST94: "GOST94",
SHA384: "SHA384",
SHA512: "SHA512",
}
// StringToHash is a map of names to hash IDs.
var StringToHash = reverseInt8(HashToString)
// DNSKEY flag values.
const (
SEP = 1
REVOKE = 1 << 7
ZONE = 1 << 8
)
// The RRSIG needs to be converted to wireformat with some of the rdata (the signature) missing.
type rrsigWireFmt struct {
TypeCovered uint16
Algorithm uint8
Labels uint8
OrigTtl uint32
Expiration uint32
Inception uint32
KeyTag uint16
SignerName string `dns:"domain-name"`
/* No Signature */
}
// Used for converting DNSKEY's rdata to wirefmt.
type dnskeyWireFmt struct {
Flags uint16
Protocol uint8
Algorithm uint8
PublicKey string `dns:"base64"`
/* Nothing is left out */
}
func divRoundUp(a, b int) int {
return (a + b - 1) / b
}
// KeyTag calculates the keytag (or key-id) of the DNSKEY.
func (k *DNSKEY) KeyTag() uint16 {
if k == nil {
return 0
}
var keytag int
switch k.Algorithm {
case RSAMD5:
// Look at the bottom two bytes of the modules, which the last
// item in the pubkey. We could do this faster by looking directly
// at the base64 values. But I'm lazy.
modulus, _ := fromBase64([]byte(k.PublicKey))
if len(modulus) > 1 {
x := binary.BigEndian.Uint16(modulus[len(modulus)-2:])
keytag = int(x)
}
default:
keywire := new(dnskeyWireFmt)
keywire.Flags = k.Flags
keywire.Protocol = k.Protocol
keywire.Algorithm = k.Algorithm
keywire.PublicKey = k.PublicKey
wire := make([]byte, DefaultMsgSize)
n, err := packKeyWire(keywire, wire)
if err != nil {
return 0
}
wire = wire[:n]
for i, v := range wire {
if i&1 != 0 {
keytag += int(v) // must be larger than uint32
} else {
keytag += int(v) << 8
}
}
keytag += (keytag >> 16) & 0xFFFF
keytag &= 0xFFFF
}
return uint16(keytag)
}
// ToDS converts a DNSKEY record to a DS record.
func (k *DNSKEY) ToDS(h uint8) *DS {
if k == nil {
return nil
}
ds := new(DS)
ds.Hdr.Name = k.Hdr.Name
ds.Hdr.Class = k.Hdr.Class
ds.Hdr.Rrtype = TypeDS
ds.Hdr.Ttl = k.Hdr.Ttl
ds.Algorithm = k.Algorithm
ds.DigestType = h
ds.KeyTag = k.KeyTag()
keywire := new(dnskeyWireFmt)
keywire.Flags = k.Flags
keywire.Protocol = k.Protocol
keywire.Algorithm = k.Algorithm
keywire.PublicKey = k.PublicKey
wire := make([]byte, DefaultMsgSize)
n, err := packKeyWire(keywire, wire)
if err != nil {
return nil
}
wire = wire[:n]
owner := make([]byte, 255)
off, err1 := PackDomainName(strings.ToLower(k.Hdr.Name), owner, 0, nil, false)
if err1 != nil {
return nil
}
owner = owner[:off]
// RFC4034:
// digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA);
// "|" denotes concatenation
// DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key.
var hash crypto.Hash
switch h {
case SHA1:
hash = crypto.SHA1
case SHA256:
hash = crypto.SHA256
case SHA384:
hash = crypto.SHA384
case SHA512:
hash = crypto.SHA512
default:
return nil
}
s := hash.New()
s.Write(owner)
s.Write(wire)
ds.Digest = hex.EncodeToString(s.Sum(nil))
return ds
}
// ToCDNSKEY converts a DNSKEY record to a CDNSKEY record.
func (k *DNSKEY) ToCDNSKEY() *CDNSKEY {
c := &CDNSKEY{DNSKEY: *k}
c.Hdr = *k.Hdr.copyHeader()
c.Hdr.Rrtype = TypeCDNSKEY
return c
}
// ToCDS converts a DS record to a CDS record.
func (d *DS) ToCDS() *CDS {
c := &CDS{DS: *d}
c.Hdr = *d.Hdr.copyHeader()
c.Hdr.Rrtype = TypeCDS
return c
}
// Sign signs an RRSet. The signature needs to be filled in with the values:
// Inception, Expiration, KeyTag, SignerName and Algorithm. The rest is copied
// from the RRset. Sign returns a non-nill error when the signing went OK.
// There is no check if RRSet is a proper (RFC 2181) RRSet. If OrigTTL is non
// zero, it is used as-is, otherwise the TTL of the RRset is used as the
// OrigTTL.
func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
if k == nil {
return ErrPrivKey
}
// s.Inception and s.Expiration may be 0 (rollover etc.), the rest must be set
if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
return ErrKey
}
rr.Hdr.Rrtype = TypeRRSIG
rr.Hdr.Name = rrset[0].Header().Name
rr.Hdr.Class = rrset[0].Header().Class
if rr.OrigTtl == 0 { // If set don't override
rr.OrigTtl = rrset[0].Header().Ttl
}
rr.TypeCovered = rrset[0].Header().Rrtype
rr.Labels = uint8(CountLabel(rrset[0].Header().Name))
if strings.HasPrefix(rrset[0].Header().Name, "*") {
rr.Labels-- // wildcard, remove from label count
}
sigwire := new(rrsigWireFmt)
sigwire.TypeCovered = rr.TypeCovered
sigwire.Algorithm = rr.Algorithm
sigwire.Labels = rr.Labels
sigwire.OrigTtl = rr.OrigTtl
sigwire.Expiration = rr.Expiration
sigwire.Inception = rr.Inception
sigwire.KeyTag = rr.KeyTag
// For signing, lowercase this name
sigwire.SignerName = strings.ToLower(rr.SignerName)
// Create the desired binary blob
signdata := make([]byte, DefaultMsgSize)
n, err := packSigWire(sigwire, signdata)
if err != nil {
return err
}
signdata = signdata[:n]
wire, err := rawSignatureData(rrset, rr)
if err != nil {
return err
}
hash, ok := AlgorithmToHash[rr.Algorithm]
if !ok {
return ErrAlg
}
h := hash.New()
h.Write(signdata)
h.Write(wire)
signature, err := sign(k, h.Sum(nil), hash, rr.Algorithm)
if err != nil {
return err
}
rr.Signature = toBase64(signature)
return nil
}
func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte, error) {
signature, err := k.Sign(rand.Reader, hashed, hash)
if err != nil {
return nil, err
}
switch alg {
case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512:
return signature, nil
case ECDSAP256SHA256, ECDSAP384SHA384:
ecdsaSignature := &struct {
R, S *big.Int
}{}
if _, err := asn1.Unmarshal(signature, ecdsaSignature); err != nil {
return nil, err
}
var intlen int
switch alg {
case ECDSAP256SHA256:
intlen = 32
case ECDSAP384SHA384:
intlen = 48
}
signature := intToBytes(ecdsaSignature.R, intlen)
signature = append(signature, intToBytes(ecdsaSignature.S, intlen)...)
return signature, nil
// There is no defined interface for what a DSA backed crypto.Signer returns
case DSA, DSANSEC3SHA1:
// t := divRoundUp(divRoundUp(p.PublicKey.Y.BitLen(), 8)-64, 8)
// signature := []byte{byte(t)}
// signature = append(signature, intToBytes(r1, 20)...)
// signature = append(signature, intToBytes(s1, 20)...)
// rr.Signature = signature
}
return nil, ErrAlg
}
// Verify validates an RRSet with the signature and key. This is only the
// cryptographic test, the signature validity period must be checked separately.
// This function copies the rdata of some RRs (to lowercase domain names) for the validation to work.
func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
// First the easy checks
if !IsRRset(rrset) {
return ErrRRset
}
if rr.KeyTag != k.KeyTag() {
return ErrKey
}
if rr.Hdr.Class != k.Hdr.Class {
return ErrKey
}
if rr.Algorithm != k.Algorithm {
return ErrKey
}
if strings.ToLower(rr.SignerName) != strings.ToLower(k.Hdr.Name) {
return ErrKey
}
if k.Protocol != 3 {
return ErrKey
}
// IsRRset checked that we have at least one RR and that the RRs in
// the set have consistent type, class, and name. Also check that type and
// class matches the RRSIG record.
if rrset[0].Header().Class != rr.Hdr.Class {
return ErrRRset
}
if rrset[0].Header().Rrtype != rr.TypeCovered {
return ErrRRset
}
// RFC 4035 5.3.2. Reconstructing the Signed Data
// Copy the sig, except the rrsig data
sigwire := new(rrsigWireFmt)
sigwire.TypeCovered = rr.TypeCovered
sigwire.Algorithm = rr.Algorithm
sigwire.Labels = rr.Labels
sigwire.OrigTtl = rr.OrigTtl
sigwire.Expiration = rr.Expiration
sigwire.Inception = rr.Inception
sigwire.KeyTag = rr.KeyTag
sigwire.SignerName = strings.ToLower(rr.SignerName)
// Create the desired binary blob
signeddata := make([]byte, DefaultMsgSize)
n, err := packSigWire(sigwire, signeddata)
if err != nil {
return err
}
signeddata = signeddata[:n]
wire, err := rawSignatureData(rrset, rr)
if err != nil {
return err
}
sigbuf := rr.sigBuf() // Get the binary signature data
if rr.Algorithm == PRIVATEDNS { // PRIVATEOID
// TODO(miek)
// remove the domain name and assume its ours?
}
hash, ok := AlgorithmToHash[rr.Algorithm]
if !ok {
return ErrAlg
}
switch rr.Algorithm {
case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512, RSAMD5:
// TODO(mg): this can be done quicker, ie. cache the pubkey data somewhere??
pubkey := k.publicKeyRSA() // Get the key
if pubkey == nil {
return ErrKey
}
h := hash.New()
h.Write(signeddata)
h.Write(wire)
return rsa.VerifyPKCS1v15(pubkey, hash, h.Sum(nil), sigbuf)
case ECDSAP256SHA256, ECDSAP384SHA384:
pubkey := k.publicKeyECDSA()
if pubkey == nil {
return ErrKey
}
// Split sigbuf into the r and s coordinates
r := new(big.Int).SetBytes(sigbuf[:len(sigbuf)/2])
s := new(big.Int).SetBytes(sigbuf[len(sigbuf)/2:])
h := hash.New()
h.Write(signeddata)
h.Write(wire)
if ecdsa.Verify(pubkey, h.Sum(nil), r, s) {
return nil
}
return ErrSig
default:
return ErrAlg
}
}
// ValidityPeriod uses RFC1982 serial arithmetic to calculate
// if a signature period is valid. If t is the zero time, the
// current time is taken other t is. Returns true if the signature
// is valid at the given time, otherwise returns false.
func (rr *RRSIG) ValidityPeriod(t time.Time) bool {
var utc int64
if t.IsZero() {
utc = time.Now().UTC().Unix()
} else {
utc = t.UTC().Unix()
}
modi := (int64(rr.Inception) - utc) / year68
mode := (int64(rr.Expiration) - utc) / year68
ti := int64(rr.Inception) + (modi * year68)
te := int64(rr.Expiration) + (mode * year68)
return ti <= utc && utc <= te
}
// Return the signatures base64 encodedig sigdata as a byte slice.
func (rr *RRSIG) sigBuf() []byte {
sigbuf, err := fromBase64([]byte(rr.Signature))
if err != nil {
return nil
}
return sigbuf
}
// publicKeyRSA returns the RSA public key from a DNSKEY record.
func (k *DNSKEY) publicKeyRSA() *rsa.PublicKey {
keybuf, err := fromBase64([]byte(k.PublicKey))
if err != nil {
return nil
}
// RFC 2537/3110, section 2. RSA Public KEY Resource Records
// Length is in the 0th byte, unless its zero, then it
// it in bytes 1 and 2 and its a 16 bit number
explen := uint16(keybuf[0])
keyoff := 1
if explen == 0 {
explen = uint16(keybuf[1])<<8 | uint16(keybuf[2])
keyoff = 3
}
pubkey := new(rsa.PublicKey)
pubkey.N = big.NewInt(0)
shift := uint64((explen - 1) * 8)
expo := uint64(0)
for i := int(explen - 1); i > 0; i-- {
expo += uint64(keybuf[keyoff+i]) << shift
shift -= 8
}
// Remainder
expo += uint64(keybuf[keyoff])
if expo > (2<<31)+1 {
// Larger expo than supported.
// println("dns: F5 primes (or larger) are not supported")
return nil
}
pubkey.E = int(expo)
pubkey.N.SetBytes(keybuf[keyoff+int(explen):])
return pubkey
}
// publicKeyECDSA returns the Curve public key from the DNSKEY record.
func (k *DNSKEY) publicKeyECDSA() *ecdsa.PublicKey {
keybuf, err := fromBase64([]byte(k.PublicKey))
if err != nil {
return nil
}
pubkey := new(ecdsa.PublicKey)
switch k.Algorithm {
case ECDSAP256SHA256:
pubkey.Curve = elliptic.P256()
if len(keybuf) != 64 {
// wrongly encoded key
return nil
}
case ECDSAP384SHA384:
pubkey.Curve = elliptic.P384()
if len(keybuf) != 96 {
// Wrongly encoded key
return nil
}
}
pubkey.X = big.NewInt(0)
pubkey.X.SetBytes(keybuf[:len(keybuf)/2])
pubkey.Y = big.NewInt(0)
pubkey.Y.SetBytes(keybuf[len(keybuf)/2:])
return pubkey
}
func (k *DNSKEY) publicKeyDSA() *dsa.PublicKey {
keybuf, err := fromBase64([]byte(k.PublicKey))
if err != nil {
return nil
}
if len(keybuf) < 22 {
return nil
}
t, keybuf := int(keybuf[0]), keybuf[1:]
size := 64 + t*8
q, keybuf := keybuf[:20], keybuf[20:]
if len(keybuf) != 3*size {
return nil
}
p, keybuf := keybuf[:size], keybuf[size:]
g, y := keybuf[:size], keybuf[size:]
pubkey := new(dsa.PublicKey)
pubkey.Parameters.Q = big.NewInt(0).SetBytes(q)
pubkey.Parameters.P = big.NewInt(0).SetBytes(p)
pubkey.Parameters.G = big.NewInt(0).SetBytes(g)
pubkey.Y = big.NewInt(0).SetBytes(y)
return pubkey
}
type wireSlice [][]byte
func (p wireSlice) Len() int { return len(p) }
func (p wireSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p wireSlice) Less(i, j int) bool {
_, ioff, _ := UnpackDomainName(p[i], 0)
_, joff, _ := UnpackDomainName(p[j], 0)
return bytes.Compare(p[i][ioff+10:], p[j][joff+10:]) < 0
}
// Return the raw signature data.
func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) {
wires := make(wireSlice, len(rrset))
for i, r := range rrset {
r1 := r.copy()
r1.Header().Ttl = s.OrigTtl
labels := SplitDomainName(r1.Header().Name)
// 6.2. Canonical RR Form. (4) - wildcards
if len(labels) > int(s.Labels) {
// Wildcard
r1.Header().Name = "*." + strings.Join(labels[len(labels)-int(s.Labels):], ".") + "."
}
// RFC 4034: 6.2. Canonical RR Form. (2) - domain name to lowercase
r1.Header().Name = strings.ToLower(r1.Header().Name)
// 6.2. Canonical RR Form. (3) - domain rdata to lowercase.
// NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
// HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
// SRV, DNAME, A6
//
// RFC 6840 - Clarifications and Implementation Notes for DNS Security (DNSSEC):
// Section 6.2 of [RFC4034] also erroneously lists HINFO as a record
// that needs conversion to lowercase, and twice at that. Since HINFO
// records contain no domain names, they are not subject to case
// conversion.
switch x := r1.(type) {
case *NS:
x.Ns = strings.ToLower(x.Ns)
case *CNAME:
x.Target = strings.ToLower(x.Target)
case *SOA:
x.Ns = strings.ToLower(x.Ns)
x.Mbox = strings.ToLower(x.Mbox)
case *MB:
x.Mb = strings.ToLower(x.Mb)
case *MG:
x.Mg = strings.ToLower(x.Mg)
case *MR:
x.Mr = strings.ToLower(x.Mr)
case *PTR:
x.Ptr = strings.ToLower(x.Ptr)
case *MINFO:
x.Rmail = strings.ToLower(x.Rmail)
x.Email = strings.ToLower(x.Email)
case *MX:
x.Mx = strings.ToLower(x.Mx)
case *NAPTR:
x.Replacement = strings.ToLower(x.Replacement)
case *KX:
x.Exchanger = strings.ToLower(x.Exchanger)
case *SRV:
x.Target = strings.ToLower(x.Target)
case *DNAME:
x.Target = strings.ToLower(x.Target)
}
// 6.2. Canonical RR Form. (5) - origTTL
wire := make([]byte, r1.len()+1) // +1 to be safe(r)
off, err1 := PackRR(r1, wire, 0, nil, false)
if err1 != nil {
return nil, err1
}
wire = wire[:off]
wires[i] = wire
}
sort.Sort(wires)
for i, wire := range wires {
if i > 0 && bytes.Equal(wire, wires[i-1]) {
continue
}
buf = append(buf, wire...)
}
return buf, nil
}
func packSigWire(sw *rrsigWireFmt, msg []byte) (int, error) {
// copied from zmsg.go RRSIG packing
off, err := packUint16(sw.TypeCovered, msg, 0)
if err != nil {
return off, err
}
off, err = packUint8(sw.Algorithm, msg, off)
if err != nil {
return off, err
}
off, err = packUint8(sw.Labels, msg, off)
if err != nil {
return off, err
}
off, err = packUint32(sw.OrigTtl, msg, off)
if err != nil {
return off, err
}
off, err = packUint32(sw.Expiration, msg, off)
if err != nil {
return off, err
}
off, err = packUint32(sw.Inception, msg, off)
if err != nil {
return off, err
}
off, err = packUint16(sw.KeyTag, msg, off)
if err != nil {
return off, err
}
off, err = PackDomainName(sw.SignerName, msg, off, nil, false)
if err != nil {
return off, err
}
return off, nil
}
func packKeyWire(dw *dnskeyWireFmt, msg []byte) (int, error) {
// copied from zmsg.go DNSKEY packing
off, err := packUint16(dw.Flags, msg, 0)
if err != nil {
return off, err
}
off, err = packUint8(dw.Protocol, msg, off)
if err != nil {
return off, err
}
off, err = packUint8(dw.Algorithm, msg, off)
if err != nil {
return off, err
}
off, err = packStringBase64(dw.PublicKey, msg, off)
if err != nil {
return off, err
}
return off, nil
}

View file

@ -1,156 +0,0 @@
package dns
import (
"crypto"
"crypto/dsa"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"math/big"
)
// Generate generates a DNSKEY of the given bit size.
// The public part is put inside the DNSKEY record.
// The Algorithm in the key must be set as this will define
// what kind of DNSKEY will be generated.
// The ECDSA algorithms imply a fixed keysize, in that case
// bits should be set to the size of the algorithm.
func (k *DNSKEY) Generate(bits int) (crypto.PrivateKey, error) {
switch k.Algorithm {
case DSA, DSANSEC3SHA1:
if bits != 1024 {
return nil, ErrKeySize
}
case RSAMD5, RSASHA1, RSASHA256, RSASHA1NSEC3SHA1:
if bits < 512 || bits > 4096 {
return nil, ErrKeySize
}
case RSASHA512:
if bits < 1024 || bits > 4096 {
return nil, ErrKeySize
}
case ECDSAP256SHA256:
if bits != 256 {
return nil, ErrKeySize
}
case ECDSAP384SHA384:
if bits != 384 {
return nil, ErrKeySize
}
}
switch k.Algorithm {
case DSA, DSANSEC3SHA1:
params := new(dsa.Parameters)
if err := dsa.GenerateParameters(params, rand.Reader, dsa.L1024N160); err != nil {
return nil, err
}
priv := new(dsa.PrivateKey)
priv.PublicKey.Parameters = *params
err := dsa.GenerateKey(priv, rand.Reader)
if err != nil {
return nil, err
}
k.setPublicKeyDSA(params.Q, params.P, params.G, priv.PublicKey.Y)
return priv, nil
case RSAMD5, RSASHA1, RSASHA256, RSASHA512, RSASHA1NSEC3SHA1:
priv, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return nil, err
}
k.setPublicKeyRSA(priv.PublicKey.E, priv.PublicKey.N)
return priv, nil
case ECDSAP256SHA256, ECDSAP384SHA384:
var c elliptic.Curve
switch k.Algorithm {
case ECDSAP256SHA256:
c = elliptic.P256()
case ECDSAP384SHA384:
c = elliptic.P384()
}
priv, err := ecdsa.GenerateKey(c, rand.Reader)
if err != nil {
return nil, err
}
k.setPublicKeyECDSA(priv.PublicKey.X, priv.PublicKey.Y)
return priv, nil
default:
return nil, ErrAlg
}
}
// Set the public key (the value E and N)
func (k *DNSKEY) setPublicKeyRSA(_E int, _N *big.Int) bool {
if _E == 0 || _N == nil {
return false
}
buf := exponentToBuf(_E)
buf = append(buf, _N.Bytes()...)
k.PublicKey = toBase64(buf)
return true
}
// Set the public key for Elliptic Curves
func (k *DNSKEY) setPublicKeyECDSA(_X, _Y *big.Int) bool {
if _X == nil || _Y == nil {
return false
}
var intlen int
switch k.Algorithm {
case ECDSAP256SHA256:
intlen = 32
case ECDSAP384SHA384:
intlen = 48
}
k.PublicKey = toBase64(curveToBuf(_X, _Y, intlen))
return true
}
// Set the public key for DSA
func (k *DNSKEY) setPublicKeyDSA(_Q, _P, _G, _Y *big.Int) bool {
if _Q == nil || _P == nil || _G == nil || _Y == nil {
return false
}
buf := dsaToBuf(_Q, _P, _G, _Y)
k.PublicKey = toBase64(buf)
return true
}
// Set the public key (the values E and N) for RSA
// RFC 3110: Section 2. RSA Public KEY Resource Records
func exponentToBuf(_E int) []byte {
var buf []byte
i := big.NewInt(int64(_E)).Bytes()
if len(i) < 256 {
buf = make([]byte, 1, 1+len(i))
buf[0] = uint8(len(i))
} else {
buf = make([]byte, 3, 3+len(i))
buf[0] = 0
buf[1] = uint8(len(i) >> 8)
buf[2] = uint8(len(i))
}
buf = append(buf, i...)
return buf
}
// Set the public key for X and Y for Curve. The two
// values are just concatenated.
func curveToBuf(_X, _Y *big.Int, intlen int) []byte {
buf := intToBytes(_X, intlen)
buf = append(buf, intToBytes(_Y, intlen)...)
return buf
}
// Set the public key for X and Y for Curve. The two
// values are just concatenated.
func dsaToBuf(_Q, _P, _G, _Y *big.Int) []byte {
t := divRoundUp(divRoundUp(_G.BitLen(), 8)-64, 8)
buf := []byte{byte(t)}
buf = append(buf, intToBytes(_Q, 20)...)
buf = append(buf, intToBytes(_P, 64+t*8)...)
buf = append(buf, intToBytes(_G, 64+t*8)...)
buf = append(buf, intToBytes(_Y, 64+t*8)...)
return buf
}

View file

@ -1,249 +0,0 @@
package dns
import (
"crypto"
"crypto/dsa"
"crypto/ecdsa"
"crypto/rsa"
"io"
"math/big"
"strconv"
"strings"
)
// NewPrivateKey returns a PrivateKey by parsing the string s.
// s should be in the same form of the BIND private key files.
func (k *DNSKEY) NewPrivateKey(s string) (crypto.PrivateKey, error) {
if s == "" || s[len(s)-1] != '\n' { // We need a closing newline
return k.ReadPrivateKey(strings.NewReader(s+"\n"), "")
}
return k.ReadPrivateKey(strings.NewReader(s), "")
}
// ReadPrivateKey reads a private key from the io.Reader q. The string file is
// only used in error reporting.
// The public key must be known, because some cryptographic algorithms embed
// the public inside the privatekey.
func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (crypto.PrivateKey, error) {
m, err := parseKey(q, file)
if m == nil {
return nil, err
}
if _, ok := m["private-key-format"]; !ok {
return nil, ErrPrivKey
}
if m["private-key-format"] != "v1.2" && m["private-key-format"] != "v1.3" {
return nil, ErrPrivKey
}
// TODO(mg): check if the pubkey matches the private key
algo, err := strconv.ParseUint(strings.SplitN(m["algorithm"], " ", 2)[0], 10, 8)
if err != nil {
return nil, ErrPrivKey
}
switch uint8(algo) {
case DSA:
priv, err := readPrivateKeyDSA(m)
if err != nil {
return nil, err
}
pub := k.publicKeyDSA()
if pub == nil {
return nil, ErrKey
}
priv.PublicKey = *pub
return priv, nil
case RSAMD5:
fallthrough
case RSASHA1:
fallthrough
case RSASHA1NSEC3SHA1:
fallthrough
case RSASHA256:
fallthrough
case RSASHA512:
priv, err := readPrivateKeyRSA(m)
if err != nil {
return nil, err
}
pub := k.publicKeyRSA()
if pub == nil {
return nil, ErrKey
}
priv.PublicKey = *pub
return priv, nil
case ECCGOST:
return nil, ErrPrivKey
case ECDSAP256SHA256:
fallthrough
case ECDSAP384SHA384:
priv, err := readPrivateKeyECDSA(m)
if err != nil {
return nil, err
}
pub := k.publicKeyECDSA()
if pub == nil {
return nil, ErrKey
}
priv.PublicKey = *pub
return priv, nil
default:
return nil, ErrPrivKey
}
}
// Read a private key (file) string and create a public key. Return the private key.
func readPrivateKeyRSA(m map[string]string) (*rsa.PrivateKey, error) {
p := new(rsa.PrivateKey)
p.Primes = []*big.Int{nil, nil}
for k, v := range m {
switch k {
case "modulus", "publicexponent", "privateexponent", "prime1", "prime2":
v1, err := fromBase64([]byte(v))
if err != nil {
return nil, err
}
switch k {
case "modulus":
p.PublicKey.N = big.NewInt(0)
p.PublicKey.N.SetBytes(v1)
case "publicexponent":
i := big.NewInt(0)
i.SetBytes(v1)
p.PublicKey.E = int(i.Int64()) // int64 should be large enough
case "privateexponent":
p.D = big.NewInt(0)
p.D.SetBytes(v1)
case "prime1":
p.Primes[0] = big.NewInt(0)
p.Primes[0].SetBytes(v1)
case "prime2":
p.Primes[1] = big.NewInt(0)
p.Primes[1].SetBytes(v1)
}
case "exponent1", "exponent2", "coefficient":
// not used in Go (yet)
case "created", "publish", "activate":
// not used in Go (yet)
}
}
return p, nil
}
func readPrivateKeyDSA(m map[string]string) (*dsa.PrivateKey, error) {
p := new(dsa.PrivateKey)
p.X = big.NewInt(0)
for k, v := range m {
switch k {
case "private_value(x)":
v1, err := fromBase64([]byte(v))
if err != nil {
return nil, err
}
p.X.SetBytes(v1)
case "created", "publish", "activate":
/* not used in Go (yet) */
}
}
return p, nil
}
func readPrivateKeyECDSA(m map[string]string) (*ecdsa.PrivateKey, error) {
p := new(ecdsa.PrivateKey)
p.D = big.NewInt(0)
// TODO: validate that the required flags are present
for k, v := range m {
switch k {
case "privatekey":
v1, err := fromBase64([]byte(v))
if err != nil {
return nil, err
}
p.D.SetBytes(v1)
case "created", "publish", "activate":
/* not used in Go (yet) */
}
}
return p, nil
}
// parseKey reads a private key from r. It returns a map[string]string,
// with the key-value pairs, or an error when the file is not correct.
func parseKey(r io.Reader, file string) (map[string]string, error) {
s := scanInit(r)
m := make(map[string]string)
c := make(chan lex)
k := ""
// Start the lexer
go klexer(s, c)
for l := range c {
// It should alternate
switch l.value {
case zKey:
k = l.token
case zValue:
if k == "" {
return nil, &ParseError{file, "no private key seen", l}
}
//println("Setting", strings.ToLower(k), "to", l.token, "b")
m[strings.ToLower(k)] = l.token
k = ""
}
}
return m, nil
}
// klexer scans the sourcefile and returns tokens on the channel c.
func klexer(s *scan, c chan lex) {
var l lex
str := "" // Hold the current read text
commt := false
key := true
x, err := s.tokenText()
defer close(c)
for err == nil {
l.column = s.position.Column
l.line = s.position.Line
switch x {
case ':':
if commt {
break
}
l.token = str
if key {
l.value = zKey
c <- l
// Next token is a space, eat it
s.tokenText()
key = false
str = ""
} else {
l.value = zValue
}
case ';':
commt = true
case '\n':
if commt {
// Reset a comment
commt = false
}
l.value = zValue
l.token = str
c <- l
str = ""
commt = false
key = true
default:
if commt {
break
}
str += string(x)
}
x, err = s.tokenText()
}
if len(str) > 0 {
// Send remainder
l.token = str
l.value = zValue
c <- l
}
}

View file

@ -1,85 +0,0 @@
package dns
import (
"crypto"
"crypto/dsa"
"crypto/ecdsa"
"crypto/rsa"
"math/big"
"strconv"
)
const format = "Private-key-format: v1.3\n"
// PrivateKeyString converts a PrivateKey to a string. This string has the same
// format as the private-key-file of BIND9 (Private-key-format: v1.3).
// It needs some info from the key (the algorithm), so its a method of the DNSKEY
// It supports rsa.PrivateKey, ecdsa.PrivateKey and dsa.PrivateKey
func (r *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string {
algorithm := strconv.Itoa(int(r.Algorithm))
algorithm += " (" + AlgorithmToString[r.Algorithm] + ")"
switch p := p.(type) {
case *rsa.PrivateKey:
modulus := toBase64(p.PublicKey.N.Bytes())
e := big.NewInt(int64(p.PublicKey.E))
publicExponent := toBase64(e.Bytes())
privateExponent := toBase64(p.D.Bytes())
prime1 := toBase64(p.Primes[0].Bytes())
prime2 := toBase64(p.Primes[1].Bytes())
// Calculate Exponent1/2 and Coefficient as per: http://en.wikipedia.org/wiki/RSA#Using_the_Chinese_remainder_algorithm
// and from: http://code.google.com/p/go/issues/detail?id=987
one := big.NewInt(1)
p1 := big.NewInt(0).Sub(p.Primes[0], one)
q1 := big.NewInt(0).Sub(p.Primes[1], one)
exp1 := big.NewInt(0).Mod(p.D, p1)
exp2 := big.NewInt(0).Mod(p.D, q1)
coeff := big.NewInt(0).ModInverse(p.Primes[1], p.Primes[0])
exponent1 := toBase64(exp1.Bytes())
exponent2 := toBase64(exp2.Bytes())
coefficient := toBase64(coeff.Bytes())
return format +
"Algorithm: " + algorithm + "\n" +
"Modulus: " + modulus + "\n" +
"PublicExponent: " + publicExponent + "\n" +
"PrivateExponent: " + privateExponent + "\n" +
"Prime1: " + prime1 + "\n" +
"Prime2: " + prime2 + "\n" +
"Exponent1: " + exponent1 + "\n" +
"Exponent2: " + exponent2 + "\n" +
"Coefficient: " + coefficient + "\n"
case *ecdsa.PrivateKey:
var intlen int
switch r.Algorithm {
case ECDSAP256SHA256:
intlen = 32
case ECDSAP384SHA384:
intlen = 48
}
private := toBase64(intToBytes(p.D, intlen))
return format +
"Algorithm: " + algorithm + "\n" +
"PrivateKey: " + private + "\n"
case *dsa.PrivateKey:
T := divRoundUp(divRoundUp(p.PublicKey.Parameters.G.BitLen(), 8)-64, 8)
prime := toBase64(intToBytes(p.PublicKey.Parameters.P, 64+T*8))
subprime := toBase64(intToBytes(p.PublicKey.Parameters.Q, 20))
base := toBase64(intToBytes(p.PublicKey.Parameters.G, 64+T*8))
priv := toBase64(intToBytes(p.X, 20))
pub := toBase64(intToBytes(p.PublicKey.Y, 64+T*8))
return format +
"Algorithm: " + algorithm + "\n" +
"Prime(p): " + prime + "\n" +
"Subprime(q): " + subprime + "\n" +
"Base(g): " + base + "\n" +
"Private_value(x): " + priv + "\n" +
"Public_value(y): " + pub + "\n"
default:
return ""
}
}

View file

@ -1,733 +0,0 @@
package dns
import (
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"reflect"
"strings"
"testing"
"time"
)
func getKey() *DNSKEY {
key := new(DNSKEY)
key.Hdr.Name = "miek.nl."
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 14400
key.Flags = 256
key.Protocol = 3
key.Algorithm = RSASHA256
key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
return key
}
func getSoa() *SOA {
soa := new(SOA)
soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0}
soa.Ns = "open.nlnetlabs.nl."
soa.Mbox = "miekg.atoom.net."
soa.Serial = 1293945905
soa.Refresh = 14400
soa.Retry = 3600
soa.Expire = 604800
soa.Minttl = 86400
return soa
}
func TestGenerateEC(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
key := new(DNSKEY)
key.Hdr.Rrtype = TypeDNSKEY
key.Hdr.Name = "miek.nl."
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 14400
key.Flags = 256
key.Protocol = 3
key.Algorithm = ECDSAP256SHA256
privkey, _ := key.Generate(256)
t.Log(key.String())
t.Log(key.PrivateKeyString(privkey))
}
func TestGenerateDSA(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
key := new(DNSKEY)
key.Hdr.Rrtype = TypeDNSKEY
key.Hdr.Name = "miek.nl."
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 14400
key.Flags = 256
key.Protocol = 3
key.Algorithm = DSA
privkey, _ := key.Generate(1024)
t.Log(key.String())
t.Log(key.PrivateKeyString(privkey))
}
func TestGenerateRSA(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
key := new(DNSKEY)
key.Hdr.Rrtype = TypeDNSKEY
key.Hdr.Name = "miek.nl."
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 14400
key.Flags = 256
key.Protocol = 3
key.Algorithm = RSASHA256
privkey, _ := key.Generate(1024)
t.Log(key.String())
t.Log(key.PrivateKeyString(privkey))
}
func TestSecure(t *testing.T) {
soa := getSoa()
sig := new(RRSIG)
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
sig.TypeCovered = TypeSOA
sig.Algorithm = RSASHA256
sig.Labels = 2
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
sig.OrigTtl = 14400
sig.KeyTag = 12051
sig.SignerName = "miek.nl."
sig.Signature = "oMCbslaAVIp/8kVtLSms3tDABpcPRUgHLrOR48OOplkYo+8TeEGWwkSwaz/MRo2fB4FxW0qj/hTlIjUGuACSd+b1wKdH5GvzRJc2pFmxtCbm55ygAh4EUL0F6U5cKtGJGSXxxg6UFCQ0doJCmiGFa78LolaUOXImJrk6AFrGa0M="
key := new(DNSKEY)
key.Hdr.Name = "miek.nl."
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 14400
key.Flags = 256
key.Protocol = 3
key.Algorithm = RSASHA256
key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
// It should validate. Period is checked separately, so this will keep on working
if sig.Verify(key, []RR{soa}) != nil {
t.Error("failure to validate")
}
}
func TestSignature(t *testing.T) {
sig := new(RRSIG)
sig.Hdr.Name = "miek.nl."
sig.Hdr.Class = ClassINET
sig.Hdr.Ttl = 3600
sig.TypeCovered = TypeDNSKEY
sig.Algorithm = RSASHA1
sig.Labels = 2
sig.OrigTtl = 4000
sig.Expiration = 1000 //Thu Jan 1 02:06:40 CET 1970
sig.Inception = 800 //Thu Jan 1 01:13:20 CET 1970
sig.KeyTag = 34641
sig.SignerName = "miek.nl."
sig.Signature = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"
// Should not be valid
if sig.ValidityPeriod(time.Now()) {
t.Error("should not be valid")
}
sig.Inception = 315565800 //Tue Jan 1 10:10:00 CET 1980
sig.Expiration = 4102477800 //Fri Jan 1 10:10:00 CET 2100
if !sig.ValidityPeriod(time.Now()) {
t.Error("should be valid")
}
}
func TestSignVerify(t *testing.T) {
// The record we want to sign
soa := new(SOA)
soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0}
soa.Ns = "open.nlnetlabs.nl."
soa.Mbox = "miekg.atoom.net."
soa.Serial = 1293945905
soa.Refresh = 14400
soa.Retry = 3600
soa.Expire = 604800
soa.Minttl = 86400
soa1 := new(SOA)
soa1.Hdr = RR_Header{"*.miek.nl.", TypeSOA, ClassINET, 14400, 0}
soa1.Ns = "open.nlnetlabs.nl."
soa1.Mbox = "miekg.atoom.net."
soa1.Serial = 1293945905
soa1.Refresh = 14400
soa1.Retry = 3600
soa1.Expire = 604800
soa1.Minttl = 86400
srv := new(SRV)
srv.Hdr = RR_Header{"srv.miek.nl.", TypeSRV, ClassINET, 14400, 0}
srv.Port = 1000
srv.Weight = 800
srv.Target = "web1.miek.nl."
hinfo := &HINFO{
Hdr: RR_Header{
Name: "miek.nl.",
Rrtype: TypeHINFO,
Class: ClassINET,
Ttl: 3789,
},
Cpu: "X",
Os: "Y",
}
// With this key
key := new(DNSKEY)
key.Hdr.Rrtype = TypeDNSKEY
key.Hdr.Name = "miek.nl."
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 14400
key.Flags = 256
key.Protocol = 3
key.Algorithm = RSASHA256
privkey, _ := key.Generate(512)
// Fill in the values of the Sig, before signing
sig := new(RRSIG)
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
sig.TypeCovered = soa.Hdr.Rrtype
sig.Labels = uint8(CountLabel(soa.Hdr.Name)) // works for all 3
sig.OrigTtl = soa.Hdr.Ttl
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
sig.KeyTag = key.KeyTag() // Get the keyfrom the Key
sig.SignerName = key.Hdr.Name
sig.Algorithm = RSASHA256
for _, r := range []RR{soa, soa1, srv, hinfo} {
if err := sig.Sign(privkey.(*rsa.PrivateKey), []RR{r}); err != nil {
t.Error("failure to sign the record:", err)
continue
}
if err := sig.Verify(key, []RR{r}); err != nil {
t.Error("failure to validate")
continue
}
t.Logf("validated: %s", r.Header().Name)
}
}
func Test65534(t *testing.T) {
t6 := new(RFC3597)
t6.Hdr = RR_Header{"miek.nl.", 65534, ClassINET, 14400, 0}
t6.Rdata = "505D870001"
key := new(DNSKEY)
key.Hdr.Name = "miek.nl."
key.Hdr.Rrtype = TypeDNSKEY
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 14400
key.Flags = 256
key.Protocol = 3
key.Algorithm = RSASHA256
privkey, _ := key.Generate(1024)
sig := new(RRSIG)
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
sig.TypeCovered = t6.Hdr.Rrtype
sig.Labels = uint8(CountLabel(t6.Hdr.Name))
sig.OrigTtl = t6.Hdr.Ttl
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
sig.KeyTag = key.KeyTag()
sig.SignerName = key.Hdr.Name
sig.Algorithm = RSASHA256
if err := sig.Sign(privkey.(*rsa.PrivateKey), []RR{t6}); err != nil {
t.Error(err)
t.Error("failure to sign the TYPE65534 record")
}
if err := sig.Verify(key, []RR{t6}); err != nil {
t.Error(err)
t.Error("failure to validate")
} else {
t.Logf("validated: %s", t6.Header().Name)
}
}
func TestDnskey(t *testing.T) {
pubkey, err := ReadRR(strings.NewReader(`
miek.nl. IN DNSKEY 256 3 10 AwEAAZuMCu2FdugHkTrXYgl5qixvcDw1aDDlvL46/xJKbHBAHY16fNUb2b65cwko2Js/aJxUYJbZk5dwCDZxYfrfbZVtDPQuc3o8QaChVxC7/JYz2AHc9qHvqQ1j4VrH71RWINlQo6VYjzN/BGpMhOZoZOEwzp1HfsOE3lNYcoWU1smL ;{id = 5240 (zsk), size = 1024b}
`), "Kmiek.nl.+010+05240.key")
if err != nil {
t.Fatal(err)
}
privStr := `Private-key-format: v1.3
Algorithm: 10 (RSASHA512)
Modulus: m4wK7YV26AeROtdiCXmqLG9wPDVoMOW8vjr/EkpscEAdjXp81RvZvrlzCSjYmz9onFRgltmTl3AINnFh+t9tlW0M9C5zejxBoKFXELv8ljPYAdz2oe+pDWPhWsfvVFYg2VCjpViPM38EakyE5mhk4TDOnUd+w4TeU1hyhZTWyYs=
PublicExponent: AQAB
PrivateExponent: UfCoIQ/Z38l8vB6SSqOI/feGjHEl/fxIPX4euKf0D/32k30fHbSaNFrFOuIFmWMB3LimWVEs6u3dpbB9CQeCVg7hwU5puG7OtuiZJgDAhNeOnxvo5btp4XzPZrJSxR4WNQnwIiYWbl0aFlL1VGgHC/3By89ENZyWaZcMLW4KGWE=
Prime1: yxwC6ogAu8aVcDx2wg1V0b5M5P6jP8qkRFVMxWNTw60Vkn+ECvw6YAZZBHZPaMyRYZLzPgUlyYRd0cjupy4+fQ==
Prime2: xA1bF8M0RTIQ6+A11AoVG6GIR/aPGg5sogRkIZ7ID/sF6g9HMVU/CM2TqVEBJLRPp73cv6ZeC3bcqOCqZhz+pw==
Exponent1: xzkblyZ96bGYxTVZm2/vHMOXswod4KWIyMoOepK6B/ZPcZoIT6omLCgtypWtwHLfqyCz3MK51Nc0G2EGzg8rFQ==
Exponent2: Pu5+mCEb7T5F+kFNZhQadHUklt0JUHbi3hsEvVoHpEGSw3BGDQrtIflDde0/rbWHgDPM4WQY+hscd8UuTXrvLw==
Coefficient: UuRoNqe7YHnKmQzE6iDWKTMIWTuoqqrFAmXPmKQnC+Y+BQzOVEHUo9bXdDnoI9hzXP1gf8zENMYwYLeWpuYlFQ==
`
privkey, err := pubkey.(*DNSKEY).ReadPrivateKey(strings.NewReader(privStr),
"Kmiek.nl.+010+05240.private")
if err != nil {
t.Fatal(err)
}
if pubkey.(*DNSKEY).PublicKey != "AwEAAZuMCu2FdugHkTrXYgl5qixvcDw1aDDlvL46/xJKbHBAHY16fNUb2b65cwko2Js/aJxUYJbZk5dwCDZxYfrfbZVtDPQuc3o8QaChVxC7/JYz2AHc9qHvqQ1j4VrH71RWINlQo6VYjzN/BGpMhOZoZOEwzp1HfsOE3lNYcoWU1smL" {
t.Error("pubkey is not what we've read")
}
if pubkey.(*DNSKEY).PrivateKeyString(privkey) != privStr {
t.Error("privkey is not what we've read")
t.Errorf("%v", pubkey.(*DNSKEY).PrivateKeyString(privkey))
}
}
func TestTag(t *testing.T) {
key := new(DNSKEY)
key.Hdr.Name = "miek.nl."
key.Hdr.Rrtype = TypeDNSKEY
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 3600
key.Flags = 256
key.Protocol = 3
key.Algorithm = RSASHA256
key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
tag := key.KeyTag()
if tag != 12051 {
t.Errorf("wrong key tag: %d for key %v", tag, key)
}
}
func TestKeyRSA(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
key := new(DNSKEY)
key.Hdr.Name = "miek.nl."
key.Hdr.Rrtype = TypeDNSKEY
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 3600
key.Flags = 256
key.Protocol = 3
key.Algorithm = RSASHA256
priv, _ := key.Generate(2048)
soa := new(SOA)
soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0}
soa.Ns = "open.nlnetlabs.nl."
soa.Mbox = "miekg.atoom.net."
soa.Serial = 1293945905
soa.Refresh = 14400
soa.Retry = 3600
soa.Expire = 604800
soa.Minttl = 86400
sig := new(RRSIG)
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
sig.TypeCovered = TypeSOA
sig.Algorithm = RSASHA256
sig.Labels = 2
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
sig.OrigTtl = soa.Hdr.Ttl
sig.KeyTag = key.KeyTag()
sig.SignerName = key.Hdr.Name
if err := sig.Sign(priv.(*rsa.PrivateKey), []RR{soa}); err != nil {
t.Error("failed to sign")
return
}
if err := sig.Verify(key, []RR{soa}); err != nil {
t.Error("failed to verify")
}
}
func TestKeyToDS(t *testing.T) {
key := new(DNSKEY)
key.Hdr.Name = "miek.nl."
key.Hdr.Rrtype = TypeDNSKEY
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 3600
key.Flags = 256
key.Protocol = 3
key.Algorithm = RSASHA256
key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
ds := key.ToDS(SHA1)
if strings.ToUpper(ds.Digest) != "B5121BDB5B8D86D0CC5FFAFBAAABE26C3E20BAC1" {
t.Errorf("wrong DS digest for SHA1\n%v", ds)
}
}
func TestSignRSA(t *testing.T) {
pub := "miek.nl. IN DNSKEY 256 3 5 AwEAAb+8lGNCxJgLS8rYVer6EnHVuIkQDghdjdtewDzU3G5R7PbMbKVRvH2Ma7pQyYceoaqWZQirSj72euPWfPxQnMy9ucCylA+FuH9cSjIcPf4PqJfdupHk9X6EBYjxrCLY4p1/yBwgyBIRJtZtAqM3ceAH2WovEJD6rTtOuHo5AluJ"
priv := `Private-key-format: v1.3
Algorithm: 5 (RSASHA1)
Modulus: v7yUY0LEmAtLythV6voScdW4iRAOCF2N217APNTcblHs9sxspVG8fYxrulDJhx6hqpZlCKtKPvZ649Z8/FCczL25wLKUD4W4f1xKMhw9/g+ol926keT1foQFiPGsItjinX/IHCDIEhEm1m0Cozdx4AfZai8QkPqtO064ejkCW4k=
PublicExponent: AQAB
PrivateExponent: YPwEmwjk5HuiROKU4xzHQ6l1hG8Iiha4cKRG3P5W2b66/EN/GUh07ZSf0UiYB67o257jUDVEgwCuPJz776zfApcCB4oGV+YDyEu7Hp/rL8KcSN0la0k2r9scKwxTp4BTJT23zyBFXsV/1wRDK1A5NxsHPDMYi2SoK63Enm/1ptk=
Prime1: /wjOG+fD0ybNoSRn7nQ79udGeR1b0YhUA5mNjDx/x2fxtIXzygYk0Rhx9QFfDy6LOBvz92gbNQlzCLz3DJt5hw==
Prime2: wHZsJ8OGhkp5p3mrJFZXMDc2mbYusDVTA+t+iRPdS797Tj0pjvU2HN4vTnTj8KBQp6hmnY7dLp9Y1qserySGbw==
Exponent1: N0A7FsSRIg+IAN8YPQqlawoTtG1t1OkJ+nWrurPootScApX6iMvn8fyvw3p2k51rv84efnzpWAYiC8SUaQDNxQ==
Exponent2: SvuYRaGyvo0zemE3oS+WRm2scxR8eiA8WJGeOc+obwOKCcBgeZblXzfdHGcEC1KaOcetOwNW/vwMA46lpLzJNw==
Coefficient: 8+7ZN/JgByqv0NfULiFKTjtyegUcijRuyij7yNxYbCBneDvZGxJwKNi4YYXWx743pcAj4Oi4Oh86gcmxLs+hGw==
Created: 20110302104537
Publish: 20110302104537
Activate: 20110302104537`
xk, _ := NewRR(pub)
k := xk.(*DNSKEY)
p, err := k.NewPrivateKey(priv)
if err != nil {
t.Error(err)
}
switch priv := p.(type) {
case *rsa.PrivateKey:
if 65537 != priv.PublicKey.E {
t.Error("exponenent should be 65537")
}
default:
t.Errorf("we should have read an RSA key: %v", priv)
}
if k.KeyTag() != 37350 {
t.Errorf("keytag should be 37350, got %d %v", k.KeyTag(), k)
}
soa := new(SOA)
soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0}
soa.Ns = "open.nlnetlabs.nl."
soa.Mbox = "miekg.atoom.net."
soa.Serial = 1293945905
soa.Refresh = 14400
soa.Retry = 3600
soa.Expire = 604800
soa.Minttl = 86400
sig := new(RRSIG)
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
sig.KeyTag = k.KeyTag()
sig.SignerName = k.Hdr.Name
sig.Algorithm = k.Algorithm
sig.Sign(p.(*rsa.PrivateKey), []RR{soa})
if sig.Signature != "D5zsobpQcmMmYsUMLxCVEtgAdCvTu8V/IEeP4EyLBjqPJmjt96bwM9kqihsccofA5LIJ7DN91qkCORjWSTwNhzCv7bMyr2o5vBZElrlpnRzlvsFIoAZCD9xg6ZY7ZyzUJmU6IcTwG4v3xEYajcpbJJiyaw/RqR90MuRdKPiBzSo=" {
t.Errorf("signature is not correct: %v", sig)
}
}
func TestSignVerifyECDSA(t *testing.T) {
pub := `example.net. 3600 IN DNSKEY 257 3 14 (
xKYaNhWdGOfJ+nPrL8/arkwf2EY3MDJ+SErKivBVSum1
w/egsXvSADtNJhyem5RCOpgQ6K8X1DRSEkrbYQ+OB+v8
/uX45NBwY8rp65F6Glur8I/mlVNgF6W/qTI37m40 )`
priv := `Private-key-format: v1.2
Algorithm: 14 (ECDSAP384SHA384)
PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
eckey, err := NewRR(pub)
if err != nil {
t.Fatal(err)
}
privkey, err := eckey.(*DNSKEY).NewPrivateKey(priv)
if err != nil {
t.Fatal(err)
}
// TODO: Create separate test for this
ds := eckey.(*DNSKEY).ToDS(SHA384)
if ds.KeyTag != 10771 {
t.Fatal("wrong keytag on DS")
}
if ds.Digest != "72d7b62976ce06438e9c0bf319013cf801f09ecc84b8d7e9495f27e305c6a9b0563a9b5f4d288405c3008a946df983d6" {
t.Fatal("wrong DS Digest")
}
a, _ := NewRR("www.example.net. 3600 IN A 192.0.2.1")
sig := new(RRSIG)
sig.Hdr = RR_Header{"example.net.", TypeRRSIG, ClassINET, 14400, 0}
sig.Expiration, _ = StringToTime("20100909102025")
sig.Inception, _ = StringToTime("20100812102025")
sig.KeyTag = eckey.(*DNSKEY).KeyTag()
sig.SignerName = eckey.(*DNSKEY).Hdr.Name
sig.Algorithm = eckey.(*DNSKEY).Algorithm
if sig.Sign(privkey.(*ecdsa.PrivateKey), []RR{a}) != nil {
t.Fatal("failure to sign the record")
}
if err := sig.Verify(eckey.(*DNSKEY), []RR{a}); err != nil {
t.Fatalf("failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v",
eckey.(*DNSKEY).String(),
a.String(),
sig.String(),
eckey.(*DNSKEY).PrivateKeyString(privkey),
err,
)
}
}
func TestSignVerifyECDSA2(t *testing.T) {
srv1, err := NewRR("srv.miek.nl. IN SRV 1000 800 0 web1.miek.nl.")
if err != nil {
t.Fatal(err)
}
srv := srv1.(*SRV)
// With this key
key := new(DNSKEY)
key.Hdr.Rrtype = TypeDNSKEY
key.Hdr.Name = "miek.nl."
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 14400
key.Flags = 256
key.Protocol = 3
key.Algorithm = ECDSAP256SHA256
privkey, err := key.Generate(256)
if err != nil {
t.Fatal("failure to generate key")
}
// Fill in the values of the Sig, before signing
sig := new(RRSIG)
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
sig.TypeCovered = srv.Hdr.Rrtype
sig.Labels = uint8(CountLabel(srv.Hdr.Name)) // works for all 3
sig.OrigTtl = srv.Hdr.Ttl
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
sig.KeyTag = key.KeyTag() // Get the keyfrom the Key
sig.SignerName = key.Hdr.Name
sig.Algorithm = ECDSAP256SHA256
if sig.Sign(privkey.(*ecdsa.PrivateKey), []RR{srv}) != nil {
t.Fatal("failure to sign the record")
}
err = sig.Verify(key, []RR{srv})
if err != nil {
t.Logf("failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v",
key.String(),
srv.String(),
sig.String(),
key.PrivateKeyString(privkey),
err,
)
}
}
// Here the test vectors from the relevant RFCs are checked.
// rfc6605 6.1
func TestRFC6605P256(t *testing.T) {
exDNSKEY := `example.net. 3600 IN DNSKEY 257 3 13 (
GojIhhXUN/u4v54ZQqGSnyhWJwaubCvTmeexv7bR6edb
krSqQpF64cYbcB7wNcP+e+MAnLr+Wi9xMWyQLc8NAA== )`
exPriv := `Private-key-format: v1.2
Algorithm: 13 (ECDSAP256SHA256)
PrivateKey: GU6SnQ/Ou+xC5RumuIUIuJZteXT2z0O/ok1s38Et6mQ=`
rrDNSKEY, err := NewRR(exDNSKEY)
if err != nil {
t.Fatal(err)
}
priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)
if err != nil {
t.Fatal(err)
}
exDS := `example.net. 3600 IN DS 55648 13 2 (
b4c8c1fe2e7477127b27115656ad6256f424625bf5c1
e2770ce6d6e37df61d17 )`
rrDS, err := NewRR(exDS)
if err != nil {
t.Fatal(err)
}
ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA256)
if !reflect.DeepEqual(ourDS, rrDS.(*DS)) {
t.Errorf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS))
}
exA := `www.example.net. 3600 IN A 192.0.2.1`
exRRSIG := `www.example.net. 3600 IN RRSIG A 13 3 3600 (
20100909100439 20100812100439 55648 example.net.
qx6wLYqmh+l9oCKTN6qIc+bw6ya+KJ8oMz0YP107epXA
yGmt+3SNruPFKG7tZoLBLlUzGGus7ZwmwWep666VCw== )`
rrA, err := NewRR(exA)
if err != nil {
t.Fatal(err)
}
rrRRSIG, err := NewRR(exRRSIG)
if err != nil {
t.Fatal(err)
}
if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
t.Errorf("failure to validate the spec RRSIG: %v", err)
}
ourRRSIG := &RRSIG{
Hdr: RR_Header{
Ttl: rrA.Header().Ttl,
},
KeyTag: rrDNSKEY.(*DNSKEY).KeyTag(),
SignerName: rrDNSKEY.(*DNSKEY).Hdr.Name,
Algorithm: rrDNSKEY.(*DNSKEY).Algorithm,
}
ourRRSIG.Expiration, _ = StringToTime("20100909100439")
ourRRSIG.Inception, _ = StringToTime("20100812100439")
err = ourRRSIG.Sign(priv.(*ecdsa.PrivateKey), []RR{rrA})
if err != nil {
t.Fatal(err)
}
if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
t.Errorf("failure to validate our RRSIG: %v", err)
}
// Signatures are randomized
rrRRSIG.(*RRSIG).Signature = ""
ourRRSIG.Signature = ""
if !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) {
t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG))
}
}
// rfc6605 6.2
func TestRFC6605P384(t *testing.T) {
exDNSKEY := `example.net. 3600 IN DNSKEY 257 3 14 (
xKYaNhWdGOfJ+nPrL8/arkwf2EY3MDJ+SErKivBVSum1
w/egsXvSADtNJhyem5RCOpgQ6K8X1DRSEkrbYQ+OB+v8
/uX45NBwY8rp65F6Glur8I/mlVNgF6W/qTI37m40 )`
exPriv := `Private-key-format: v1.2
Algorithm: 14 (ECDSAP384SHA384)
PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
rrDNSKEY, err := NewRR(exDNSKEY)
if err != nil {
t.Fatal(err)
}
priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)
if err != nil {
t.Fatal(err)
}
exDS := `example.net. 3600 IN DS 10771 14 4 (
72d7b62976ce06438e9c0bf319013cf801f09ecc84b8
d7e9495f27e305c6a9b0563a9b5f4d288405c3008a94
6df983d6 )`
rrDS, err := NewRR(exDS)
if err != nil {
t.Fatal(err)
}
ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA384)
if !reflect.DeepEqual(ourDS, rrDS.(*DS)) {
t.Fatalf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS))
}
exA := `www.example.net. 3600 IN A 192.0.2.1`
exRRSIG := `www.example.net. 3600 IN RRSIG A 14 3 3600 (
20100909102025 20100812102025 10771 example.net.
/L5hDKIvGDyI1fcARX3z65qrmPsVz73QD1Mr5CEqOiLP
95hxQouuroGCeZOvzFaxsT8Glr74hbavRKayJNuydCuz
WTSSPdz7wnqXL5bdcJzusdnI0RSMROxxwGipWcJm )`
rrA, err := NewRR(exA)
if err != nil {
t.Fatal(err)
}
rrRRSIG, err := NewRR(exRRSIG)
if err != nil {
t.Fatal(err)
}
if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
t.Errorf("failure to validate the spec RRSIG: %v", err)
}
ourRRSIG := &RRSIG{
Hdr: RR_Header{
Ttl: rrA.Header().Ttl,
},
KeyTag: rrDNSKEY.(*DNSKEY).KeyTag(),
SignerName: rrDNSKEY.(*DNSKEY).Hdr.Name,
Algorithm: rrDNSKEY.(*DNSKEY).Algorithm,
}
ourRRSIG.Expiration, _ = StringToTime("20100909102025")
ourRRSIG.Inception, _ = StringToTime("20100812102025")
err = ourRRSIG.Sign(priv.(*ecdsa.PrivateKey), []RR{rrA})
if err != nil {
t.Fatal(err)
}
if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
t.Errorf("failure to validate our RRSIG: %v", err)
}
// Signatures are randomized
rrRRSIG.(*RRSIG).Signature = ""
ourRRSIG.Signature = ""
if !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) {
t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG))
}
}
func TestInvalidRRSet(t *testing.T) {
goodRecords := make([]RR, 2)
goodRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
goodRecords[1] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"_o/"}}
// Generate key
keyname := "cloudflare.com."
key := &DNSKEY{
Hdr: RR_Header{Name: keyname, Rrtype: TypeDNSKEY, Class: ClassINET, Ttl: 0},
Algorithm: ECDSAP256SHA256,
Flags: ZONE,
Protocol: 3,
}
privatekey, err := key.Generate(256)
if err != nil {
t.Fatal(err.Error())
}
// Need to fill in: Inception, Expiration, KeyTag, SignerName and Algorithm
curTime := time.Now()
signature := &RRSIG{
Inception: uint32(curTime.Unix()),
Expiration: uint32(curTime.Add(time.Hour).Unix()),
KeyTag: key.KeyTag(),
SignerName: keyname,
Algorithm: ECDSAP256SHA256,
}
// Inconsistent name between records
badRecords := make([]RR, 2)
badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
badRecords[1] = &TXT{Hdr: RR_Header{Name: "nama.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"_o/"}}
if IsRRset(badRecords) {
t.Fatal("Record set with inconsistent names considered valid")
}
badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
badRecords[1] = &A{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeA, Class: ClassINET, Ttl: 0}}
if IsRRset(badRecords) {
t.Fatal("Record set with inconsistent record types considered valid")
}
badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
badRecords[1] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassCHAOS, Ttl: 0}, Txt: []string{"_o/"}}
if IsRRset(badRecords) {
t.Fatal("Record set with inconsistent record class considered valid")
}
// Sign the good record set and then make sure verification fails on the bad record set
if err := signature.Sign(privatekey.(crypto.Signer), goodRecords); err != nil {
t.Fatal("Signing good records failed")
}
if err := signature.Verify(key, badRecords); err != ErrRRset {
t.Fatal("Verification did not return ErrRRset with inconsistent records")
}
}

251
vendor/github.com/miekg/dns/doc.go generated vendored
View file

@ -1,251 +0,0 @@
/*
Package dns implements a full featured interface to the Domain Name System.
Server- and client-side programming is supported.
The package allows complete control over what is send out to the DNS. The package
API follows the less-is-more principle, by presenting a small, clean interface.
The package dns supports (asynchronous) querying/replying, incoming/outgoing zone transfers,
TSIG, EDNS0, dynamic updates, notifies and DNSSEC validation/signing.
Note that domain names MUST be fully qualified, before sending them, unqualified
names in a message will result in a packing failure.
Resource records are native types. They are not stored in wire format.
Basic usage pattern for creating a new resource record:
r := new(dns.MX)
r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX,
Class: dns.ClassINET, Ttl: 3600}
r.Preference = 10
r.Mx = "mx.miek.nl."
Or directly from a string:
mx, err := dns.NewRR("miek.nl. 3600 IN MX 10 mx.miek.nl.")
Or when the default TTL (3600) and class (IN) suit you:
mx, err := dns.NewRR("miek.nl. MX 10 mx.miek.nl.")
Or even:
mx, err := dns.NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek")
In the DNS messages are exchanged, these messages contain resource
records (sets). Use pattern for creating a message:
m := new(dns.Msg)
m.SetQuestion("miek.nl.", dns.TypeMX)
Or when not certain if the domain name is fully qualified:
m.SetQuestion(dns.Fqdn("miek.nl"), dns.TypeMX)
The message m is now a message with the question section set to ask
the MX records for the miek.nl. zone.
The following is slightly more verbose, but more flexible:
m1 := new(dns.Msg)
m1.Id = dns.Id()
m1.RecursionDesired = true
m1.Question = make([]dns.Question, 1)
m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET}
After creating a message it can be send.
Basic use pattern for synchronous querying the DNS at a
server configured on 127.0.0.1 and port 53:
c := new(dns.Client)
in, rtt, err := c.Exchange(m1, "127.0.0.1:53")
Suppressing multiple outstanding queries (with the same question, type and
class) is as easy as setting:
c.SingleInflight = true
If these "advanced" features are not needed, a simple UDP query can be send,
with:
in, err := dns.Exchange(m1, "127.0.0.1:53")
When this functions returns you will get dns message. A dns message consists
out of four sections.
The question section: in.Question, the answer section: in.Answer,
the authority section: in.Ns and the additional section: in.Extra.
Each of these sections (except the Question section) contain a []RR. Basic
use pattern for accessing the rdata of a TXT RR as the first RR in
the Answer section:
if t, ok := in.Answer[0].(*dns.TXT); ok {
// do something with t.Txt
}
Domain Name and TXT Character String Representations
Both domain names and TXT character strings are converted to presentation
form both when unpacked and when converted to strings.
For TXT character strings, tabs, carriage returns and line feeds will be
converted to \t, \r and \n respectively. Back slashes and quotations marks
will be escaped. Bytes below 32 and above 127 will be converted to \DDD
form.
For domain names, in addition to the above rules brackets, periods,
spaces, semicolons and the at symbol are escaped.
DNSSEC
DNSSEC (DNS Security Extension) adds a layer of security to the DNS. It
uses public key cryptography to sign resource records. The
public keys are stored in DNSKEY records and the signatures in RRSIG records.
Requesting DNSSEC information for a zone is done by adding the DO (DNSSEC OK) bit
to a request.
m := new(dns.Msg)
m.SetEdns0(4096, true)
Signature generation, signature verification and key generation are all supported.
DYNAMIC UPDATES
Dynamic updates reuses the DNS message format, but renames three of
the sections. Question is Zone, Answer is Prerequisite, Authority is
Update, only the Additional is not renamed. See RFC 2136 for the gory details.
You can set a rather complex set of rules for the existence of absence of
certain resource records or names in a zone to specify if resource records
should be added or removed. The table from RFC 2136 supplemented with the Go
DNS function shows which functions exist to specify the prerequisites.
3.2.4 - Table Of Metavalues Used In Prerequisite Section
CLASS TYPE RDATA Meaning Function
--------------------------------------------------------------
ANY ANY empty Name is in use dns.NameUsed
ANY rrset empty RRset exists (value indep) dns.RRsetUsed
NONE ANY empty Name is not in use dns.NameNotUsed
NONE rrset empty RRset does not exist dns.RRsetNotUsed
zone rrset rr RRset exists (value dep) dns.Used
The prerequisite section can also be left empty.
If you have decided on the prerequisites you can tell what RRs should
be added or deleted. The next table shows the options you have and
what functions to call.
3.4.2.6 - Table Of Metavalues Used In Update Section
CLASS TYPE RDATA Meaning Function
---------------------------------------------------------------
ANY ANY empty Delete all RRsets from name dns.RemoveName
ANY rrset empty Delete an RRset dns.RemoveRRset
NONE rrset rr Delete an RR from RRset dns.Remove
zone rrset rr Add to an RRset dns.Insert
TRANSACTION SIGNATURE
An TSIG or transaction signature adds a HMAC TSIG record to each message sent.
The supported algorithms include: HmacMD5, HmacSHA1, HmacSHA256 and HmacSHA512.
Basic use pattern when querying with a TSIG name "axfr." (note that these key names
must be fully qualified - as they are domain names) and the base64 secret
"so6ZGir4GPAqINNh9U5c3A==":
c := new(dns.Client)
c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
m := new(dns.Msg)
m.SetQuestion("miek.nl.", dns.TypeMX)
m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
...
// When sending the TSIG RR is calculated and filled in before sending
When requesting an zone transfer (almost all TSIG usage is when requesting zone transfers), with
TSIG, this is the basic use pattern. In this example we request an AXFR for
miek.nl. with TSIG key named "axfr." and secret "so6ZGir4GPAqINNh9U5c3A=="
and using the server 176.58.119.54:
t := new(dns.Transfer)
m := new(dns.Msg)
t.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
m.SetAxfr("miek.nl.")
m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
c, err := t.In(m, "176.58.119.54:53")
for r := range c { ... }
You can now read the records from the transfer as they come in. Each envelope is checked with TSIG.
If something is not correct an error is returned.
Basic use pattern validating and replying to a message that has TSIG set.
server := &dns.Server{Addr: ":53", Net: "udp"}
server.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
go server.ListenAndServe()
dns.HandleFunc(".", handleRequest)
func handleRequest(w dns.ResponseWriter, r *dns.Msg) {
m := new(dns.Msg)
m.SetReply(r)
if r.IsTsig() != nil {
if w.TsigStatus() == nil {
// *Msg r has an TSIG record and it was validated
m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
} else {
// *Msg r has an TSIG records and it was not valided
}
}
w.WriteMsg(m)
}
PRIVATE RRS
RFC 6895 sets aside a range of type codes for private use. This range
is 65,280 - 65,534 (0xFF00 - 0xFFFE). When experimenting with new Resource Records these
can be used, before requesting an official type code from IANA.
see http://miek.nl/2014/September/21/idn-and-private-rr-in-go-dns/ for more
information.
EDNS0
EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated
by RFC 6891. It defines an new RR type, the OPT RR, which is then completely
abused.
Basic use pattern for creating an (empty) OPT RR:
o := new(dns.OPT)
o.Hdr.Name = "." // MUST be the root zone, per definition.
o.Hdr.Rrtype = dns.TypeOPT
The rdata of an OPT RR consists out of a slice of EDNS0 (RFC 6891)
interfaces. Currently only a few have been standardized: EDNS0_NSID
(RFC 5001) and EDNS0_SUBNET (draft-vandergaast-edns-client-subnet-02). Note
that these options may be combined in an OPT RR.
Basic use pattern for a server to check if (and which) options are set:
// o is a dns.OPT
for _, s := range o.Option {
switch e := s.(type) {
case *dns.EDNS0_NSID:
// do stuff with e.Nsid
case *dns.EDNS0_SUBNET:
// access e.Family, e.Address, etc.
}
}
SIG(0)
From RFC 2931:
SIG(0) provides protection for DNS transactions and requests ....
... protection for glue records, DNS requests, protection for message headers
on requests and responses, and protection of the overall integrity of a response.
It works like TSIG, except that SIG(0) uses public key cryptography, instead of the shared
secret approach in TSIG.
Supported algorithms: DSA, ECDSAP256SHA256, ECDSAP384SHA384, RSASHA1, RSASHA256 and
RSASHA512.
Signing subsequent messages in multi-message sessions is not implemented.
*/
package dns

View file

@ -1,3 +0,0 @@
package dns
// Find better solution

597
vendor/github.com/miekg/dns/edns.go generated vendored
View file

@ -1,597 +0,0 @@
package dns
import (
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"net"
"strconv"
)
// EDNS0 Option codes.
const (
EDNS0LLQ = 0x1 // long lived queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01
EDNS0UL = 0x2 // update lease draft: http://files.dns-sd.org/draft-sekar-dns-ul.txt
EDNS0NSID = 0x3 // nsid (RFC5001)
EDNS0DAU = 0x5 // DNSSEC Algorithm Understood
EDNS0DHU = 0x6 // DS Hash Understood
EDNS0N3U = 0x7 // NSEC3 Hash Understood
EDNS0SUBNET = 0x8 // client-subnet (RFC6891)
EDNS0EXPIRE = 0x9 // EDNS0 expire
EDNS0COOKIE = 0xa // EDNS0 Cookie
EDNS0TCPKEEPALIVE = 0xb // EDNS0 tcp keep alive (RFC7828)
EDNS0SUBNETDRAFT = 0x50fa // Don't use! Use EDNS0SUBNET
EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (RFC6891)
EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (RFC6891)
_DO = 1 << 15 // dnssec ok
)
// OPT is the EDNS0 RR appended to messages to convey extra (meta) information.
// See RFC 6891.
type OPT struct {
Hdr RR_Header
Option []EDNS0 `dns:"opt"`
}
func (rr *OPT) String() string {
s := "\n;; OPT PSEUDOSECTION:\n; EDNS: version " + strconv.Itoa(int(rr.Version())) + "; "
if rr.Do() {
s += "flags: do; "
} else {
s += "flags: ; "
}
s += "udp: " + strconv.Itoa(int(rr.UDPSize()))
for _, o := range rr.Option {
switch o.(type) {
case *EDNS0_NSID:
s += "\n; NSID: " + o.String()
h, e := o.pack()
var r string
if e == nil {
for _, c := range h {
r += "(" + string(c) + ")"
}
s += " " + r
}
case *EDNS0_SUBNET:
s += "\n; SUBNET: " + o.String()
if o.(*EDNS0_SUBNET).DraftOption {
s += " (draft)"
}
case *EDNS0_COOKIE:
s += "\n; COOKIE: " + o.String()
case *EDNS0_UL:
s += "\n; UPDATE LEASE: " + o.String()
case *EDNS0_LLQ:
s += "\n; LONG LIVED QUERIES: " + o.String()
case *EDNS0_DAU:
s += "\n; DNSSEC ALGORITHM UNDERSTOOD: " + o.String()
case *EDNS0_DHU:
s += "\n; DS HASH UNDERSTOOD: " + o.String()
case *EDNS0_N3U:
s += "\n; NSEC3 HASH UNDERSTOOD: " + o.String()
case *EDNS0_LOCAL:
s += "\n; LOCAL OPT: " + o.String()
}
}
return s
}
func (rr *OPT) len() int {
l := rr.Hdr.len()
for i := 0; i < len(rr.Option); i++ {
l += 4 // Account for 2-byte option code and 2-byte option length.
lo, _ := rr.Option[i].pack()
l += len(lo)
}
return l
}
// return the old value -> delete SetVersion?
// Version returns the EDNS version used. Only zero is defined.
func (rr *OPT) Version() uint8 {
return uint8((rr.Hdr.Ttl & 0x00FF0000) >> 16)
}
// SetVersion sets the version of EDNS. This is usually zero.
func (rr *OPT) SetVersion(v uint8) {
rr.Hdr.Ttl = rr.Hdr.Ttl&0xFF00FFFF | (uint32(v) << 16)
}
// ExtendedRcode returns the EDNS extended RCODE field (the upper 8 bits of the TTL).
func (rr *OPT) ExtendedRcode() int {
return int((rr.Hdr.Ttl&0xFF000000)>>24) + 15
}
// SetExtendedRcode sets the EDNS extended RCODE field.
func (rr *OPT) SetExtendedRcode(v uint8) {
if v < RcodeBadVers { // Smaller than 16.. Use the 4 bits you have!
return
}
rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | (uint32(v-15) << 24)
}
// UDPSize returns the UDP buffer size.
func (rr *OPT) UDPSize() uint16 {
return rr.Hdr.Class
}
// SetUDPSize sets the UDP buffer size.
func (rr *OPT) SetUDPSize(size uint16) {
rr.Hdr.Class = size
}
// Do returns the value of the DO (DNSSEC OK) bit.
func (rr *OPT) Do() bool {
return rr.Hdr.Ttl&_DO == _DO
}
// SetDo sets the DO (DNSSEC OK) bit.
// If we pass an argument, set the DO bit to that value.
// It is possible to pass 2 or more arguments. Any arguments after the 1st is silently ignored.
func (rr *OPT) SetDo(do ...bool) {
if len(do) == 1 {
if do[0] {
rr.Hdr.Ttl |= _DO
} else {
rr.Hdr.Ttl &^= _DO
}
} else {
rr.Hdr.Ttl |= _DO
}
}
// EDNS0 defines an EDNS0 Option. An OPT RR can have multiple options appended to it.
type EDNS0 interface {
// Option returns the option code for the option.
Option() uint16
// pack returns the bytes of the option data.
pack() ([]byte, error)
// unpack sets the data as found in the buffer. Is also sets
// the length of the slice as the length of the option data.
unpack([]byte) error
// String returns the string representation of the option.
String() string
}
// EDNS0_NSID option is used to retrieve a nameserver
// identifier. When sending a request Nsid must be set to the empty string
// The identifier is an opaque string encoded as hex.
// Basic use pattern for creating an nsid option:
//
// o := new(dns.OPT)
// o.Hdr.Name = "."
// o.Hdr.Rrtype = dns.TypeOPT
// e := new(dns.EDNS0_NSID)
// e.Code = dns.EDNS0NSID
// e.Nsid = "AA"
// o.Option = append(o.Option, e)
type EDNS0_NSID struct {
Code uint16 // Always EDNS0NSID
Nsid string // This string needs to be hex encoded
}
func (e *EDNS0_NSID) pack() ([]byte, error) {
h, err := hex.DecodeString(e.Nsid)
if err != nil {
return nil, err
}
return h, nil
}
func (e *EDNS0_NSID) Option() uint16 { return EDNS0NSID }
func (e *EDNS0_NSID) unpack(b []byte) error { e.Nsid = hex.EncodeToString(b); return nil }
func (e *EDNS0_NSID) String() string { return string(e.Nsid) }
// EDNS0_SUBNET is the subnet option that is used to give the remote nameserver
// an idea of where the client lives. It can then give back a different
// answer depending on the location or network topology.
// Basic use pattern for creating an subnet option:
//
// o := new(dns.OPT)
// o.Hdr.Name = "."
// o.Hdr.Rrtype = dns.TypeOPT
// e := new(dns.EDNS0_SUBNET)
// e.Code = dns.EDNS0SUBNET
// e.Family = 1 // 1 for IPv4 source address, 2 for IPv6
// e.SourceNetmask = 32 // 32 for IPV4, 128 for IPv6
// e.SourceScope = 0
// e.Address = net.ParseIP("127.0.0.1").To4() // for IPv4
// // e.Address = net.ParseIP("2001:7b8:32a::2") // for IPV6
// o.Option = append(o.Option, e)
//
// Note: the spec (draft-ietf-dnsop-edns-client-subnet-00) has some insane logic
// for which netmask applies to the address. This code will parse all the
// available bits when unpacking (up to optlen). When packing it will apply
// SourceNetmask. If you need more advanced logic, patches welcome and good luck.
type EDNS0_SUBNET struct {
Code uint16 // Always EDNS0SUBNET
Family uint16 // 1 for IP, 2 for IP6
SourceNetmask uint8
SourceScope uint8
Address net.IP
DraftOption bool // Set to true if using the old (0x50fa) option code
}
func (e *EDNS0_SUBNET) Option() uint16 {
if e.DraftOption {
return EDNS0SUBNETDRAFT
}
return EDNS0SUBNET
}
func (e *EDNS0_SUBNET) pack() ([]byte, error) {
b := make([]byte, 4)
binary.BigEndian.PutUint16(b[0:], e.Family)
b[2] = e.SourceNetmask
b[3] = e.SourceScope
switch e.Family {
case 1:
if e.SourceNetmask > net.IPv4len*8 {
return nil, errors.New("dns: bad netmask")
}
if len(e.Address.To4()) != net.IPv4len {
return nil, errors.New("dns: bad address")
}
ip := e.Address.To4().Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv4len*8))
needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up
b = append(b, ip[:needLength]...)
case 2:
if e.SourceNetmask > net.IPv6len*8 {
return nil, errors.New("dns: bad netmask")
}
if len(e.Address) != net.IPv6len {
return nil, errors.New("dns: bad address")
}
ip := e.Address.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv6len*8))
needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up
b = append(b, ip[:needLength]...)
default:
return nil, errors.New("dns: bad address family")
}
return b, nil
}
func (e *EDNS0_SUBNET) unpack(b []byte) error {
if len(b) < 4 {
return ErrBuf
}
e.Family = binary.BigEndian.Uint16(b)
e.SourceNetmask = b[2]
e.SourceScope = b[3]
switch e.Family {
case 1:
if e.SourceNetmask > net.IPv4len*8 || e.SourceScope > net.IPv4len*8 {
return errors.New("dns: bad netmask")
}
addr := make([]byte, net.IPv4len)
for i := 0; i < net.IPv4len && 4+i < len(b); i++ {
addr[i] = b[4+i]
}
e.Address = net.IPv4(addr[0], addr[1], addr[2], addr[3])
case 2:
if e.SourceNetmask > net.IPv6len*8 || e.SourceScope > net.IPv6len*8 {
return errors.New("dns: bad netmask")
}
addr := make([]byte, net.IPv6len)
for i := 0; i < net.IPv6len && 4+i < len(b); i++ {
addr[i] = b[4+i]
}
e.Address = net.IP{addr[0], addr[1], addr[2], addr[3], addr[4],
addr[5], addr[6], addr[7], addr[8], addr[9], addr[10],
addr[11], addr[12], addr[13], addr[14], addr[15]}
default:
return errors.New("dns: bad address family")
}
return nil
}
func (e *EDNS0_SUBNET) String() (s string) {
if e.Address == nil {
s = "<nil>"
} else if e.Address.To4() != nil {
s = e.Address.String()
} else {
s = "[" + e.Address.String() + "]"
}
s += "/" + strconv.Itoa(int(e.SourceNetmask)) + "/" + strconv.Itoa(int(e.SourceScope))
return
}
// The EDNS0_COOKIE option is used to add a DNS Cookie to a message.
//
// o := new(dns.OPT)
// o.Hdr.Name = "."
// o.Hdr.Rrtype = dns.TypeOPT
// e := new(dns.EDNS0_COOKIE)
// e.Code = dns.EDNS0COOKIE
// e.Cookie = "24a5ac.."
// o.Option = append(o.Option, e)
//
// The Cookie field consists out of a client cookie (RFC 7873 Section 4), that is
// always 8 bytes. It may then optionally be followed by the server cookie. The server
// cookie is of variable length, 8 to a maximum of 32 bytes. In other words:
//
// cCookie := o.Cookie[:16]
// sCookie := o.Cookie[16:]
//
// There is no guarantee that the Cookie string has a specific length.
type EDNS0_COOKIE struct {
Code uint16 // Always EDNS0COOKIE
Cookie string // Hex-encoded cookie data
}
func (e *EDNS0_COOKIE) pack() ([]byte, error) {
h, err := hex.DecodeString(e.Cookie)
if err != nil {
return nil, err
}
return h, nil
}
func (e *EDNS0_COOKIE) Option() uint16 { return EDNS0COOKIE }
func (e *EDNS0_COOKIE) unpack(b []byte) error { e.Cookie = hex.EncodeToString(b); return nil }
func (e *EDNS0_COOKIE) String() string { return e.Cookie }
// The EDNS0_UL (Update Lease) (draft RFC) option is used to tell the server to set
// an expiration on an update RR. This is helpful for clients that cannot clean
// up after themselves. This is a draft RFC and more information can be found at
// http://files.dns-sd.org/draft-sekar-dns-ul.txt
//
// o := new(dns.OPT)
// o.Hdr.Name = "."
// o.Hdr.Rrtype = dns.TypeOPT
// e := new(dns.EDNS0_UL)
// e.Code = dns.EDNS0UL
// e.Lease = 120 // in seconds
// o.Option = append(o.Option, e)
type EDNS0_UL struct {
Code uint16 // Always EDNS0UL
Lease uint32
}
func (e *EDNS0_UL) Option() uint16 { return EDNS0UL }
func (e *EDNS0_UL) String() string { return strconv.FormatUint(uint64(e.Lease), 10) }
// Copied: http://golang.org/src/pkg/net/dnsmsg.go
func (e *EDNS0_UL) pack() ([]byte, error) {
b := make([]byte, 4)
binary.BigEndian.PutUint32(b, e.Lease)
return b, nil
}
func (e *EDNS0_UL) unpack(b []byte) error {
if len(b) < 4 {
return ErrBuf
}
e.Lease = binary.BigEndian.Uint32(b)
return nil
}
// EDNS0_LLQ stands for Long Lived Queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01
// Implemented for completeness, as the EDNS0 type code is assigned.
type EDNS0_LLQ struct {
Code uint16 // Always EDNS0LLQ
Version uint16
Opcode uint16
Error uint16
Id uint64
LeaseLife uint32
}
func (e *EDNS0_LLQ) Option() uint16 { return EDNS0LLQ }
func (e *EDNS0_LLQ) pack() ([]byte, error) {
b := make([]byte, 18)
binary.BigEndian.PutUint16(b[0:], e.Version)
binary.BigEndian.PutUint16(b[2:], e.Opcode)
binary.BigEndian.PutUint16(b[4:], e.Error)
binary.BigEndian.PutUint64(b[6:], e.Id)
binary.BigEndian.PutUint32(b[14:], e.LeaseLife)
return b, nil
}
func (e *EDNS0_LLQ) unpack(b []byte) error {
if len(b) < 18 {
return ErrBuf
}
e.Version = binary.BigEndian.Uint16(b[0:])
e.Opcode = binary.BigEndian.Uint16(b[2:])
e.Error = binary.BigEndian.Uint16(b[4:])
e.Id = binary.BigEndian.Uint64(b[6:])
e.LeaseLife = binary.BigEndian.Uint32(b[14:])
return nil
}
func (e *EDNS0_LLQ) String() string {
s := strconv.FormatUint(uint64(e.Version), 10) + " " + strconv.FormatUint(uint64(e.Opcode), 10) +
" " + strconv.FormatUint(uint64(e.Error), 10) + " " + strconv.FormatUint(uint64(e.Id), 10) +
" " + strconv.FormatUint(uint64(e.LeaseLife), 10)
return s
}
type EDNS0_DAU struct {
Code uint16 // Always EDNS0DAU
AlgCode []uint8
}
func (e *EDNS0_DAU) Option() uint16 { return EDNS0DAU }
func (e *EDNS0_DAU) pack() ([]byte, error) { return e.AlgCode, nil }
func (e *EDNS0_DAU) unpack(b []byte) error { e.AlgCode = b; return nil }
func (e *EDNS0_DAU) String() string {
s := ""
for i := 0; i < len(e.AlgCode); i++ {
if a, ok := AlgorithmToString[e.AlgCode[i]]; ok {
s += " " + a
} else {
s += " " + strconv.Itoa(int(e.AlgCode[i]))
}
}
return s
}
type EDNS0_DHU struct {
Code uint16 // Always EDNS0DHU
AlgCode []uint8
}
func (e *EDNS0_DHU) Option() uint16 { return EDNS0DHU }
func (e *EDNS0_DHU) pack() ([]byte, error) { return e.AlgCode, nil }
func (e *EDNS0_DHU) unpack(b []byte) error { e.AlgCode = b; return nil }
func (e *EDNS0_DHU) String() string {
s := ""
for i := 0; i < len(e.AlgCode); i++ {
if a, ok := HashToString[e.AlgCode[i]]; ok {
s += " " + a
} else {
s += " " + strconv.Itoa(int(e.AlgCode[i]))
}
}
return s
}
type EDNS0_N3U struct {
Code uint16 // Always EDNS0N3U
AlgCode []uint8
}
func (e *EDNS0_N3U) Option() uint16 { return EDNS0N3U }
func (e *EDNS0_N3U) pack() ([]byte, error) { return e.AlgCode, nil }
func (e *EDNS0_N3U) unpack(b []byte) error { e.AlgCode = b; return nil }
func (e *EDNS0_N3U) String() string {
// Re-use the hash map
s := ""
for i := 0; i < len(e.AlgCode); i++ {
if a, ok := HashToString[e.AlgCode[i]]; ok {
s += " " + a
} else {
s += " " + strconv.Itoa(int(e.AlgCode[i]))
}
}
return s
}
type EDNS0_EXPIRE struct {
Code uint16 // Always EDNS0EXPIRE
Expire uint32
}
func (e *EDNS0_EXPIRE) Option() uint16 { return EDNS0EXPIRE }
func (e *EDNS0_EXPIRE) String() string { return strconv.FormatUint(uint64(e.Expire), 10) }
func (e *EDNS0_EXPIRE) pack() ([]byte, error) {
b := make([]byte, 4)
b[0] = byte(e.Expire >> 24)
b[1] = byte(e.Expire >> 16)
b[2] = byte(e.Expire >> 8)
b[3] = byte(e.Expire)
return b, nil
}
func (e *EDNS0_EXPIRE) unpack(b []byte) error {
if len(b) < 4 {
return ErrBuf
}
e.Expire = binary.BigEndian.Uint32(b)
return nil
}
// The EDNS0_LOCAL option is used for local/experimental purposes. The option
// code is recommended to be within the range [EDNS0LOCALSTART, EDNS0LOCALEND]
// (RFC6891), although any unassigned code can actually be used. The content of
// the option is made available in Data, unaltered.
// Basic use pattern for creating a local option:
//
// o := new(dns.OPT)
// o.Hdr.Name = "."
// o.Hdr.Rrtype = dns.TypeOPT
// e := new(dns.EDNS0_LOCAL)
// e.Code = dns.EDNS0LOCALSTART
// e.Data = []byte{72, 82, 74}
// o.Option = append(o.Option, e)
type EDNS0_LOCAL struct {
Code uint16
Data []byte
}
func (e *EDNS0_LOCAL) Option() uint16 { return e.Code }
func (e *EDNS0_LOCAL) String() string {
return strconv.FormatInt(int64(e.Code), 10) + ":0x" + hex.EncodeToString(e.Data)
}
func (e *EDNS0_LOCAL) pack() ([]byte, error) {
b := make([]byte, len(e.Data))
copied := copy(b, e.Data)
if copied != len(e.Data) {
return nil, ErrBuf
}
return b, nil
}
func (e *EDNS0_LOCAL) unpack(b []byte) error {
e.Data = make([]byte, len(b))
copied := copy(e.Data, b)
if copied != len(b) {
return ErrBuf
}
return nil
}
// EDNS0_TCP_KEEPALIVE is an EDNS0 option that instructs the server to keep
// the TCP connection alive. See RFC 7828.
type EDNS0_TCP_KEEPALIVE struct {
Code uint16 // Always EDNSTCPKEEPALIVE
Length uint16 // the value 0 if the TIMEOUT is omitted, the value 2 if it is present;
Timeout uint16 // an idle timeout value for the TCP connection, specified in units of 100 milliseconds, encoded in network byte order.
}
func (e *EDNS0_TCP_KEEPALIVE) Option() uint16 { return EDNS0TCPKEEPALIVE }
func (e *EDNS0_TCP_KEEPALIVE) pack() ([]byte, error) {
if e.Timeout != 0 && e.Length != 2 {
return nil, errors.New("dns: timeout specified but length is not 2")
}
if e.Timeout == 0 && e.Length != 0 {
return nil, errors.New("dns: timeout not specified but length is not 0")
}
b := make([]byte, 4+e.Length)
binary.BigEndian.PutUint16(b[0:], e.Code)
binary.BigEndian.PutUint16(b[2:], e.Length)
if e.Length == 2 {
binary.BigEndian.PutUint16(b[4:], e.Timeout)
}
return b, nil
}
func (e *EDNS0_TCP_KEEPALIVE) unpack(b []byte) error {
if len(b) < 4 {
return ErrBuf
}
e.Length = binary.BigEndian.Uint16(b[2:4])
if e.Length != 0 && e.Length != 2 {
return errors.New("dns: length mismatch, want 0/2 but got " + strconv.FormatUint(uint64(e.Length), 10))
}
if e.Length == 2 {
if len(b) < 6 {
return ErrBuf
}
e.Timeout = binary.BigEndian.Uint16(b[4:6])
}
return nil
}
func (e *EDNS0_TCP_KEEPALIVE) String() (s string) {
s = "use tcp keep-alive"
if e.Length == 0 {
s += ", timeout omitted"
} else {
s += fmt.Sprintf(", timeout %dms", e.Timeout*100)
}
return
}

View file

@ -1,68 +0,0 @@
package dns
import "testing"
func TestOPTTtl(t *testing.T) {
e := &OPT{}
e.Hdr.Name = "."
e.Hdr.Rrtype = TypeOPT
// verify the default setting of DO=0
if e.Do() {
t.Errorf("DO bit should be zero")
}
// There are 6 possible invocations of SetDo():
//
// 1. Starting with DO=0, using SetDo()
// 2. Starting with DO=0, using SetDo(true)
// 3. Starting with DO=0, using SetDo(false)
// 4. Starting with DO=1, using SetDo()
// 5. Starting with DO=1, using SetDo(true)
// 6. Starting with DO=1, using SetDo(false)
// verify that invoking SetDo() sets DO=1 (TEST #1)
e.SetDo()
if !e.Do() {
t.Errorf("DO bit should be non-zero")
}
// verify that using SetDo(true) works when DO=1 (TEST #5)
e.SetDo(true)
if !e.Do() {
t.Errorf("DO bit should still be non-zero")
}
// verify that we can use SetDo(false) to set DO=0 (TEST #6)
e.SetDo(false)
if e.Do() {
t.Errorf("DO bit should be zero")
}
// verify that if we call SetDo(false) when DO=0 that it is unchanged (TEST #3)
e.SetDo(false)
if e.Do() {
t.Errorf("DO bit should still be zero")
}
// verify that using SetDo(true) works for DO=0 (TEST #2)
e.SetDo(true)
if !e.Do() {
t.Errorf("DO bit should be non-zero")
}
// verify that using SetDo() works for DO=1 (TEST #4)
e.SetDo()
if !e.Do() {
t.Errorf("DO bit should be non-zero")
}
if e.Version() != 0 {
t.Errorf("version should be non-zero")
}
e.SetVersion(42)
if e.Version() != 42 {
t.Errorf("set 42, expected %d, got %d", 42, e.Version())
}
e.SetExtendedRcode(42)
if e.ExtendedRcode() != 42 {
t.Errorf("set 42, expected %d, got %d", 42-15, e.ExtendedRcode())
}
}

View file

@ -1,146 +0,0 @@
package dns_test
import (
"errors"
"fmt"
"log"
"net"
"github.com/miekg/dns"
)
// Retrieve the MX records for miek.nl.
func ExampleMX() {
config, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
c := new(dns.Client)
m := new(dns.Msg)
m.SetQuestion("miek.nl.", dns.TypeMX)
m.RecursionDesired = true
r, _, err := c.Exchange(m, config.Servers[0]+":"+config.Port)
if err != nil {
return
}
if r.Rcode != dns.RcodeSuccess {
return
}
for _, a := range r.Answer {
if mx, ok := a.(*dns.MX); ok {
fmt.Printf("%s\n", mx.String())
}
}
}
// Retrieve the DNSKEY records of a zone and convert them
// to DS records for SHA1, SHA256 and SHA384.
func ExampleDS() {
config, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
c := new(dns.Client)
m := new(dns.Msg)
zone := "miek.nl"
m.SetQuestion(dns.Fqdn(zone), dns.TypeDNSKEY)
m.SetEdns0(4096, true)
r, _, err := c.Exchange(m, config.Servers[0]+":"+config.Port)
if err != nil {
return
}
if r.Rcode != dns.RcodeSuccess {
return
}
for _, k := range r.Answer {
if key, ok := k.(*dns.DNSKEY); ok {
for _, alg := range []uint8{dns.SHA1, dns.SHA256, dns.SHA384} {
fmt.Printf("%s; %d\n", key.ToDS(alg).String(), key.Flags)
}
}
}
}
const TypeAPAIR = 0x0F99
type APAIR struct {
addr [2]net.IP
}
func NewAPAIR() dns.PrivateRdata { return new(APAIR) }
func (rd *APAIR) String() string { return rd.addr[0].String() + " " + rd.addr[1].String() }
func (rd *APAIR) Parse(txt []string) error {
if len(txt) != 2 {
return errors.New("two addresses required for APAIR")
}
for i, s := range txt {
ip := net.ParseIP(s)
if ip == nil {
return errors.New("invalid IP in APAIR text representation")
}
rd.addr[i] = ip
}
return nil
}
func (rd *APAIR) Pack(buf []byte) (int, error) {
b := append([]byte(rd.addr[0]), []byte(rd.addr[1])...)
n := copy(buf, b)
if n != len(b) {
return n, dns.ErrBuf
}
return n, nil
}
func (rd *APAIR) Unpack(buf []byte) (int, error) {
ln := net.IPv4len * 2
if len(buf) != ln {
return 0, errors.New("invalid length of APAIR rdata")
}
cp := make([]byte, ln)
copy(cp, buf) // clone bytes to use them in IPs
rd.addr[0] = net.IP(cp[:3])
rd.addr[1] = net.IP(cp[4:])
return len(buf), nil
}
func (rd *APAIR) Copy(dest dns.PrivateRdata) error {
cp := make([]byte, rd.Len())
_, err := rd.Pack(cp)
if err != nil {
return err
}
d := dest.(*APAIR)
d.addr[0] = net.IP(cp[:3])
d.addr[1] = net.IP(cp[4:])
return nil
}
func (rd *APAIR) Len() int {
return net.IPv4len * 2
}
func ExamplePrivateHandle() {
dns.PrivateHandle("APAIR", TypeAPAIR, NewAPAIR)
defer dns.PrivateHandleRemove(TypeAPAIR)
rr, err := dns.NewRR("miek.nl. APAIR (1.2.3.4 1.2.3.5)")
if err != nil {
log.Fatal("could not parse APAIR record: ", err)
}
fmt.Println(rr)
// Output: miek.nl. 3600 IN APAIR 1.2.3.4 1.2.3.5
m := new(dns.Msg)
m.Id = 12345
m.SetQuestion("miek.nl.", TypeAPAIR)
m.Answer = append(m.Answer, rr)
fmt.Println(m)
// ;; opcode: QUERY, status: NOERROR, id: 12345
// ;; flags: rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
//
// ;; QUESTION SECTION:
// ;miek.nl. IN APAIR
//
// ;; ANSWER SECTION:
// miek.nl. 3600 IN APAIR 1.2.3.4 1.2.3.5
}

View file

@ -1,87 +0,0 @@
package dns
import (
"net"
"reflect"
"strconv"
)
// NumField returns the number of rdata fields r has.
func NumField(r RR) int {
return reflect.ValueOf(r).Elem().NumField() - 1 // Remove RR_Header
}
// Field returns the rdata field i as a string. Fields are indexed starting from 1.
// RR types that holds slice data, for instance the NSEC type bitmap will return a single
// string where the types are concatenated using a space.
// Accessing non existing fields will cause a panic.
func Field(r RR, i int) string {
if i == 0 {
return ""
}
d := reflect.ValueOf(r).Elem().Field(i)
switch k := d.Kind(); k {
case reflect.String:
return d.String()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.FormatInt(d.Int(), 10)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.FormatUint(d.Uint(), 10)
case reflect.Slice:
switch reflect.ValueOf(r).Elem().Type().Field(i).Tag {
case `dns:"a"`:
// TODO(miek): Hmm store this as 16 bytes
if d.Len() < net.IPv6len {
return net.IPv4(byte(d.Index(0).Uint()),
byte(d.Index(1).Uint()),
byte(d.Index(2).Uint()),
byte(d.Index(3).Uint())).String()
}
return net.IPv4(byte(d.Index(12).Uint()),
byte(d.Index(13).Uint()),
byte(d.Index(14).Uint()),
byte(d.Index(15).Uint())).String()
case `dns:"aaaa"`:
return net.IP{
byte(d.Index(0).Uint()),
byte(d.Index(1).Uint()),
byte(d.Index(2).Uint()),
byte(d.Index(3).Uint()),
byte(d.Index(4).Uint()),
byte(d.Index(5).Uint()),
byte(d.Index(6).Uint()),
byte(d.Index(7).Uint()),
byte(d.Index(8).Uint()),
byte(d.Index(9).Uint()),
byte(d.Index(10).Uint()),
byte(d.Index(11).Uint()),
byte(d.Index(12).Uint()),
byte(d.Index(13).Uint()),
byte(d.Index(14).Uint()),
byte(d.Index(15).Uint()),
}.String()
case `dns:"nsec"`:
if d.Len() == 0 {
return ""
}
s := Type(d.Index(0).Uint()).String()
for i := 1; i < d.Len(); i++ {
s += " " + Type(d.Index(i).Uint()).String()
}
return s
default:
// if it does not have a tag its a string slice
fallthrough
case `dns:"txt"`:
if d.Len() == 0 {
return ""
}
s := d.Index(0).String()
for i := 1; i < d.Len(); i++ {
s += " " + d.Index(i).String()
}
return s
}
}
return ""
}

View file

@ -1,25 +0,0 @@
package dns
import "testing"
func TestFuzzString(t *testing.T) {
testcases := []string{"", " MINFO ", " RP ", " NSEC 0 0", " \" NSEC 0 0\"", " \" MINFO \"",
";a ", ";a<><61><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>",
" NSAP O ", " NSAP N ",
" TYPE4 TYPE6a789a3bc0045c8a5fb42c7d1bd998f5444 IN 9579b47d46817afbd17273e6",
" TYPE45 3 3 4147994 TYPE\\(\\)\\)\\(\\)\\(\\(\\)\\(\\)\\)\\)\\(\\)\\(\\)\\(\\(\\R 948\"\")\\(\\)\\)\\)\\(\\ ",
"$GENERATE 0-3 ${441189,5039418474430,o}",
"$INCLUDE 00 TYPE00000000000n ",
"$INCLUDE PE4 TYPE061463623/727071511 \\(\\)\\$GENERATE 6-462/0",
}
for i, tc := range testcases {
rr, err := NewRR(tc)
if err == nil {
// rr can be nil because we can (for instance) just parse a comment
if rr == nil {
continue
}
t.Fatalf("parsed mailformed RR %d: %s", i, rr.String())
}
}
}

View file

@ -1,159 +0,0 @@
package dns
import (
"bytes"
"errors"
"fmt"
"strconv"
"strings"
)
// Parse the $GENERATE statement as used in BIND9 zones.
// See http://www.zytrax.com/books/dns/ch8/generate.html for instance.
// We are called after '$GENERATE '. After which we expect:
// * the range (12-24/2)
// * lhs (ownername)
// * [[ttl][class]]
// * type
// * rhs (rdata)
// But we are lazy here, only the range is parsed *all* occurrences
// of $ after that are interpreted.
// Any error are returned as a string value, the empty string signals
// "no error".
func generate(l lex, c chan lex, t chan *Token, o string) string {
step := 1
if i := strings.IndexAny(l.token, "/"); i != -1 {
if i+1 == len(l.token) {
return "bad step in $GENERATE range"
}
if s, err := strconv.Atoi(l.token[i+1:]); err == nil {
if s < 0 {
return "bad step in $GENERATE range"
}
step = s
} else {
return "bad step in $GENERATE range"
}
l.token = l.token[:i]
}
sx := strings.SplitN(l.token, "-", 2)
if len(sx) != 2 {
return "bad start-stop in $GENERATE range"
}
start, err := strconv.Atoi(sx[0])
if err != nil {
return "bad start in $GENERATE range"
}
end, err := strconv.Atoi(sx[1])
if err != nil {
return "bad stop in $GENERATE range"
}
if end < 0 || start < 0 || end < start {
return "bad range in $GENERATE range"
}
<-c // _BLANK
// Create a complete new string, which we then parse again.
s := ""
BuildRR:
l = <-c
if l.value != zNewline && l.value != zEOF {
s += l.token
goto BuildRR
}
for i := start; i <= end; i += step {
var (
escape bool
dom bytes.Buffer
mod string
err error
offset int
)
for j := 0; j < len(s); j++ { // No 'range' because we need to jump around
switch s[j] {
case '\\':
if escape {
dom.WriteByte('\\')
escape = false
continue
}
escape = true
case '$':
mod = "%d"
offset = 0
if escape {
dom.WriteByte('$')
escape = false
continue
}
escape = false
if j+1 >= len(s) { // End of the string
dom.WriteString(fmt.Sprintf(mod, i+offset))
continue
} else {
if s[j+1] == '$' {
dom.WriteByte('$')
j++
continue
}
}
// Search for { and }
if s[j+1] == '{' { // Modifier block
sep := strings.Index(s[j+2:], "}")
if sep == -1 {
return "bad modifier in $GENERATE"
}
mod, offset, err = modToPrintf(s[j+2 : j+2+sep])
if err != nil {
return err.Error()
}
j += 2 + sep // Jump to it
}
dom.WriteString(fmt.Sprintf(mod, i+offset))
default:
if escape { // Pretty useless here
escape = false
continue
}
dom.WriteByte(s[j])
}
}
// Re-parse the RR and send it on the current channel t
rx, err := NewRR("$ORIGIN " + o + "\n" + dom.String())
if err != nil {
return err.Error()
}
t <- &Token{RR: rx}
// Its more efficient to first built the rrlist and then parse it in
// one go! But is this a problem?
}
return ""
}
// Convert a $GENERATE modifier 0,0,d to something Printf can deal with.
func modToPrintf(s string) (string, int, error) {
xs := strings.SplitN(s, ",", 3)
if len(xs) != 3 {
return "", 0, errors.New("bad modifier in $GENERATE")
}
// xs[0] is offset, xs[1] is width, xs[2] is base
if xs[2] != "o" && xs[2] != "d" && xs[2] != "x" && xs[2] != "X" {
return "", 0, errors.New("bad base in $GENERATE")
}
offset, err := strconv.Atoi(xs[0])
if err != nil || offset > 255 {
return "", 0, errors.New("bad offset in $GENERATE")
}
width, err := strconv.Atoi(xs[1])
if err != nil || width > 255 {
return "", offset, errors.New("bad width in $GENERATE")
}
switch {
case width < 0:
return "", offset, errors.New("bad width in $GENERATE")
case width == 0:
return "%" + xs[1] + xs[2], offset, nil
}
return "%0" + xs[1] + xs[2], offset, nil
}

View file

@ -1,68 +0,0 @@
package dns
// Tests that solve that an specific issue.
import (
"strings"
"testing"
)
func TestTCPRtt(t *testing.T) {
m := new(Msg)
m.RecursionDesired = true
m.SetQuestion("example.org.", TypeA)
c := &Client{}
for _, proto := range []string{"udp", "tcp"} {
c.Net = proto
_, rtt, err := c.Exchange(m, "8.8.4.4:53")
if err != nil {
t.Fatal(err)
}
if rtt == 0 {
t.Fatalf("expecting non zero rtt %s, got zero", c.Net)
}
}
}
func TestNSEC3MissingSalt(t *testing.T) {
rr, err := NewRR("ji6neoaepv8b5o6k4ev33abha8ht9fgc.example. NSEC3 1 1 12 aabbccdd K8UDEMVP1J2F7EG6JEBPS17VP3N8I58H")
if err != nil {
t.Fatalf("failed to parse example rr: %s", err)
}
m := new(Msg)
m.Answer = []RR{rr}
mb, err := m.Pack()
if err != nil {
t.Fatalf("expected to pack message. err: %s", err)
}
if err := m.Unpack(mb); err != nil {
t.Fatalf("expected to unpack message. missing salt? err: %s", err)
}
in := rr.(*NSEC3).Salt
out := m.Answer[0].(*NSEC3).Salt
if in != out {
t.Fatalf("expected salts to match. packed: `%s`. returned: `%s`", in, out)
}
}
func TestNSEC3MixedNextDomain(t *testing.T) {
rr, err := NewRR("ji6neoaepv8b5o6k4ev33abha8ht9fgc.example. NSEC3 1 1 12 - k8udemvp1j2f7eg6jebps17vp3n8i58h")
if err != nil {
t.Fatalf("failed to parse example rr: %s", err)
}
m := new(Msg)
m.Answer = []RR{rr}
mb, err := m.Pack()
if err != nil {
t.Fatalf("expected to pack message. err: %s", err)
}
if err := m.Unpack(mb); err != nil {
t.Fatalf("expected to unpack message. err: %s", err)
}
in := strings.ToUpper(rr.(*NSEC3).NextDomain)
out := m.Answer[0].(*NSEC3).NextDomain
if in != out {
t.Fatalf("expected round trip to produce NextDomain `%s`, instead `%s`", in, out)
}
}

171
vendor/github.com/miekg/dns/labels.go generated vendored
View file

@ -1,171 +0,0 @@
package dns
import "strings"
// Holds a bunch of helper functions for dealing with labels.
// SplitDomainName splits a name string into it's labels.
// www.miek.nl. returns []string{"www", "miek", "nl"}
// .www.miek.nl. returns []string{"", "www", "miek", "nl"},
// The root label (.) returns nil. Note that using
// strings.Split(s) will work in most cases, but does not handle
// escaped dots (\.) for instance.
// s must be a syntactically valid domain name, see IsDomainName.
func SplitDomainName(s string) (labels []string) {
if len(s) == 0 {
return nil
}
fqdnEnd := 0 // offset of the final '.' or the length of the name
idx := Split(s)
begin := 0
if s[len(s)-1] == '.' {
fqdnEnd = len(s) - 1
} else {
fqdnEnd = len(s)
}
switch len(idx) {
case 0:
return nil
case 1:
// no-op
default:
end := 0
for i := 1; i < len(idx); i++ {
end = idx[i]
labels = append(labels, s[begin:end-1])
begin = end
}
}
labels = append(labels, s[begin:fqdnEnd])
return labels
}
// CompareDomainName compares the names s1 and s2 and
// returns how many labels they have in common starting from the *right*.
// The comparison stops at the first inequality. The names are not downcased
// before the comparison.
//
// www.miek.nl. and miek.nl. have two labels in common: miek and nl
// www.miek.nl. and www.bla.nl. have one label in common: nl
//
// s1 and s2 must be syntactically valid domain names.
func CompareDomainName(s1, s2 string) (n int) {
s1, s2 = strings.ToLower(s1), strings.ToLower(s2)
s1 = Fqdn(s1)
s2 = Fqdn(s2)
l1 := Split(s1)
l2 := Split(s2)
// the first check: root label
if l1 == nil || l2 == nil {
return
}
j1 := len(l1) - 1 // end
i1 := len(l1) - 2 // start
j2 := len(l2) - 1
i2 := len(l2) - 2
// the second check can be done here: last/only label
// before we fall through into the for-loop below
if s1[l1[j1]:] == s2[l2[j2]:] {
n++
} else {
return
}
for {
if i1 < 0 || i2 < 0 {
break
}
if s1[l1[i1]:l1[j1]] == s2[l2[i2]:l2[j2]] {
n++
} else {
break
}
j1--
i1--
j2--
i2--
}
return
}
// CountLabel counts the the number of labels in the string s.
// s must be a syntactically valid domain name.
func CountLabel(s string) (labels int) {
if s == "." {
return
}
off := 0
end := false
for {
off, end = NextLabel(s, off)
labels++
if end {
return
}
}
}
// Split splits a name s into its label indexes.
// www.miek.nl. returns []int{0, 4, 9}, www.miek.nl also returns []int{0, 4, 9}.
// The root name (.) returns nil. Also see SplitDomainName.
// s must be a syntactically valid domain name.
func Split(s string) []int {
if s == "." {
return nil
}
idx := make([]int, 1, 3)
off := 0
end := false
for {
off, end = NextLabel(s, off)
if end {
return idx
}
idx = append(idx, off)
}
}
// NextLabel returns the index of the start of the next label in the
// string s starting at offset.
// The bool end is true when the end of the string has been reached.
// Also see PrevLabel.
func NextLabel(s string, offset int) (i int, end bool) {
quote := false
for i = offset; i < len(s)-1; i++ {
switch s[i] {
case '\\':
quote = !quote
default:
quote = false
case '.':
if quote {
quote = !quote
continue
}
return i + 1, false
}
}
return i + 1, true
}
// PrevLabel returns the index of the label when starting from the right and
// jumping n labels to the left.
// The bool start is true when the start of the string has been overshot.
// Also see NextLabel.
func PrevLabel(s string, n int) (i int, start bool) {
if n == 0 {
return len(s), false
}
lab := Split(s)
if lab == nil {
return 0, true
}
if n > len(lab) {
return 0, true
}
return lab[len(lab)-n], false
}

View file

@ -1,203 +0,0 @@
package dns
import "testing"
func TestCompareDomainName(t *testing.T) {
s1 := "www.miek.nl."
s2 := "miek.nl."
s3 := "www.bla.nl."
s4 := "nl.www.bla."
s5 := "nl"
s6 := "miek.nl"
if CompareDomainName(s1, s2) != 2 {
t.Errorf("%s with %s should be %d", s1, s2, 2)
}
if CompareDomainName(s1, s3) != 1 {
t.Errorf("%s with %s should be %d", s1, s3, 1)
}
if CompareDomainName(s3, s4) != 0 {
t.Errorf("%s with %s should be %d", s3, s4, 0)
}
// Non qualified tests
if CompareDomainName(s1, s5) != 1 {
t.Errorf("%s with %s should be %d", s1, s5, 1)
}
if CompareDomainName(s1, s6) != 2 {
t.Errorf("%s with %s should be %d", s1, s5, 2)
}
if CompareDomainName(s1, ".") != 0 {
t.Errorf("%s with %s should be %d", s1, s5, 0)
}
if CompareDomainName(".", ".") != 0 {
t.Errorf("%s with %s should be %d", ".", ".", 0)
}
if CompareDomainName("test.com.", "TEST.COM.") != 2 {
t.Errorf("test.com. and TEST.COM. should be an exact match")
}
}
func TestSplit(t *testing.T) {
splitter := map[string]int{
"www.miek.nl.": 3,
"www.miek.nl": 3,
"www..miek.nl": 4,
`www\.miek.nl.`: 2,
`www\\.miek.nl.`: 3,
".": 0,
"nl.": 1,
"nl": 1,
"com.": 1,
".com.": 2,
}
for s, i := range splitter {
if x := len(Split(s)); x != i {
t.Errorf("labels should be %d, got %d: %s %v", i, x, s, Split(s))
} else {
t.Logf("%s %v", s, Split(s))
}
}
}
func TestSplit2(t *testing.T) {
splitter := map[string][]int{
"www.miek.nl.": {0, 4, 9},
"www.miek.nl": {0, 4, 9},
"nl": {0},
}
for s, i := range splitter {
x := Split(s)
switch len(i) {
case 1:
if x[0] != i[0] {
t.Errorf("labels should be %v, got %v: %s", i, x, s)
}
default:
if x[0] != i[0] || x[1] != i[1] || x[2] != i[2] {
t.Errorf("labels should be %v, got %v: %s", i, x, s)
}
}
}
}
func TestPrevLabel(t *testing.T) {
type prev struct {
string
int
}
prever := map[prev]int{
prev{"www.miek.nl.", 0}: 12,
prev{"www.miek.nl.", 1}: 9,
prev{"www.miek.nl.", 2}: 4,
prev{"www.miek.nl", 0}: 11,
prev{"www.miek.nl", 1}: 9,
prev{"www.miek.nl", 2}: 4,
prev{"www.miek.nl.", 5}: 0,
prev{"www.miek.nl", 5}: 0,
prev{"www.miek.nl.", 3}: 0,
prev{"www.miek.nl", 3}: 0,
}
for s, i := range prever {
x, ok := PrevLabel(s.string, s.int)
if i != x {
t.Errorf("label should be %d, got %d, %t: preving %d, %s", i, x, ok, s.int, s.string)
}
}
}
func TestCountLabel(t *testing.T) {
splitter := map[string]int{
"www.miek.nl.": 3,
"www.miek.nl": 3,
"nl": 1,
".": 0,
}
for s, i := range splitter {
x := CountLabel(s)
if x != i {
t.Errorf("CountLabel should have %d, got %d", i, x)
}
}
}
func TestSplitDomainName(t *testing.T) {
labels := map[string][]string{
"miek.nl": {"miek", "nl"},
".": nil,
"www.miek.nl.": {"www", "miek", "nl"},
"www.miek.nl": {"www", "miek", "nl"},
"www..miek.nl": {"www", "", "miek", "nl"},
`www\.miek.nl`: {`www\.miek`, "nl"},
`www\\.miek.nl`: {`www\\`, "miek", "nl"},
".www.miek.nl.": {"", "www", "miek", "nl"},
}
domainLoop:
for domain, splits := range labels {
parts := SplitDomainName(domain)
if len(parts) != len(splits) {
t.Errorf("SplitDomainName returned %v for %s, expected %v", parts, domain, splits)
continue domainLoop
}
for i := range parts {
if parts[i] != splits[i] {
t.Errorf("SplitDomainName returned %v for %s, expected %v", parts, domain, splits)
continue domainLoop
}
}
}
}
func TestIsDomainName(t *testing.T) {
type ret struct {
ok bool
lab int
}
names := map[string]*ret{
"..": {false, 1},
"@.": {true, 1},
"www.example.com": {true, 3},
"www.e%ample.com": {true, 3},
"www.example.com.": {true, 3},
"mi\\k.nl.": {true, 2},
"mi\\k.nl": {true, 2},
}
for d, ok := range names {
l, k := IsDomainName(d)
if ok.ok != k || ok.lab != l {
t.Errorf(" got %v %d for %s ", k, l, d)
t.Errorf("have %v %d for %s ", ok.ok, ok.lab, d)
}
}
}
func BenchmarkSplitLabels(b *testing.B) {
for i := 0; i < b.N; i++ {
Split("www.example.com")
}
}
func BenchmarkLenLabels(b *testing.B) {
for i := 0; i < b.N; i++ {
CountLabel("www.example.com")
}
}
func BenchmarkCompareLabels(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
CompareDomainName("www.example.com", "aa.example.com")
}
}
func BenchmarkIsSubDomain(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
IsSubDomain("www.example.com", "aa.example.com")
IsSubDomain("example.com", "aa.example.com")
IsSubDomain("miek.nl", "aa.example.com")
}
}

1159
vendor/github.com/miekg/dns/msg.go generated vendored

File diff suppressed because it is too large Load diff

View file

@ -1,349 +0,0 @@
//+build ignore
// msg_generate.go is meant to run with go generate. It will use
// go/{importer,types} to track down all the RR struct types. Then for each type
// it will generate pack/unpack methods based on the struct tags. The generated source is
// written to zmsg.go, and is meant to be checked into git.
package main
import (
"bytes"
"fmt"
"go/format"
"go/importer"
"go/types"
"log"
"os"
"strings"
)
var packageHdr = `
// *** DO NOT MODIFY ***
// AUTOGENERATED BY go generate from msg_generate.go
package dns
`
// getTypeStruct will take a type and the package scope, and return the
// (innermost) struct if the type is considered a RR type (currently defined as
// those structs beginning with a RR_Header, could be redefined as implementing
// the RR interface). The bool return value indicates if embedded structs were
// resolved.
func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) {
st, ok := t.Underlying().(*types.Struct)
if !ok {
return nil, false
}
if st.Field(0).Type() == scope.Lookup("RR_Header").Type() {
return st, false
}
if st.Field(0).Anonymous() {
st, _ := getTypeStruct(st.Field(0).Type(), scope)
return st, true
}
return nil, false
}
func main() {
// Import and type-check the package
pkg, err := importer.Default().Import("github.com/miekg/dns")
fatalIfErr(err)
scope := pkg.Scope()
// Collect actual types (*X)
var namedTypes []string
for _, name := range scope.Names() {
o := scope.Lookup(name)
if o == nil || !o.Exported() {
continue
}
if st, _ := getTypeStruct(o.Type(), scope); st == nil {
continue
}
if name == "PrivateRR" {
continue
}
// Check if corresponding TypeX exists
if scope.Lookup("Type"+o.Name()) == nil && o.Name() != "RFC3597" {
log.Fatalf("Constant Type%s does not exist.", o.Name())
}
namedTypes = append(namedTypes, o.Name())
}
b := &bytes.Buffer{}
b.WriteString(packageHdr)
fmt.Fprint(b, "// pack*() functions\n\n")
for _, name := range namedTypes {
o := scope.Lookup(name)
st, _ := getTypeStruct(o.Type(), scope)
fmt.Fprintf(b, "func (rr *%s) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) {\n", name)
fmt.Fprint(b, `off, err := rr.Hdr.pack(msg, off, compression, compress)
if err != nil {
return off, err
}
headerEnd := off
`)
for i := 1; i < st.NumFields(); i++ {
o := func(s string) {
fmt.Fprintf(b, s, st.Field(i).Name())
fmt.Fprint(b, `if err != nil {
return off, err
}
`)
}
if _, ok := st.Field(i).Type().(*types.Slice); ok {
switch st.Tag(i) {
case `dns:"-"`: // ignored
case `dns:"txt"`:
o("off, err = packStringTxt(rr.%s, msg, off)\n")
case `dns:"opt"`:
o("off, err = packDataOpt(rr.%s, msg, off)\n")
case `dns:"nsec"`:
o("off, err = packDataNsec(rr.%s, msg, off)\n")
case `dns:"domain-name"`:
o("off, err = packDataDomainNames(rr.%s, msg, off, compression, compress)\n")
default:
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
}
continue
}
switch {
case st.Tag(i) == `dns:"-"`: // ignored
case st.Tag(i) == `dns:"cdomain-name"`:
o("off, err = PackDomainName(rr.%s, msg, off, compression, compress)\n")
case st.Tag(i) == `dns:"domain-name"`:
o("off, err = PackDomainName(rr.%s, msg, off, compression, false)\n")
case st.Tag(i) == `dns:"a"`:
o("off, err = packDataA(rr.%s, msg, off)\n")
case st.Tag(i) == `dns:"aaaa"`:
o("off, err = packDataAAAA(rr.%s, msg, off)\n")
case st.Tag(i) == `dns:"uint48"`:
o("off, err = packUint48(rr.%s, msg, off)\n")
case st.Tag(i) == `dns:"txt"`:
o("off, err = packString(rr.%s, msg, off)\n")
case strings.HasPrefix(st.Tag(i), `dns:"size-base32`): // size-base32 can be packed just like base32
fallthrough
case st.Tag(i) == `dns:"base32"`:
o("off, err = packStringBase32(rr.%s, msg, off)\n")
case strings.HasPrefix(st.Tag(i), `dns:"size-base64`): // size-base64 can be packed just like base64
fallthrough
case st.Tag(i) == `dns:"base64"`:
o("off, err = packStringBase64(rr.%s, msg, off)\n")
case strings.HasPrefix(st.Tag(i), `dns:"size-hex:SaltLength`):
// directly write instead of using o() so we get the error check in the correct place
field := st.Field(i).Name()
fmt.Fprintf(b, `// Only pack salt if value is not "-", i.e. empty
if rr.%s != "-" {
off, err = packStringHex(rr.%s, msg, off)
if err != nil {
return off, err
}
}
`, field, field)
continue
case strings.HasPrefix(st.Tag(i), `dns:"size-hex`): // size-hex can be packed just like hex
fallthrough
case st.Tag(i) == `dns:"hex"`:
o("off, err = packStringHex(rr.%s, msg, off)\n")
case st.Tag(i) == `dns:"octet"`:
o("off, err = packStringOctet(rr.%s, msg, off)\n")
case st.Tag(i) == "":
switch st.Field(i).Type().(*types.Basic).Kind() {
case types.Uint8:
o("off, err = packUint8(rr.%s, msg, off)\n")
case types.Uint16:
o("off, err = packUint16(rr.%s, msg, off)\n")
case types.Uint32:
o("off, err = packUint32(rr.%s, msg, off)\n")
case types.Uint64:
o("off, err = packUint64(rr.%s, msg, off)\n")
case types.String:
o("off, err = packString(rr.%s, msg, off)\n")
default:
log.Fatalln(name, st.Field(i).Name())
}
default:
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
}
}
// We have packed everything, only now we know the rdlength of this RR
fmt.Fprintln(b, "rr.Header().Rdlength = uint16(off-headerEnd)")
fmt.Fprintln(b, "return off, nil }\n")
}
fmt.Fprint(b, "// unpack*() functions\n\n")
for _, name := range namedTypes {
o := scope.Lookup(name)
st, _ := getTypeStruct(o.Type(), scope)
fmt.Fprintf(b, "func unpack%s(h RR_Header, msg []byte, off int) (RR, int, error) {\n", name)
fmt.Fprintf(b, "rr := new(%s)\n", name)
fmt.Fprint(b, "rr.Hdr = h\n")
fmt.Fprint(b, `if noRdata(h) {
return rr, off, nil
}
var err error
rdStart := off
_ = rdStart
`)
for i := 1; i < st.NumFields(); i++ {
o := func(s string) {
fmt.Fprintf(b, s, st.Field(i).Name())
fmt.Fprint(b, `if err != nil {
return rr, off, err
}
`)
}
// size-* are special, because they reference a struct member we should use for the length.
if strings.HasPrefix(st.Tag(i), `dns:"size-`) {
structMember := structMember(st.Tag(i))
structTag := structTag(st.Tag(i))
switch structTag {
case "hex":
fmt.Fprintf(b, "rr.%s, off, err = unpackStringHex(msg, off, off + int(rr.%s))\n", st.Field(i).Name(), structMember)
case "base32":
fmt.Fprintf(b, "rr.%s, off, err = unpackStringBase32(msg, off, off + int(rr.%s))\n", st.Field(i).Name(), structMember)
case "base64":
fmt.Fprintf(b, "rr.%s, off, err = unpackStringBase64(msg, off, off + int(rr.%s))\n", st.Field(i).Name(), structMember)
default:
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
}
fmt.Fprint(b, `if err != nil {
return rr, off, err
}
`)
continue
}
if _, ok := st.Field(i).Type().(*types.Slice); ok {
switch st.Tag(i) {
case `dns:"-"`: // ignored
case `dns:"txt"`:
o("rr.%s, off, err = unpackStringTxt(msg, off)\n")
case `dns:"opt"`:
o("rr.%s, off, err = unpackDataOpt(msg, off)\n")
case `dns:"nsec"`:
o("rr.%s, off, err = unpackDataNsec(msg, off)\n")
case `dns:"domain-name"`:
o("rr.%s, off, err = unpackDataDomainNames(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
default:
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
}
continue
}
switch st.Tag(i) {
case `dns:"-"`: // ignored
case `dns:"cdomain-name"`:
fallthrough
case `dns:"domain-name"`:
o("rr.%s, off, err = UnpackDomainName(msg, off)\n")
case `dns:"a"`:
o("rr.%s, off, err = unpackDataA(msg, off)\n")
case `dns:"aaaa"`:
o("rr.%s, off, err = unpackDataAAAA(msg, off)\n")
case `dns:"uint48"`:
o("rr.%s, off, err = unpackUint48(msg, off)\n")
case `dns:"txt"`:
o("rr.%s, off, err = unpackString(msg, off)\n")
case `dns:"base32"`:
o("rr.%s, off, err = unpackStringBase32(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
case `dns:"base64"`:
o("rr.%s, off, err = unpackStringBase64(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
case `dns:"hex"`:
o("rr.%s, off, err = unpackStringHex(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
case `dns:"octet"`:
o("rr.%s, off, err = unpackStringOctet(msg, off)\n")
case "":
switch st.Field(i).Type().(*types.Basic).Kind() {
case types.Uint8:
o("rr.%s, off, err = unpackUint8(msg, off)\n")
case types.Uint16:
o("rr.%s, off, err = unpackUint16(msg, off)\n")
case types.Uint32:
o("rr.%s, off, err = unpackUint32(msg, off)\n")
case types.Uint64:
o("rr.%s, off, err = unpackUint64(msg, off)\n")
case types.String:
o("rr.%s, off, err = unpackString(msg, off)\n")
default:
log.Fatalln(name, st.Field(i).Name())
}
default:
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
}
// If we've hit len(msg) we return without error.
if i < st.NumFields()-1 {
fmt.Fprintf(b, `if off == len(msg) {
return rr, off, nil
}
`)
}
}
fmt.Fprintf(b, "return rr, off, err }\n\n")
}
// Generate typeToUnpack map
fmt.Fprintln(b, "var typeToUnpack = map[uint16]func(RR_Header, []byte, int) (RR, int, error){")
for _, name := range namedTypes {
if name == "RFC3597" {
continue
}
fmt.Fprintf(b, "Type%s: unpack%s,\n", name, name)
}
fmt.Fprintln(b, "}\n")
// gofmt
res, err := format.Source(b.Bytes())
if err != nil {
b.WriteTo(os.Stderr)
log.Fatal(err)
}
// write result
f, err := os.Create("zmsg.go")
fatalIfErr(err)
defer f.Close()
f.Write(res)
}
// structMember will take a tag like dns:"size-base32:SaltLength" and return the last part of this string.
func structMember(s string) string {
fields := strings.Split(s, ":")
if len(fields) == 0 {
return ""
}
f := fields[len(fields)-1]
// f should have a closing "
if len(f) > 1 {
return f[:len(f)-1]
}
return f
}
// structTag will take a tag like dns:"size-base32:SaltLength" and return base32.
func structTag(s string) string {
fields := strings.Split(s, ":")
if len(fields) < 2 {
return ""
}
return fields[1][len("\"size-"):]
}
func fatalIfErr(err error) {
if err != nil {
log.Fatal(err)
}
}

View file

@ -1,633 +0,0 @@
package dns
import (
"encoding/base32"
"encoding/base64"
"encoding/binary"
"encoding/hex"
"net"
"strconv"
)
// helper functions called from the generated zmsg.go
// These function are named after the tag to help pack/unpack, if there is no tag it is the name
// of the type they pack/unpack (string, int, etc). We prefix all with unpackData or packData, so packDataA or
// packDataDomainName.
func unpackDataA(msg []byte, off int) (net.IP, int, error) {
if off+net.IPv4len > len(msg) {
return nil, len(msg), &Error{err: "overflow unpacking a"}
}
a := append(make(net.IP, 0, net.IPv4len), msg[off:off+net.IPv4len]...)
off += net.IPv4len
return a, off, nil
}
func packDataA(a net.IP, msg []byte, off int) (int, error) {
// It must be a slice of 4, even if it is 16, we encode only the first 4
if off+net.IPv4len > len(msg) {
return len(msg), &Error{err: "overflow packing a"}
}
switch len(a) {
case net.IPv4len, net.IPv6len:
copy(msg[off:], a.To4())
off += net.IPv4len
case 0:
// Allowed, for dynamic updates.
default:
return len(msg), &Error{err: "overflow packing a"}
}
return off, nil
}
func unpackDataAAAA(msg []byte, off int) (net.IP, int, error) {
if off+net.IPv6len > len(msg) {
return nil, len(msg), &Error{err: "overflow unpacking aaaa"}
}
aaaa := append(make(net.IP, 0, net.IPv6len), msg[off:off+net.IPv6len]...)
off += net.IPv6len
return aaaa, off, nil
}
func packDataAAAA(aaaa net.IP, msg []byte, off int) (int, error) {
if off+net.IPv6len > len(msg) {
return len(msg), &Error{err: "overflow packing aaaa"}
}
switch len(aaaa) {
case net.IPv6len:
copy(msg[off:], aaaa)
off += net.IPv6len
case 0:
// Allowed, dynamic updates.
default:
return len(msg), &Error{err: "overflow packing aaaa"}
}
return off, nil
}
// unpackHeader unpacks an RR header, returning the offset to the end of the header and a
// re-sliced msg according to the expected length of the RR.
func unpackHeader(msg []byte, off int) (rr RR_Header, off1 int, truncmsg []byte, err error) {
hdr := RR_Header{}
if off == len(msg) {
return hdr, off, msg, nil
}
hdr.Name, off, err = UnpackDomainName(msg, off)
if err != nil {
return hdr, len(msg), msg, err
}
hdr.Rrtype, off, err = unpackUint16(msg, off)
if err != nil {
return hdr, len(msg), msg, err
}
hdr.Class, off, err = unpackUint16(msg, off)
if err != nil {
return hdr, len(msg), msg, err
}
hdr.Ttl, off, err = unpackUint32(msg, off)
if err != nil {
return hdr, len(msg), msg, err
}
hdr.Rdlength, off, err = unpackUint16(msg, off)
if err != nil {
return hdr, len(msg), msg, err
}
msg, err = truncateMsgFromRdlength(msg, off, hdr.Rdlength)
return hdr, off, msg, nil
}
// pack packs an RR header, returning the offset to the end of the header.
// See PackDomainName for documentation about the compression.
func (hdr RR_Header) pack(msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) {
if off == len(msg) {
return off, nil
}
off, err = PackDomainName(hdr.Name, msg, off, compression, compress)
if err != nil {
return len(msg), err
}
off, err = packUint16(hdr.Rrtype, msg, off)
if err != nil {
return len(msg), err
}
off, err = packUint16(hdr.Class, msg, off)
if err != nil {
return len(msg), err
}
off, err = packUint32(hdr.Ttl, msg, off)
if err != nil {
return len(msg), err
}
off, err = packUint16(hdr.Rdlength, msg, off)
if err != nil {
return len(msg), err
}
return off, nil
}
// helper helper functions.
// truncateMsgFromRdLength truncates msg to match the expected length of the RR.
// Returns an error if msg is smaller than the expected size.
func truncateMsgFromRdlength(msg []byte, off int, rdlength uint16) (truncmsg []byte, err error) {
lenrd := off + int(rdlength)
if lenrd > len(msg) {
return msg, &Error{err: "overflowing header size"}
}
return msg[:lenrd], nil
}
func fromBase32(s []byte) (buf []byte, err error) {
for i, b := range s {
if b >= 'a' && b <= 'z' {
s[i] = b - 32
}
}
buflen := base32.HexEncoding.DecodedLen(len(s))
buf = make([]byte, buflen)
n, err := base32.HexEncoding.Decode(buf, s)
buf = buf[:n]
return
}
func toBase32(b []byte) string { return base32.HexEncoding.EncodeToString(b) }
func fromBase64(s []byte) (buf []byte, err error) {
buflen := base64.StdEncoding.DecodedLen(len(s))
buf = make([]byte, buflen)
n, err := base64.StdEncoding.Decode(buf, s)
buf = buf[:n]
return
}
func toBase64(b []byte) string { return base64.StdEncoding.EncodeToString(b) }
// dynamicUpdate returns true if the Rdlength is zero.
func noRdata(h RR_Header) bool { return h.Rdlength == 0 }
func unpackUint8(msg []byte, off int) (i uint8, off1 int, err error) {
if off+1 > len(msg) {
return 0, len(msg), &Error{err: "overflow unpacking uint8"}
}
return uint8(msg[off]), off + 1, nil
}
func packUint8(i uint8, msg []byte, off int) (off1 int, err error) {
if off+1 > len(msg) {
return len(msg), &Error{err: "overflow packing uint8"}
}
msg[off] = byte(i)
return off + 1, nil
}
func unpackUint16(msg []byte, off int) (i uint16, off1 int, err error) {
if off+2 > len(msg) {
return 0, len(msg), &Error{err: "overflow unpacking uint16"}
}
return binary.BigEndian.Uint16(msg[off:]), off + 2, nil
}
func packUint16(i uint16, msg []byte, off int) (off1 int, err error) {
if off+2 > len(msg) {
return len(msg), &Error{err: "overflow packing uint16"}
}
binary.BigEndian.PutUint16(msg[off:], i)
return off + 2, nil
}
func unpackUint32(msg []byte, off int) (i uint32, off1 int, err error) {
if off+4 > len(msg) {
return 0, len(msg), &Error{err: "overflow unpacking uint32"}
}
return binary.BigEndian.Uint32(msg[off:]), off + 4, nil
}
func packUint32(i uint32, msg []byte, off int) (off1 int, err error) {
if off+4 > len(msg) {
return len(msg), &Error{err: "overflow packing uint32"}
}
binary.BigEndian.PutUint32(msg[off:], i)
return off + 4, nil
}
func unpackUint48(msg []byte, off int) (i uint64, off1 int, err error) {
if off+6 > len(msg) {
return 0, len(msg), &Error{err: "overflow unpacking uint64 as uint48"}
}
// Used in TSIG where the last 48 bits are occupied, so for now, assume a uint48 (6 bytes)
i = (uint64(uint64(msg[off])<<40 | uint64(msg[off+1])<<32 | uint64(msg[off+2])<<24 | uint64(msg[off+3])<<16 |
uint64(msg[off+4])<<8 | uint64(msg[off+5])))
off += 6
return i, off, nil
}
func packUint48(i uint64, msg []byte, off int) (off1 int, err error) {
if off+6 > len(msg) {
return len(msg), &Error{err: "overflow packing uint64 as uint48"}
}
msg[off] = byte(i >> 40)
msg[off+1] = byte(i >> 32)
msg[off+2] = byte(i >> 24)
msg[off+3] = byte(i >> 16)
msg[off+4] = byte(i >> 8)
msg[off+5] = byte(i)
off += 6
return off, nil
}
func unpackUint64(msg []byte, off int) (i uint64, off1 int, err error) {
if off+8 > len(msg) {
return 0, len(msg), &Error{err: "overflow unpacking uint64"}
}
return binary.BigEndian.Uint64(msg[off:]), off + 8, nil
}
func packUint64(i uint64, msg []byte, off int) (off1 int, err error) {
if off+8 > len(msg) {
return len(msg), &Error{err: "overflow packing uint64"}
}
binary.BigEndian.PutUint64(msg[off:], i)
off += 8
return off, nil
}
func unpackString(msg []byte, off int) (string, int, error) {
if off+1 > len(msg) {
return "", off, &Error{err: "overflow unpacking txt"}
}
l := int(msg[off])
if off+l+1 > len(msg) {
return "", off, &Error{err: "overflow unpacking txt"}
}
s := make([]byte, 0, l)
for _, b := range msg[off+1 : off+1+l] {
switch b {
case '"', '\\':
s = append(s, '\\', b)
default:
if b < 32 || b > 127 { // unprintable
var buf [3]byte
bufs := strconv.AppendInt(buf[:0], int64(b), 10)
s = append(s, '\\')
for i := 0; i < 3-len(bufs); i++ {
s = append(s, '0')
}
for _, r := range bufs {
s = append(s, r)
}
} else {
s = append(s, b)
}
}
}
off += 1 + l
return string(s), off, nil
}
func packString(s string, msg []byte, off int) (int, error) {
txtTmp := make([]byte, 256*4+1)
off, err := packTxtString(s, msg, off, txtTmp)
if err != nil {
return len(msg), err
}
return off, nil
}
func unpackStringBase32(msg []byte, off, end int) (string, int, error) {
if end > len(msg) {
return "", len(msg), &Error{err: "overflow unpacking base32"}
}
s := toBase32(msg[off:end])
return s, end, nil
}
func packStringBase32(s string, msg []byte, off int) (int, error) {
b32, err := fromBase32([]byte(s))
if err != nil {
return len(msg), err
}
if off+len(b32) > len(msg) {
return len(msg), &Error{err: "overflow packing base32"}
}
copy(msg[off:off+len(b32)], b32)
off += len(b32)
return off, nil
}
func unpackStringBase64(msg []byte, off, end int) (string, int, error) {
// Rest of the RR is base64 encoded value, so we don't need an explicit length
// to be set. Thus far all RR's that have base64 encoded fields have those as their
// last one. What we do need is the end of the RR!
if end > len(msg) {
return "", len(msg), &Error{err: "overflow unpacking base64"}
}
s := toBase64(msg[off:end])
return s, end, nil
}
func packStringBase64(s string, msg []byte, off int) (int, error) {
b64, err := fromBase64([]byte(s))
if err != nil {
return len(msg), err
}
if off+len(b64) > len(msg) {
return len(msg), &Error{err: "overflow packing base64"}
}
copy(msg[off:off+len(b64)], b64)
off += len(b64)
return off, nil
}
func unpackStringHex(msg []byte, off, end int) (string, int, error) {
// Rest of the RR is hex encoded value, so we don't need an explicit length
// to be set. NSEC and TSIG have hex fields with a length field.
// What we do need is the end of the RR!
if end > len(msg) {
return "", len(msg), &Error{err: "overflow unpacking hex"}
}
s := hex.EncodeToString(msg[off:end])
return s, end, nil
}
func packStringHex(s string, msg []byte, off int) (int, error) {
h, err := hex.DecodeString(s)
if err != nil {
return len(msg), err
}
if off+(len(h)) > len(msg) {
return len(msg), &Error{err: "overflow packing hex"}
}
copy(msg[off:off+len(h)], h)
off += len(h)
return off, nil
}
func unpackStringTxt(msg []byte, off int) ([]string, int, error) {
txt, off, err := unpackTxt(msg, off)
if err != nil {
return nil, len(msg), err
}
return txt, off, nil
}
func packStringTxt(s []string, msg []byte, off int) (int, error) {
txtTmp := make([]byte, 256*4+1) // If the whole string consists out of \DDD we need this many.
off, err := packTxt(s, msg, off, txtTmp)
if err != nil {
return len(msg), err
}
return off, nil
}
func unpackDataOpt(msg []byte, off int) ([]EDNS0, int, error) {
var edns []EDNS0
Option:
code := uint16(0)
if off+4 > len(msg) {
return nil, len(msg), &Error{err: "overflow unpacking opt"}
}
code = binary.BigEndian.Uint16(msg[off:])
off += 2
optlen := binary.BigEndian.Uint16(msg[off:])
off += 2
if off+int(optlen) > len(msg) {
return nil, len(msg), &Error{err: "overflow unpacking opt"}
}
switch code {
case EDNS0NSID:
e := new(EDNS0_NSID)
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
return nil, len(msg), err
}
edns = append(edns, e)
off += int(optlen)
case EDNS0SUBNET, EDNS0SUBNETDRAFT:
e := new(EDNS0_SUBNET)
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
return nil, len(msg), err
}
edns = append(edns, e)
off += int(optlen)
if code == EDNS0SUBNETDRAFT {
e.DraftOption = true
}
case EDNS0COOKIE:
e := new(EDNS0_COOKIE)
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
return nil, len(msg), err
}
edns = append(edns, e)
off += int(optlen)
case EDNS0UL:
e := new(EDNS0_UL)
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
return nil, len(msg), err
}
edns = append(edns, e)
off += int(optlen)
case EDNS0LLQ:
e := new(EDNS0_LLQ)
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
return nil, len(msg), err
}
edns = append(edns, e)
off += int(optlen)
case EDNS0DAU:
e := new(EDNS0_DAU)
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
return nil, len(msg), err
}
edns = append(edns, e)
off += int(optlen)
case EDNS0DHU:
e := new(EDNS0_DHU)
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
return nil, len(msg), err
}
edns = append(edns, e)
off += int(optlen)
case EDNS0N3U:
e := new(EDNS0_N3U)
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
return nil, len(msg), err
}
edns = append(edns, e)
off += int(optlen)
default:
e := new(EDNS0_LOCAL)
e.Code = code
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
return nil, len(msg), err
}
edns = append(edns, e)
off += int(optlen)
}
if off < len(msg) {
goto Option
}
return edns, off, nil
}
func packDataOpt(options []EDNS0, msg []byte, off int) (int, error) {
for _, el := range options {
b, err := el.pack()
if err != nil || off+3 > len(msg) {
return len(msg), &Error{err: "overflow packing opt"}
}
binary.BigEndian.PutUint16(msg[off:], el.Option()) // Option code
binary.BigEndian.PutUint16(msg[off+2:], uint16(len(b))) // Length
off += 4
if off+len(b) > len(msg) {
copy(msg[off:], b)
off = len(msg)
continue
}
// Actual data
copy(msg[off:off+len(b)], b)
off += len(b)
}
return off, nil
}
func unpackStringOctet(msg []byte, off int) (string, int, error) {
s := string(msg[off:])
return s, len(msg), nil
}
func packStringOctet(s string, msg []byte, off int) (int, error) {
txtTmp := make([]byte, 256*4+1)
off, err := packOctetString(s, msg, off, txtTmp)
if err != nil {
return len(msg), err
}
return off, nil
}
func unpackDataNsec(msg []byte, off int) ([]uint16, int, error) {
var nsec []uint16
length, window, lastwindow := 0, 0, -1
for off < len(msg) {
if off+2 > len(msg) {
return nsec, len(msg), &Error{err: "overflow unpacking nsecx"}
}
window = int(msg[off])
length = int(msg[off+1])
off += 2
if window <= lastwindow {
// RFC 4034: Blocks are present in the NSEC RR RDATA in
// increasing numerical order.
return nsec, len(msg), &Error{err: "out of order NSEC block"}
}
if length == 0 {
// RFC 4034: Blocks with no types present MUST NOT be included.
return nsec, len(msg), &Error{err: "empty NSEC block"}
}
if length > 32 {
return nsec, len(msg), &Error{err: "NSEC block too long"}
}
if off+length > len(msg) {
return nsec, len(msg), &Error{err: "overflowing NSEC block"}
}
// Walk the bytes in the window and extract the type bits
for j := 0; j < length; j++ {
b := msg[off+j]
// Check the bits one by one, and set the type
if b&0x80 == 0x80 {
nsec = append(nsec, uint16(window*256+j*8+0))
}
if b&0x40 == 0x40 {
nsec = append(nsec, uint16(window*256+j*8+1))
}
if b&0x20 == 0x20 {
nsec = append(nsec, uint16(window*256+j*8+2))
}
if b&0x10 == 0x10 {
nsec = append(nsec, uint16(window*256+j*8+3))
}
if b&0x8 == 0x8 {
nsec = append(nsec, uint16(window*256+j*8+4))
}
if b&0x4 == 0x4 {
nsec = append(nsec, uint16(window*256+j*8+5))
}
if b&0x2 == 0x2 {
nsec = append(nsec, uint16(window*256+j*8+6))
}
if b&0x1 == 0x1 {
nsec = append(nsec, uint16(window*256+j*8+7))
}
}
off += length
lastwindow = window
}
return nsec, off, nil
}
func packDataNsec(bitmap []uint16, msg []byte, off int) (int, error) {
if len(bitmap) == 0 {
return off, nil
}
var lastwindow, lastlength uint16
for j := 0; j < len(bitmap); j++ {
t := bitmap[j]
window := t / 256
length := (t-window*256)/8 + 1
if window > lastwindow && lastlength != 0 { // New window, jump to the new offset
off += int(lastlength) + 2
lastlength = 0
}
if window < lastwindow || length < lastlength {
return len(msg), &Error{err: "nsec bits out of order"}
}
if off+2+int(length) > len(msg) {
return len(msg), &Error{err: "overflow packing nsec"}
}
// Setting the window #
msg[off] = byte(window)
// Setting the octets length
msg[off+1] = byte(length)
// Setting the bit value for the type in the right octet
msg[off+1+int(length)] |= byte(1 << (7 - (t % 8)))
lastwindow, lastlength = window, length
}
off += int(lastlength) + 2
return off, nil
}
func unpackDataDomainNames(msg []byte, off, end int) ([]string, int, error) {
var (
servers []string
s string
err error
)
if end > len(msg) {
return nil, len(msg), &Error{err: "overflow unpacking domain names"}
}
for off < end {
s, off, err = UnpackDomainName(msg, off)
if err != nil {
return servers, len(msg), err
}
servers = append(servers, s)
}
return servers, off, nil
}
func packDataDomainNames(names []string, msg []byte, off int, compression map[string]int, compress bool) (int, error) {
var err error
for j := 0; j < len(names); j++ {
off, err = PackDomainName(names[j], msg, off, compression, false && compress)
if err != nil {
return len(msg), err
}
}
return off, nil
}

View file

@ -1,124 +0,0 @@
package dns
import (
"fmt"
"regexp"
"strconv"
"strings"
"testing"
)
const (
maxPrintableLabel = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789x"
tooLongLabel = maxPrintableLabel + "x"
)
var (
longDomain = maxPrintableLabel[:53] + strings.TrimSuffix(
strings.Join([]string{".", ".", ".", ".", "."}, maxPrintableLabel[:49]), ".")
reChar = regexp.MustCompile(`.`)
i = -1
maxUnprintableLabel = reChar.ReplaceAllStringFunc(maxPrintableLabel, func(ch string) string {
if i++; i >= 32 {
i = 0
}
return fmt.Sprintf("\\%03d", i)
})
)
func TestUnpackDomainName(t *testing.T) {
var cases = []struct {
label string
input string
expectedOutput string
expectedError string
}{
{"empty domain",
"\x00",
".",
""},
{"long label",
string(63) + maxPrintableLabel + "\x00",
maxPrintableLabel + ".",
""},
{"unprintable label",
string(63) + regexp.MustCompile(`\\[0-9]+`).ReplaceAllStringFunc(maxUnprintableLabel,
func(escape string) string {
n, _ := strconv.ParseInt(escape[1:], 10, 8)
return string(n)
}) + "\x00",
maxUnprintableLabel + ".",
""},
{"long domain",
string(53) + strings.Replace(longDomain, ".", string(49), -1) + "\x00",
longDomain + ".",
""},
{"compression pointer",
// an unrealistic but functional test referencing an offset _inside_ a label
"\x03foo" + "\x05\x03com\x00" + "\x07example" + "\xC0\x05",
"foo.\\003com\\000.example.com.",
""},
{"too long domain",
string(54) + "x" + strings.Replace(longDomain, ".", string(49), -1) + "\x00",
"x" + longDomain + ".",
ErrLongDomain.Error()},
{"too long by pointer",
// a matryoshka doll name to get over 255 octets after expansion via internal pointers
string([]byte{
// 11 length values, first to last
40, 37, 34, 31, 28, 25, 22, 19, 16, 13, 0,
// 12 filler values
120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120,
// 10 pointers, last to first
192, 10, 192, 9, 192, 8, 192, 7, 192, 6, 192, 5, 192, 4, 192, 3, 192, 2, 192, 1,
}),
"",
ErrLongDomain.Error()},
{"long by pointer",
// a matryoshka doll name _not_ exceeding 255 octets after expansion
string([]byte{
// 11 length values, first to last
37, 34, 31, 28, 25, 22, 19, 16, 13, 10, 0,
// 9 filler values
120, 120, 120, 120, 120, 120, 120, 120, 120,
// 10 pointers, last to first
192, 10, 192, 9, 192, 8, 192, 7, 192, 6, 192, 5, 192, 4, 192, 3, 192, 2, 192, 1,
}),
"" +
(`\"\031\028\025\022\019\016\013\010\000xxxxxxxxx` +
`\192\010\192\009\192\008\192\007\192\006\192\005\192\004\192\003\192\002.`) +
(`\031\028\025\022\019\016\013\010\000xxxxxxxxx` +
`\192\010\192\009\192\008\192\007\192\006\192\005\192\004\192\003.`) +
(`\028\025\022\019\016\013\010\000xxxxxxxxx` +
`\192\010\192\009\192\008\192\007\192\006\192\005\192\004.`) +
(`\025\022\019\016\013\010\000xxxxxxxxx` +
`\192\010\192\009\192\008\192\007\192\006\192\005.`) +
`\022\019\016\013\010\000xxxxxxxxx\192\010\192\009\192\008\192\007\192\006.` +
`\019\016\013\010\000xxxxxxxxx\192\010\192\009\192\008\192\007.` +
`\016\013\010\000xxxxxxxxx\192\010\192\009\192\008.` +
`\013\010\000xxxxxxxxx\192\010\192\009.` +
`\010\000xxxxxxxxx\192\010.` +
`\000xxxxxxxxx.`,
""},
{"truncated name", "\x07example\x03", "", "dns: buffer size too small"},
{"non-absolute name", "\x07example\x03com", "", "dns: buffer size too small"},
{"compression pointer cycle",
"\x03foo" + "\x03bar" + "\x07example" + "\xC0\x04",
"",
"dns: too many compression pointers"},
{"reserved compression pointer 0b10", "\x07example\x80", "", "dns: bad rdata"},
{"reserved compression pointer 0b01", "\x07example\x40", "", "dns: bad rdata"},
}
for _, test := range cases {
output, idx, err := UnpackDomainName([]byte(test.input), 0)
if test.expectedOutput != "" && output != test.expectedOutput {
t.Errorf("%s: expected %s, got %s", test.label, test.expectedOutput, output)
}
if test.expectedError == "" && err != nil {
t.Errorf("%s: expected no error, got %d %v", test.label, idx, err)
} else if test.expectedError != "" && (err == nil || err.Error() != test.expectedError) {
t.Errorf("%s: expected error %s, got %d %v", test.label, test.expectedError, idx, err)
}
}
}

106
vendor/github.com/miekg/dns/nsecx.go generated vendored
View file

@ -1,106 +0,0 @@
package dns
import (
"crypto/sha1"
"hash"
"strings"
)
type saltWireFmt struct {
Salt string `dns:"size-hex"`
}
// HashName hashes a string (label) according to RFC 5155. It returns the hashed string in uppercase.
func HashName(label string, ha uint8, iter uint16, salt string) string {
saltwire := new(saltWireFmt)
saltwire.Salt = salt
wire := make([]byte, DefaultMsgSize)
n, err := packSaltWire(saltwire, wire)
if err != nil {
return ""
}
wire = wire[:n]
name := make([]byte, 255)
off, err := PackDomainName(strings.ToLower(label), name, 0, nil, false)
if err != nil {
return ""
}
name = name[:off]
var s hash.Hash
switch ha {
case SHA1:
s = sha1.New()
default:
return ""
}
// k = 0
s.Write(name)
s.Write(wire)
nsec3 := s.Sum(nil)
// k > 0
for k := uint16(0); k < iter; k++ {
s.Reset()
s.Write(nsec3)
s.Write(wire)
nsec3 = s.Sum(nsec3[:0])
}
return toBase32(nsec3)
}
// Cover returns true if a name is covered by the NSEC3 record
func (rr *NSEC3) Cover(name string) bool {
nameHash := HashName(name, rr.Hash, rr.Iterations, rr.Salt)
owner := strings.ToUpper(rr.Hdr.Name)
labelIndices := Split(owner)
if len(labelIndices) < 2 {
return false
}
ownerHash := owner[:labelIndices[1]-1]
ownerZone := owner[labelIndices[1]:]
if !IsSubDomain(ownerZone, strings.ToUpper(name)) { // name is outside owner zone
return false
}
nextHash := rr.NextDomain
if ownerHash == nextHash { // empty interval
return false
}
if ownerHash > nextHash { // end of zone
if nameHash > ownerHash { // covered since there is nothing after ownerHash
return true
}
return nameHash < nextHash // if nameHash is before beginning of zone it is covered
}
if nameHash < ownerHash { // nameHash is before ownerHash, not covered
return false
}
return nameHash < nextHash // if nameHash is before nextHash is it covered (between ownerHash and nextHash)
}
// Match returns true if a name matches the NSEC3 record
func (rr *NSEC3) Match(name string) bool {
nameHash := HashName(name, rr.Hash, rr.Iterations, rr.Salt)
owner := strings.ToUpper(rr.Hdr.Name)
labelIndices := Split(owner)
if len(labelIndices) < 2 {
return false
}
ownerHash := owner[:labelIndices[1]-1]
ownerZone := owner[labelIndices[1]:]
if !IsSubDomain(ownerZone, strings.ToUpper(name)) { // name is outside owner zone
return false
}
if ownerHash == nameHash {
return true
}
return false
}
func packSaltWire(sw *saltWireFmt, msg []byte) (int, error) {
off, err := packStringHex(sw.Salt, msg, 0)
if err != nil {
return off, err
}
return off, nil
}

View file

@ -1,133 +0,0 @@
package dns
import "testing"
func TestPackNsec3(t *testing.T) {
nsec3 := HashName("dnsex.nl.", SHA1, 0, "DEAD")
if nsec3 != "ROCCJAE8BJJU7HN6T7NG3TNM8ACRS87J" {
t.Error(nsec3)
}
nsec3 = HashName("a.b.c.example.org.", SHA1, 2, "DEAD")
if nsec3 != "6LQ07OAHBTOOEU2R9ANI2AT70K5O0RCG" {
t.Error(nsec3)
}
}
func TestNsec3(t *testing.T) {
nsec3, _ := NewRR("sk4e8fj94u78smusb40o1n0oltbblu2r.nl. IN NSEC3 1 1 5 F10E9F7EA83FC8F3 SK4F38CQ0ATIEI8MH3RGD0P5I4II6QAN NS SOA TXT RRSIG DNSKEY NSEC3PARAM")
if !nsec3.(*NSEC3).Match("nl.") { // name hash = sk4e8fj94u78smusb40o1n0oltbblu2r
t.Fatal("sk4e8fj94u78smusb40o1n0oltbblu2r.nl. should match sk4e8fj94u78smusb40o1n0oltbblu2r.nl.")
}
if !nsec3.(*NSEC3).Match("NL.") { // name hash = sk4e8fj94u78smusb40o1n0oltbblu2r
t.Fatal("sk4e8fj94u78smusb40o1n0oltbblu2r.NL. should match sk4e8fj94u78smusb40o1n0oltbblu2r.nl.")
}
if nsec3.(*NSEC3).Match("com.") { //
t.Fatal("com. is not in the zone nl.")
}
if nsec3.(*NSEC3).Match("test.nl.") { // name hash = gd0ptr5bnfpimpu2d3v6gd4n0bai7s0q
t.Fatal("gd0ptr5bnfpimpu2d3v6gd4n0bai7s0q.nl. should not match sk4e8fj94u78smusb40o1n0oltbblu2r.nl.")
}
nsec3, _ = NewRR("nl. IN NSEC3 1 1 5 F10E9F7EA83FC8F3 SK4F38CQ0ATIEI8MH3RGD0P5I4II6QAN NS SOA TXT RRSIG DNSKEY NSEC3PARAM")
if nsec3.(*NSEC3).Match("nl.") {
t.Fatal("sk4e8fj94u78smusb40o1n0oltbblu2r.nl. should not match a record without a owner hash")
}
for _, tc := range []struct {
rr *NSEC3
name string
covers bool
}{
// positive tests
{ // name hash between owner hash and next hash
rr: &NSEC3{
Hdr: RR_Header{Name: "2N1TB3VAIRUOBL6RKDVII42N9TFMIALP.com."},
Hash: 1,
Flags: 1,
Iterations: 5,
Salt: "F10E9F7EA83FC8F3",
NextDomain: "PT3RON8N7PM3A0OE989IB84OOSADP7O8",
},
name: "bsd.com.",
covers: true,
},
{ // end of zone, name hash is after owner hash
rr: &NSEC3{
Hdr: RR_Header{Name: "3v62ulr0nre83v0rja2vjgtlif9v6rab.com."},
Hash: 1,
Flags: 1,
Iterations: 5,
Salt: "F10E9F7EA83FC8F3",
NextDomain: "2N1TB3VAIRUOBL6RKDVII42N9TFMIALP",
},
name: "csd.com.",
covers: true,
},
{ // end of zone, name hash is before beginning of zone
rr: &NSEC3{
Hdr: RR_Header{Name: "PT3RON8N7PM3A0OE989IB84OOSADP7O8.com."},
Hash: 1,
Flags: 1,
Iterations: 5,
Salt: "F10E9F7EA83FC8F3",
NextDomain: "3V62ULR0NRE83V0RJA2VJGTLIF9V6RAB",
},
name: "asd.com.",
covers: true,
},
// negative tests
{ // too short owner name
rr: &NSEC3{
Hdr: RR_Header{Name: "nl."},
Hash: 1,
Flags: 1,
Iterations: 5,
Salt: "F10E9F7EA83FC8F3",
NextDomain: "39P99DCGG0MDLARTCRMCF6OFLLUL7PR6",
},
name: "asd.com.",
covers: false,
},
{ // outside of zone
rr: &NSEC3{
Hdr: RR_Header{Name: "39p91242oslggest5e6a7cci4iaeqvnk.nl."},
Hash: 1,
Flags: 1,
Iterations: 5,
Salt: "F10E9F7EA83FC8F3",
NextDomain: "39P99DCGG0MDLARTCRMCF6OFLLUL7PR6",
},
name: "asd.com.",
covers: false,
},
{ // empty interval
rr: &NSEC3{
Hdr: RR_Header{Name: "2n1tb3vairuobl6rkdvii42n9tfmialp.com."},
Hash: 1,
Flags: 1,
Iterations: 5,
Salt: "F10E9F7EA83FC8F3",
NextDomain: "2N1TB3VAIRUOBL6RKDVII42N9TFMIALP",
},
name: "asd.com.",
covers: false,
},
{ // name hash is before owner hash, not covered
rr: &NSEC3{
Hdr: RR_Header{Name: "3V62ULR0NRE83V0RJA2VJGTLIF9V6RAB.com."},
Hash: 1,
Flags: 1,
Iterations: 5,
Salt: "F10E9F7EA83FC8F3",
NextDomain: "PT3RON8N7PM3A0OE989IB84OOSADP7O8",
},
name: "asd.com.",
covers: false,
},
} {
covers := tc.rr.Cover(tc.name)
if tc.covers != covers {
t.Fatalf("Cover failed for %s: expected %t, got %t [record: %s]", tc.name, tc.covers, covers, tc.rr)
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,149 +0,0 @@
package dns
import (
"fmt"
"strings"
)
// PrivateRdata is an interface used for implementing "Private Use" RR types, see
// RFC 6895. This allows one to experiment with new RR types, without requesting an
// official type code. Also see dns.PrivateHandle and dns.PrivateHandleRemove.
type PrivateRdata interface {
// String returns the text presentaton of the Rdata of the Private RR.
String() string
// Parse parses the Rdata of the private RR.
Parse([]string) error
// Pack is used when packing a private RR into a buffer.
Pack([]byte) (int, error)
// Unpack is used when unpacking a private RR from a buffer.
// TODO(miek): diff. signature than Pack, see edns0.go for instance.
Unpack([]byte) (int, error)
// Copy copies the Rdata.
Copy(PrivateRdata) error
// Len returns the length in octets of the Rdata.
Len() int
}
// PrivateRR represents an RR that uses a PrivateRdata user-defined type.
// It mocks normal RRs and implements dns.RR interface.
type PrivateRR struct {
Hdr RR_Header
Data PrivateRdata
}
func mkPrivateRR(rrtype uint16) *PrivateRR {
// Panics if RR is not an instance of PrivateRR.
rrfunc, ok := TypeToRR[rrtype]
if !ok {
panic(fmt.Sprintf("dns: invalid operation with Private RR type %d", rrtype))
}
anyrr := rrfunc()
switch rr := anyrr.(type) {
case *PrivateRR:
return rr
}
panic(fmt.Sprintf("dns: RR is not a PrivateRR, TypeToRR[%d] generator returned %T", rrtype, anyrr))
}
// Header return the RR header of r.
func (r *PrivateRR) Header() *RR_Header { return &r.Hdr }
func (r *PrivateRR) String() string { return r.Hdr.String() + r.Data.String() }
// Private len and copy parts to satisfy RR interface.
func (r *PrivateRR) len() int { return r.Hdr.len() + r.Data.Len() }
func (r *PrivateRR) copy() RR {
// make new RR like this:
rr := mkPrivateRR(r.Hdr.Rrtype)
newh := r.Hdr.copyHeader()
rr.Hdr = *newh
err := r.Data.Copy(rr.Data)
if err != nil {
panic("dns: got value that could not be used to copy Private rdata")
}
return rr
}
func (r *PrivateRR) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) {
off, err := r.Hdr.pack(msg, off, compression, compress)
if err != nil {
return off, err
}
headerEnd := off
n, err := r.Data.Pack(msg[off:])
if err != nil {
return len(msg), err
}
off += n
r.Header().Rdlength = uint16(off - headerEnd)
return off, nil
}
// PrivateHandle registers a private resource record type. It requires
// string and numeric representation of private RR type and generator function as argument.
func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata) {
rtypestr = strings.ToUpper(rtypestr)
TypeToRR[rtype] = func() RR { return &PrivateRR{RR_Header{}, generator()} }
TypeToString[rtype] = rtypestr
StringToType[rtypestr] = rtype
typeToUnpack[rtype] = func(h RR_Header, msg []byte, off int) (RR, int, error) {
if noRdata(h) {
return &h, off, nil
}
var err error
rr := mkPrivateRR(h.Rrtype)
rr.Hdr = h
off1, err := rr.Data.Unpack(msg[off:])
off += off1
if err != nil {
return rr, off, err
}
return rr, off, err
}
setPrivateRR := func(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
rr := mkPrivateRR(h.Rrtype)
rr.Hdr = h
var l lex
text := make([]string, 0, 2) // could be 0..N elements, median is probably 1
Fetch:
for {
// TODO(miek): we could also be returning _QUOTE, this might or might not
// be an issue (basically parsing TXT becomes hard)
switch l = <-c; l.value {
case zNewline, zEOF:
break Fetch
case zString:
text = append(text, l.token)
}
}
err := rr.Data.Parse(text)
if err != nil {
return nil, &ParseError{f, err.Error(), l}, ""
}
return rr, nil, ""
}
typeToparserFunc[rtype] = parserFunc{setPrivateRR, true}
}
// PrivateHandleRemove removes defenitions required to support private RR type.
func PrivateHandleRemove(rtype uint16) {
rtypestr, ok := TypeToString[rtype]
if ok {
delete(TypeToRR, rtype)
delete(TypeToString, rtype)
delete(typeToparserFunc, rtype)
delete(StringToType, rtypestr)
delete(typeToUnpack, rtype)
}
return
}

View file

@ -1,171 +0,0 @@
package dns_test
import (
"strings"
"testing"
"github.com/miekg/dns"
)
const TypeISBN uint16 = 0xFF00
// A crazy new RR type :)
type ISBN struct {
x string // rdata with 10 or 13 numbers, dashes or spaces allowed
}
func NewISBN() dns.PrivateRdata { return &ISBN{""} }
func (rd *ISBN) Len() int { return len([]byte(rd.x)) }
func (rd *ISBN) String() string { return rd.x }
func (rd *ISBN) Parse(txt []string) error {
rd.x = strings.TrimSpace(strings.Join(txt, " "))
return nil
}
func (rd *ISBN) Pack(buf []byte) (int, error) {
b := []byte(rd.x)
n := copy(buf, b)
if n != len(b) {
return n, dns.ErrBuf
}
return n, nil
}
func (rd *ISBN) Unpack(buf []byte) (int, error) {
rd.x = string(buf)
return len(buf), nil
}
func (rd *ISBN) Copy(dest dns.PrivateRdata) error {
isbn, ok := dest.(*ISBN)
if !ok {
return dns.ErrRdata
}
isbn.x = rd.x
return nil
}
var testrecord = strings.Join([]string{"example.org.", "3600", "IN", "ISBN", "12-3 456789-0-123"}, "\t")
func TestPrivateText(t *testing.T) {
dns.PrivateHandle("ISBN", TypeISBN, NewISBN)
defer dns.PrivateHandleRemove(TypeISBN)
rr, err := dns.NewRR(testrecord)
if err != nil {
t.Fatal(err)
}
if rr.String() != testrecord {
t.Errorf("record string representation did not match original %#v != %#v", rr.String(), testrecord)
} else {
t.Log(rr.String())
}
}
func TestPrivateByteSlice(t *testing.T) {
dns.PrivateHandle("ISBN", TypeISBN, NewISBN)
defer dns.PrivateHandleRemove(TypeISBN)
rr, err := dns.NewRR(testrecord)
if err != nil {
t.Fatal(err)
}
buf := make([]byte, 100)
off, err := dns.PackRR(rr, buf, 0, nil, false)
if err != nil {
t.Errorf("got error packing ISBN: %v", err)
}
custrr := rr.(*dns.PrivateRR)
if ln := custrr.Data.Len() + len(custrr.Header().Name) + 11; ln != off {
t.Errorf("offset is not matching to length of Private RR: %d!=%d", off, ln)
}
rr1, off1, err := dns.UnpackRR(buf[:off], 0)
if err != nil {
t.Errorf("got error unpacking ISBN: %v", err)
return
}
if off1 != off {
t.Errorf("offset after unpacking differs: %d != %d", off1, off)
}
if rr1.String() != testrecord {
t.Errorf("record string representation did not match original %#v != %#v", rr1.String(), testrecord)
} else {
t.Log(rr1.String())
}
}
const TypeVERSION uint16 = 0xFF01
type VERSION struct {
x string
}
func NewVersion() dns.PrivateRdata { return &VERSION{""} }
func (rd *VERSION) String() string { return rd.x }
func (rd *VERSION) Parse(txt []string) error {
rd.x = strings.TrimSpace(strings.Join(txt, " "))
return nil
}
func (rd *VERSION) Pack(buf []byte) (int, error) {
b := []byte(rd.x)
n := copy(buf, b)
if n != len(b) {
return n, dns.ErrBuf
}
return n, nil
}
func (rd *VERSION) Unpack(buf []byte) (int, error) {
rd.x = string(buf)
return len(buf), nil
}
func (rd *VERSION) Copy(dest dns.PrivateRdata) error {
isbn, ok := dest.(*VERSION)
if !ok {
return dns.ErrRdata
}
isbn.x = rd.x
return nil
}
func (rd *VERSION) Len() int {
return len([]byte(rd.x))
}
var smallzone = `$ORIGIN example.org.
@ SOA sns.dns.icann.org. noc.dns.icann.org. (
2014091518 7200 3600 1209600 3600
)
A 1.2.3.4
ok ISBN 1231-92110-12
go VERSION (
1.3.1 ; comment
)
www ISBN 1231-92110-16
* CNAME @
`
func TestPrivateZoneParser(t *testing.T) {
dns.PrivateHandle("ISBN", TypeISBN, NewISBN)
dns.PrivateHandle("VERSION", TypeVERSION, NewVersion)
defer dns.PrivateHandleRemove(TypeISBN)
defer dns.PrivateHandleRemove(TypeVERSION)
r := strings.NewReader(smallzone)
for x := range dns.ParseZone(r, ".", "") {
if err := x.Error; err != nil {
t.Fatal(err)
}
t.Log(x.RR)
}
}

View file

@ -1,49 +0,0 @@
package dns
import "encoding/binary"
// rawSetRdlength sets the rdlength in the header of
// the RR. The offset 'off' must be positioned at the
// start of the header of the RR, 'end' must be the
// end of the RR.
func rawSetRdlength(msg []byte, off, end int) bool {
l := len(msg)
Loop:
for {
if off+1 > l {
return false
}
c := int(msg[off])
off++
switch c & 0xC0 {
case 0x00:
if c == 0x00 {
// End of the domainname
break Loop
}
if off+c > l {
return false
}
off += c
case 0xC0:
// pointer, next byte included, ends domainname
off++
break Loop
}
}
// The domainname has been seen, we at the start of the fixed part in the header.
// Type is 2 bytes, class is 2 bytes, ttl 4 and then 2 bytes for the length.
off += 2 + 2 + 4
if off+2 > l {
return false
}
//off+1 is the end of the header, 'end' is the end of the rr
//so 'end' - 'off+2' is the length of the rdata
rdatalen := end - (off + 2)
if rdatalen > 0xFFFF {
return false
}
binary.BigEndian.PutUint16(msg[off:], uint16(rdatalen))
return true
}

View file

@ -1,19 +0,0 @@
package dns
import "testing"
const LinodeAddr = "176.58.119.54:53"
func TestClientRemote(t *testing.T) {
m := new(Msg)
m.SetQuestion("go.dns.miek.nl.", TypeTXT)
c := new(Client)
r, _, err := c.Exchange(m, LinodeAddr)
if err != nil {
t.Errorf("failed to exchange: %v", err)
}
if r != nil && r.Rcode != RcodeSuccess {
t.Errorf("failed to get an valid answer\n%v", r)
}
}

View file

@ -1,38 +0,0 @@
package dns
// StringToType is the reverse of TypeToString, needed for string parsing.
var StringToType = reverseInt16(TypeToString)
// StringToClass is the reverse of ClassToString, needed for string parsing.
var StringToClass = reverseInt16(ClassToString)
// StringToOpcode is a map of opcodes to strings.
var StringToOpcode = reverseInt(OpcodeToString)
// StringToRcode is a map of rcodes to strings.
var StringToRcode = reverseInt(RcodeToString)
// Reverse a map
func reverseInt8(m map[uint8]string) map[string]uint8 {
n := make(map[string]uint8, len(m))
for u, s := range m {
n[s] = u
}
return n
}
func reverseInt16(m map[uint16]string) map[string]uint16 {
n := make(map[string]uint16, len(m))
for u, s := range m {
n[s] = u
}
return n
}
func reverseInt(m map[int]string) map[string]int {
n := make(map[string]int, len(m))
for u, s := range m {
n[s] = u
}
return n
}

View file

@ -1,84 +0,0 @@
package dns
// Dedup removes identical RRs from rrs. It preserves the original ordering.
// The lowest TTL of any duplicates is used in the remaining one. Dedup modifies
// rrs.
// m is used to store the RRs temporay. If it is nil a new map will be allocated.
func Dedup(rrs []RR, m map[string]RR) []RR {
if m == nil {
m = make(map[string]RR)
}
// Save the keys, so we don't have to call normalizedString twice.
keys := make([]*string, 0, len(rrs))
for _, r := range rrs {
key := normalizedString(r)
keys = append(keys, &key)
if _, ok := m[key]; ok {
// Shortest TTL wins.
if m[key].Header().Ttl > r.Header().Ttl {
m[key].Header().Ttl = r.Header().Ttl
}
continue
}
m[key] = r
}
// If the length of the result map equals the amount of RRs we got,
// it means they were all different. We can then just return the original rrset.
if len(m) == len(rrs) {
return rrs
}
j := 0
for i, r := range rrs {
// If keys[i] lives in the map, we should copy and remove it.
if _, ok := m[*keys[i]]; ok {
delete(m, *keys[i])
rrs[j] = r
j++
}
if len(m) == 0 {
break
}
}
return rrs[:j]
}
// normalizedString returns a normalized string from r. The TTL
// is removed and the domain name is lowercased. We go from this:
// DomainName<TAB>TTL<TAB>CLASS<TAB>TYPE<TAB>RDATA to:
// lowercasename<TAB>CLASS<TAB>TYPE...
func normalizedString(r RR) string {
// A string Go DNS makes has: domainname<TAB>TTL<TAB>...
b := []byte(r.String())
// find the first non-escaped tab, then another, so we capture where the TTL lives.
esc := false
ttlStart, ttlEnd := 0, 0
for i := 0; i < len(b) && ttlEnd == 0; i++ {
switch {
case b[i] == '\\':
esc = !esc
case b[i] == '\t' && !esc:
if ttlStart == 0 {
ttlStart = i
continue
}
if ttlEnd == 0 {
ttlEnd = i
}
case b[i] >= 'A' && b[i] <= 'Z' && !esc:
b[i] += 32
default:
esc = false
}
}
// remove TTL.
copy(b[ttlStart:], b[ttlEnd:])
cut := ttlEnd - ttlStart
return string(b[:len(b)-cut])
}

View file

@ -1,84 +0,0 @@
package dns
import "testing"
func TestDedup(t *testing.T) {
// make it []string
testcases := map[[3]RR][]string{
[...]RR{
newRR(t, "mIek.nl. IN A 127.0.0.1"),
newRR(t, "mieK.nl. IN A 127.0.0.1"),
newRR(t, "miek.Nl. IN A 127.0.0.1"),
}: {"mIek.nl.\t3600\tIN\tA\t127.0.0.1"},
[...]RR{
newRR(t, "miEk.nl. 2000 IN A 127.0.0.1"),
newRR(t, "mieK.Nl. 1000 IN A 127.0.0.1"),
newRR(t, "Miek.nL. 500 IN A 127.0.0.1"),
}: {"miEk.nl.\t500\tIN\tA\t127.0.0.1"},
[...]RR{
newRR(t, "miek.nl. IN A 127.0.0.1"),
newRR(t, "miek.nl. CH A 127.0.0.1"),
newRR(t, "miek.nl. IN A 127.0.0.1"),
}: {"miek.nl.\t3600\tIN\tA\t127.0.0.1",
"miek.nl.\t3600\tCH\tA\t127.0.0.1",
},
[...]RR{
newRR(t, "miek.nl. CH A 127.0.0.1"),
newRR(t, "miek.nl. IN A 127.0.0.1"),
newRR(t, "miek.de. IN A 127.0.0.1"),
}: {"miek.nl.\t3600\tCH\tA\t127.0.0.1",
"miek.nl.\t3600\tIN\tA\t127.0.0.1",
"miek.de.\t3600\tIN\tA\t127.0.0.1",
},
[...]RR{
newRR(t, "miek.de. IN A 127.0.0.1"),
newRR(t, "miek.nl. 200 IN A 127.0.0.1"),
newRR(t, "miek.nl. 300 IN A 127.0.0.1"),
}: {"miek.de.\t3600\tIN\tA\t127.0.0.1",
"miek.nl.\t200\tIN\tA\t127.0.0.1",
},
}
for rr, expected := range testcases {
out := Dedup([]RR{rr[0], rr[1], rr[2]}, nil)
for i, o := range out {
if o.String() != expected[i] {
t.Fatalf("expected %v, got %v", expected[i], o.String())
}
}
}
}
func BenchmarkDedup(b *testing.B) {
rrs := []RR{
newRR(nil, "miEk.nl. 2000 IN A 127.0.0.1"),
newRR(nil, "mieK.Nl. 1000 IN A 127.0.0.1"),
newRR(nil, "Miek.nL. 500 IN A 127.0.0.1"),
}
m := make(map[string]RR)
for i := 0; i < b.N; i++ {
Dedup(rrs, m)
}
}
func TestNormalizedString(t *testing.T) {
tests := map[RR]string{
newRR(t, "mIEk.Nl. 3600 IN A 127.0.0.1"): "miek.nl.\tIN\tA\t127.0.0.1",
newRR(t, "m\\ iek.nL. 3600 IN A 127.0.0.1"): "m\\ iek.nl.\tIN\tA\t127.0.0.1",
newRR(t, "m\\\tIeK.nl. 3600 in A 127.0.0.1"): "m\\009iek.nl.\tIN\tA\t127.0.0.1",
}
for tc, expected := range tests {
n := normalizedString(tc)
if n != expected {
t.Errorf("expected %s, got %s", expected, n)
}
}
}
func newRR(t *testing.T, s string) RR {
r, err := NewRR(s)
if err != nil {
t.Logf("newRR: %v", err)
}
return r
}

987
vendor/github.com/miekg/dns/scan.go generated vendored
View file

@ -1,987 +0,0 @@
package dns
import (
"io"
"log"
"os"
"strconv"
"strings"
)
type debugging bool
const debug debugging = false
func (d debugging) Printf(format string, args ...interface{}) {
if d {
log.Printf(format, args...)
}
}
const maxTok = 2048 // Largest token we can return.
const maxUint16 = 1<<16 - 1
// Tokinize a RFC 1035 zone file. The tokenizer will normalize it:
// * Add ownernames if they are left blank;
// * Suppress sequences of spaces;
// * Make each RR fit on one line (_NEWLINE is send as last)
// * Handle comments: ;
// * Handle braces - anywhere.
const (
// Zonefile
zEOF = iota
zString
zBlank
zQuote
zNewline
zRrtpe
zOwner
zClass
zDirOrigin // $ORIGIN
zDirTtl // $TTL
zDirInclude // $INCLUDE
zDirGenerate // $GENERATE
// Privatekey file
zValue
zKey
zExpectOwnerDir // Ownername
zExpectOwnerBl // Whitespace after the ownername
zExpectAny // Expect rrtype, ttl or class
zExpectAnyNoClass // Expect rrtype or ttl
zExpectAnyNoClassBl // The whitespace after _EXPECT_ANY_NOCLASS
zExpectAnyNoTtl // Expect rrtype or class
zExpectAnyNoTtlBl // Whitespace after _EXPECT_ANY_NOTTL
zExpectRrtype // Expect rrtype
zExpectRrtypeBl // Whitespace BEFORE rrtype
zExpectRdata // The first element of the rdata
zExpectDirTtlBl // Space after directive $TTL
zExpectDirTtl // Directive $TTL
zExpectDirOriginBl // Space after directive $ORIGIN
zExpectDirOrigin // Directive $ORIGIN
zExpectDirIncludeBl // Space after directive $INCLUDE
zExpectDirInclude // Directive $INCLUDE
zExpectDirGenerate // Directive $GENERATE
zExpectDirGenerateBl // Space after directive $GENERATE
)
// ParseError is a parsing error. It contains the parse error and the location in the io.Reader
// where the error occurred.
type ParseError struct {
file string
err string
lex lex
}
func (e *ParseError) Error() (s string) {
if e.file != "" {
s = e.file + ": "
}
s += "dns: " + e.err + ": " + strconv.QuoteToASCII(e.lex.token) + " at line: " +
strconv.Itoa(e.lex.line) + ":" + strconv.Itoa(e.lex.column)
return
}
type lex struct {
token string // text of the token
tokenUpper string // uppercase text of the token
length int // length of the token
err bool // when true, token text has lexer error
value uint8 // value: zString, _BLANK, etc.
line int // line in the file
column int // column in the file
torc uint16 // type or class as parsed in the lexer, we only need to look this up in the grammar
comment string // any comment text seen
}
// Token holds the token that are returned when a zone file is parsed.
type Token struct {
// The scanned resource record when error is not nil.
RR
// When an error occurred, this has the error specifics.
Error *ParseError
// A potential comment positioned after the RR and on the same line.
Comment string
}
// NewRR reads the RR contained in the string s. Only the first RR is
// returned. If s contains no RR, return nil with no error. The class
// defaults to IN and TTL defaults to 3600. The full zone file syntax
// like $TTL, $ORIGIN, etc. is supported. All fields of the returned
// RR are set, except RR.Header().Rdlength which is set to 0.
func NewRR(s string) (RR, error) {
if len(s) > 0 && s[len(s)-1] != '\n' { // We need a closing newline
return ReadRR(strings.NewReader(s+"\n"), "")
}
return ReadRR(strings.NewReader(s), "")
}
// ReadRR reads the RR contained in q.
// See NewRR for more documentation.
func ReadRR(q io.Reader, filename string) (RR, error) {
r := <-parseZoneHelper(q, ".", filename, 1)
if r == nil {
return nil, nil
}
if r.Error != nil {
return nil, r.Error
}
return r.RR, nil
}
// ParseZone reads a RFC 1035 style zonefile from r. It returns *Tokens on the
// returned channel, which consist out the parsed RR, a potential comment or an error.
// If there is an error the RR is nil. The string file is only used
// in error reporting. The string origin is used as the initial origin, as
// if the file would start with: $ORIGIN origin .
// The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are supported.
// The channel t is closed by ParseZone when the end of r is reached.
//
// Basic usage pattern when reading from a string (z) containing the
// zone data:
//
// for x := range dns.ParseZone(strings.NewReader(z), "", "") {
// if x.Error != nil {
// // log.Println(x.Error)
// } else {
// // Do something with x.RR
// }
// }
//
// Comments specified after an RR (and on the same line!) are returned too:
//
// foo. IN A 10.0.0.1 ; this is a comment
//
// The text "; this is comment" is returned in Token.Comment. Comments inside the
// RR are discarded. Comments on a line by themselves are discarded too.
func ParseZone(r io.Reader, origin, file string) chan *Token {
return parseZoneHelper(r, origin, file, 10000)
}
func parseZoneHelper(r io.Reader, origin, file string, chansize int) chan *Token {
t := make(chan *Token, chansize)
go parseZone(r, origin, file, t, 0)
return t
}
func parseZone(r io.Reader, origin, f string, t chan *Token, include int) {
defer func() {
if include == 0 {
close(t)
}
}()
s := scanInit(r)
c := make(chan lex)
// Start the lexer
go zlexer(s, c)
// 6 possible beginnings of a line, _ is a space
// 0. zRRTYPE -> all omitted until the rrtype
// 1. zOwner _ zRrtype -> class/ttl omitted
// 2. zOwner _ zString _ zRrtype -> class omitted
// 3. zOwner _ zString _ zClass _ zRrtype -> ttl/class
// 4. zOwner _ zClass _ zRrtype -> ttl omitted
// 5. zOwner _ zClass _ zString _ zRrtype -> class/ttl (reversed)
// After detecting these, we know the zRrtype so we can jump to functions
// handling the rdata for each of these types.
if origin == "" {
origin = "."
}
origin = Fqdn(origin)
if _, ok := IsDomainName(origin); !ok {
t <- &Token{Error: &ParseError{f, "bad initial origin name", lex{}}}
return
}
st := zExpectOwnerDir // initial state
var h RR_Header
var defttl uint32 = defaultTtl
var prevName string
for l := range c {
// Lexer spotted an error already
if l.err == true {
t <- &Token{Error: &ParseError{f, l.token, l}}
return
}
switch st {
case zExpectOwnerDir:
// We can also expect a directive, like $TTL or $ORIGIN
h.Ttl = defttl
h.Class = ClassINET
switch l.value {
case zNewline:
st = zExpectOwnerDir
case zOwner:
h.Name = l.token
if l.token[0] == '@' {
h.Name = origin
prevName = h.Name
st = zExpectOwnerBl
break
}
if h.Name[l.length-1] != '.' {
h.Name = appendOrigin(h.Name, origin)
}
_, ok := IsDomainName(l.token)
if !ok {
t <- &Token{Error: &ParseError{f, "bad owner name", l}}
return
}
prevName = h.Name
st = zExpectOwnerBl
case zDirTtl:
st = zExpectDirTtlBl
case zDirOrigin:
st = zExpectDirOriginBl
case zDirInclude:
st = zExpectDirIncludeBl
case zDirGenerate:
st = zExpectDirGenerateBl
case zRrtpe:
h.Name = prevName
h.Rrtype = l.torc
st = zExpectRdata
case zClass:
h.Name = prevName
h.Class = l.torc
st = zExpectAnyNoClassBl
case zBlank:
// Discard, can happen when there is nothing on the
// line except the RR type
case zString:
ttl, ok := stringToTtl(l.token)
if !ok {
t <- &Token{Error: &ParseError{f, "not a TTL", l}}
return
}
h.Ttl = ttl
// Don't about the defttl, we should take the $TTL value
// defttl = ttl
st = zExpectAnyNoTtlBl
default:
t <- &Token{Error: &ParseError{f, "syntax error at beginning", l}}
return
}
case zExpectDirIncludeBl:
if l.value != zBlank {
t <- &Token{Error: &ParseError{f, "no blank after $INCLUDE-directive", l}}
return
}
st = zExpectDirInclude
case zExpectDirInclude:
if l.value != zString {
t <- &Token{Error: &ParseError{f, "expecting $INCLUDE value, not this...", l}}
return
}
neworigin := origin // There may be optionally a new origin set after the filename, if not use current one
l := <-c
switch l.value {
case zBlank:
l := <-c
if l.value == zString {
if _, ok := IsDomainName(l.token); !ok || l.length == 0 || l.err {
t <- &Token{Error: &ParseError{f, "bad origin name", l}}
return
}
// a new origin is specified.
if l.token[l.length-1] != '.' {
if origin != "." { // Prevent .. endings
neworigin = l.token + "." + origin
} else {
neworigin = l.token + origin
}
} else {
neworigin = l.token
}
}
case zNewline, zEOF:
// Ok
default:
t <- &Token{Error: &ParseError{f, "garbage after $INCLUDE", l}}
return
}
// Start with the new file
r1, e1 := os.Open(l.token)
if e1 != nil {
t <- &Token{Error: &ParseError{f, "failed to open `" + l.token + "'", l}}
return
}
if include+1 > 7 {
t <- &Token{Error: &ParseError{f, "too deeply nested $INCLUDE", l}}
return
}
parseZone(r1, l.token, neworigin, t, include+1)
st = zExpectOwnerDir
case zExpectDirTtlBl:
if l.value != zBlank {
t <- &Token{Error: &ParseError{f, "no blank after $TTL-directive", l}}
return
}
st = zExpectDirTtl
case zExpectDirTtl:
if l.value != zString {
t <- &Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}}
return
}
if e, _ := slurpRemainder(c, f); e != nil {
t <- &Token{Error: e}
return
}
ttl, ok := stringToTtl(l.token)
if !ok {
t <- &Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}}
return
}
defttl = ttl
st = zExpectOwnerDir
case zExpectDirOriginBl:
if l.value != zBlank {
t <- &Token{Error: &ParseError{f, "no blank after $ORIGIN-directive", l}}
return
}
st = zExpectDirOrigin
case zExpectDirOrigin:
if l.value != zString {
t <- &Token{Error: &ParseError{f, "expecting $ORIGIN value, not this...", l}}
return
}
if e, _ := slurpRemainder(c, f); e != nil {
t <- &Token{Error: e}
}
if _, ok := IsDomainName(l.token); !ok {
t <- &Token{Error: &ParseError{f, "bad origin name", l}}
return
}
if l.token[l.length-1] != '.' {
if origin != "." { // Prevent .. endings
origin = l.token + "." + origin
} else {
origin = l.token + origin
}
} else {
origin = l.token
}
st = zExpectOwnerDir
case zExpectDirGenerateBl:
if l.value != zBlank {
t <- &Token{Error: &ParseError{f, "no blank after $GENERATE-directive", l}}
return
}
st = zExpectDirGenerate
case zExpectDirGenerate:
if l.value != zString {
t <- &Token{Error: &ParseError{f, "expecting $GENERATE value, not this...", l}}
return
}
if errMsg := generate(l, c, t, origin); errMsg != "" {
t <- &Token{Error: &ParseError{f, errMsg, l}}
return
}
st = zExpectOwnerDir
case zExpectOwnerBl:
if l.value != zBlank {
t <- &Token{Error: &ParseError{f, "no blank after owner", l}}
return
}
st = zExpectAny
case zExpectAny:
switch l.value {
case zRrtpe:
h.Rrtype = l.torc
st = zExpectRdata
case zClass:
h.Class = l.torc
st = zExpectAnyNoClassBl
case zString:
ttl, ok := stringToTtl(l.token)
if !ok {
t <- &Token{Error: &ParseError{f, "not a TTL", l}}
return
}
h.Ttl = ttl
// defttl = ttl // don't set the defttl here
st = zExpectAnyNoTtlBl
default:
t <- &Token{Error: &ParseError{f, "expecting RR type, TTL or class, not this...", l}}
return
}
case zExpectAnyNoClassBl:
if l.value != zBlank {
t <- &Token{Error: &ParseError{f, "no blank before class", l}}
return
}
st = zExpectAnyNoClass
case zExpectAnyNoTtlBl:
if l.value != zBlank {
t <- &Token{Error: &ParseError{f, "no blank before TTL", l}}
return
}
st = zExpectAnyNoTtl
case zExpectAnyNoTtl:
switch l.value {
case zClass:
h.Class = l.torc
st = zExpectRrtypeBl
case zRrtpe:
h.Rrtype = l.torc
st = zExpectRdata
default:
t <- &Token{Error: &ParseError{f, "expecting RR type or class, not this...", l}}
return
}
case zExpectAnyNoClass:
switch l.value {
case zString:
ttl, ok := stringToTtl(l.token)
if !ok {
t <- &Token{Error: &ParseError{f, "not a TTL", l}}
return
}
h.Ttl = ttl
// defttl = ttl // don't set the def ttl anymore
st = zExpectRrtypeBl
case zRrtpe:
h.Rrtype = l.torc
st = zExpectRdata
default:
t <- &Token{Error: &ParseError{f, "expecting RR type or TTL, not this...", l}}
return
}
case zExpectRrtypeBl:
if l.value != zBlank {
t <- &Token{Error: &ParseError{f, "no blank before RR type", l}}
return
}
st = zExpectRrtype
case zExpectRrtype:
if l.value != zRrtpe {
t <- &Token{Error: &ParseError{f, "unknown RR type", l}}
return
}
h.Rrtype = l.torc
st = zExpectRdata
case zExpectRdata:
r, e, c1 := setRR(h, c, origin, f)
if e != nil {
// If e.lex is nil than we have encounter a unknown RR type
// in that case we substitute our current lex token
if e.lex.token == "" && e.lex.value == 0 {
e.lex = l // Uh, dirty
}
t <- &Token{Error: e}
return
}
t <- &Token{RR: r, Comment: c1}
st = zExpectOwnerDir
}
}
// If we get here, we and the h.Rrtype is still zero, we haven't parsed anything, this
// is not an error, because an empty zone file is still a zone file.
}
// zlexer scans the sourcefile and returns tokens on the channel c.
func zlexer(s *scan, c chan lex) {
var l lex
str := make([]byte, maxTok) // Should be enough for any token
stri := 0 // Offset in str (0 means empty)
com := make([]byte, maxTok) // Hold comment text
comi := 0
quote := false
escape := false
space := false
commt := false
rrtype := false
owner := true
brace := 0
x, err := s.tokenText()
defer close(c)
for err == nil {
l.column = s.position.Column
l.line = s.position.Line
if stri >= maxTok {
l.token = "token length insufficient for parsing"
l.err = true
debug.Printf("[%+v]", l.token)
c <- l
return
}
if comi >= maxTok {
l.token = "comment length insufficient for parsing"
l.err = true
debug.Printf("[%+v]", l.token)
c <- l
return
}
switch x {
case ' ', '\t':
if escape {
escape = false
str[stri] = x
stri++
break
}
if quote {
// Inside quotes this is legal
str[stri] = x
stri++
break
}
if commt {
com[comi] = x
comi++
break
}
if stri == 0 {
// Space directly in the beginning, handled in the grammar
} else if owner {
// If we have a string and its the first, make it an owner
l.value = zOwner
l.token = string(str[:stri])
l.tokenUpper = strings.ToUpper(l.token)
l.length = stri
// escape $... start with a \ not a $, so this will work
switch l.tokenUpper {
case "$TTL":
l.value = zDirTtl
case "$ORIGIN":
l.value = zDirOrigin
case "$INCLUDE":
l.value = zDirInclude
case "$GENERATE":
l.value = zDirGenerate
}
debug.Printf("[7 %+v]", l.token)
c <- l
} else {
l.value = zString
l.token = string(str[:stri])
l.tokenUpper = strings.ToUpper(l.token)
l.length = stri
if !rrtype {
if t, ok := StringToType[l.tokenUpper]; ok {
l.value = zRrtpe
l.torc = t
rrtype = true
} else {
if strings.HasPrefix(l.tokenUpper, "TYPE") {
t, ok := typeToInt(l.token)
if !ok {
l.token = "unknown RR type"
l.err = true
c <- l
return
}
l.value = zRrtpe
l.torc = t
}
}
if t, ok := StringToClass[l.tokenUpper]; ok {
l.value = zClass
l.torc = t
} else {
if strings.HasPrefix(l.tokenUpper, "CLASS") {
t, ok := classToInt(l.token)
if !ok {
l.token = "unknown class"
l.err = true
c <- l
return
}
l.value = zClass
l.torc = t
}
}
}
debug.Printf("[6 %+v]", l.token)
c <- l
}
stri = 0
// I reverse space stuff here
if !space && !commt {
l.value = zBlank
l.token = " "
l.length = 1
debug.Printf("[5 %+v]", l.token)
c <- l
}
owner = false
space = true
case ';':
if escape {
escape = false
str[stri] = x
stri++
break
}
if quote {
// Inside quotes this is legal
str[stri] = x
stri++
break
}
if stri > 0 {
l.value = zString
l.token = string(str[:stri])
l.tokenUpper = strings.ToUpper(l.token)
l.length = stri
debug.Printf("[4 %+v]", l.token)
c <- l
stri = 0
}
commt = true
com[comi] = ';'
comi++
case '\r':
escape = false
if quote {
str[stri] = x
stri++
break
}
// discard if outside of quotes
case '\n':
escape = false
// Escaped newline
if quote {
str[stri] = x
stri++
break
}
// inside quotes this is legal
if commt {
// Reset a comment
commt = false
rrtype = false
stri = 0
// If not in a brace this ends the comment AND the RR
if brace == 0 {
owner = true
owner = true
l.value = zNewline
l.token = "\n"
l.tokenUpper = l.token
l.length = 1
l.comment = string(com[:comi])
debug.Printf("[3 %+v %+v]", l.token, l.comment)
c <- l
l.comment = ""
comi = 0
break
}
com[comi] = ' ' // convert newline to space
comi++
break
}
if brace == 0 {
// If there is previous text, we should output it here
if stri != 0 {
l.value = zString
l.token = string(str[:stri])
l.tokenUpper = strings.ToUpper(l.token)
l.length = stri
if !rrtype {
if t, ok := StringToType[l.tokenUpper]; ok {
l.value = zRrtpe
l.torc = t
rrtype = true
}
}
debug.Printf("[2 %+v]", l.token)
c <- l
}
l.value = zNewline
l.token = "\n"
l.tokenUpper = l.token
l.length = 1
debug.Printf("[1 %+v]", l.token)
c <- l
stri = 0
commt = false
rrtype = false
owner = true
comi = 0
}
case '\\':
// comments do not get escaped chars, everything is copied
if commt {
com[comi] = x
comi++
break
}
// something already escaped must be in string
if escape {
str[stri] = x
stri++
escape = false
break
}
// something escaped outside of string gets added to string
str[stri] = x
stri++
escape = true
case '"':
if commt {
com[comi] = x
comi++
break
}
if escape {
str[stri] = x
stri++
escape = false
break
}
space = false
// send previous gathered text and the quote
if stri != 0 {
l.value = zString
l.token = string(str[:stri])
l.tokenUpper = strings.ToUpper(l.token)
l.length = stri
debug.Printf("[%+v]", l.token)
c <- l
stri = 0
}
// send quote itself as separate token
l.value = zQuote
l.token = "\""
l.tokenUpper = l.token
l.length = 1
c <- l
quote = !quote
case '(', ')':
if commt {
com[comi] = x
comi++
break
}
if escape {
str[stri] = x
stri++
escape = false
break
}
if quote {
str[stri] = x
stri++
break
}
switch x {
case ')':
brace--
if brace < 0 {
l.token = "extra closing brace"
l.tokenUpper = l.token
l.err = true
debug.Printf("[%+v]", l.token)
c <- l
return
}
case '(':
brace++
}
default:
escape = false
if commt {
com[comi] = x
comi++
break
}
str[stri] = x
stri++
space = false
}
x, err = s.tokenText()
}
if stri > 0 {
// Send remainder
l.token = string(str[:stri])
l.tokenUpper = strings.ToUpper(l.token)
l.length = stri
l.value = zString
debug.Printf("[%+v]", l.token)
c <- l
}
if brace != 0 {
l.token = "unbalanced brace"
l.tokenUpper = l.token
l.err = true
c <- l
}
}
// Extract the class number from CLASSxx
func classToInt(token string) (uint16, bool) {
offset := 5
if len(token) < offset+1 {
return 0, false
}
class, err := strconv.ParseUint(token[offset:], 10, 16)
if err != nil {
return 0, false
}
return uint16(class), true
}
// Extract the rr number from TYPExxx
func typeToInt(token string) (uint16, bool) {
offset := 4
if len(token) < offset+1 {
return 0, false
}
typ, err := strconv.ParseUint(token[offset:], 10, 16)
if err != nil {
return 0, false
}
return uint16(typ), true
}
// Parse things like 2w, 2m, etc, Return the time in seconds.
func stringToTtl(token string) (uint32, bool) {
s := uint32(0)
i := uint32(0)
for _, c := range token {
switch c {
case 's', 'S':
s += i
i = 0
case 'm', 'M':
s += i * 60
i = 0
case 'h', 'H':
s += i * 60 * 60
i = 0
case 'd', 'D':
s += i * 60 * 60 * 24
i = 0
case 'w', 'W':
s += i * 60 * 60 * 24 * 7
i = 0
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
i *= 10
i += uint32(c) - '0'
default:
return 0, false
}
}
return s + i, true
}
// Parse LOC records' <digits>[.<digits>][mM] into a
// mantissa exponent format. Token should contain the entire
// string (i.e. no spaces allowed)
func stringToCm(token string) (e, m uint8, ok bool) {
if token[len(token)-1] == 'M' || token[len(token)-1] == 'm' {
token = token[0 : len(token)-1]
}
s := strings.SplitN(token, ".", 2)
var meters, cmeters, val int
var err error
switch len(s) {
case 2:
if cmeters, err = strconv.Atoi(s[1]); err != nil {
return
}
fallthrough
case 1:
if meters, err = strconv.Atoi(s[0]); err != nil {
return
}
case 0:
// huh?
return 0, 0, false
}
ok = true
if meters > 0 {
e = 2
val = meters
} else {
e = 0
val = cmeters
}
for val > 10 {
e++
val /= 10
}
if e > 9 {
ok = false
}
m = uint8(val)
return
}
func appendOrigin(name, origin string) string {
if origin == "." {
return name + origin
}
return name + "." + origin
}
// LOC record helper function
func locCheckNorth(token string, latitude uint32) (uint32, bool) {
switch token {
case "n", "N":
return LOC_EQUATOR + latitude, true
case "s", "S":
return LOC_EQUATOR - latitude, true
}
return latitude, false
}
// LOC record helper function
func locCheckEast(token string, longitude uint32) (uint32, bool) {
switch token {
case "e", "E":
return LOC_EQUATOR + longitude, true
case "w", "W":
return LOC_EQUATOR - longitude, true
}
return longitude, false
}
// "Eat" the rest of the "line". Return potential comments
func slurpRemainder(c chan lex, f string) (*ParseError, string) {
l := <-c
com := ""
switch l.value {
case zBlank:
l = <-c
com = l.comment
if l.value != zNewline && l.value != zEOF {
return &ParseError{f, "garbage after rdata", l}, ""
}
case zNewline:
com = l.comment
case zEOF:
default:
return &ParseError{f, "garbage after rdata", l}, ""
}
return nil, com
}
// Parse a 64 bit-like ipv6 address: "0014:4fff:ff20:ee64"
// Used for NID and L64 record.
func stringToNodeID(l lex) (uint64, *ParseError) {
if len(l.token) < 19 {
return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
}
// There must be three colons at fixes postitions, if not its a parse error
if l.token[4] != ':' && l.token[9] != ':' && l.token[14] != ':' {
return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
}
s := l.token[0:4] + l.token[5:9] + l.token[10:14] + l.token[15:19]
u, err := strconv.ParseUint(s, 16, 64)
if err != nil {
return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
}
return u, nil
}

2184
vendor/github.com/miekg/dns/scan_rr.go generated vendored

File diff suppressed because it is too large Load diff

View file

@ -1,43 +0,0 @@
package dns
// Implement a simple scanner, return a byte stream from an io reader.
import (
"bufio"
"io"
"text/scanner"
)
type scan struct {
src *bufio.Reader
position scanner.Position
eof bool // Have we just seen a eof
}
func scanInit(r io.Reader) *scan {
s := new(scan)
s.src = bufio.NewReader(r)
s.position.Line = 1
return s
}
// tokenText returns the next byte from the input
func (s *scan) tokenText() (byte, error) {
c, err := s.src.ReadByte()
if err != nil {
return c, err
}
// delay the newline handling until the next token is delivered,
// fixes off-by-one errors when reporting a parse error.
if s.eof == true {
s.position.Line++
s.position.Column = 0
s.eof = false
}
if c == '\n' {
s.eof = true
return c, nil
}
s.position.Column++
return c, nil
}

734
vendor/github.com/miekg/dns/server.go generated vendored
View file

@ -1,734 +0,0 @@
// DNS server implementation.
package dns
import (
"bytes"
"crypto/tls"
"encoding/binary"
"io"
"net"
"sync"
"time"
)
// Maximum number of TCP queries before we close the socket.
const maxTCPQueries = 128
// Handler is implemented by any value that implements ServeDNS.
type Handler interface {
ServeDNS(w ResponseWriter, r *Msg)
}
// A ResponseWriter interface is used by an DNS handler to
// construct an DNS response.
type ResponseWriter interface {
// LocalAddr returns the net.Addr of the server
LocalAddr() net.Addr
// RemoteAddr returns the net.Addr of the client that sent the current request.
RemoteAddr() net.Addr
// WriteMsg writes a reply back to the client.
WriteMsg(*Msg) error
// Write writes a raw buffer back to the client.
Write([]byte) (int, error)
// Close closes the connection.
Close() error
// TsigStatus returns the status of the Tsig.
TsigStatus() error
// TsigTimersOnly sets the tsig timers only boolean.
TsigTimersOnly(bool)
// Hijack lets the caller take over the connection.
// After a call to Hijack(), the DNS package will not do anything with the connection.
Hijack()
}
type response struct {
hijacked bool // connection has been hijacked by handler
tsigStatus error
tsigTimersOnly bool
tsigRequestMAC string
tsigSecret map[string]string // the tsig secrets
udp *net.UDPConn // i/o connection if UDP was used
tcp net.Conn // i/o connection if TCP was used
udpSession *SessionUDP // oob data to get egress interface right
remoteAddr net.Addr // address of the client
writer Writer // writer to output the raw DNS bits
}
// ServeMux is an DNS request multiplexer. It matches the
// zone name of each incoming request against a list of
// registered patterns add calls the handler for the pattern
// that most closely matches the zone name. ServeMux is DNSSEC aware, meaning
// that queries for the DS record are redirected to the parent zone (if that
// is also registered), otherwise the child gets the query.
// ServeMux is also safe for concurrent access from multiple goroutines.
type ServeMux struct {
z map[string]Handler
m *sync.RWMutex
}
// NewServeMux allocates and returns a new ServeMux.
func NewServeMux() *ServeMux { return &ServeMux{z: make(map[string]Handler), m: new(sync.RWMutex)} }
// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = NewServeMux()
// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as DNS handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler object that calls f.
type HandlerFunc func(ResponseWriter, *Msg)
// ServeDNS calls f(w, r).
func (f HandlerFunc) ServeDNS(w ResponseWriter, r *Msg) {
f(w, r)
}
// HandleFailed returns a HandlerFunc that returns SERVFAIL for every request it gets.
func HandleFailed(w ResponseWriter, r *Msg) {
m := new(Msg)
m.SetRcode(r, RcodeServerFailure)
// does not matter if this write fails
w.WriteMsg(m)
}
func failedHandler() Handler { return HandlerFunc(HandleFailed) }
// ListenAndServe Starts a server on address and network specified Invoke handler
// for incoming queries.
func ListenAndServe(addr string, network string, handler Handler) error {
server := &Server{Addr: addr, Net: network, Handler: handler}
return server.ListenAndServe()
}
// ListenAndServeTLS acts like http.ListenAndServeTLS, more information in
// http://golang.org/pkg/net/http/#ListenAndServeTLS
func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return err
}
config := tls.Config{
Certificates: []tls.Certificate{cert},
}
server := &Server{
Addr: addr,
Net: "tcp-tls",
TLSConfig: &config,
Handler: handler,
}
return server.ListenAndServe()
}
// ActivateAndServe activates a server with a listener from systemd,
// l and p should not both be non-nil.
// If both l and p are not nil only p will be used.
// Invoke handler for incoming queries.
func ActivateAndServe(l net.Listener, p net.PacketConn, handler Handler) error {
server := &Server{Listener: l, PacketConn: p, Handler: handler}
return server.ActivateAndServe()
}
func (mux *ServeMux) match(q string, t uint16) Handler {
mux.m.RLock()
defer mux.m.RUnlock()
var handler Handler
b := make([]byte, len(q)) // worst case, one label of length q
off := 0
end := false
for {
l := len(q[off:])
for i := 0; i < l; i++ {
b[i] = q[off+i]
if b[i] >= 'A' && b[i] <= 'Z' {
b[i] |= ('a' - 'A')
}
}
if h, ok := mux.z[string(b[:l])]; ok { // causes garbage, might want to change the map key
if t != TypeDS {
return h
}
// Continue for DS to see if we have a parent too, if so delegeate to the parent
handler = h
}
off, end = NextLabel(q, off)
if end {
break
}
}
// Wildcard match, if we have found nothing try the root zone as a last resort.
if h, ok := mux.z["."]; ok {
return h
}
return handler
}
// Handle adds a handler to the ServeMux for pattern.
func (mux *ServeMux) Handle(pattern string, handler Handler) {
if pattern == "" {
panic("dns: invalid pattern " + pattern)
}
mux.m.Lock()
mux.z[Fqdn(pattern)] = handler
mux.m.Unlock()
}
// HandleFunc adds a handler function to the ServeMux for pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
mux.Handle(pattern, HandlerFunc(handler))
}
// HandleRemove deregistrars the handler specific for pattern from the ServeMux.
func (mux *ServeMux) HandleRemove(pattern string) {
if pattern == "" {
panic("dns: invalid pattern " + pattern)
}
mux.m.Lock()
delete(mux.z, Fqdn(pattern))
mux.m.Unlock()
}
// ServeDNS dispatches the request to the handler whose
// pattern most closely matches the request message. If DefaultServeMux
// is used the correct thing for DS queries is done: a possible parent
// is sought.
// If no handler is found a standard SERVFAIL message is returned
// If the request message does not have exactly one question in the
// question section a SERVFAIL is returned, unlesss Unsafe is true.
func (mux *ServeMux) ServeDNS(w ResponseWriter, request *Msg) {
var h Handler
if len(request.Question) < 1 { // allow more than one question
h = failedHandler()
} else {
if h = mux.match(request.Question[0].Name, request.Question[0].Qtype); h == nil {
h = failedHandler()
}
}
h.ServeDNS(w, request)
}
// Handle registers the handler with the given pattern
// in the DefaultServeMux. The documentation for
// ServeMux explains how patterns are matched.
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
// HandleRemove deregisters the handle with the given pattern
// in the DefaultServeMux.
func HandleRemove(pattern string) { DefaultServeMux.HandleRemove(pattern) }
// HandleFunc registers the handler function with the given pattern
// in the DefaultServeMux.
func HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
// Writer writes raw DNS messages; each call to Write should send an entire message.
type Writer interface {
io.Writer
}
// Reader reads raw DNS messages; each call to ReadTCP or ReadUDP should return an entire message.
type Reader interface {
// ReadTCP reads a raw message from a TCP connection. Implementations may alter
// connection properties, for example the read-deadline.
ReadTCP(conn net.Conn, timeout time.Duration) ([]byte, error)
// ReadUDP reads a raw message from a UDP connection. Implementations may alter
// connection properties, for example the read-deadline.
ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error)
}
// defaultReader is an adapter for the Server struct that implements the Reader interface
// using the readTCP and readUDP func of the embedded Server.
type defaultReader struct {
*Server
}
func (dr *defaultReader) ReadTCP(conn net.Conn, timeout time.Duration) ([]byte, error) {
return dr.readTCP(conn, timeout)
}
func (dr *defaultReader) ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) {
return dr.readUDP(conn, timeout)
}
// DecorateReader is a decorator hook for extending or supplanting the functionality of a Reader.
// Implementations should never return a nil Reader.
type DecorateReader func(Reader) Reader
// DecorateWriter is a decorator hook for extending or supplanting the functionality of a Writer.
// Implementations should never return a nil Writer.
type DecorateWriter func(Writer) Writer
// A Server defines parameters for running an DNS server.
type Server struct {
// Address to listen on, ":dns" if empty.
Addr string
// if "tcp" or "tcp-tls" (DNS over TLS) it will invoke a TCP listener, otherwise an UDP one
Net string
// TCP Listener to use, this is to aid in systemd's socket activation.
Listener net.Listener
// TLS connection configuration
TLSConfig *tls.Config
// UDP "Listener" to use, this is to aid in systemd's socket activation.
PacketConn net.PacketConn
// Handler to invoke, dns.DefaultServeMux if nil.
Handler Handler
// Default buffer size to use to read incoming UDP messages. If not set
// it defaults to MinMsgSize (512 B).
UDPSize int
// The net.Conn.SetReadTimeout value for new connections, defaults to 2 * time.Second.
ReadTimeout time.Duration
// The net.Conn.SetWriteTimeout value for new connections, defaults to 2 * time.Second.
WriteTimeout time.Duration
// TCP idle timeout for multiple queries, if nil, defaults to 8 * time.Second (RFC 5966).
IdleTimeout func() time.Duration
// Secret(s) for Tsig map[<zonename>]<base64 secret>.
TsigSecret map[string]string
// Unsafe instructs the server to disregard any sanity checks and directly hand the message to
// the handler. It will specifically not check if the query has the QR bit not set.
Unsafe bool
// If NotifyStartedFunc is set it is called once the server has started listening.
NotifyStartedFunc func()
// DecorateReader is optional, allows customization of the process that reads raw DNS messages.
DecorateReader DecorateReader
// DecorateWriter is optional, allows customization of the process that writes raw DNS messages.
DecorateWriter DecorateWriter
// Graceful shutdown handling
inFlight sync.WaitGroup
lock sync.RWMutex
started bool
}
// ListenAndServe starts a nameserver on the configured address in *Server.
func (srv *Server) ListenAndServe() error {
srv.lock.Lock()
defer srv.lock.Unlock()
if srv.started {
return &Error{err: "server already started"}
}
addr := srv.Addr
if addr == "" {
addr = ":domain"
}
if srv.UDPSize == 0 {
srv.UDPSize = MinMsgSize
}
switch srv.Net {
case "tcp", "tcp4", "tcp6":
a, err := net.ResolveTCPAddr(srv.Net, addr)
if err != nil {
return err
}
l, err := net.ListenTCP(srv.Net, a)
if err != nil {
return err
}
srv.Listener = l
srv.started = true
srv.lock.Unlock()
err = srv.serveTCP(l)
srv.lock.Lock() // to satisfy the defer at the top
return err
case "tcp-tls", "tcp4-tls", "tcp6-tls":
network := "tcp"
if srv.Net == "tcp4-tls" {
network = "tcp4"
} else if srv.Net == "tcp6-tls" {
network = "tcp6"
}
l, err := tls.Listen(network, addr, srv.TLSConfig)
if err != nil {
return err
}
srv.Listener = l
srv.started = true
srv.lock.Unlock()
err = srv.serveTCP(l)
srv.lock.Lock() // to satisfy the defer at the top
return err
case "udp", "udp4", "udp6":
a, err := net.ResolveUDPAddr(srv.Net, addr)
if err != nil {
return err
}
l, err := net.ListenUDP(srv.Net, a)
if err != nil {
return err
}
if e := setUDPSocketOptions(l); e != nil {
return e
}
srv.PacketConn = l
srv.started = true
srv.lock.Unlock()
err = srv.serveUDP(l)
srv.lock.Lock() // to satisfy the defer at the top
return err
}
return &Error{err: "bad network"}
}
// ActivateAndServe starts a nameserver with the PacketConn or Listener
// configured in *Server. Its main use is to start a server from systemd.
func (srv *Server) ActivateAndServe() error {
srv.lock.Lock()
defer srv.lock.Unlock()
if srv.started {
return &Error{err: "server already started"}
}
pConn := srv.PacketConn
l := srv.Listener
if pConn != nil {
if srv.UDPSize == 0 {
srv.UDPSize = MinMsgSize
}
// Check PacketConn interface's type is valid and value
// is not nil
if t, ok := pConn.(*net.UDPConn); ok && t != nil {
if e := setUDPSocketOptions(t); e != nil {
return e
}
srv.started = true
srv.lock.Unlock()
e := srv.serveUDP(t)
srv.lock.Lock() // to satisfy the defer at the top
return e
}
}
if l != nil {
srv.started = true
srv.lock.Unlock()
e := srv.serveTCP(l)
srv.lock.Lock() // to satisfy the defer at the top
return e
}
return &Error{err: "bad listeners"}
}
// Shutdown gracefully shuts down a server. After a call to Shutdown, ListenAndServe and
// ActivateAndServe will return. All in progress queries are completed before the server
// is taken down. If the Shutdown is taking longer than the reading timeout an error
// is returned.
func (srv *Server) Shutdown() error {
srv.lock.Lock()
if !srv.started {
srv.lock.Unlock()
return &Error{err: "server not started"}
}
srv.started = false
srv.lock.Unlock()
if srv.PacketConn != nil {
srv.PacketConn.Close()
}
if srv.Listener != nil {
srv.Listener.Close()
}
fin := make(chan bool)
go func() {
srv.inFlight.Wait()
fin <- true
}()
select {
case <-time.After(srv.getReadTimeout()):
return &Error{err: "server shutdown is pending"}
case <-fin:
return nil
}
}
// getReadTimeout is a helper func to use system timeout if server did not intend to change it.
func (srv *Server) getReadTimeout() time.Duration {
rtimeout := dnsTimeout
if srv.ReadTimeout != 0 {
rtimeout = srv.ReadTimeout
}
return rtimeout
}
// serveTCP starts a TCP listener for the server.
// Each request is handled in a separate goroutine.
func (srv *Server) serveTCP(l net.Listener) error {
defer l.Close()
if srv.NotifyStartedFunc != nil {
srv.NotifyStartedFunc()
}
reader := Reader(&defaultReader{srv})
if srv.DecorateReader != nil {
reader = srv.DecorateReader(reader)
}
handler := srv.Handler
if handler == nil {
handler = DefaultServeMux
}
rtimeout := srv.getReadTimeout()
// deadline is not used here
for {
rw, err := l.Accept()
if err != nil {
if neterr, ok := err.(net.Error); ok && neterr.Temporary() {
continue
}
return err
}
m, err := reader.ReadTCP(rw, rtimeout)
srv.lock.RLock()
if !srv.started {
srv.lock.RUnlock()
return nil
}
srv.lock.RUnlock()
if err != nil {
continue
}
srv.inFlight.Add(1)
go srv.serve(rw.RemoteAddr(), handler, m, nil, nil, rw)
}
}
// serveUDP starts a UDP listener for the server.
// Each request is handled in a separate goroutine.
func (srv *Server) serveUDP(l *net.UDPConn) error {
defer l.Close()
if srv.NotifyStartedFunc != nil {
srv.NotifyStartedFunc()
}
reader := Reader(&defaultReader{srv})
if srv.DecorateReader != nil {
reader = srv.DecorateReader(reader)
}
handler := srv.Handler
if handler == nil {
handler = DefaultServeMux
}
rtimeout := srv.getReadTimeout()
// deadline is not used here
for {
m, s, err := reader.ReadUDP(l, rtimeout)
srv.lock.RLock()
if !srv.started {
srv.lock.RUnlock()
return nil
}
srv.lock.RUnlock()
if err != nil {
continue
}
srv.inFlight.Add(1)
go srv.serve(s.RemoteAddr(), handler, m, l, s, nil)
}
}
// Serve a new connection.
func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, s *SessionUDP, t net.Conn) {
defer srv.inFlight.Done()
w := &response{tsigSecret: srv.TsigSecret, udp: u, tcp: t, remoteAddr: a, udpSession: s}
if srv.DecorateWriter != nil {
w.writer = srv.DecorateWriter(w)
} else {
w.writer = w
}
q := 0 // counter for the amount of TCP queries we get
reader := Reader(&defaultReader{srv})
if srv.DecorateReader != nil {
reader = srv.DecorateReader(reader)
}
Redo:
req := new(Msg)
err := req.Unpack(m)
if err != nil { // Send a FormatError back
x := new(Msg)
x.SetRcodeFormatError(req)
w.WriteMsg(x)
goto Exit
}
if !srv.Unsafe && req.Response {
goto Exit
}
w.tsigStatus = nil
if w.tsigSecret != nil {
if t := req.IsTsig(); t != nil {
secret := t.Hdr.Name
if _, ok := w.tsigSecret[secret]; !ok {
w.tsigStatus = ErrKeyAlg
}
w.tsigStatus = TsigVerify(m, w.tsigSecret[secret], "", false)
w.tsigTimersOnly = false
w.tsigRequestMAC = req.Extra[len(req.Extra)-1].(*TSIG).MAC
}
}
h.ServeDNS(w, req) // Writes back to the client
Exit:
if w.tcp == nil {
return
}
// TODO(miek): make this number configurable?
if q > maxTCPQueries { // close socket after this many queries
w.Close()
return
}
if w.hijacked {
return // client calls Close()
}
if u != nil { // UDP, "close" and return
w.Close()
return
}
idleTimeout := tcpIdleTimeout
if srv.IdleTimeout != nil {
idleTimeout = srv.IdleTimeout()
}
m, err = reader.ReadTCP(w.tcp, idleTimeout)
if err == nil {
q++
goto Redo
}
w.Close()
return
}
func (srv *Server) readTCP(conn net.Conn, timeout time.Duration) ([]byte, error) {
conn.SetReadDeadline(time.Now().Add(timeout))
l := make([]byte, 2)
n, err := conn.Read(l)
if err != nil || n != 2 {
if err != nil {
return nil, err
}
return nil, ErrShortRead
}
length := binary.BigEndian.Uint16(l)
if length == 0 {
return nil, ErrShortRead
}
m := make([]byte, int(length))
n, err = conn.Read(m[:int(length)])
if err != nil || n == 0 {
if err != nil {
return nil, err
}
return nil, ErrShortRead
}
i := n
for i < int(length) {
j, err := conn.Read(m[i:int(length)])
if err != nil {
return nil, err
}
i += j
}
n = i
m = m[:n]
return m, nil
}
func (srv *Server) readUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) {
conn.SetReadDeadline(time.Now().Add(timeout))
m := make([]byte, srv.UDPSize)
n, s, err := ReadFromSessionUDP(conn, m)
if err != nil || n == 0 {
if err != nil {
return nil, nil, err
}
return nil, nil, ErrShortRead
}
m = m[:n]
return m, s, nil
}
// WriteMsg implements the ResponseWriter.WriteMsg method.
func (w *response) WriteMsg(m *Msg) (err error) {
var data []byte
if w.tsigSecret != nil { // if no secrets, dont check for the tsig (which is a longer check)
if t := m.IsTsig(); t != nil {
data, w.tsigRequestMAC, err = TsigGenerate(m, w.tsigSecret[t.Hdr.Name], w.tsigRequestMAC, w.tsigTimersOnly)
if err != nil {
return err
}
_, err = w.writer.Write(data)
return err
}
}
data, err = m.Pack()
if err != nil {
return err
}
_, err = w.writer.Write(data)
return err
}
// Write implements the ResponseWriter.Write method.
func (w *response) Write(m []byte) (int, error) {
switch {
case w.udp != nil:
n, err := WriteToSessionUDP(w.udp, m, w.udpSession)
return n, err
case w.tcp != nil:
lm := len(m)
if lm < 2 {
return 0, io.ErrShortBuffer
}
if lm > MaxMsgSize {
return 0, &Error{err: "message too large"}
}
l := make([]byte, 2, 2+lm)
binary.BigEndian.PutUint16(l, uint16(lm))
m = append(l, m...)
n, err := io.Copy(w.tcp, bytes.NewReader(m))
return int(n), err
}
panic("not reached")
}
// LocalAddr implements the ResponseWriter.LocalAddr method.
func (w *response) LocalAddr() net.Addr {
if w.tcp != nil {
return w.tcp.LocalAddr()
}
return w.udp.LocalAddr()
}
// RemoteAddr implements the ResponseWriter.RemoteAddr method.
func (w *response) RemoteAddr() net.Addr { return w.remoteAddr }
// TsigStatus implements the ResponseWriter.TsigStatus method.
func (w *response) TsigStatus() error { return w.tsigStatus }
// TsigTimersOnly implements the ResponseWriter.TsigTimersOnly method.
func (w *response) TsigTimersOnly(b bool) { w.tsigTimersOnly = b }
// Hijack implements the ResponseWriter.Hijack method.
func (w *response) Hijack() { w.hijacked = true }
// Close implements the ResponseWriter.Close method
func (w *response) Close() error {
// Can't close the udp conn, as that is actually the listener.
if w.tcp != nil {
e := w.tcp.Close()
w.tcp = nil
return e
}
return nil
}

View file

@ -1,719 +0,0 @@
package dns
import (
"crypto/tls"
"fmt"
"io"
"net"
"runtime"
"sync"
"testing"
"time"
)
func HelloServer(w ResponseWriter, req *Msg) {
m := new(Msg)
m.SetReply(req)
m.Extra = make([]RR, 1)
m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
w.WriteMsg(m)
}
func HelloServerBadID(w ResponseWriter, req *Msg) {
m := new(Msg)
m.SetReply(req)
m.Id++
m.Extra = make([]RR, 1)
m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
w.WriteMsg(m)
}
func AnotherHelloServer(w ResponseWriter, req *Msg) {
m := new(Msg)
m.SetReply(req)
m.Extra = make([]RR, 1)
m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello example"}}
w.WriteMsg(m)
}
func RunLocalUDPServer(laddr string) (*Server, string, error) {
server, l, _, err := RunLocalUDPServerWithFinChan(laddr)
return server, l, err
}
func RunLocalUDPServerWithFinChan(laddr string) (*Server, string, chan struct{}, error) {
pc, err := net.ListenPacket("udp", laddr)
if err != nil {
return nil, "", nil, err
}
server := &Server{PacketConn: pc, ReadTimeout: time.Hour, WriteTimeout: time.Hour}
waitLock := sync.Mutex{}
waitLock.Lock()
server.NotifyStartedFunc = waitLock.Unlock
fin := make(chan struct{}, 0)
go func() {
server.ActivateAndServe()
close(fin)
pc.Close()
}()
waitLock.Lock()
return server, pc.LocalAddr().String(), fin, nil
}
func RunLocalUDPServerUnsafe(laddr string) (*Server, string, error) {
pc, err := net.ListenPacket("udp", laddr)
if err != nil {
return nil, "", err
}
server := &Server{PacketConn: pc, Unsafe: true,
ReadTimeout: time.Hour, WriteTimeout: time.Hour}
waitLock := sync.Mutex{}
waitLock.Lock()
server.NotifyStartedFunc = waitLock.Unlock
go func() {
server.ActivateAndServe()
pc.Close()
}()
waitLock.Lock()
return server, pc.LocalAddr().String(), nil
}
func RunLocalTCPServer(laddr string) (*Server, string, error) {
l, err := net.Listen("tcp", laddr)
if err != nil {
return nil, "", err
}
server := &Server{Listener: l, ReadTimeout: time.Hour, WriteTimeout: time.Hour}
waitLock := sync.Mutex{}
waitLock.Lock()
server.NotifyStartedFunc = waitLock.Unlock
go func() {
server.ActivateAndServe()
l.Close()
}()
waitLock.Lock()
return server, l.Addr().String(), nil
}
func RunLocalTLSServer(laddr string, config *tls.Config) (*Server, string, error) {
l, err := tls.Listen("tcp", laddr, config)
if err != nil {
return nil, "", err
}
server := &Server{Listener: l, ReadTimeout: time.Hour, WriteTimeout: time.Hour}
waitLock := sync.Mutex{}
waitLock.Lock()
server.NotifyStartedFunc = waitLock.Unlock
go func() {
server.ActivateAndServe()
l.Close()
}()
waitLock.Lock()
return server, l.Addr().String(), nil
}
func TestServing(t *testing.T) {
HandleFunc("miek.nl.", HelloServer)
HandleFunc("example.com.", AnotherHelloServer)
defer HandleRemove("miek.nl.")
defer HandleRemove("example.com.")
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("unable to run test server: %v", err)
}
defer s.Shutdown()
c := new(Client)
m := new(Msg)
m.SetQuestion("miek.nl.", TypeTXT)
r, _, err := c.Exchange(m, addrstr)
if err != nil || len(r.Extra) == 0 {
t.Fatal("failed to exchange miek.nl", err)
}
txt := r.Extra[0].(*TXT).Txt[0]
if txt != "Hello world" {
t.Error("unexpected result for miek.nl", txt, "!= Hello world")
}
m.SetQuestion("example.com.", TypeTXT)
r, _, err = c.Exchange(m, addrstr)
if err != nil {
t.Fatal("failed to exchange example.com", err)
}
txt = r.Extra[0].(*TXT).Txt[0]
if txt != "Hello example" {
t.Error("unexpected result for example.com", txt, "!= Hello example")
}
// Test Mixes cased as noticed by Ask.
m.SetQuestion("eXaMplE.cOm.", TypeTXT)
r, _, err = c.Exchange(m, addrstr)
if err != nil {
t.Error("failed to exchange eXaMplE.cOm", err)
}
txt = r.Extra[0].(*TXT).Txt[0]
if txt != "Hello example" {
t.Error("unexpected result for example.com", txt, "!= Hello example")
}
}
func TestServingTLS(t *testing.T) {
HandleFunc("miek.nl.", HelloServer)
HandleFunc("example.com.", AnotherHelloServer)
defer HandleRemove("miek.nl.")
defer HandleRemove("example.com.")
cert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock)
if err != nil {
t.Fatalf("unable to build certificate: %v", err)
}
config := tls.Config{
Certificates: []tls.Certificate{cert},
}
s, addrstr, err := RunLocalTLSServer("127.0.0.1:0", &config)
if err != nil {
t.Fatalf("unable to run test server: %v", err)
}
defer s.Shutdown()
c := new(Client)
c.Net = "tcp-tls"
c.TLSConfig = &tls.Config{
InsecureSkipVerify: true,
}
m := new(Msg)
m.SetQuestion("miek.nl.", TypeTXT)
r, _, err := c.Exchange(m, addrstr)
if err != nil || len(r.Extra) == 0 {
t.Fatal("failed to exchange miek.nl", err)
}
txt := r.Extra[0].(*TXT).Txt[0]
if txt != "Hello world" {
t.Error("unexpected result for miek.nl", txt, "!= Hello world")
}
m.SetQuestion("example.com.", TypeTXT)
r, _, err = c.Exchange(m, addrstr)
if err != nil {
t.Fatal("failed to exchange example.com", err)
}
txt = r.Extra[0].(*TXT).Txt[0]
if txt != "Hello example" {
t.Error("unexpected result for example.com", txt, "!= Hello example")
}
// Test Mixes cased as noticed by Ask.
m.SetQuestion("eXaMplE.cOm.", TypeTXT)
r, _, err = c.Exchange(m, addrstr)
if err != nil {
t.Error("failed to exchange eXaMplE.cOm", err)
}
txt = r.Extra[0].(*TXT).Txt[0]
if txt != "Hello example" {
t.Error("unexpected result for example.com", txt, "!= Hello example")
}
}
func BenchmarkServe(b *testing.B) {
b.StopTimer()
HandleFunc("miek.nl.", HelloServer)
defer HandleRemove("miek.nl.")
a := runtime.GOMAXPROCS(4)
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
if err != nil {
b.Fatalf("unable to run test server: %v", err)
}
defer s.Shutdown()
c := new(Client)
m := new(Msg)
m.SetQuestion("miek.nl", TypeSOA)
b.StartTimer()
for i := 0; i < b.N; i++ {
c.Exchange(m, addrstr)
}
runtime.GOMAXPROCS(a)
}
func benchmarkServe6(b *testing.B) {
b.StopTimer()
HandleFunc("miek.nl.", HelloServer)
defer HandleRemove("miek.nl.")
a := runtime.GOMAXPROCS(4)
s, addrstr, err := RunLocalUDPServer("[::1]:0")
if err != nil {
b.Fatalf("unable to run test server: %v", err)
}
defer s.Shutdown()
c := new(Client)
m := new(Msg)
m.SetQuestion("miek.nl", TypeSOA)
b.StartTimer()
for i := 0; i < b.N; i++ {
c.Exchange(m, addrstr)
}
runtime.GOMAXPROCS(a)
}
func HelloServerCompress(w ResponseWriter, req *Msg) {
m := new(Msg)
m.SetReply(req)
m.Extra = make([]RR, 1)
m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
m.Compress = true
w.WriteMsg(m)
}
func BenchmarkServeCompress(b *testing.B) {
b.StopTimer()
HandleFunc("miek.nl.", HelloServerCompress)
defer HandleRemove("miek.nl.")
a := runtime.GOMAXPROCS(4)
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
if err != nil {
b.Fatalf("unable to run test server: %v", err)
}
defer s.Shutdown()
c := new(Client)
m := new(Msg)
m.SetQuestion("miek.nl", TypeSOA)
b.StartTimer()
for i := 0; i < b.N; i++ {
c.Exchange(m, addrstr)
}
runtime.GOMAXPROCS(a)
}
func TestDotAsCatchAllWildcard(t *testing.T) {
mux := NewServeMux()
mux.Handle(".", HandlerFunc(HelloServer))
mux.Handle("example.com.", HandlerFunc(AnotherHelloServer))
handler := mux.match("www.miek.nl.", TypeTXT)
if handler == nil {
t.Error("wildcard match failed")
}
handler = mux.match("www.example.com.", TypeTXT)
if handler == nil {
t.Error("example.com match failed")
}
handler = mux.match("a.www.example.com.", TypeTXT)
if handler == nil {
t.Error("a.www.example.com match failed")
}
handler = mux.match("boe.", TypeTXT)
if handler == nil {
t.Error("boe. match failed")
}
}
func TestCaseFolding(t *testing.T) {
mux := NewServeMux()
mux.Handle("_udp.example.com.", HandlerFunc(HelloServer))
handler := mux.match("_dns._udp.example.com.", TypeSRV)
if handler == nil {
t.Error("case sensitive characters folded")
}
handler = mux.match("_DNS._UDP.EXAMPLE.COM.", TypeSRV)
if handler == nil {
t.Error("case insensitive characters not folded")
}
}
func TestRootServer(t *testing.T) {
mux := NewServeMux()
mux.Handle(".", HandlerFunc(HelloServer))
handler := mux.match(".", TypeNS)
if handler == nil {
t.Error("root match failed")
}
}
type maxRec struct {
max int
sync.RWMutex
}
var M = new(maxRec)
func HelloServerLargeResponse(resp ResponseWriter, req *Msg) {
m := new(Msg)
m.SetReply(req)
m.Authoritative = true
m1 := 0
M.RLock()
m1 = M.max
M.RUnlock()
for i := 0; i < m1; i++ {
aRec := &A{
Hdr: RR_Header{
Name: req.Question[0].Name,
Rrtype: TypeA,
Class: ClassINET,
Ttl: 0,
},
A: net.ParseIP(fmt.Sprintf("127.0.0.%d", i+1)).To4(),
}
m.Answer = append(m.Answer, aRec)
}
resp.WriteMsg(m)
}
func TestServingLargeResponses(t *testing.T) {
HandleFunc("example.", HelloServerLargeResponse)
defer HandleRemove("example.")
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("unable to run test server: %v", err)
}
defer s.Shutdown()
// Create request
m := new(Msg)
m.SetQuestion("web.service.example.", TypeANY)
c := new(Client)
c.Net = "udp"
M.Lock()
M.max = 2
M.Unlock()
_, _, err = c.Exchange(m, addrstr)
if err != nil {
t.Errorf("failed to exchange: %v", err)
}
// This must fail
M.Lock()
M.max = 20
M.Unlock()
_, _, err = c.Exchange(m, addrstr)
if err == nil {
t.Error("failed to fail exchange, this should generate packet error")
}
// But this must work again
c.UDPSize = 7000
_, _, err = c.Exchange(m, addrstr)
if err != nil {
t.Errorf("failed to exchange: %v", err)
}
}
func TestServingResponse(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
HandleFunc("miek.nl.", HelloServer)
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("unable to run test server: %v", err)
}
c := new(Client)
m := new(Msg)
m.SetQuestion("miek.nl.", TypeTXT)
m.Response = false
_, _, err = c.Exchange(m, addrstr)
if err != nil {
t.Fatal("failed to exchange", err)
}
m.Response = true
_, _, err = c.Exchange(m, addrstr)
if err == nil {
t.Fatal("exchanged response message")
}
s.Shutdown()
s, addrstr, err = RunLocalUDPServerUnsafe("127.0.0.1:0")
if err != nil {
t.Fatalf("unable to run test server: %v", err)
}
defer s.Shutdown()
m.Response = true
_, _, err = c.Exchange(m, addrstr)
if err != nil {
t.Fatal("could exchanged response message in Unsafe mode")
}
}
func TestShutdownTCP(t *testing.T) {
s, _, err := RunLocalTCPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("unable to run test server: %v", err)
}
err = s.Shutdown()
if err != nil {
t.Errorf("could not shutdown test TCP server, %v", err)
}
}
func TestShutdownTLS(t *testing.T) {
cert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock)
if err != nil {
t.Fatalf("unable to build certificate: %v", err)
}
config := tls.Config{
Certificates: []tls.Certificate{cert},
}
s, _, err := RunLocalTLSServer("127.0.0.1:0", &config)
if err != nil {
t.Fatalf("unable to run test server: %v", err)
}
err = s.Shutdown()
if err != nil {
t.Errorf("could not shutdown test TLS server, %v", err)
}
}
type trigger struct {
done bool
sync.RWMutex
}
func (t *trigger) Set() {
t.Lock()
defer t.Unlock()
t.done = true
}
func (t *trigger) Get() bool {
t.RLock()
defer t.RUnlock()
return t.done
}
func TestHandlerCloseTCP(t *testing.T) {
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
panic(err)
}
addr := ln.Addr().String()
server := &Server{Addr: addr, Net: "tcp", Listener: ln}
hname := "testhandlerclosetcp."
triggered := &trigger{}
HandleFunc(hname, func(w ResponseWriter, r *Msg) {
triggered.Set()
w.Close()
})
defer HandleRemove(hname)
go func() {
defer server.Shutdown()
c := &Client{Net: "tcp"}
m := new(Msg).SetQuestion(hname, 1)
tries := 0
exchange:
_, _, err := c.Exchange(m, addr)
if err != nil && err != io.EOF {
t.Logf("exchange failed: %s\n", err)
if tries == 3 {
return
}
time.Sleep(time.Second / 10)
tries++
goto exchange
}
}()
server.ActivateAndServe()
if !triggered.Get() {
t.Fatalf("handler never called")
}
}
func TestShutdownUDP(t *testing.T) {
s, _, fin, err := RunLocalUDPServerWithFinChan("127.0.0.1:0")
if err != nil {
t.Fatalf("unable to run test server: %v", err)
}
err = s.Shutdown()
if err != nil {
t.Errorf("could not shutdown test UDP server, %v", err)
}
select {
case <-fin:
case <-time.After(2 * time.Second):
t.Error("Could not shutdown test UDP server. Gave up waiting")
}
}
type ExampleFrameLengthWriter struct {
Writer
}
func (e *ExampleFrameLengthWriter) Write(m []byte) (int, error) {
fmt.Println("writing raw DNS message of length", len(m))
return e.Writer.Write(m)
}
func ExampleDecorateWriter() {
// instrument raw DNS message writing
wf := DecorateWriter(func(w Writer) Writer {
return &ExampleFrameLengthWriter{w}
})
// simple UDP server
pc, err := net.ListenPacket("udp", "127.0.0.1:0")
if err != nil {
fmt.Println(err.Error())
return
}
server := &Server{
PacketConn: pc,
DecorateWriter: wf,
ReadTimeout: time.Hour, WriteTimeout: time.Hour,
}
waitLock := sync.Mutex{}
waitLock.Lock()
server.NotifyStartedFunc = waitLock.Unlock
defer server.Shutdown()
go func() {
server.ActivateAndServe()
pc.Close()
}()
waitLock.Lock()
HandleFunc("miek.nl.", HelloServer)
c := new(Client)
m := new(Msg)
m.SetQuestion("miek.nl.", TypeTXT)
_, _, err = c.Exchange(m, pc.LocalAddr().String())
if err != nil {
fmt.Println("failed to exchange", err.Error())
return
}
// Output: writing raw DNS message of length 56
}
var (
// CertPEMBlock is a X509 data used to test TLS servers (used with tls.X509KeyPair)
CertPEMBlock = []byte(`-----BEGIN CERTIFICATE-----
MIIDAzCCAeugAwIBAgIRAJFYMkcn+b8dpU15wjf++GgwDQYJKoZIhvcNAQELBQAw
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNjAxMDgxMjAzNTNaFw0xNzAxMDcxMjAz
NTNaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQDXjqO6skvP03k58CNjQggd9G/mt+Wa+xRU+WXiKCCHttawM8x+slq5
yfsHCwxlwsGn79HmJqecNqgHb2GWBXAvVVokFDTcC1hUP4+gp2gu9Ny27UHTjlLm
O0l/xZ5MN8tfKyYlFw18tXu3fkaPyHj8v/D1RDkuo4ARdFvGSe8TqisbhLk2+9ow
xfIGbEM9Fdiw8qByC2+d+FfvzIKz3GfQVwn0VoRom8L6NBIANq1IGrB5JefZB6nv
DnfuxkBmY7F1513HKuEJ8KsLWWZWV9OPU4j4I4Rt+WJNlKjbD2srHxyrS2RDsr91
8nCkNoWVNO3sZq0XkWKecdc921vL4ginAgMBAAGjVDBSMA4GA1UdDwEB/wQEAwIC
pDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MBoGA1UdEQQT
MBGCCWxvY2FsaG9zdIcEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEAGcU3iyLBIVZj
aDzSvEDHUd1bnLBl1C58Xu/CyKlPqVU7mLfK0JcgEaYQTSX6fCJVNLbbCrcGLsPJ
fbjlBbyeLjTV413fxPVuona62pBFjqdtbli2Qe8FRH2KBdm41JUJGdo+SdsFu7nc
BFOcubdw6LLIXvsTvwndKcHWx1rMX709QU1Vn1GAIsbJV/DWI231Jyyb+lxAUx/C
8vce5uVxiKcGS+g6OjsN3D3TtiEQGSXLh013W6Wsih8td8yMCMZ3w8LQ38br1GUe
ahLIgUJ9l6HDguM17R7kGqxNvbElsMUHfTtXXP7UDQUiYXDakg8xDP6n9DCDhJ8Y
bSt7OLB7NQ==
-----END CERTIFICATE-----`)
// KeyPEMBlock is a X509 data used to test TLS servers (used with tls.X509KeyPair)
KeyPEMBlock = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA146jurJLz9N5OfAjY0IIHfRv5rflmvsUVPll4iggh7bWsDPM
frJaucn7BwsMZcLBp+/R5iannDaoB29hlgVwL1VaJBQ03AtYVD+PoKdoLvTctu1B
045S5jtJf8WeTDfLXysmJRcNfLV7t35Gj8h4/L/w9UQ5LqOAEXRbxknvE6orG4S5
NvvaMMXyBmxDPRXYsPKgcgtvnfhX78yCs9xn0FcJ9FaEaJvC+jQSADatSBqweSXn
2Qep7w537sZAZmOxdeddxyrhCfCrC1lmVlfTj1OI+COEbfliTZSo2w9rKx8cq0tk
Q7K/dfJwpDaFlTTt7GatF5FinnHXPdtby+IIpwIDAQABAoIBAAJK4RDmPooqTJrC
JA41MJLo+5uvjwCT9QZmVKAQHzByUFw1YNJkITTiognUI0CdzqNzmH7jIFs39ZeG
proKusO2G6xQjrNcZ4cV2fgyb5g4QHStl0qhs94A+WojduiGm2IaumAgm6Mc5wDv
ld6HmknN3Mku/ZCyanVFEIjOVn2WB7ZQLTBs6ZYaebTJG2Xv6p9t2YJW7pPQ9Xce
s9ohAWohyM4X/OvfnfnLtQp2YLw/BxwehBsCR5SXM3ibTKpFNtxJC8hIfTuWtxZu
2ywrmXShYBRB1WgtZt5k04bY/HFncvvcHK3YfI1+w4URKtwdaQgPUQRbVwDwuyBn
flfkCJECgYEA/eWt01iEyE/lXkGn6V9lCocUU7lCU6yk5UT8VXVUc5If4KZKPfCk
p4zJDOqwn2eM673aWz/mG9mtvAvmnugaGjcaVCyXOp/D/GDmKSoYcvW5B/yjfkLy
dK6Yaa5LDRVYlYgyzcdCT5/9Qc626NzFwKCZNI4ncIU8g7ViATRxWJ8CgYEA2Ver
vZ0M606sfgC0H3NtwNBxmuJ+lIF5LNp/wDi07lDfxRR1rnZMX5dnxjcpDr/zvm8J
WtJJX3xMgqjtHuWKL3yKKony9J5ZPjichSbSbhrzfovgYIRZLxLLDy4MP9L3+CX/
yBXnqMWuSnFX+M5fVGxdDWiYF3V+wmeOv9JvavkCgYEAiXAPDFzaY+R78O3xiu7M
r0o3wqqCMPE/wav6O/hrYrQy9VSO08C0IM6g9pEEUwWmzuXSkZqhYWoQFb8Lc/GI
T7CMXAxXQLDDUpbRgG79FR3Wr3AewHZU8LyiXHKwxcBMV4WGmsXGK3wbh8fyU1NO
6NsGk+BvkQVOoK1LBAPzZ1kCgYEAsBSmD8U33T9s4dxiEYTrqyV0lH3g/SFz8ZHH
pAyNEPI2iC1ONhyjPWKlcWHpAokiyOqeUpVBWnmSZtzC1qAydsxYB6ShT+sl9BHb
RMix/QAauzBJhQhUVJ3OIys0Q1UBDmqCsjCE8SfOT4NKOUnA093C+YT+iyrmmktZ
zDCJkckCgYEAndqM5KXGk5xYo+MAA1paZcbTUXwaWwjLU+XSRSSoyBEi5xMtfvUb
7+a1OMhLwWbuz+pl64wFKrbSUyimMOYQpjVE/1vk/kb99pxbgol27hdKyTH1d+ov
kFsxKCqxAnBVGEWAvVZAiiTOxleQFjz5RnL0BQp9Lg2cQe+dvuUmIAA=
-----END RSA PRIVATE KEY-----`)
)
func testShutdownBindPort(t *testing.T, protocol string, port string) {
handler := NewServeMux()
handler.HandleFunc(".", func(w ResponseWriter, r *Msg) {})
startedCh := make(chan struct{})
s := &Server{
Addr: net.JoinHostPort("127.0.0.1", port),
Net: protocol,
Handler: handler,
NotifyStartedFunc: func() {
startedCh <- struct{}{}
},
}
go func() {
if err := s.ListenAndServe(); err != nil {
t.Log(err)
}
}()
<-startedCh
t.Logf("DNS server is started on: %s", s.Addr)
if err := s.Shutdown(); err != nil {
t.Fatal(err)
}
time.Sleep(100 * time.Millisecond)
go func() {
if err := s.ListenAndServe(); err != nil {
t.Fatal(err)
}
}()
<-startedCh
t.Logf("DNS server is started on: %s", s.Addr)
}
func TestShutdownBindPortUDP(t *testing.T) {
testShutdownBindPort(t, "udp", "1153")
}
func TestShutdownBindPortTCP(t *testing.T) {
testShutdownBindPort(t, "tcp", "1154")
}

218
vendor/github.com/miekg/dns/sig0.go generated vendored
View file

@ -1,218 +0,0 @@
package dns
import (
"crypto"
"crypto/dsa"
"crypto/ecdsa"
"crypto/rsa"
"encoding/binary"
"math/big"
"strings"
"time"
)
// Sign signs a dns.Msg. It fills the signature with the appropriate data.
// The SIG record should have the SignerName, KeyTag, Algorithm, Inception
// and Expiration set.
func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error) {
if k == nil {
return nil, ErrPrivKey
}
if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
return nil, ErrKey
}
rr.Header().Rrtype = TypeSIG
rr.Header().Class = ClassANY
rr.Header().Ttl = 0
rr.Header().Name = "."
rr.OrigTtl = 0
rr.TypeCovered = 0
rr.Labels = 0
buf := make([]byte, m.Len()+rr.len())
mbuf, err := m.PackBuffer(buf)
if err != nil {
return nil, err
}
if &buf[0] != &mbuf[0] {
return nil, ErrBuf
}
off, err := PackRR(rr, buf, len(mbuf), nil, false)
if err != nil {
return nil, err
}
buf = buf[:off:cap(buf)]
hash, ok := AlgorithmToHash[rr.Algorithm]
if !ok {
return nil, ErrAlg
}
hasher := hash.New()
// Write SIG rdata
hasher.Write(buf[len(mbuf)+1+2+2+4+2:])
// Write message
hasher.Write(buf[:len(mbuf)])
signature, err := sign(k, hasher.Sum(nil), hash, rr.Algorithm)
if err != nil {
return nil, err
}
rr.Signature = toBase64(signature)
buf = append(buf, signature...)
if len(buf) > int(^uint16(0)) {
return nil, ErrBuf
}
// Adjust sig data length
rdoff := len(mbuf) + 1 + 2 + 2 + 4
rdlen := binary.BigEndian.Uint16(buf[rdoff:])
rdlen += uint16(len(signature))
binary.BigEndian.PutUint16(buf[rdoff:], rdlen)
// Adjust additional count
adc := binary.BigEndian.Uint16(buf[10:])
adc++
binary.BigEndian.PutUint16(buf[10:], adc)
return buf, nil
}
// Verify validates the message buf using the key k.
// It's assumed that buf is a valid message from which rr was unpacked.
func (rr *SIG) Verify(k *KEY, buf []byte) error {
if k == nil {
return ErrKey
}
if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
return ErrKey
}
var hash crypto.Hash
switch rr.Algorithm {
case DSA, RSASHA1:
hash = crypto.SHA1
case RSASHA256, ECDSAP256SHA256:
hash = crypto.SHA256
case ECDSAP384SHA384:
hash = crypto.SHA384
case RSASHA512:
hash = crypto.SHA512
default:
return ErrAlg
}
hasher := hash.New()
buflen := len(buf)
qdc := binary.BigEndian.Uint16(buf[4:])
anc := binary.BigEndian.Uint16(buf[6:])
auc := binary.BigEndian.Uint16(buf[8:])
adc := binary.BigEndian.Uint16(buf[10:])
offset := 12
var err error
for i := uint16(0); i < qdc && offset < buflen; i++ {
_, offset, err = UnpackDomainName(buf, offset)
if err != nil {
return err
}
// Skip past Type and Class
offset += 2 + 2
}
for i := uint16(1); i < anc+auc+adc && offset < buflen; i++ {
_, offset, err = UnpackDomainName(buf, offset)
if err != nil {
return err
}
// Skip past Type, Class and TTL
offset += 2 + 2 + 4
if offset+1 >= buflen {
continue
}
var rdlen uint16
rdlen = binary.BigEndian.Uint16(buf[offset:])
offset += 2
offset += int(rdlen)
}
if offset >= buflen {
return &Error{err: "overflowing unpacking signed message"}
}
// offset should be just prior to SIG
bodyend := offset
// owner name SHOULD be root
_, offset, err = UnpackDomainName(buf, offset)
if err != nil {
return err
}
// Skip Type, Class, TTL, RDLen
offset += 2 + 2 + 4 + 2
sigstart := offset
// Skip Type Covered, Algorithm, Labels, Original TTL
offset += 2 + 1 + 1 + 4
if offset+4+4 >= buflen {
return &Error{err: "overflow unpacking signed message"}
}
expire := binary.BigEndian.Uint32(buf[offset:])
offset += 4
incept := binary.BigEndian.Uint32(buf[offset:])
offset += 4
now := uint32(time.Now().Unix())
if now < incept || now > expire {
return ErrTime
}
// Skip key tag
offset += 2
var signername string
signername, offset, err = UnpackDomainName(buf, offset)
if err != nil {
return err
}
// If key has come from the DNS name compression might
// have mangled the case of the name
if strings.ToLower(signername) != strings.ToLower(k.Header().Name) {
return &Error{err: "signer name doesn't match key name"}
}
sigend := offset
hasher.Write(buf[sigstart:sigend])
hasher.Write(buf[:10])
hasher.Write([]byte{
byte((adc - 1) << 8),
byte(adc - 1),
})
hasher.Write(buf[12:bodyend])
hashed := hasher.Sum(nil)
sig := buf[sigend:]
switch k.Algorithm {
case DSA:
pk := k.publicKeyDSA()
sig = sig[1:]
r := big.NewInt(0)
r.SetBytes(sig[:len(sig)/2])
s := big.NewInt(0)
s.SetBytes(sig[len(sig)/2:])
if pk != nil {
if dsa.Verify(pk, hashed, r, s) {
return nil
}
return ErrSig
}
case RSASHA1, RSASHA256, RSASHA512:
pk := k.publicKeyRSA()
if pk != nil {
return rsa.VerifyPKCS1v15(pk, hash, hashed, sig)
}
case ECDSAP256SHA256, ECDSAP384SHA384:
pk := k.publicKeyECDSA()
r := big.NewInt(0)
r.SetBytes(sig[:len(sig)/2])
s := big.NewInt(0)
s.SetBytes(sig[len(sig)/2:])
if pk != nil {
if ecdsa.Verify(pk, hashed, r, s) {
return nil
}
return ErrSig
}
}
return ErrKeyAlg
}

View file

@ -1,89 +0,0 @@
package dns
import (
"crypto"
"testing"
"time"
)
func TestSIG0(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
m := new(Msg)
m.SetQuestion("example.org.", TypeSOA)
for _, alg := range []uint8{ECDSAP256SHA256, ECDSAP384SHA384, RSASHA1, RSASHA256, RSASHA512} {
algstr := AlgorithmToString[alg]
keyrr := new(KEY)
keyrr.Hdr.Name = algstr + "."
keyrr.Hdr.Rrtype = TypeKEY
keyrr.Hdr.Class = ClassINET
keyrr.Algorithm = alg
keysize := 1024
switch alg {
case ECDSAP256SHA256:
keysize = 256
case ECDSAP384SHA384:
keysize = 384
}
pk, err := keyrr.Generate(keysize)
if err != nil {
t.Errorf("failed to generate key for “%s”: %v", algstr, err)
continue
}
now := uint32(time.Now().Unix())
sigrr := new(SIG)
sigrr.Hdr.Name = "."
sigrr.Hdr.Rrtype = TypeSIG
sigrr.Hdr.Class = ClassANY
sigrr.Algorithm = alg
sigrr.Expiration = now + 300
sigrr.Inception = now - 300
sigrr.KeyTag = keyrr.KeyTag()
sigrr.SignerName = keyrr.Hdr.Name
mb, err := sigrr.Sign(pk.(crypto.Signer), m)
if err != nil {
t.Errorf("failed to sign message using “%s”: %v", algstr, err)
continue
}
m := new(Msg)
if err := m.Unpack(mb); err != nil {
t.Errorf("failed to unpack message signed using “%s”: %v", algstr, err)
continue
}
if len(m.Extra) != 1 {
t.Errorf("missing SIG for message signed using “%s”", algstr)
continue
}
var sigrrwire *SIG
switch rr := m.Extra[0].(type) {
case *SIG:
sigrrwire = rr
default:
t.Errorf("expected SIG RR, instead: %v", rr)
continue
}
for _, rr := range []*SIG{sigrr, sigrrwire} {
id := "sigrr"
if rr == sigrrwire {
id = "sigrrwire"
}
if err := rr.Verify(keyrr, mb); err != nil {
t.Errorf("failed to verify “%s” signed SIG(%s): %v", algstr, id, err)
continue
}
}
mb[13]++
if err := sigrr.Verify(keyrr, mb); err == nil {
t.Errorf("verify succeeded on an altered message using “%s”", algstr)
continue
}
sigrr.Expiration = 2
sigrr.Inception = 1
mb, _ = sigrr.Sign(pk.(crypto.Signer), m)
if err := sigrr.Verify(keyrr, mb); err == nil {
t.Errorf("verify succeeded on an expired message using “%s”", algstr)
continue
}
}
}

View file

@ -1,57 +0,0 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Adapted for dns package usage by Miek Gieben.
package dns
import "sync"
import "time"
// call is an in-flight or completed singleflight.Do call
type call struct {
wg sync.WaitGroup
val *Msg
rtt time.Duration
err error
dups int
}
// singleflight represents a class of work and forms a namespace in
// which units of work can be executed with duplicate suppression.
type singleflight struct {
sync.Mutex // protects m
m map[string]*call // lazily initialized
}
// Do executes and returns the results of the given function, making
// sure that only one execution is in-flight for a given key at a
// time. If a duplicate comes in, the duplicate caller waits for the
// original to complete and receives the same results.
// The return value shared indicates whether v was given to multiple callers.
func (g *singleflight) Do(key string, fn func() (*Msg, time.Duration, error)) (v *Msg, rtt time.Duration, err error, shared bool) {
g.Lock()
if g.m == nil {
g.m = make(map[string]*call)
}
if c, ok := g.m[key]; ok {
c.dups++
g.Unlock()
c.wg.Wait()
return c.val, c.rtt, c.err, true
}
c := new(call)
c.wg.Add(1)
g.m[key] = c
g.Unlock()
c.val, c.rtt, c.err = fn()
c.wg.Done()
g.Lock()
delete(g.m, key)
g.Unlock()
return c.val, c.rtt, c.err, c.dups > 0
}

View file

@ -1,47 +0,0 @@
package dns
import (
"crypto/sha256"
"crypto/x509"
"encoding/hex"
)
// Sign creates a SMIMEA record from an SSL certificate.
func (r *SMIMEA) Sign(usage, selector, matchingType int, cert *x509.Certificate) (err error) {
r.Hdr.Rrtype = TypeSMIMEA
r.Usage = uint8(usage)
r.Selector = uint8(selector)
r.MatchingType = uint8(matchingType)
r.Certificate, err = CertificateToDANE(r.Selector, r.MatchingType, cert)
if err != nil {
return err
}
return nil
}
// Verify verifies a SMIMEA record against an SSL certificate. If it is OK
// a nil error is returned.
func (r *SMIMEA) Verify(cert *x509.Certificate) error {
c, err := CertificateToDANE(r.Selector, r.MatchingType, cert)
if err != nil {
return err // Not also ErrSig?
}
if r.Certificate == c {
return nil
}
return ErrSig // ErrSig, really?
}
// SMIMEAName returns the ownername of a SMIMEA resource record as per the
// format specified in RFC 'draft-ietf-dane-smime-12' Section 2 and 3
func SMIMEAName(email, domain string) (string, error) {
hasher := sha256.New()
hasher.Write([]byte(email))
// RFC Section 3: "The local-part is hashed using the SHA2-256
// algorithm with the hash truncated to 28 octets and
// represented in its hexadecimal representation to become the
// left-most label in the prepared domain name"
return hex.EncodeToString(hasher.Sum(nil)[:28]) + "." + "_smimecert." + domain, nil
}

47
vendor/github.com/miekg/dns/tlsa.go generated vendored
View file

@ -1,47 +0,0 @@
package dns
import (
"crypto/x509"
"net"
"strconv"
)
// Sign creates a TLSA record from an SSL certificate.
func (r *TLSA) Sign(usage, selector, matchingType int, cert *x509.Certificate) (err error) {
r.Hdr.Rrtype = TypeTLSA
r.Usage = uint8(usage)
r.Selector = uint8(selector)
r.MatchingType = uint8(matchingType)
r.Certificate, err = CertificateToDANE(r.Selector, r.MatchingType, cert)
if err != nil {
return err
}
return nil
}
// Verify verifies a TLSA record against an SSL certificate. If it is OK
// a nil error is returned.
func (r *TLSA) Verify(cert *x509.Certificate) error {
c, err := CertificateToDANE(r.Selector, r.MatchingType, cert)
if err != nil {
return err // Not also ErrSig?
}
if r.Certificate == c {
return nil
}
return ErrSig // ErrSig, really?
}
// TLSAName returns the ownername of a TLSA resource record as per the
// rules specified in RFC 6698, Section 3.
func TLSAName(name, service, network string) (string, error) {
if !IsFqdn(name) {
return "", ErrFqdn
}
p, err := net.LookupPort(network, service)
if err != nil {
return "", err
}
return "_" + strconv.Itoa(p) + "._" + network + "." + name, nil
}

383
vendor/github.com/miekg/dns/tsig.go generated vendored
View file

@ -1,383 +0,0 @@
package dns
import (
"crypto/hmac"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/binary"
"encoding/hex"
"hash"
"strconv"
"strings"
"time"
)
// HMAC hashing codes. These are transmitted as domain names.
const (
HmacMD5 = "hmac-md5.sig-alg.reg.int."
HmacSHA1 = "hmac-sha1."
HmacSHA256 = "hmac-sha256."
HmacSHA512 = "hmac-sha512."
)
// TSIG is the RR the holds the transaction signature of a message.
// See RFC 2845 and RFC 4635.
type TSIG struct {
Hdr RR_Header
Algorithm string `dns:"domain-name"`
TimeSigned uint64 `dns:"uint48"`
Fudge uint16
MACSize uint16
MAC string `dns:"size-hex:MACSize"`
OrigId uint16
Error uint16
OtherLen uint16
OtherData string `dns:"size-hex:OtherLen"`
}
// TSIG has no official presentation format, but this will suffice.
func (rr *TSIG) String() string {
s := "\n;; TSIG PSEUDOSECTION:\n"
s += rr.Hdr.String() +
" " + rr.Algorithm +
" " + tsigTimeToString(rr.TimeSigned) +
" " + strconv.Itoa(int(rr.Fudge)) +
" " + strconv.Itoa(int(rr.MACSize)) +
" " + strings.ToUpper(rr.MAC) +
" " + strconv.Itoa(int(rr.OrigId)) +
" " + strconv.Itoa(int(rr.Error)) + // BIND prints NOERROR
" " + strconv.Itoa(int(rr.OtherLen)) +
" " + rr.OtherData
return s
}
// The following values must be put in wireformat, so that the MAC can be calculated.
// RFC 2845, section 3.4.2. TSIG Variables.
type tsigWireFmt struct {
// From RR_Header
Name string `dns:"domain-name"`
Class uint16
Ttl uint32
// Rdata of the TSIG
Algorithm string `dns:"domain-name"`
TimeSigned uint64 `dns:"uint48"`
Fudge uint16
// MACSize, MAC and OrigId excluded
Error uint16
OtherLen uint16
OtherData string `dns:"size-hex:OtherLen"`
}
// If we have the MAC use this type to convert it to wiredata. Section 3.4.3. Request MAC
type macWireFmt struct {
MACSize uint16
MAC string `dns:"size-hex:MACSize"`
}
// 3.3. Time values used in TSIG calculations
type timerWireFmt struct {
TimeSigned uint64 `dns:"uint48"`
Fudge uint16
}
// TsigGenerate fills out the TSIG record attached to the message.
// The message should contain
// a "stub" TSIG RR with the algorithm, key name (owner name of the RR),
// time fudge (defaults to 300 seconds) and the current time
// The TSIG MAC is saved in that Tsig RR.
// When TsigGenerate is called for the first time requestMAC is set to the empty string and
// timersOnly is false.
// If something goes wrong an error is returned, otherwise it is nil.
func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) {
if m.IsTsig() == nil {
panic("dns: TSIG not last RR in additional")
}
// If we barf here, the caller is to blame
rawsecret, err := fromBase64([]byte(secret))
if err != nil {
return nil, "", err
}
rr := m.Extra[len(m.Extra)-1].(*TSIG)
m.Extra = m.Extra[0 : len(m.Extra)-1] // kill the TSIG from the msg
mbuf, err := m.Pack()
if err != nil {
return nil, "", err
}
buf := tsigBuffer(mbuf, rr, requestMAC, timersOnly)
t := new(TSIG)
var h hash.Hash
switch strings.ToLower(rr.Algorithm) {
case HmacMD5:
h = hmac.New(md5.New, []byte(rawsecret))
case HmacSHA1:
h = hmac.New(sha1.New, []byte(rawsecret))
case HmacSHA256:
h = hmac.New(sha256.New, []byte(rawsecret))
case HmacSHA512:
h = hmac.New(sha512.New, []byte(rawsecret))
default:
return nil, "", ErrKeyAlg
}
h.Write(buf)
t.MAC = hex.EncodeToString(h.Sum(nil))
t.MACSize = uint16(len(t.MAC) / 2) // Size is half!
t.Hdr = RR_Header{Name: rr.Hdr.Name, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0}
t.Fudge = rr.Fudge
t.TimeSigned = rr.TimeSigned
t.Algorithm = rr.Algorithm
t.OrigId = m.Id
tbuf := make([]byte, t.len())
if off, err := PackRR(t, tbuf, 0, nil, false); err == nil {
tbuf = tbuf[:off] // reset to actual size used
} else {
return nil, "", err
}
mbuf = append(mbuf, tbuf...)
// Update the ArCount directly in the buffer.
binary.BigEndian.PutUint16(mbuf[10:], uint16(len(m.Extra)+1))
return mbuf, t.MAC, nil
}
// TsigVerify verifies the TSIG on a message.
// If the signature does not validate err contains the
// error, otherwise it is nil.
func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {
rawsecret, err := fromBase64([]byte(secret))
if err != nil {
return err
}
// Strip the TSIG from the incoming msg
stripped, tsig, err := stripTsig(msg)
if err != nil {
return err
}
msgMAC, err := hex.DecodeString(tsig.MAC)
if err != nil {
return err
}
buf := tsigBuffer(stripped, tsig, requestMAC, timersOnly)
// Fudge factor works both ways. A message can arrive before it was signed because
// of clock skew.
now := uint64(time.Now().Unix())
ti := now - tsig.TimeSigned
if now < tsig.TimeSigned {
ti = tsig.TimeSigned - now
}
if uint64(tsig.Fudge) < ti {
return ErrTime
}
var h hash.Hash
switch strings.ToLower(tsig.Algorithm) {
case HmacMD5:
h = hmac.New(md5.New, rawsecret)
case HmacSHA1:
h = hmac.New(sha1.New, rawsecret)
case HmacSHA256:
h = hmac.New(sha256.New, rawsecret)
case HmacSHA512:
h = hmac.New(sha512.New, rawsecret)
default:
return ErrKeyAlg
}
h.Write(buf)
if !hmac.Equal(h.Sum(nil), msgMAC) {
return ErrSig
}
return nil
}
// Create a wiredata buffer for the MAC calculation.
func tsigBuffer(msgbuf []byte, rr *TSIG, requestMAC string, timersOnly bool) []byte {
var buf []byte
if rr.TimeSigned == 0 {
rr.TimeSigned = uint64(time.Now().Unix())
}
if rr.Fudge == 0 {
rr.Fudge = 300 // Standard (RFC) default.
}
if requestMAC != "" {
m := new(macWireFmt)
m.MACSize = uint16(len(requestMAC) / 2)
m.MAC = requestMAC
buf = make([]byte, len(requestMAC)) // long enough
n, _ := packMacWire(m, buf)
buf = buf[:n]
}
tsigvar := make([]byte, DefaultMsgSize)
if timersOnly {
tsig := new(timerWireFmt)
tsig.TimeSigned = rr.TimeSigned
tsig.Fudge = rr.Fudge
n, _ := packTimerWire(tsig, tsigvar)
tsigvar = tsigvar[:n]
} else {
tsig := new(tsigWireFmt)
tsig.Name = strings.ToLower(rr.Hdr.Name)
tsig.Class = ClassANY
tsig.Ttl = rr.Hdr.Ttl
tsig.Algorithm = strings.ToLower(rr.Algorithm)
tsig.TimeSigned = rr.TimeSigned
tsig.Fudge = rr.Fudge
tsig.Error = rr.Error
tsig.OtherLen = rr.OtherLen
tsig.OtherData = rr.OtherData
n, _ := packTsigWire(tsig, tsigvar)
tsigvar = tsigvar[:n]
}
if requestMAC != "" {
x := append(buf, msgbuf...)
buf = append(x, tsigvar...)
} else {
buf = append(msgbuf, tsigvar...)
}
return buf
}
// Strip the TSIG from the raw message.
func stripTsig(msg []byte) ([]byte, *TSIG, error) {
// Copied from msg.go's Unpack() Header, but modified.
var (
dh Header
err error
)
off, tsigoff := 0, 0
if dh, off, err = unpackMsgHdr(msg, off); err != nil {
return nil, nil, err
}
if dh.Arcount == 0 {
return nil, nil, ErrNoSig
}
// Rcode, see msg.go Unpack()
if int(dh.Bits&0xF) == RcodeNotAuth {
return nil, nil, ErrAuth
}
for i := 0; i < int(dh.Qdcount); i++ {
_, off, err = unpackQuestion(msg, off)
if err != nil {
return nil, nil, err
}
}
_, off, err = unpackRRslice(int(dh.Ancount), msg, off)
if err != nil {
return nil, nil, err
}
_, off, err = unpackRRslice(int(dh.Nscount), msg, off)
if err != nil {
return nil, nil, err
}
rr := new(TSIG)
var extra RR
for i := 0; i < int(dh.Arcount); i++ {
tsigoff = off
extra, off, err = UnpackRR(msg, off)
if err != nil {
return nil, nil, err
}
if extra.Header().Rrtype == TypeTSIG {
rr = extra.(*TSIG)
// Adjust Arcount.
arcount := binary.BigEndian.Uint16(msg[10:])
binary.BigEndian.PutUint16(msg[10:], arcount-1)
break
}
}
if rr == nil {
return nil, nil, ErrNoSig
}
return msg[:tsigoff], rr, nil
}
// Translate the TSIG time signed into a date. There is no
// need for RFC1982 calculations as this date is 48 bits.
func tsigTimeToString(t uint64) string {
ti := time.Unix(int64(t), 0).UTC()
return ti.Format("20060102150405")
}
func packTsigWire(tw *tsigWireFmt, msg []byte) (int, error) {
// copied from zmsg.go TSIG packing
// RR_Header
off, err := PackDomainName(tw.Name, msg, 0, nil, false)
if err != nil {
return off, err
}
off, err = packUint16(tw.Class, msg, off)
if err != nil {
return off, err
}
off, err = packUint32(tw.Ttl, msg, off)
if err != nil {
return off, err
}
off, err = PackDomainName(tw.Algorithm, msg, off, nil, false)
if err != nil {
return off, err
}
off, err = packUint48(tw.TimeSigned, msg, off)
if err != nil {
return off, err
}
off, err = packUint16(tw.Fudge, msg, off)
if err != nil {
return off, err
}
off, err = packUint16(tw.Error, msg, off)
if err != nil {
return off, err
}
off, err = packUint16(tw.OtherLen, msg, off)
if err != nil {
return off, err
}
off, err = packStringHex(tw.OtherData, msg, off)
if err != nil {
return off, err
}
return off, nil
}
func packMacWire(mw *macWireFmt, msg []byte) (int, error) {
off, err := packUint16(mw.MACSize, msg, 0)
if err != nil {
return off, err
}
off, err = packStringHex(mw.MAC, msg, off)
if err != nil {
return off, err
}
return off, nil
}
func packTimerWire(tw *timerWireFmt, msg []byte) (int, error) {
off, err := packUint48(tw.TimeSigned, msg, 0)
if err != nil {
return off, err
}
off, err = packUint16(tw.Fudge, msg, off)
if err != nil {
return off, err
}
return off, nil
}

View file

@ -1,37 +0,0 @@
package dns
import (
"testing"
"time"
)
func newTsig(algo string) *Msg {
m := new(Msg)
m.SetQuestion("example.org.", TypeA)
m.SetTsig("example.", algo, 300, time.Now().Unix())
return m
}
func TestTsig(t *testing.T) {
m := newTsig(HmacMD5)
buf, _, err := TsigGenerate(m, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false)
if err != nil {
t.Fatal(err)
}
err = TsigVerify(buf, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false)
if err != nil {
t.Fatal(err)
}
}
func TestTsigCase(t *testing.T) {
m := newTsig("HmAc-mD5.sig-ALg.rEg.int.") // HmacMD5
buf, _, err := TsigGenerate(m, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false)
if err != nil {
t.Fatal(err)
}
err = TsigVerify(buf, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false)
if err != nil {
t.Fatal(err)
}
}

1287
vendor/github.com/miekg/dns/types.go generated vendored

File diff suppressed because it is too large Load diff

View file

@ -1,271 +0,0 @@
//+build ignore
// types_generate.go is meant to run with go generate. It will use
// go/{importer,types} to track down all the RR struct types. Then for each type
// it will generate conversion tables (TypeToRR and TypeToString) and banal
// methods (len, Header, copy) based on the struct tags. The generated source is
// written to ztypes.go, and is meant to be checked into git.
package main
import (
"bytes"
"fmt"
"go/format"
"go/importer"
"go/types"
"log"
"os"
"strings"
"text/template"
)
var skipLen = map[string]struct{}{
"NSEC": {},
"NSEC3": {},
"OPT": {},
}
var packageHdr = `
// *** DO NOT MODIFY ***
// AUTOGENERATED BY go generate from type_generate.go
package dns
import (
"encoding/base64"
"net"
)
`
var TypeToRR = template.Must(template.New("TypeToRR").Parse(`
// TypeToRR is a map of constructors for each RR type.
var TypeToRR = map[uint16]func() RR{
{{range .}}{{if ne . "RFC3597"}} Type{{.}}: func() RR { return new({{.}}) },
{{end}}{{end}} }
`))
var typeToString = template.Must(template.New("typeToString").Parse(`
// TypeToString is a map of strings for each RR type.
var TypeToString = map[uint16]string{
{{range .}}{{if ne . "NSAPPTR"}} Type{{.}}: "{{.}}",
{{end}}{{end}} TypeNSAPPTR: "NSAP-PTR",
}
`))
var headerFunc = template.Must(template.New("headerFunc").Parse(`
// Header() functions
{{range .}} func (rr *{{.}}) Header() *RR_Header { return &rr.Hdr }
{{end}}
`))
// getTypeStruct will take a type and the package scope, and return the
// (innermost) struct if the type is considered a RR type (currently defined as
// those structs beginning with a RR_Header, could be redefined as implementing
// the RR interface). The bool return value indicates if embedded structs were
// resolved.
func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) {
st, ok := t.Underlying().(*types.Struct)
if !ok {
return nil, false
}
if st.Field(0).Type() == scope.Lookup("RR_Header").Type() {
return st, false
}
if st.Field(0).Anonymous() {
st, _ := getTypeStruct(st.Field(0).Type(), scope)
return st, true
}
return nil, false
}
func main() {
// Import and type-check the package
pkg, err := importer.Default().Import("github.com/miekg/dns")
fatalIfErr(err)
scope := pkg.Scope()
// Collect constants like TypeX
var numberedTypes []string
for _, name := range scope.Names() {
o := scope.Lookup(name)
if o == nil || !o.Exported() {
continue
}
b, ok := o.Type().(*types.Basic)
if !ok || b.Kind() != types.Uint16 {
continue
}
if !strings.HasPrefix(o.Name(), "Type") {
continue
}
name := strings.TrimPrefix(o.Name(), "Type")
if name == "PrivateRR" {
continue
}
numberedTypes = append(numberedTypes, name)
}
// Collect actual types (*X)
var namedTypes []string
for _, name := range scope.Names() {
o := scope.Lookup(name)
if o == nil || !o.Exported() {
continue
}
if st, _ := getTypeStruct(o.Type(), scope); st == nil {
continue
}
if name == "PrivateRR" {
continue
}
// Check if corresponding TypeX exists
if scope.Lookup("Type"+o.Name()) == nil && o.Name() != "RFC3597" {
log.Fatalf("Constant Type%s does not exist.", o.Name())
}
namedTypes = append(namedTypes, o.Name())
}
b := &bytes.Buffer{}
b.WriteString(packageHdr)
// Generate TypeToRR
fatalIfErr(TypeToRR.Execute(b, namedTypes))
// Generate typeToString
fatalIfErr(typeToString.Execute(b, numberedTypes))
// Generate headerFunc
fatalIfErr(headerFunc.Execute(b, namedTypes))
// Generate len()
fmt.Fprint(b, "// len() functions\n")
for _, name := range namedTypes {
if _, ok := skipLen[name]; ok {
continue
}
o := scope.Lookup(name)
st, isEmbedded := getTypeStruct(o.Type(), scope)
if isEmbedded {
continue
}
fmt.Fprintf(b, "func (rr *%s) len() int {\n", name)
fmt.Fprintf(b, "l := rr.Hdr.len()\n")
for i := 1; i < st.NumFields(); i++ {
o := func(s string) { fmt.Fprintf(b, s, st.Field(i).Name()) }
if _, ok := st.Field(i).Type().(*types.Slice); ok {
switch st.Tag(i) {
case `dns:"-"`:
// ignored
case `dns:"cdomain-name"`, `dns:"domain-name"`, `dns:"txt"`:
o("for _, x := range rr.%s { l += len(x) + 1 }\n")
default:
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
}
continue
}
switch {
case st.Tag(i) == `dns:"-"`:
// ignored
case st.Tag(i) == `dns:"cdomain-name"`, st.Tag(i) == `dns:"domain-name"`:
o("l += len(rr.%s) + 1\n")
case st.Tag(i) == `dns:"octet"`:
o("l += len(rr.%s)\n")
case strings.HasPrefix(st.Tag(i), `dns:"size-base64`):
fallthrough
case st.Tag(i) == `dns:"base64"`:
o("l += base64.StdEncoding.DecodedLen(len(rr.%s))\n")
case strings.HasPrefix(st.Tag(i), `dns:"size-hex`):
fallthrough
case st.Tag(i) == `dns:"hex"`:
o("l += len(rr.%s)/2 + 1\n")
case st.Tag(i) == `dns:"a"`:
o("l += net.IPv4len // %s\n")
case st.Tag(i) == `dns:"aaaa"`:
o("l += net.IPv6len // %s\n")
case st.Tag(i) == `dns:"txt"`:
o("for _, t := range rr.%s { l += len(t) + 1 }\n")
case st.Tag(i) == `dns:"uint48"`:
o("l += 6 // %s\n")
case st.Tag(i) == "":
switch st.Field(i).Type().(*types.Basic).Kind() {
case types.Uint8:
o("l++ // %s\n")
case types.Uint16:
o("l += 2 // %s\n")
case types.Uint32:
o("l += 4 // %s\n")
case types.Uint64:
o("l += 8 // %s\n")
case types.String:
o("l += len(rr.%s) + 1\n")
default:
log.Fatalln(name, st.Field(i).Name())
}
default:
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
}
}
fmt.Fprintf(b, "return l }\n")
}
// Generate copy()
fmt.Fprint(b, "// copy() functions\n")
for _, name := range namedTypes {
o := scope.Lookup(name)
st, isEmbedded := getTypeStruct(o.Type(), scope)
if isEmbedded {
continue
}
fmt.Fprintf(b, "func (rr *%s) copy() RR {\n", name)
fields := []string{"*rr.Hdr.copyHeader()"}
for i := 1; i < st.NumFields(); i++ {
f := st.Field(i).Name()
if sl, ok := st.Field(i).Type().(*types.Slice); ok {
t := sl.Underlying().String()
t = strings.TrimPrefix(t, "[]")
if strings.Contains(t, ".") {
splits := strings.Split(t, ".")
t = splits[len(splits)-1]
}
fmt.Fprintf(b, "%s := make([]%s, len(rr.%s)); copy(%s, rr.%s)\n",
f, t, f, f, f)
fields = append(fields, f)
continue
}
if st.Field(i).Type().String() == "net.IP" {
fields = append(fields, "copyIP(rr."+f+")")
continue
}
fields = append(fields, "rr."+f)
}
fmt.Fprintf(b, "return &%s{%s}\n", name, strings.Join(fields, ","))
fmt.Fprintf(b, "}\n")
}
// gofmt
res, err := format.Source(b.Bytes())
if err != nil {
b.WriteTo(os.Stderr)
log.Fatal(err)
}
// write result
f, err := os.Create("ztypes.go")
fatalIfErr(err)
defer f.Close()
f.Write(res)
}
func fatalIfErr(err error) {
if err != nil {
log.Fatal(err)
}
}

View file

@ -1,74 +0,0 @@
package dns
import (
"testing"
)
func TestCmToM(t *testing.T) {
s := cmToM(0, 0)
if s != "0.00" {
t.Error("0, 0")
}
s = cmToM(1, 0)
if s != "0.01" {
t.Error("1, 0")
}
s = cmToM(3, 1)
if s != "0.30" {
t.Error("3, 1")
}
s = cmToM(4, 2)
if s != "4" {
t.Error("4, 2")
}
s = cmToM(5, 3)
if s != "50" {
t.Error("5, 3")
}
s = cmToM(7, 5)
if s != "7000" {
t.Error("7, 5")
}
s = cmToM(9, 9)
if s != "90000000" {
t.Error("9, 9")
}
}
func TestSplitN(t *testing.T) {
xs := splitN("abc", 5)
if len(xs) != 1 && xs[0] != "abc" {
t.Errorf("Failure to split abc")
}
s := ""
for i := 0; i < 255; i++ {
s += "a"
}
xs = splitN(s, 255)
if len(xs) != 1 && xs[0] != s {
t.Errorf("failure to split 255 char long string")
}
s += "b"
xs = splitN(s, 255)
if len(xs) != 2 || xs[1] != "b" {
t.Errorf("failure to split 256 char long string: %d", len(xs))
}
// Make s longer
for i := 0; i < 255; i++ {
s += "a"
}
xs = splitN(s, 255)
if len(xs) != 3 || xs[2] != "a" {
t.Errorf("failure to split 510 char long string: %d", len(xs))
}
}

34
vendor/github.com/miekg/dns/udp.go generated vendored
View file

@ -1,34 +0,0 @@
// +build !windows
package dns
import (
"net"
)
// SessionUDP holds the remote address and the associated
// out-of-band data.
type SessionUDP struct {
raddr *net.UDPAddr
context []byte
}
// RemoteAddr returns the remote network address.
func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr }
// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a
// net.UDPAddr.
func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {
oob := make([]byte, 40)
n, oobn, _, raddr, err := conn.ReadMsgUDP(b, oob)
if err != nil {
return n, nil, err
}
return n, &SessionUDP{raddr, oob[:oobn]}, err
}
// WriteToSessionUDP acts just like net.UDPConn.WritetTo(), but uses a *SessionUDP instead of a net.Addr.
func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) {
n, _, err := conn.WriteMsgUDP(b, session.context, session.raddr)
return n, err
}

View file

@ -1,105 +0,0 @@
// +build linux,!appengine
package dns
// See:
// * http://stackoverflow.com/questions/3062205/setting-the-source-ip-for-a-udp-socket and
// * http://blog.powerdns.com/2012/10/08/on-binding-datagram-udp-sockets-to-the-any-addresses/
//
// Why do we need this: When listening on 0.0.0.0 with UDP so kernel decides what is the outgoing
// interface, this might not always be the correct one. This code will make sure the egress
// packet's interface matched the ingress' one.
import (
"net"
"syscall"
)
// setUDPSocketOptions sets the UDP socket options.
// This function is implemented on a per platform basis. See udp_*.go for more details
func setUDPSocketOptions(conn *net.UDPConn) error {
sa, err := getUDPSocketName(conn)
if err != nil {
return err
}
switch sa.(type) {
case *syscall.SockaddrInet6:
v6only, err := getUDPSocketOptions6Only(conn)
if err != nil {
return err
}
setUDPSocketOptions6(conn)
if !v6only {
setUDPSocketOptions4(conn)
}
case *syscall.SockaddrInet4:
setUDPSocketOptions4(conn)
}
return nil
}
// setUDPSocketOptions4 prepares the v4 socket for sessions.
func setUDPSocketOptions4(conn *net.UDPConn) error {
file, err := conn.File()
if err != nil {
return err
}
if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IP, syscall.IP_PKTINFO, 1); err != nil {
file.Close()
return err
}
// Calling File() above results in the connection becoming blocking, we must fix that.
// See https://github.com/miekg/dns/issues/279
err = syscall.SetNonblock(int(file.Fd()), true)
if err != nil {
file.Close()
return err
}
file.Close()
return nil
}
// setUDPSocketOptions6 prepares the v6 socket for sessions.
func setUDPSocketOptions6(conn *net.UDPConn) error {
file, err := conn.File()
if err != nil {
return err
}
if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_RECVPKTINFO, 1); err != nil {
file.Close()
return err
}
err = syscall.SetNonblock(int(file.Fd()), true)
if err != nil {
file.Close()
return err
}
file.Close()
return nil
}
// getUDPSocketOption6Only return true if the socket is v6 only and false when it is v4/v6 combined
// (dualstack).
func getUDPSocketOptions6Only(conn *net.UDPConn) (bool, error) {
file, err := conn.File()
if err != nil {
return false, err
}
// dual stack. See http://stackoverflow.com/questions/1618240/how-to-support-both-ipv4-and-ipv6-connections
v6only, err := syscall.GetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY)
if err != nil {
file.Close()
return false, err
}
file.Close()
return v6only == 1, nil
}
func getUDPSocketName(conn *net.UDPConn) (syscall.Sockaddr, error) {
file, err := conn.File()
if err != nil {
return nil, err
}
defer file.Close()
return syscall.Getsockname(int(file.Fd()))
}

View file

@ -1,15 +0,0 @@
// +build !linux appengine
package dns
import (
"net"
)
// These do nothing. See udp_linux.go for an example of how to implement this.
// We tried to adhire to some kind of naming scheme.
func setUDPSocketOptions(conn *net.UDPConn) error { return nil }
func setUDPSocketOptions4(conn *net.UDPConn) error { return nil }
func setUDPSocketOptions6(conn *net.UDPConn) error { return nil }
func getUDPSocketOptions6Only(conn *net.UDPConn) (bool, error) { return false, nil }

View file

@ -1,29 +0,0 @@
// +build windows
package dns
import "net"
type SessionUDP struct {
raddr *net.UDPAddr
}
func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr }
// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a
// net.UDPAddr.
func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {
n, raddr, err := conn.ReadFrom(b)
if err != nil {
return n, nil, err
}
session := &SessionUDP{raddr.(*net.UDPAddr)}
return n, session, err
}
// WriteToSessionUDP acts just like net.UDPConn.WritetTo(), but uses a *SessionUDP instead of a net.Addr.
func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) {
n, err := conn.WriteTo(b, session.raddr)
return n, err
}

106
vendor/github.com/miekg/dns/update.go generated vendored
View file

@ -1,106 +0,0 @@
package dns
// NameUsed sets the RRs in the prereq section to
// "Name is in use" RRs. RFC 2136 section 2.4.4.
func (u *Msg) NameUsed(rr []RR) {
if u.Answer == nil {
u.Answer = make([]RR, 0, len(rr))
}
for _, r := range rr {
u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}})
}
}
// NameNotUsed sets the RRs in the prereq section to
// "Name is in not use" RRs. RFC 2136 section 2.4.5.
func (u *Msg) NameNotUsed(rr []RR) {
if u.Answer == nil {
u.Answer = make([]RR, 0, len(rr))
}
for _, r := range rr {
u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassNONE}})
}
}
// Used sets the RRs in the prereq section to
// "RRset exists (value dependent -- with rdata)" RRs. RFC 2136 section 2.4.2.
func (u *Msg) Used(rr []RR) {
if len(u.Question) == 0 {
panic("dns: empty question section")
}
if u.Answer == nil {
u.Answer = make([]RR, 0, len(rr))
}
for _, r := range rr {
r.Header().Class = u.Question[0].Qclass
u.Answer = append(u.Answer, r)
}
}
// RRsetUsed sets the RRs in the prereq section to
// "RRset exists (value independent -- no rdata)" RRs. RFC 2136 section 2.4.1.
func (u *Msg) RRsetUsed(rr []RR) {
if u.Answer == nil {
u.Answer = make([]RR, 0, len(rr))
}
for _, r := range rr {
u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: r.Header().Rrtype, Class: ClassANY}})
}
}
// RRsetNotUsed sets the RRs in the prereq section to
// "RRset does not exist" RRs. RFC 2136 section 2.4.3.
func (u *Msg) RRsetNotUsed(rr []RR) {
if u.Answer == nil {
u.Answer = make([]RR, 0, len(rr))
}
for _, r := range rr {
u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: r.Header().Rrtype, Class: ClassNONE}})
}
}
// Insert creates a dynamic update packet that adds an complete RRset, see RFC 2136 section 2.5.1.
func (u *Msg) Insert(rr []RR) {
if len(u.Question) == 0 {
panic("dns: empty question section")
}
if u.Ns == nil {
u.Ns = make([]RR, 0, len(rr))
}
for _, r := range rr {
r.Header().Class = u.Question[0].Qclass
u.Ns = append(u.Ns, r)
}
}
// RemoveRRset creates a dynamic update packet that deletes an RRset, see RFC 2136 section 2.5.2.
func (u *Msg) RemoveRRset(rr []RR) {
if u.Ns == nil {
u.Ns = make([]RR, 0, len(rr))
}
for _, r := range rr {
u.Ns = append(u.Ns, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: r.Header().Rrtype, Class: ClassANY}})
}
}
// RemoveName creates a dynamic update packet that deletes all RRsets of a name, see RFC 2136 section 2.5.3
func (u *Msg) RemoveName(rr []RR) {
if u.Ns == nil {
u.Ns = make([]RR, 0, len(rr))
}
for _, r := range rr {
u.Ns = append(u.Ns, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}})
}
}
// Remove creates a dynamic update packet deletes RR from a RRSset, see RFC 2136 section 2.5.4
func (u *Msg) Remove(rr []RR) {
if u.Ns == nil {
u.Ns = make([]RR, 0, len(rr))
}
for _, r := range rr {
r.Header().Class = ClassNONE
r.Header().Ttl = 0
u.Ns = append(u.Ns, r)
}
}

View file

@ -1,145 +0,0 @@
package dns
import (
"bytes"
"testing"
)
func TestDynamicUpdateParsing(t *testing.T) {
prefix := "example.com. IN "
for _, typ := range TypeToString {
if typ == "OPT" || typ == "AXFR" || typ == "IXFR" || typ == "ANY" || typ == "TKEY" ||
typ == "TSIG" || typ == "ISDN" || typ == "UNSPEC" || typ == "NULL" || typ == "ATMA" ||
typ == "Reserved" || typ == "None" || typ == "NXT" || typ == "MAILB" || typ == "MAILA" {
continue
}
r, err := NewRR(prefix + typ)
if err != nil {
t.Errorf("failure to parse: %s %s: %v", prefix, typ, err)
} else {
t.Logf("parsed: %s", r.String())
}
}
}
func TestDynamicUpdateUnpack(t *testing.T) {
// From https://github.com/miekg/dns/issues/150#issuecomment-62296803
// It should be an update message for the zone "example.",
// deleting the A RRset "example." and then adding an A record at "example.".
// class ANY, TYPE A
buf := []byte{171, 68, 40, 0, 0, 1, 0, 0, 0, 2, 0, 0, 7, 101, 120, 97, 109, 112, 108, 101, 0, 0, 6, 0, 1, 192, 12, 0, 1, 0, 255, 0, 0, 0, 0, 0, 0, 192, 12, 0, 1, 0, 1, 0, 0, 0, 0, 0, 4, 127, 0, 0, 1}
msg := new(Msg)
err := msg.Unpack(buf)
if err != nil {
t.Errorf("failed to unpack: %v\n%s", err, msg.String())
}
}
func TestDynamicUpdateZeroRdataUnpack(t *testing.T) {
m := new(Msg)
rr := &RR_Header{Name: ".", Rrtype: 0, Class: 1, Ttl: ^uint32(0), Rdlength: 0}
m.Answer = []RR{rr, rr, rr, rr, rr}
m.Ns = m.Answer
for n, s := range TypeToString {
rr.Rrtype = n
bytes, err := m.Pack()
if err != nil {
t.Errorf("failed to pack %s: %v", s, err)
continue
}
if err := new(Msg).Unpack(bytes); err != nil {
t.Errorf("failed to unpack %s: %v", s, err)
}
}
}
func TestRemoveRRset(t *testing.T) {
// Should add a zero data RR in Class ANY with a TTL of 0
// for each set mentioned in the RRs provided to it.
rr, err := NewRR(". 100 IN A 127.0.0.1")
if err != nil {
t.Fatalf("error constructing RR: %v", err)
}
m := new(Msg)
m.Ns = []RR{&RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY, Ttl: 0, Rdlength: 0}}
expectstr := m.String()
expect, err := m.Pack()
if err != nil {
t.Fatalf("error packing expected msg: %v", err)
}
m.Ns = nil
m.RemoveRRset([]RR{rr})
actual, err := m.Pack()
if err != nil {
t.Fatalf("error packing actual msg: %v", err)
}
if !bytes.Equal(actual, expect) {
tmp := new(Msg)
if err := tmp.Unpack(actual); err != nil {
t.Fatalf("error unpacking actual msg: %v\nexpected: %v\ngot: %v\n", err, expect, actual)
}
t.Errorf("expected msg:\n%s", expectstr)
t.Errorf("actual msg:\n%v", tmp)
}
}
func TestPreReqAndRemovals(t *testing.T) {
// Build a list of multiple prereqs and then somes removes followed by an insert.
// We should be able to add multiple prereqs and updates.
m := new(Msg)
m.SetUpdate("example.org.")
m.Id = 1234
// Use a full set of RRs each time, so we are sure the rdata is stripped.
rrName1, _ := NewRR("name_used. 3600 IN A 127.0.0.1")
rrName2, _ := NewRR("name_not_used. 3600 IN A 127.0.0.1")
rrRemove1, _ := NewRR("remove1. 3600 IN A 127.0.0.1")
rrRemove2, _ := NewRR("remove2. 3600 IN A 127.0.0.1")
rrRemove3, _ := NewRR("remove3. 3600 IN A 127.0.0.1")
rrInsert, _ := NewRR("insert. 3600 IN A 127.0.0.1")
rrRrset1, _ := NewRR("rrset_used1. 3600 IN A 127.0.0.1")
rrRrset2, _ := NewRR("rrset_used2. 3600 IN A 127.0.0.1")
rrRrset3, _ := NewRR("rrset_not_used. 3600 IN A 127.0.0.1")
// Handle the prereqs.
m.NameUsed([]RR{rrName1})
m.NameNotUsed([]RR{rrName2})
m.RRsetUsed([]RR{rrRrset1})
m.Used([]RR{rrRrset2})
m.RRsetNotUsed([]RR{rrRrset3})
// and now the updates.
m.RemoveName([]RR{rrRemove1})
m.RemoveRRset([]RR{rrRemove2})
m.Remove([]RR{rrRemove3})
m.Insert([]RR{rrInsert})
// This test function isn't a Example function because we print these RR with tabs at the
// end and the Example function trim these, thus they never match.
// TODO(miek): don't print these tabs and make this into an Example function.
expect := `;; opcode: UPDATE, status: NOERROR, id: 1234
;; flags:; QUERY: 1, ANSWER: 5, AUTHORITY: 4, ADDITIONAL: 0
;; QUESTION SECTION:
;example.org. IN SOA
;; ANSWER SECTION:
name_used. 0 ANY ANY
name_not_used. 0 NONE ANY
rrset_used1. 0 ANY A
rrset_used2. 3600 IN A 127.0.0.1
rrset_not_used. 0 NONE A
;; AUTHORITY SECTION:
remove1. 0 ANY ANY
remove2. 0 ANY A
remove3. 0 NONE A 127.0.0.1
insert. 3600 IN A 127.0.0.1
`
if m.String() != expect {
t.Errorf("expected msg:\n%s", expect)
t.Errorf("actual msg:\n%v", m.String())
}
}

255
vendor/github.com/miekg/dns/xfr.go generated vendored
View file

@ -1,255 +0,0 @@
package dns
import (
"fmt"
"time"
)
// Envelope is used when doing a zone transfer with a remote server.
type Envelope struct {
RR []RR // The set of RRs in the answer section of the xfr reply message.
Error error // If something went wrong, this contains the error.
}
// A Transfer defines parameters that are used during a zone transfer.
type Transfer struct {
*Conn
DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds
ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds
WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds
TsigSecret map[string]string // Secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be fully qualified
tsigTimersOnly bool
}
// Think we need to away to stop the transfer
// In performs an incoming transfer with the server in a.
// If you would like to set the source IP, or some other attribute
// of a Dialer for a Transfer, you can do so by specifying the attributes
// in the Transfer.Conn:
//
// d := net.Dialer{LocalAddr: transfer_source}
// con, err := d.Dial("tcp", master)
// dnscon := &dns.Conn{Conn:con}
// transfer = &dns.Transfer{Conn: dnscon}
// channel, err := transfer.In(message, master)
//
func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) {
timeout := dnsTimeout
if t.DialTimeout != 0 {
timeout = t.DialTimeout
}
if t.Conn == nil {
t.Conn, err = DialTimeout("tcp", a, timeout)
if err != nil {
return nil, err
}
}
if err := t.WriteMsg(q); err != nil {
return nil, err
}
env = make(chan *Envelope)
go func() {
if q.Question[0].Qtype == TypeAXFR {
go t.inAxfr(q.Id, env)
return
}
if q.Question[0].Qtype == TypeIXFR {
go t.inIxfr(q.Id, env)
return
}
}()
return env, nil
}
func (t *Transfer) inAxfr(id uint16, c chan *Envelope) {
first := true
defer t.Close()
defer close(c)
timeout := dnsTimeout
if t.ReadTimeout != 0 {
timeout = t.ReadTimeout
}
for {
t.Conn.SetReadDeadline(time.Now().Add(timeout))
in, err := t.ReadMsg()
if err != nil {
c <- &Envelope{nil, err}
return
}
if id != in.Id {
c <- &Envelope{in.Answer, ErrId}
return
}
if first {
if in.Rcode != RcodeSuccess {
c <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}}
return
}
if !isSOAFirst(in) {
c <- &Envelope{in.Answer, ErrSoa}
return
}
first = !first
// only one answer that is SOA, receive more
if len(in.Answer) == 1 {
t.tsigTimersOnly = true
c <- &Envelope{in.Answer, nil}
continue
}
}
if !first {
t.tsigTimersOnly = true // Subsequent envelopes use this.
if isSOALast(in) {
c <- &Envelope{in.Answer, nil}
return
}
c <- &Envelope{in.Answer, nil}
}
}
}
func (t *Transfer) inIxfr(id uint16, c chan *Envelope) {
serial := uint32(0) // The first serial seen is the current server serial
first := true
defer t.Close()
defer close(c)
timeout := dnsTimeout
if t.ReadTimeout != 0 {
timeout = t.ReadTimeout
}
for {
t.SetReadDeadline(time.Now().Add(timeout))
in, err := t.ReadMsg()
if err != nil {
c <- &Envelope{nil, err}
return
}
if id != in.Id {
c <- &Envelope{in.Answer, ErrId}
return
}
if first {
if in.Rcode != RcodeSuccess {
c <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}}
return
}
// A single SOA RR signals "no changes"
if len(in.Answer) == 1 && isSOAFirst(in) {
c <- &Envelope{in.Answer, nil}
return
}
// Check if the returned answer is ok
if !isSOAFirst(in) {
c <- &Envelope{in.Answer, ErrSoa}
return
}
// This serial is important
serial = in.Answer[0].(*SOA).Serial
first = !first
}
// Now we need to check each message for SOA records, to see what we need to do
if !first {
t.tsigTimersOnly = true
// If the last record in the IXFR contains the servers' SOA, we should quit
if v, ok := in.Answer[len(in.Answer)-1].(*SOA); ok {
if v.Serial == serial {
c <- &Envelope{in.Answer, nil}
return
}
}
c <- &Envelope{in.Answer, nil}
}
}
}
// Out performs an outgoing transfer with the client connecting in w.
// Basic use pattern:
//
// ch := make(chan *dns.Envelope)
// tr := new(dns.Transfer)
// go tr.Out(w, r, ch)
// ch <- &dns.Envelope{RR: []dns.RR{soa, rr1, rr2, rr3, soa}}
// close(ch)
// w.Hijack()
// // w.Close() // Client closes connection
//
// The server is responsible for sending the correct sequence of RRs through the
// channel ch.
func (t *Transfer) Out(w ResponseWriter, q *Msg, ch chan *Envelope) error {
for x := range ch {
r := new(Msg)
// Compress?
r.SetReply(q)
r.Authoritative = true
// assume it fits TODO(miek): fix
r.Answer = append(r.Answer, x.RR...)
if err := w.WriteMsg(r); err != nil {
return err
}
}
w.TsigTimersOnly(true)
return nil
}
// ReadMsg reads a message from the transfer connection t.
func (t *Transfer) ReadMsg() (*Msg, error) {
m := new(Msg)
p := make([]byte, MaxMsgSize)
n, err := t.Read(p)
if err != nil && n == 0 {
return nil, err
}
p = p[:n]
if err := m.Unpack(p); err != nil {
return nil, err
}
if ts := m.IsTsig(); ts != nil && t.TsigSecret != nil {
if _, ok := t.TsigSecret[ts.Hdr.Name]; !ok {
return m, ErrSecret
}
// Need to work on the original message p, as that was used to calculate the tsig.
err = TsigVerify(p, t.TsigSecret[ts.Hdr.Name], t.tsigRequestMAC, t.tsigTimersOnly)
t.tsigRequestMAC = ts.MAC
}
return m, err
}
// WriteMsg writes a message through the transfer connection t.
func (t *Transfer) WriteMsg(m *Msg) (err error) {
var out []byte
if ts := m.IsTsig(); ts != nil && t.TsigSecret != nil {
if _, ok := t.TsigSecret[ts.Hdr.Name]; !ok {
return ErrSecret
}
out, t.tsigRequestMAC, err = TsigGenerate(m, t.TsigSecret[ts.Hdr.Name], t.tsigRequestMAC, t.tsigTimersOnly)
} else {
out, err = m.Pack()
}
if err != nil {
return err
}
if _, err = t.Write(out); err != nil {
return err
}
return nil
}
func isSOAFirst(in *Msg) bool {
if len(in.Answer) > 0 {
return in.Answer[0].Header().Rrtype == TypeSOA
}
return false
}
func isSOALast(in *Msg) bool {
if len(in.Answer) > 0 {
return in.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA
}
return false
}
const errXFR = "bad xfr rcode: %d"

View file

@ -1,184 +0,0 @@
// +build net
package dns
import (
"net"
"strings"
"testing"
"time"
)
func getIP(s string) string {
a, err := net.LookupAddr(s)
if err != nil {
return ""
}
return a[0]
}
// flaky, need to setup local server and test from that.
func TestAXFR_Miek(t *testing.T) {
// This test runs against a server maintained by Miek
if testing.Short() {
return
}
m := new(Msg)
m.SetAxfr("miek.nl.")
server := getIP("linode.atoom.net")
tr := new(Transfer)
if a, err := tr.In(m, net.JoinHostPort(server, "53")); err != nil {
t.Fatal("failed to setup axfr: ", err)
} else {
for ex := range a {
if ex.Error != nil {
t.Errorf("error %v", ex.Error)
break
}
for _, rr := range ex.RR {
t.Log(rr.String())
}
}
}
}
// fails.
func TestAXFR_NLNL_MultipleEnvelopes(t *testing.T) {
// This test runs against a server maintained by NLnet Labs
if testing.Short() {
return
}
m := new(Msg)
m.SetAxfr("nlnetlabs.nl.")
server := getIP("open.nlnetlabs.nl.")
tr := new(Transfer)
if a, err := tr.In(m, net.JoinHostPort(server, "53")); err != nil {
t.Fatalf("failed to setup axfr %v for server: %v", err, server)
} else {
for ex := range a {
if ex.Error != nil {
t.Errorf("error %v", ex.Error)
break
}
}
}
}
func TestAXFR_Miek_Tsig(t *testing.T) {
// This test runs against a server maintained by Miek
if testing.Short() {
return
}
m := new(Msg)
m.SetAxfr("example.nl.")
m.SetTsig("axfr.", HmacMD5, 300, time.Now().Unix())
tr := new(Transfer)
tr.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
if a, err := tr.In(m, "176.58.119.54:53"); err != nil {
t.Fatal("failed to setup axfr: ", err)
} else {
for ex := range a {
if ex.Error != nil {
t.Errorf("error %v", ex.Error)
break
}
for _, rr := range ex.RR {
t.Log(rr.String())
}
}
}
}
func TestAXFR_SIDN_NSD3_NONE(t *testing.T) { testAXFRSIDN(t, "nsd", "") }
func TestAXFR_SIDN_NSD3_MD5(t *testing.T) { testAXFRSIDN(t, "nsd", HmacMD5) }
func TestAXFR_SIDN_NSD3_SHA1(t *testing.T) { testAXFRSIDN(t, "nsd", HmacSHA1) }
func TestAXFR_SIDN_NSD3_SHA256(t *testing.T) { testAXFRSIDN(t, "nsd", HmacSHA256) }
func TestAXFR_SIDN_NSD4_NONE(t *testing.T) { testAXFRSIDN(t, "nsd4", "") }
func TestAXFR_SIDN_NSD4_MD5(t *testing.T) { testAXFRSIDN(t, "nsd4", HmacMD5) }
func TestAXFR_SIDN_NSD4_SHA1(t *testing.T) { testAXFRSIDN(t, "nsd4", HmacSHA1) }
func TestAXFR_SIDN_NSD4_SHA256(t *testing.T) { testAXFRSIDN(t, "nsd4", HmacSHA256) }
func TestAXFR_SIDN_BIND9_NONE(t *testing.T) { testAXFRSIDN(t, "bind9", "") }
func TestAXFR_SIDN_BIND9_MD5(t *testing.T) { testAXFRSIDN(t, "bind9", HmacMD5) }
func TestAXFR_SIDN_BIND9_SHA1(t *testing.T) { testAXFRSIDN(t, "bind9", HmacSHA1) }
func TestAXFR_SIDN_BIND9_SHA256(t *testing.T) { testAXFRSIDN(t, "bind9", HmacSHA256) }
func TestAXFR_SIDN_KNOT_NONE(t *testing.T) { testAXFRSIDN(t, "knot", "") }
func TestAXFR_SIDN_KNOT_MD5(t *testing.T) { testAXFRSIDN(t, "knot", HmacMD5) }
func TestAXFR_SIDN_KNOT_SHA1(t *testing.T) { testAXFRSIDN(t, "knot", HmacSHA1) }
func TestAXFR_SIDN_KNOT_SHA256(t *testing.T) { testAXFRSIDN(t, "knot", HmacSHA256) }
func TestAXFR_SIDN_POWERDNS_NONE(t *testing.T) { testAXFRSIDN(t, "powerdns", "") }
func TestAXFR_SIDN_POWERDNS_MD5(t *testing.T) { testAXFRSIDN(t, "powerdns", HmacMD5) }
func TestAXFR_SIDN_POWERDNS_SHA1(t *testing.T) { testAXFRSIDN(t, "powerdns", HmacSHA1) }
func TestAXFR_SIDN_POWERDNS_SHA256(t *testing.T) { testAXFRSIDN(t, "powerdns", HmacSHA256) }
func TestAXFR_SIDN_YADIFA_NONE(t *testing.T) { testAXFRSIDN(t, "yadifa", "") }
func TestAXFR_SIDN_YADIFA_MD5(t *testing.T) { testAXFRSIDN(t, "yadifa", HmacMD5) }
func TestAXFR_SIDN_YADIFA_SHA1(t *testing.T) { testAXFRSIDN(t, "yadifa", HmacSHA1) }
func TestAXFR_SIDN_YADIFA_SHA256(t *testing.T) { testAXFRSIDN(t, "yadifa", HmacSHA256) }
func testAXFRSIDN(t *testing.T, host, alg string) {
// This tests run against a server maintained by SIDN labs, see:
// https://workbench.sidnlabs.nl/
if testing.Short() {
return
}
x := new(Transfer)
x.TsigSecret = map[string]string{
"wb_md5.": "Wu/utSasZUkoeCNku152Zw==",
"wb_sha1_longkey.": "uhMpEhPq/RAD9Bt4mqhfmi+7ZdKmjLQb/lcrqYPXR4s/nnbsqw==",
"wb_sha256.": "npfrIJjt/MJOjGJoBNZtsjftKMhkSpIYMv2RzRZt1f8=",
}
keyname := map[string]string{
HmacMD5: "wb_md5.",
HmacSHA1: "wb_sha1_longkey.",
HmacSHA256: "wb_sha256.",
}[alg]
m := new(Msg)
m.SetAxfr("types.wb.sidnlabs.nl.")
if keyname != "" {
m.SetTsig(keyname, alg, 300, time.Now().Unix())
}
c, err := x.In(m, host+".sidnlabs.nl:53")
if err != nil {
t.Fatal(err)
}
for e := range c {
if e.Error != nil {
t.Fatal(e.Error)
}
}
}
func TestAXFRFailNotAuth(t *testing.T) {
// This tests run against a server maintained by SIDN labs, see:
// https://workbench.sidnlabs.nl/
if testing.Short() {
return
}
x := new(Transfer)
m := new(Msg)
m.SetAxfr("sidnlabs.nl.")
c, err := x.In(m, "yadifa.sidnlabs.nl:53")
if err != nil {
t.Fatal(err)
}
for e := range c {
if e.Error != nil {
if !strings.HasPrefix(e.Error.Error(), "dns: bad xfr rcode:") {
t.Fatal(e.Error)
}
}
}
}

View file

@ -1,119 +0,0 @@
// *** DO NOT MODIFY ***
// AUTOGENERATED BY go generate from compress_generate.go
package dns
func compressionLenHelperType(c map[string]int, r RR) {
switch x := r.(type) {
case *PTR:
compressionLenHelper(c, x.Ptr)
case *SOA:
compressionLenHelper(c, x.Ns)
compressionLenHelper(c, x.Mbox)
case *AFSDB:
compressionLenHelper(c, x.Hostname)
case *HIP:
for i := range x.RendezvousServers {
compressionLenHelper(c, x.RendezvousServers[i])
}
case *LP:
compressionLenHelper(c, x.Fqdn)
case *CNAME:
compressionLenHelper(c, x.Target)
case *MB:
compressionLenHelper(c, x.Mb)
case *RP:
compressionLenHelper(c, x.Mbox)
compressionLenHelper(c, x.Txt)
case *RRSIG:
compressionLenHelper(c, x.SignerName)
case *MF:
compressionLenHelper(c, x.Mf)
case *MINFO:
compressionLenHelper(c, x.Rmail)
compressionLenHelper(c, x.Email)
case *SIG:
compressionLenHelper(c, x.SignerName)
case *SRV:
compressionLenHelper(c, x.Target)
case *TSIG:
compressionLenHelper(c, x.Algorithm)
case *KX:
compressionLenHelper(c, x.Exchanger)
case *MG:
compressionLenHelper(c, x.Mg)
case *NSAPPTR:
compressionLenHelper(c, x.Ptr)
case *PX:
compressionLenHelper(c, x.Map822)
compressionLenHelper(c, x.Mapx400)
case *DNAME:
compressionLenHelper(c, x.Target)
case *MR:
compressionLenHelper(c, x.Mr)
case *MX:
compressionLenHelper(c, x.Mx)
case *TKEY:
compressionLenHelper(c, x.Algorithm)
case *NSEC:
compressionLenHelper(c, x.NextDomain)
case *TALINK:
compressionLenHelper(c, x.PreviousName)
compressionLenHelper(c, x.NextName)
case *MD:
compressionLenHelper(c, x.Md)
case *NAPTR:
compressionLenHelper(c, x.Replacement)
case *NS:
compressionLenHelper(c, x.Ns)
case *RT:
compressionLenHelper(c, x.Host)
}
}
func compressionLenSearchType(c map[string]int, r RR) (int, bool) {
switch x := r.(type) {
case *MG:
k1, ok1 := compressionLenSearch(c, x.Mg)
return k1, ok1
case *PTR:
k1, ok1 := compressionLenSearch(c, x.Ptr)
return k1, ok1
case *AFSDB:
k1, ok1 := compressionLenSearch(c, x.Hostname)
return k1, ok1
case *MB:
k1, ok1 := compressionLenSearch(c, x.Mb)
return k1, ok1
case *MD:
k1, ok1 := compressionLenSearch(c, x.Md)
return k1, ok1
case *MF:
k1, ok1 := compressionLenSearch(c, x.Mf)
return k1, ok1
case *NS:
k1, ok1 := compressionLenSearch(c, x.Ns)
return k1, ok1
case *RT:
k1, ok1 := compressionLenSearch(c, x.Host)
return k1, ok1
case *SOA:
k1, ok1 := compressionLenSearch(c, x.Ns)
k2, ok2 := compressionLenSearch(c, x.Mbox)
return k1 + k2, ok1 && ok2
case *CNAME:
k1, ok1 := compressionLenSearch(c, x.Target)
return k1, ok1
case *MINFO:
k1, ok1 := compressionLenSearch(c, x.Rmail)
k2, ok2 := compressionLenSearch(c, x.Email)
return k1 + k2, ok1 && ok2
case *MR:
k1, ok1 := compressionLenSearch(c, x.Mr)
return k1, ok1
case *MX:
k1, ok1 := compressionLenSearch(c, x.Mx)
return k1, ok1
}
return 0, false
}

3565
vendor/github.com/miekg/dns/zmsg.go generated vendored

File diff suppressed because it is too large Load diff

857
vendor/github.com/miekg/dns/ztypes.go generated vendored
View file

@ -1,857 +0,0 @@
// *** DO NOT MODIFY ***
// AUTOGENERATED BY go generate from type_generate.go
package dns
import (
"encoding/base64"
"net"
)
// TypeToRR is a map of constructors for each RR type.
var TypeToRR = map[uint16]func() RR{
TypeA: func() RR { return new(A) },
TypeAAAA: func() RR { return new(AAAA) },
TypeAFSDB: func() RR { return new(AFSDB) },
TypeANY: func() RR { return new(ANY) },
TypeAVC: func() RR { return new(AVC) },
TypeCAA: func() RR { return new(CAA) },
TypeCDNSKEY: func() RR { return new(CDNSKEY) },
TypeCDS: func() RR { return new(CDS) },
TypeCERT: func() RR { return new(CERT) },
TypeCNAME: func() RR { return new(CNAME) },
TypeDHCID: func() RR { return new(DHCID) },
TypeDLV: func() RR { return new(DLV) },
TypeDNAME: func() RR { return new(DNAME) },
TypeDNSKEY: func() RR { return new(DNSKEY) },
TypeDS: func() RR { return new(DS) },
TypeEID: func() RR { return new(EID) },
TypeEUI48: func() RR { return new(EUI48) },
TypeEUI64: func() RR { return new(EUI64) },
TypeGID: func() RR { return new(GID) },
TypeGPOS: func() RR { return new(GPOS) },
TypeHINFO: func() RR { return new(HINFO) },
TypeHIP: func() RR { return new(HIP) },
TypeKEY: func() RR { return new(KEY) },
TypeKX: func() RR { return new(KX) },
TypeL32: func() RR { return new(L32) },
TypeL64: func() RR { return new(L64) },
TypeLOC: func() RR { return new(LOC) },
TypeLP: func() RR { return new(LP) },
TypeMB: func() RR { return new(MB) },
TypeMD: func() RR { return new(MD) },
TypeMF: func() RR { return new(MF) },
TypeMG: func() RR { return new(MG) },
TypeMINFO: func() RR { return new(MINFO) },
TypeMR: func() RR { return new(MR) },
TypeMX: func() RR { return new(MX) },
TypeNAPTR: func() RR { return new(NAPTR) },
TypeNID: func() RR { return new(NID) },
TypeNIMLOC: func() RR { return new(NIMLOC) },
TypeNINFO: func() RR { return new(NINFO) },
TypeNS: func() RR { return new(NS) },
TypeNSAPPTR: func() RR { return new(NSAPPTR) },
TypeNSEC: func() RR { return new(NSEC) },
TypeNSEC3: func() RR { return new(NSEC3) },
TypeNSEC3PARAM: func() RR { return new(NSEC3PARAM) },
TypeOPENPGPKEY: func() RR { return new(OPENPGPKEY) },
TypeOPT: func() RR { return new(OPT) },
TypePTR: func() RR { return new(PTR) },
TypePX: func() RR { return new(PX) },
TypeRKEY: func() RR { return new(RKEY) },
TypeRP: func() RR { return new(RP) },
TypeRRSIG: func() RR { return new(RRSIG) },
TypeRT: func() RR { return new(RT) },
TypeSIG: func() RR { return new(SIG) },
TypeSMIMEA: func() RR { return new(SMIMEA) },
TypeSOA: func() RR { return new(SOA) },
TypeSPF: func() RR { return new(SPF) },
TypeSRV: func() RR { return new(SRV) },
TypeSSHFP: func() RR { return new(SSHFP) },
TypeTA: func() RR { return new(TA) },
TypeTALINK: func() RR { return new(TALINK) },
TypeTKEY: func() RR { return new(TKEY) },
TypeTLSA: func() RR { return new(TLSA) },
TypeTSIG: func() RR { return new(TSIG) },
TypeTXT: func() RR { return new(TXT) },
TypeUID: func() RR { return new(UID) },
TypeUINFO: func() RR { return new(UINFO) },
TypeURI: func() RR { return new(URI) },
TypeX25: func() RR { return new(X25) },
}
// TypeToString is a map of strings for each RR type.
var TypeToString = map[uint16]string{
TypeA: "A",
TypeAAAA: "AAAA",
TypeAFSDB: "AFSDB",
TypeANY: "ANY",
TypeATMA: "ATMA",
TypeAVC: "AVC",
TypeAXFR: "AXFR",
TypeCAA: "CAA",
TypeCDNSKEY: "CDNSKEY",
TypeCDS: "CDS",
TypeCERT: "CERT",
TypeCNAME: "CNAME",
TypeDHCID: "DHCID",
TypeDLV: "DLV",
TypeDNAME: "DNAME",
TypeDNSKEY: "DNSKEY",
TypeDS: "DS",
TypeEID: "EID",
TypeEUI48: "EUI48",
TypeEUI64: "EUI64",
TypeGID: "GID",
TypeGPOS: "GPOS",
TypeHINFO: "HINFO",
TypeHIP: "HIP",
TypeISDN: "ISDN",
TypeIXFR: "IXFR",
TypeKEY: "KEY",
TypeKX: "KX",
TypeL32: "L32",
TypeL64: "L64",
TypeLOC: "LOC",
TypeLP: "LP",
TypeMAILA: "MAILA",
TypeMAILB: "MAILB",
TypeMB: "MB",
TypeMD: "MD",
TypeMF: "MF",
TypeMG: "MG",
TypeMINFO: "MINFO",
TypeMR: "MR",
TypeMX: "MX",
TypeNAPTR: "NAPTR",
TypeNID: "NID",
TypeNIMLOC: "NIMLOC",
TypeNINFO: "NINFO",
TypeNS: "NS",
TypeNSEC: "NSEC",
TypeNSEC3: "NSEC3",
TypeNSEC3PARAM: "NSEC3PARAM",
TypeNULL: "NULL",
TypeNXT: "NXT",
TypeNone: "None",
TypeOPENPGPKEY: "OPENPGPKEY",
TypeOPT: "OPT",
TypePTR: "PTR",
TypePX: "PX",
TypeRKEY: "RKEY",
TypeRP: "RP",
TypeRRSIG: "RRSIG",
TypeRT: "RT",
TypeReserved: "Reserved",
TypeSIG: "SIG",
TypeSMIMEA: "SMIMEA",
TypeSOA: "SOA",
TypeSPF: "SPF",
TypeSRV: "SRV",
TypeSSHFP: "SSHFP",
TypeTA: "TA",
TypeTALINK: "TALINK",
TypeTKEY: "TKEY",
TypeTLSA: "TLSA",
TypeTSIG: "TSIG",
TypeTXT: "TXT",
TypeUID: "UID",
TypeUINFO: "UINFO",
TypeUNSPEC: "UNSPEC",
TypeURI: "URI",
TypeX25: "X25",
TypeNSAPPTR: "NSAP-PTR",
}
// Header() functions
func (rr *A) Header() *RR_Header { return &rr.Hdr }
func (rr *AAAA) Header() *RR_Header { return &rr.Hdr }
func (rr *AFSDB) Header() *RR_Header { return &rr.Hdr }
func (rr *ANY) Header() *RR_Header { return &rr.Hdr }
func (rr *AVC) Header() *RR_Header { return &rr.Hdr }
func (rr *CAA) Header() *RR_Header { return &rr.Hdr }
func (rr *CDNSKEY) Header() *RR_Header { return &rr.Hdr }
func (rr *CDS) Header() *RR_Header { return &rr.Hdr }
func (rr *CERT) Header() *RR_Header { return &rr.Hdr }
func (rr *CNAME) Header() *RR_Header { return &rr.Hdr }
func (rr *DHCID) Header() *RR_Header { return &rr.Hdr }
func (rr *DLV) Header() *RR_Header { return &rr.Hdr }
func (rr *DNAME) Header() *RR_Header { return &rr.Hdr }
func (rr *DNSKEY) Header() *RR_Header { return &rr.Hdr }
func (rr *DS) Header() *RR_Header { return &rr.Hdr }
func (rr *EID) Header() *RR_Header { return &rr.Hdr }
func (rr *EUI48) Header() *RR_Header { return &rr.Hdr }
func (rr *EUI64) Header() *RR_Header { return &rr.Hdr }
func (rr *GID) Header() *RR_Header { return &rr.Hdr }
func (rr *GPOS) Header() *RR_Header { return &rr.Hdr }
func (rr *HINFO) Header() *RR_Header { return &rr.Hdr }
func (rr *HIP) Header() *RR_Header { return &rr.Hdr }
func (rr *KEY) Header() *RR_Header { return &rr.Hdr }
func (rr *KX) Header() *RR_Header { return &rr.Hdr }
func (rr *L32) Header() *RR_Header { return &rr.Hdr }
func (rr *L64) Header() *RR_Header { return &rr.Hdr }
func (rr *LOC) Header() *RR_Header { return &rr.Hdr }
func (rr *LP) Header() *RR_Header { return &rr.Hdr }
func (rr *MB) Header() *RR_Header { return &rr.Hdr }
func (rr *MD) Header() *RR_Header { return &rr.Hdr }
func (rr *MF) Header() *RR_Header { return &rr.Hdr }
func (rr *MG) Header() *RR_Header { return &rr.Hdr }
func (rr *MINFO) Header() *RR_Header { return &rr.Hdr }
func (rr *MR) Header() *RR_Header { return &rr.Hdr }
func (rr *MX) Header() *RR_Header { return &rr.Hdr }
func (rr *NAPTR) Header() *RR_Header { return &rr.Hdr }
func (rr *NID) Header() *RR_Header { return &rr.Hdr }
func (rr *NIMLOC) Header() *RR_Header { return &rr.Hdr }
func (rr *NINFO) Header() *RR_Header { return &rr.Hdr }
func (rr *NS) Header() *RR_Header { return &rr.Hdr }
func (rr *NSAPPTR) Header() *RR_Header { return &rr.Hdr }
func (rr *NSEC) Header() *RR_Header { return &rr.Hdr }
func (rr *NSEC3) Header() *RR_Header { return &rr.Hdr }
func (rr *NSEC3PARAM) Header() *RR_Header { return &rr.Hdr }
func (rr *OPENPGPKEY) Header() *RR_Header { return &rr.Hdr }
func (rr *OPT) Header() *RR_Header { return &rr.Hdr }
func (rr *PTR) Header() *RR_Header { return &rr.Hdr }
func (rr *PX) Header() *RR_Header { return &rr.Hdr }
func (rr *RFC3597) Header() *RR_Header { return &rr.Hdr }
func (rr *RKEY) Header() *RR_Header { return &rr.Hdr }
func (rr *RP) Header() *RR_Header { return &rr.Hdr }
func (rr *RRSIG) Header() *RR_Header { return &rr.Hdr }
func (rr *RT) Header() *RR_Header { return &rr.Hdr }
func (rr *SIG) Header() *RR_Header { return &rr.Hdr }
func (rr *SMIMEA) Header() *RR_Header { return &rr.Hdr }
func (rr *SOA) Header() *RR_Header { return &rr.Hdr }
func (rr *SPF) Header() *RR_Header { return &rr.Hdr }
func (rr *SRV) Header() *RR_Header { return &rr.Hdr }
func (rr *SSHFP) Header() *RR_Header { return &rr.Hdr }
func (rr *TA) Header() *RR_Header { return &rr.Hdr }
func (rr *TALINK) Header() *RR_Header { return &rr.Hdr }
func (rr *TKEY) Header() *RR_Header { return &rr.Hdr }
func (rr *TLSA) Header() *RR_Header { return &rr.Hdr }
func (rr *TSIG) Header() *RR_Header { return &rr.Hdr }
func (rr *TXT) Header() *RR_Header { return &rr.Hdr }
func (rr *UID) Header() *RR_Header { return &rr.Hdr }
func (rr *UINFO) Header() *RR_Header { return &rr.Hdr }
func (rr *URI) Header() *RR_Header { return &rr.Hdr }
func (rr *X25) Header() *RR_Header { return &rr.Hdr }
// len() functions
func (rr *A) len() int {
l := rr.Hdr.len()
l += net.IPv4len // A
return l
}
func (rr *AAAA) len() int {
l := rr.Hdr.len()
l += net.IPv6len // AAAA
return l
}
func (rr *AFSDB) len() int {
l := rr.Hdr.len()
l += 2 // Subtype
l += len(rr.Hostname) + 1
return l
}
func (rr *ANY) len() int {
l := rr.Hdr.len()
return l
}
func (rr *AVC) len() int {
l := rr.Hdr.len()
for _, x := range rr.Txt {
l += len(x) + 1
}
return l
}
func (rr *CAA) len() int {
l := rr.Hdr.len()
l++ // Flag
l += len(rr.Tag) + 1
l += len(rr.Value)
return l
}
func (rr *CERT) len() int {
l := rr.Hdr.len()
l += 2 // Type
l += 2 // KeyTag
l++ // Algorithm
l += base64.StdEncoding.DecodedLen(len(rr.Certificate))
return l
}
func (rr *CNAME) len() int {
l := rr.Hdr.len()
l += len(rr.Target) + 1
return l
}
func (rr *DHCID) len() int {
l := rr.Hdr.len()
l += base64.StdEncoding.DecodedLen(len(rr.Digest))
return l
}
func (rr *DNAME) len() int {
l := rr.Hdr.len()
l += len(rr.Target) + 1
return l
}
func (rr *DNSKEY) len() int {
l := rr.Hdr.len()
l += 2 // Flags
l++ // Protocol
l++ // Algorithm
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
return l
}
func (rr *DS) len() int {
l := rr.Hdr.len()
l += 2 // KeyTag
l++ // Algorithm
l++ // DigestType
l += len(rr.Digest)/2 + 1
return l
}
func (rr *EID) len() int {
l := rr.Hdr.len()
l += len(rr.Endpoint)/2 + 1
return l
}
func (rr *EUI48) len() int {
l := rr.Hdr.len()
l += 6 // Address
return l
}
func (rr *EUI64) len() int {
l := rr.Hdr.len()
l += 8 // Address
return l
}
func (rr *GID) len() int {
l := rr.Hdr.len()
l += 4 // Gid
return l
}
func (rr *GPOS) len() int {
l := rr.Hdr.len()
l += len(rr.Longitude) + 1
l += len(rr.Latitude) + 1
l += len(rr.Altitude) + 1
return l
}
func (rr *HINFO) len() int {
l := rr.Hdr.len()
l += len(rr.Cpu) + 1
l += len(rr.Os) + 1
return l
}
func (rr *HIP) len() int {
l := rr.Hdr.len()
l++ // HitLength
l++ // PublicKeyAlgorithm
l += 2 // PublicKeyLength
l += len(rr.Hit)/2 + 1
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
for _, x := range rr.RendezvousServers {
l += len(x) + 1
}
return l
}
func (rr *KX) len() int {
l := rr.Hdr.len()
l += 2 // Preference
l += len(rr.Exchanger) + 1
return l
}
func (rr *L32) len() int {
l := rr.Hdr.len()
l += 2 // Preference
l += net.IPv4len // Locator32
return l
}
func (rr *L64) len() int {
l := rr.Hdr.len()
l += 2 // Preference
l += 8 // Locator64
return l
}
func (rr *LOC) len() int {
l := rr.Hdr.len()
l++ // Version
l++ // Size
l++ // HorizPre
l++ // VertPre
l += 4 // Latitude
l += 4 // Longitude
l += 4 // Altitude
return l
}
func (rr *LP) len() int {
l := rr.Hdr.len()
l += 2 // Preference
l += len(rr.Fqdn) + 1
return l
}
func (rr *MB) len() int {
l := rr.Hdr.len()
l += len(rr.Mb) + 1
return l
}
func (rr *MD) len() int {
l := rr.Hdr.len()
l += len(rr.Md) + 1
return l
}
func (rr *MF) len() int {
l := rr.Hdr.len()
l += len(rr.Mf) + 1
return l
}
func (rr *MG) len() int {
l := rr.Hdr.len()
l += len(rr.Mg) + 1
return l
}
func (rr *MINFO) len() int {
l := rr.Hdr.len()
l += len(rr.Rmail) + 1
l += len(rr.Email) + 1
return l
}
func (rr *MR) len() int {
l := rr.Hdr.len()
l += len(rr.Mr) + 1
return l
}
func (rr *MX) len() int {
l := rr.Hdr.len()
l += 2 // Preference
l += len(rr.Mx) + 1
return l
}
func (rr *NAPTR) len() int {
l := rr.Hdr.len()
l += 2 // Order
l += 2 // Preference
l += len(rr.Flags) + 1
l += len(rr.Service) + 1
l += len(rr.Regexp) + 1
l += len(rr.Replacement) + 1
return l
}
func (rr *NID) len() int {
l := rr.Hdr.len()
l += 2 // Preference
l += 8 // NodeID
return l
}
func (rr *NIMLOC) len() int {
l := rr.Hdr.len()
l += len(rr.Locator)/2 + 1
return l
}
func (rr *NINFO) len() int {
l := rr.Hdr.len()
for _, x := range rr.ZSData {
l += len(x) + 1
}
return l
}
func (rr *NS) len() int {
l := rr.Hdr.len()
l += len(rr.Ns) + 1
return l
}
func (rr *NSAPPTR) len() int {
l := rr.Hdr.len()
l += len(rr.Ptr) + 1
return l
}
func (rr *NSEC3PARAM) len() int {
l := rr.Hdr.len()
l++ // Hash
l++ // Flags
l += 2 // Iterations
l++ // SaltLength
l += len(rr.Salt)/2 + 1
return l
}
func (rr *OPENPGPKEY) len() int {
l := rr.Hdr.len()
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
return l
}
func (rr *PTR) len() int {
l := rr.Hdr.len()
l += len(rr.Ptr) + 1
return l
}
func (rr *PX) len() int {
l := rr.Hdr.len()
l += 2 // Preference
l += len(rr.Map822) + 1
l += len(rr.Mapx400) + 1
return l
}
func (rr *RFC3597) len() int {
l := rr.Hdr.len()
l += len(rr.Rdata)/2 + 1
return l
}
func (rr *RKEY) len() int {
l := rr.Hdr.len()
l += 2 // Flags
l++ // Protocol
l++ // Algorithm
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
return l
}
func (rr *RP) len() int {
l := rr.Hdr.len()
l += len(rr.Mbox) + 1
l += len(rr.Txt) + 1
return l
}
func (rr *RRSIG) len() int {
l := rr.Hdr.len()
l += 2 // TypeCovered
l++ // Algorithm
l++ // Labels
l += 4 // OrigTtl
l += 4 // Expiration
l += 4 // Inception
l += 2 // KeyTag
l += len(rr.SignerName) + 1
l += base64.StdEncoding.DecodedLen(len(rr.Signature))
return l
}
func (rr *RT) len() int {
l := rr.Hdr.len()
l += 2 // Preference
l += len(rr.Host) + 1
return l
}
func (rr *SMIMEA) len() int {
l := rr.Hdr.len()
l++ // Usage
l++ // Selector
l++ // MatchingType
l += len(rr.Certificate)/2 + 1
return l
}
func (rr *SOA) len() int {
l := rr.Hdr.len()
l += len(rr.Ns) + 1
l += len(rr.Mbox) + 1
l += 4 // Serial
l += 4 // Refresh
l += 4 // Retry
l += 4 // Expire
l += 4 // Minttl
return l
}
func (rr *SPF) len() int {
l := rr.Hdr.len()
for _, x := range rr.Txt {
l += len(x) + 1
}
return l
}
func (rr *SRV) len() int {
l := rr.Hdr.len()
l += 2 // Priority
l += 2 // Weight
l += 2 // Port
l += len(rr.Target) + 1
return l
}
func (rr *SSHFP) len() int {
l := rr.Hdr.len()
l++ // Algorithm
l++ // Type
l += len(rr.FingerPrint)/2 + 1
return l
}
func (rr *TA) len() int {
l := rr.Hdr.len()
l += 2 // KeyTag
l++ // Algorithm
l++ // DigestType
l += len(rr.Digest)/2 + 1
return l
}
func (rr *TALINK) len() int {
l := rr.Hdr.len()
l += len(rr.PreviousName) + 1
l += len(rr.NextName) + 1
return l
}
func (rr *TKEY) len() int {
l := rr.Hdr.len()
l += len(rr.Algorithm) + 1
l += 4 // Inception
l += 4 // Expiration
l += 2 // Mode
l += 2 // Error
l += 2 // KeySize
l += len(rr.Key) + 1
l += 2 // OtherLen
l += len(rr.OtherData) + 1
return l
}
func (rr *TLSA) len() int {
l := rr.Hdr.len()
l++ // Usage
l++ // Selector
l++ // MatchingType
l += len(rr.Certificate)/2 + 1
return l
}
func (rr *TSIG) len() int {
l := rr.Hdr.len()
l += len(rr.Algorithm) + 1
l += 6 // TimeSigned
l += 2 // Fudge
l += 2 // MACSize
l += len(rr.MAC)/2 + 1
l += 2 // OrigId
l += 2 // Error
l += 2 // OtherLen
l += len(rr.OtherData)/2 + 1
return l
}
func (rr *TXT) len() int {
l := rr.Hdr.len()
for _, x := range rr.Txt {
l += len(x) + 1
}
return l
}
func (rr *UID) len() int {
l := rr.Hdr.len()
l += 4 // Uid
return l
}
func (rr *UINFO) len() int {
l := rr.Hdr.len()
l += len(rr.Uinfo) + 1
return l
}
func (rr *URI) len() int {
l := rr.Hdr.len()
l += 2 // Priority
l += 2 // Weight
l += len(rr.Target)
return l
}
func (rr *X25) len() int {
l := rr.Hdr.len()
l += len(rr.PSDNAddress) + 1
return l
}
// copy() functions
func (rr *A) copy() RR {
return &A{*rr.Hdr.copyHeader(), copyIP(rr.A)}
}
func (rr *AAAA) copy() RR {
return &AAAA{*rr.Hdr.copyHeader(), copyIP(rr.AAAA)}
}
func (rr *AFSDB) copy() RR {
return &AFSDB{*rr.Hdr.copyHeader(), rr.Subtype, rr.Hostname}
}
func (rr *ANY) copy() RR {
return &ANY{*rr.Hdr.copyHeader()}
}
func (rr *AVC) copy() RR {
Txt := make([]string, len(rr.Txt))
copy(Txt, rr.Txt)
return &AVC{*rr.Hdr.copyHeader(), Txt}
}
func (rr *CAA) copy() RR {
return &CAA{*rr.Hdr.copyHeader(), rr.Flag, rr.Tag, rr.Value}
}
func (rr *CERT) copy() RR {
return &CERT{*rr.Hdr.copyHeader(), rr.Type, rr.KeyTag, rr.Algorithm, rr.Certificate}
}
func (rr *CNAME) copy() RR {
return &CNAME{*rr.Hdr.copyHeader(), rr.Target}
}
func (rr *DHCID) copy() RR {
return &DHCID{*rr.Hdr.copyHeader(), rr.Digest}
}
func (rr *DNAME) copy() RR {
return &DNAME{*rr.Hdr.copyHeader(), rr.Target}
}
func (rr *DNSKEY) copy() RR {
return &DNSKEY{*rr.Hdr.copyHeader(), rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey}
}
func (rr *DS) copy() RR {
return &DS{*rr.Hdr.copyHeader(), rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest}
}
func (rr *EID) copy() RR {
return &EID{*rr.Hdr.copyHeader(), rr.Endpoint}
}
func (rr *EUI48) copy() RR {
return &EUI48{*rr.Hdr.copyHeader(), rr.Address}
}
func (rr *EUI64) copy() RR {
return &EUI64{*rr.Hdr.copyHeader(), rr.Address}
}
func (rr *GID) copy() RR {
return &GID{*rr.Hdr.copyHeader(), rr.Gid}
}
func (rr *GPOS) copy() RR {
return &GPOS{*rr.Hdr.copyHeader(), rr.Longitude, rr.Latitude, rr.Altitude}
}
func (rr *HINFO) copy() RR {
return &HINFO{*rr.Hdr.copyHeader(), rr.Cpu, rr.Os}
}
func (rr *HIP) copy() RR {
RendezvousServers := make([]string, len(rr.RendezvousServers))
copy(RendezvousServers, rr.RendezvousServers)
return &HIP{*rr.Hdr.copyHeader(), rr.HitLength, rr.PublicKeyAlgorithm, rr.PublicKeyLength, rr.Hit, rr.PublicKey, RendezvousServers}
}
func (rr *KX) copy() RR {
return &KX{*rr.Hdr.copyHeader(), rr.Preference, rr.Exchanger}
}
func (rr *L32) copy() RR {
return &L32{*rr.Hdr.copyHeader(), rr.Preference, copyIP(rr.Locator32)}
}
func (rr *L64) copy() RR {
return &L64{*rr.Hdr.copyHeader(), rr.Preference, rr.Locator64}
}
func (rr *LOC) copy() RR {
return &LOC{*rr.Hdr.copyHeader(), rr.Version, rr.Size, rr.HorizPre, rr.VertPre, rr.Latitude, rr.Longitude, rr.Altitude}
}
func (rr *LP) copy() RR {
return &LP{*rr.Hdr.copyHeader(), rr.Preference, rr.Fqdn}
}
func (rr *MB) copy() RR {
return &MB{*rr.Hdr.copyHeader(), rr.Mb}
}
func (rr *MD) copy() RR {
return &MD{*rr.Hdr.copyHeader(), rr.Md}
}
func (rr *MF) copy() RR {
return &MF{*rr.Hdr.copyHeader(), rr.Mf}
}
func (rr *MG) copy() RR {
return &MG{*rr.Hdr.copyHeader(), rr.Mg}
}
func (rr *MINFO) copy() RR {
return &MINFO{*rr.Hdr.copyHeader(), rr.Rmail, rr.Email}
}
func (rr *MR) copy() RR {
return &MR{*rr.Hdr.copyHeader(), rr.Mr}
}
func (rr *MX) copy() RR {
return &MX{*rr.Hdr.copyHeader(), rr.Preference, rr.Mx}
}
func (rr *NAPTR) copy() RR {
return &NAPTR{*rr.Hdr.copyHeader(), rr.Order, rr.Preference, rr.Flags, rr.Service, rr.Regexp, rr.Replacement}
}
func (rr *NID) copy() RR {
return &NID{*rr.Hdr.copyHeader(), rr.Preference, rr.NodeID}
}
func (rr *NIMLOC) copy() RR {
return &NIMLOC{*rr.Hdr.copyHeader(), rr.Locator}
}
func (rr *NINFO) copy() RR {
ZSData := make([]string, len(rr.ZSData))
copy(ZSData, rr.ZSData)
return &NINFO{*rr.Hdr.copyHeader(), ZSData}
}
func (rr *NS) copy() RR {
return &NS{*rr.Hdr.copyHeader(), rr.Ns}
}
func (rr *NSAPPTR) copy() RR {
return &NSAPPTR{*rr.Hdr.copyHeader(), rr.Ptr}
}
func (rr *NSEC) copy() RR {
TypeBitMap := make([]uint16, len(rr.TypeBitMap))
copy(TypeBitMap, rr.TypeBitMap)
return &NSEC{*rr.Hdr.copyHeader(), rr.NextDomain, TypeBitMap}
}
func (rr *NSEC3) copy() RR {
TypeBitMap := make([]uint16, len(rr.TypeBitMap))
copy(TypeBitMap, rr.TypeBitMap)
return &NSEC3{*rr.Hdr.copyHeader(), rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt, rr.HashLength, rr.NextDomain, TypeBitMap}
}
func (rr *NSEC3PARAM) copy() RR {
return &NSEC3PARAM{*rr.Hdr.copyHeader(), rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt}
}
func (rr *OPENPGPKEY) copy() RR {
return &OPENPGPKEY{*rr.Hdr.copyHeader(), rr.PublicKey}
}
func (rr *OPT) copy() RR {
Option := make([]EDNS0, len(rr.Option))
copy(Option, rr.Option)
return &OPT{*rr.Hdr.copyHeader(), Option}
}
func (rr *PTR) copy() RR {
return &PTR{*rr.Hdr.copyHeader(), rr.Ptr}
}
func (rr *PX) copy() RR {
return &PX{*rr.Hdr.copyHeader(), rr.Preference, rr.Map822, rr.Mapx400}
}
func (rr *RFC3597) copy() RR {
return &RFC3597{*rr.Hdr.copyHeader(), rr.Rdata}
}
func (rr *RKEY) copy() RR {
return &RKEY{*rr.Hdr.copyHeader(), rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey}
}
func (rr *RP) copy() RR {
return &RP{*rr.Hdr.copyHeader(), rr.Mbox, rr.Txt}
}
func (rr *RRSIG) copy() RR {
return &RRSIG{*rr.Hdr.copyHeader(), rr.TypeCovered, rr.Algorithm, rr.Labels, rr.OrigTtl, rr.Expiration, rr.Inception, rr.KeyTag, rr.SignerName, rr.Signature}
}
func (rr *RT) copy() RR {
return &RT{*rr.Hdr.copyHeader(), rr.Preference, rr.Host}
}
func (rr *SMIMEA) copy() RR {
return &SMIMEA{*rr.Hdr.copyHeader(), rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate}
}
func (rr *SOA) copy() RR {
return &SOA{*rr.Hdr.copyHeader(), rr.Ns, rr.Mbox, rr.Serial, rr.Refresh, rr.Retry, rr.Expire, rr.Minttl}
}
func (rr *SPF) copy() RR {
Txt := make([]string, len(rr.Txt))
copy(Txt, rr.Txt)
return &SPF{*rr.Hdr.copyHeader(), Txt}
}
func (rr *SRV) copy() RR {
return &SRV{*rr.Hdr.copyHeader(), rr.Priority, rr.Weight, rr.Port, rr.Target}
}
func (rr *SSHFP) copy() RR {
return &SSHFP{*rr.Hdr.copyHeader(), rr.Algorithm, rr.Type, rr.FingerPrint}
}
func (rr *TA) copy() RR {
return &TA{*rr.Hdr.copyHeader(), rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest}
}
func (rr *TALINK) copy() RR {
return &TALINK{*rr.Hdr.copyHeader(), rr.PreviousName, rr.NextName}
}
func (rr *TKEY) copy() RR {
return &TKEY{*rr.Hdr.copyHeader(), rr.Algorithm, rr.Inception, rr.Expiration, rr.Mode, rr.Error, rr.KeySize, rr.Key, rr.OtherLen, rr.OtherData}
}
func (rr *TLSA) copy() RR {
return &TLSA{*rr.Hdr.copyHeader(), rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate}
}
func (rr *TSIG) copy() RR {
return &TSIG{*rr.Hdr.copyHeader(), rr.Algorithm, rr.TimeSigned, rr.Fudge, rr.MACSize, rr.MAC, rr.OrigId, rr.Error, rr.OtherLen, rr.OtherData}
}
func (rr *TXT) copy() RR {
Txt := make([]string, len(rr.Txt))
copy(Txt, rr.Txt)
return &TXT{*rr.Hdr.copyHeader(), Txt}
}
func (rr *UID) copy() RR {
return &UID{*rr.Hdr.copyHeader(), rr.Uid}
}
func (rr *UINFO) copy() RR {
return &UINFO{*rr.Hdr.copyHeader(), rr.Uinfo}
}
func (rr *URI) copy() RR {
return &URI{*rr.Hdr.copyHeader(), rr.Priority, rr.Weight, rr.Target}
}
func (rr *X25) copy() RR {
return &X25{*rr.Hdr.copyHeader(), rr.PSDNAddress}
}

View file

@ -1,10 +0,0 @@
# Treat all files in this repo as binary, with no git magic updating
# line endings. Windows users contributing to Go will need to use a
# modern version of git and editors capable of LF line endings.
#
# We'll prevent accidental CRLF line endings from entering the repo
# via the git-review gofmt checks.
#
# See golang.org/issue/9281
* -text

2
vendor/golang.org/x/net/.gitignore generated vendored
View file

@ -1,2 +0,0 @@
# Add no patterns to .hgignore except for files generated by the build.
last-change

3
vendor/golang.org/x/net/AUTHORS generated vendored
View file

@ -1,3 +0,0 @@
# This source code refers to The Go Authors for copyright purposes.
# The master list of authors is in the main Go distribution,
# visible at http://tip.golang.org/AUTHORS.

View file

@ -1,31 +0,0 @@
# Contributing to Go
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?
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.
## Contributing code
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.

View file

@ -1,3 +0,0 @@
# This source code was written by the Go contributors.
# The master list of contributors is in the main Go distribution,
# visible at http://tip.golang.org/CONTRIBUTORS.

27
vendor/golang.org/x/net/LICENSE generated vendored
View file

@ -1,27 +0,0 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
vendor/golang.org/x/net/PATENTS generated vendored
View file

@ -1,22 +0,0 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

3
vendor/golang.org/x/net/README generated vendored
View file

@ -1,3 +0,0 @@
This repository holds supplementary Go networking libraries.
To submit changes to this repository, see http://golang.org/doc/contribute.html.

View file

@ -1 +0,0 @@
issuerepo: golang/go

View file

@ -1,156 +0,0 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package context defines the Context type, which carries deadlines,
// cancelation signals, and other request-scoped values across API boundaries
// and between processes.
//
// Incoming requests to a server should create a Context, and outgoing calls to
// servers should accept a Context. The chain of function calls between must
// propagate the Context, optionally replacing it with a modified copy created
// using WithDeadline, WithTimeout, WithCancel, or WithValue.
//
// Programs that use Contexts should follow these rules to keep interfaces
// consistent across packages and enable static analysis tools to check context
// propagation:
//
// Do not store Contexts inside a struct type; instead, pass a Context
// explicitly to each function that needs it. The Context should be the first
// parameter, typically named ctx:
//
// func DoSomething(ctx context.Context, arg Arg) error {
// // ... use ctx ...
// }
//
// Do not pass a nil Context, even if a function permits it. Pass context.TODO
// if you are unsure about which Context to use.
//
// Use context Values only for request-scoped data that transits processes and
// APIs, not for passing optional parameters to functions.
//
// The same Context may be passed to functions running in different goroutines;
// Contexts are safe for simultaneous use by multiple goroutines.
//
// See http://blog.golang.org/context for example code for a server that uses
// Contexts.
package context // import "golang.org/x/net/context"
import "time"
// A Context carries a deadline, a cancelation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
//
// WithCancel arranges for Done to be closed when cancel is called;
// WithDeadline arranges for Done to be closed when the deadline
// expires; WithTimeout arranges for Done to be closed when the timeout
// elapses.
//
// Done is provided for use in select statements:
//
// // Stream generates values with DoSomething and sends them to out
// // until DoSomething returns an error or ctx.Done is closed.
// func Stream(ctx context.Context, out chan<- Value) error {
// for {
// v, err := DoSomething(ctx)
// if err != nil {
// return err
// }
// select {
// case <-ctx.Done():
// return ctx.Err()
// case out <- v:
// }
// }
// }
//
// See http://blog.golang.org/pipelines for more examples of how to use
// a Done channel for cancelation.
Done() <-chan struct{}
// Err returns a non-nil error value after Done is closed. Err returns
// Canceled if the context was canceled or DeadlineExceeded if the
// context's deadline passed. No other values for Err are defined.
// After Done is closed, successive calls to Err return the same value.
Err() error
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
//
// Use context values only for request-scoped data that transits
// processes and API boundaries, not for passing optional parameters to
// functions.
//
// A key identifies a specific value in a Context. Functions that wish
// to store values in Context typically allocate a key in a global
// variable then use that key as the argument to context.WithValue and
// Context.Value. A key can be any type that supports equality;
// packages should define keys as an unexported type to avoid
// collisions.
//
// Packages that define a Context key should provide type-safe accessors
// for the values stores using that key:
//
// // Package user defines a User type that's stored in Contexts.
// package user
//
// import "golang.org/x/net/context"
//
// // User is the type of value stored in the Contexts.
// type User struct {...}
//
// // key is an unexported type for keys defined in this package.
// // This prevents collisions with keys defined in other packages.
// type key int
//
// // userKey is the key for user.User values in Contexts. It is
// // unexported; clients use user.NewContext and user.FromContext
// // instead of using this key directly.
// var userKey key = 0
//
// // NewContext returns a new Context that carries value u.
// func NewContext(ctx context.Context, u *User) context.Context {
// return context.WithValue(ctx, userKey, u)
// }
//
// // FromContext returns the User value stored in ctx, if any.
// func FromContext(ctx context.Context) (*User, bool) {
// u, ok := ctx.Value(userKey).(*User)
// return u, ok
// }
Value(key interface{}) interface{}
}
// Background returns a non-nil, empty Context. It is never canceled, has no
// values, and has no deadline. It is typically used by the main function,
// initialization, and tests, and as the top-level Context for incoming
// requests.
func Background() Context {
return background
}
// TODO returns a non-nil, empty Context. Code should use context.TODO when
// it's unclear which Context to use or it is not yet available (because the
// surrounding function has not yet been extended to accept a Context
// parameter). TODO is recognized by static analysis tools that determine
// whether Contexts are propagated correctly in a program.
func TODO() Context {
return todo
}
// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// After the first call, subsequent calls to a CancelFunc do nothing.
type CancelFunc func()

View file

@ -1,583 +0,0 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !go1.7
package context
import (
"fmt"
"math/rand"
"runtime"
"strings"
"sync"
"testing"
"time"
)
// otherContext is a Context that's not one of the types defined in context.go.
// This lets us test code paths that differ based on the underlying type of the
// Context.
type otherContext struct {
Context
}
func TestBackground(t *testing.T) {
c := Background()
if c == nil {
t.Fatalf("Background returned nil")
}
select {
case x := <-c.Done():
t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
default:
}
if got, want := fmt.Sprint(c), "context.Background"; got != want {
t.Errorf("Background().String() = %q want %q", got, want)
}
}
func TestTODO(t *testing.T) {
c := TODO()
if c == nil {
t.Fatalf("TODO returned nil")
}
select {
case x := <-c.Done():
t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
default:
}
if got, want := fmt.Sprint(c), "context.TODO"; got != want {
t.Errorf("TODO().String() = %q want %q", got, want)
}
}
func TestWithCancel(t *testing.T) {
c1, cancel := WithCancel(Background())
if got, want := fmt.Sprint(c1), "context.Background.WithCancel"; got != want {
t.Errorf("c1.String() = %q want %q", got, want)
}
o := otherContext{c1}
c2, _ := WithCancel(o)
contexts := []Context{c1, o, c2}
for i, c := range contexts {
if d := c.Done(); d == nil {
t.Errorf("c[%d].Done() == %v want non-nil", i, d)
}
if e := c.Err(); e != nil {
t.Errorf("c[%d].Err() == %v want nil", i, e)
}
select {
case x := <-c.Done():
t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
default:
}
}
cancel()
time.Sleep(100 * time.Millisecond) // let cancelation propagate
for i, c := range contexts {
select {
case <-c.Done():
default:
t.Errorf("<-c[%d].Done() blocked, but shouldn't have", i)
}
if e := c.Err(); e != Canceled {
t.Errorf("c[%d].Err() == %v want %v", i, e, Canceled)
}
}
}
func TestParentFinishesChild(t *testing.T) {
// Context tree:
// parent -> cancelChild
// parent -> valueChild -> timerChild
parent, cancel := WithCancel(Background())
cancelChild, stop := WithCancel(parent)
defer stop()
valueChild := WithValue(parent, "key", "value")
timerChild, stop := WithTimeout(valueChild, 10000*time.Hour)
defer stop()
select {
case x := <-parent.Done():
t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
case x := <-cancelChild.Done():
t.Errorf("<-cancelChild.Done() == %v want nothing (it should block)", x)
case x := <-timerChild.Done():
t.Errorf("<-timerChild.Done() == %v want nothing (it should block)", x)
case x := <-valueChild.Done():
t.Errorf("<-valueChild.Done() == %v want nothing (it should block)", x)
default:
}
// The parent's children should contain the two cancelable children.
pc := parent.(*cancelCtx)
cc := cancelChild.(*cancelCtx)
tc := timerChild.(*timerCtx)
pc.mu.Lock()
if len(pc.children) != 2 || !pc.children[cc] || !pc.children[tc] {
t.Errorf("bad linkage: pc.children = %v, want %v and %v",
pc.children, cc, tc)
}
pc.mu.Unlock()
if p, ok := parentCancelCtx(cc.Context); !ok || p != pc {
t.Errorf("bad linkage: parentCancelCtx(cancelChild.Context) = %v, %v want %v, true", p, ok, pc)
}
if p, ok := parentCancelCtx(tc.Context); !ok || p != pc {
t.Errorf("bad linkage: parentCancelCtx(timerChild.Context) = %v, %v want %v, true", p, ok, pc)
}
cancel()
pc.mu.Lock()
if len(pc.children) != 0 {
t.Errorf("pc.cancel didn't clear pc.children = %v", pc.children)
}
pc.mu.Unlock()
// parent and children should all be finished.
check := func(ctx Context, name string) {
select {
case <-ctx.Done():
default:
t.Errorf("<-%s.Done() blocked, but shouldn't have", name)
}
if e := ctx.Err(); e != Canceled {
t.Errorf("%s.Err() == %v want %v", name, e, Canceled)
}
}
check(parent, "parent")
check(cancelChild, "cancelChild")
check(valueChild, "valueChild")
check(timerChild, "timerChild")
// WithCancel should return a canceled context on a canceled parent.
precanceledChild := WithValue(parent, "key", "value")
select {
case <-precanceledChild.Done():
default:
t.Errorf("<-precanceledChild.Done() blocked, but shouldn't have")
}
if e := precanceledChild.Err(); e != Canceled {
t.Errorf("precanceledChild.Err() == %v want %v", e, Canceled)
}
}
func TestChildFinishesFirst(t *testing.T) {
cancelable, stop := WithCancel(Background())
defer stop()
for _, parent := range []Context{Background(), cancelable} {
child, cancel := WithCancel(parent)
select {
case x := <-parent.Done():
t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
case x := <-child.Done():
t.Errorf("<-child.Done() == %v want nothing (it should block)", x)
default:
}
cc := child.(*cancelCtx)
pc, pcok := parent.(*cancelCtx) // pcok == false when parent == Background()
if p, ok := parentCancelCtx(cc.Context); ok != pcok || (ok && pc != p) {
t.Errorf("bad linkage: parentCancelCtx(cc.Context) = %v, %v want %v, %v", p, ok, pc, pcok)
}
if pcok {
pc.mu.Lock()
if len(pc.children) != 1 || !pc.children[cc] {
t.Errorf("bad linkage: pc.children = %v, cc = %v", pc.children, cc)
}
pc.mu.Unlock()
}
cancel()
if pcok {
pc.mu.Lock()
if len(pc.children) != 0 {
t.Errorf("child's cancel didn't remove self from pc.children = %v", pc.children)
}
pc.mu.Unlock()
}
// child should be finished.
select {
case <-child.Done():
default:
t.Errorf("<-child.Done() blocked, but shouldn't have")
}
if e := child.Err(); e != Canceled {
t.Errorf("child.Err() == %v want %v", e, Canceled)
}
// parent should not be finished.
select {
case x := <-parent.Done():
t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
default:
}
if e := parent.Err(); e != nil {
t.Errorf("parent.Err() == %v want nil", e)
}
}
}
func testDeadline(c Context, wait time.Duration, t *testing.T) {
select {
case <-time.After(wait):
t.Fatalf("context should have timed out")
case <-c.Done():
}
if e := c.Err(); e != DeadlineExceeded {
t.Errorf("c.Err() == %v want %v", e, DeadlineExceeded)
}
}
func TestDeadline(t *testing.T) {
t.Parallel()
const timeUnit = 500 * time.Millisecond
c, _ := WithDeadline(Background(), time.Now().Add(1*timeUnit))
if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
t.Errorf("c.String() = %q want prefix %q", got, prefix)
}
testDeadline(c, 2*timeUnit, t)
c, _ = WithDeadline(Background(), time.Now().Add(1*timeUnit))
o := otherContext{c}
testDeadline(o, 2*timeUnit, t)
c, _ = WithDeadline(Background(), time.Now().Add(1*timeUnit))
o = otherContext{c}
c, _ = WithDeadline(o, time.Now().Add(3*timeUnit))
testDeadline(c, 2*timeUnit, t)
}
func TestTimeout(t *testing.T) {
t.Parallel()
const timeUnit = 500 * time.Millisecond
c, _ := WithTimeout(Background(), 1*timeUnit)
if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
t.Errorf("c.String() = %q want prefix %q", got, prefix)
}
testDeadline(c, 2*timeUnit, t)
c, _ = WithTimeout(Background(), 1*timeUnit)
o := otherContext{c}
testDeadline(o, 2*timeUnit, t)
c, _ = WithTimeout(Background(), 1*timeUnit)
o = otherContext{c}
c, _ = WithTimeout(o, 3*timeUnit)
testDeadline(c, 2*timeUnit, t)
}
func TestCanceledTimeout(t *testing.T) {
t.Parallel()
const timeUnit = 500 * time.Millisecond
c, _ := WithTimeout(Background(), 2*timeUnit)
o := otherContext{c}
c, cancel := WithTimeout(o, 4*timeUnit)
cancel()
time.Sleep(1 * timeUnit) // let cancelation propagate
select {
case <-c.Done():
default:
t.Errorf("<-c.Done() blocked, but shouldn't have")
}
if e := c.Err(); e != Canceled {
t.Errorf("c.Err() == %v want %v", e, Canceled)
}
}
type key1 int
type key2 int
var k1 = key1(1)
var k2 = key2(1) // same int as k1, different type
var k3 = key2(3) // same type as k2, different int
func TestValues(t *testing.T) {
check := func(c Context, nm, v1, v2, v3 string) {
if v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 {
t.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0)
}
if v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 {
t.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0)
}
if v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 {
t.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0)
}
}
c0 := Background()
check(c0, "c0", "", "", "")
c1 := WithValue(Background(), k1, "c1k1")
check(c1, "c1", "c1k1", "", "")
if got, want := fmt.Sprint(c1), `context.Background.WithValue(1, "c1k1")`; got != want {
t.Errorf("c.String() = %q want %q", got, want)
}
c2 := WithValue(c1, k2, "c2k2")
check(c2, "c2", "c1k1", "c2k2", "")
c3 := WithValue(c2, k3, "c3k3")
check(c3, "c2", "c1k1", "c2k2", "c3k3")
c4 := WithValue(c3, k1, nil)
check(c4, "c4", "", "c2k2", "c3k3")
o0 := otherContext{Background()}
check(o0, "o0", "", "", "")
o1 := otherContext{WithValue(Background(), k1, "c1k1")}
check(o1, "o1", "c1k1", "", "")
o2 := WithValue(o1, k2, "o2k2")
check(o2, "o2", "c1k1", "o2k2", "")
o3 := otherContext{c4}
check(o3, "o3", "", "c2k2", "c3k3")
o4 := WithValue(o3, k3, nil)
check(o4, "o4", "", "c2k2", "")
}
func TestAllocs(t *testing.T) {
bg := Background()
for _, test := range []struct {
desc string
f func()
limit float64
gccgoLimit float64
}{
{
desc: "Background()",
f: func() { Background() },
limit: 0,
gccgoLimit: 0,
},
{
desc: fmt.Sprintf("WithValue(bg, %v, nil)", k1),
f: func() {
c := WithValue(bg, k1, nil)
c.Value(k1)
},
limit: 3,
gccgoLimit: 3,
},
{
desc: "WithTimeout(bg, 15*time.Millisecond)",
f: func() {
c, _ := WithTimeout(bg, 15*time.Millisecond)
<-c.Done()
},
limit: 8,
gccgoLimit: 16,
},
{
desc: "WithCancel(bg)",
f: func() {
c, cancel := WithCancel(bg)
cancel()
<-c.Done()
},
limit: 5,
gccgoLimit: 8,
},
{
desc: "WithTimeout(bg, 100*time.Millisecond)",
f: func() {
c, cancel := WithTimeout(bg, 100*time.Millisecond)
cancel()
<-c.Done()
},
limit: 8,
gccgoLimit: 25,
},
} {
limit := test.limit
if runtime.Compiler == "gccgo" {
// gccgo does not yet do escape analysis.
// TODO(iant): Remove this when gccgo does do escape analysis.
limit = test.gccgoLimit
}
if n := testing.AllocsPerRun(100, test.f); n > limit {
t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit))
}
}
}
func TestSimultaneousCancels(t *testing.T) {
root, cancel := WithCancel(Background())
m := map[Context]CancelFunc{root: cancel}
q := []Context{root}
// Create a tree of contexts.
for len(q) != 0 && len(m) < 100 {
parent := q[0]
q = q[1:]
for i := 0; i < 4; i++ {
ctx, cancel := WithCancel(parent)
m[ctx] = cancel
q = append(q, ctx)
}
}
// Start all the cancels in a random order.
var wg sync.WaitGroup
wg.Add(len(m))
for _, cancel := range m {
go func(cancel CancelFunc) {
cancel()
wg.Done()
}(cancel)
}
// Wait on all the contexts in a random order.
for ctx := range m {
select {
case <-ctx.Done():
case <-time.After(1 * time.Second):
buf := make([]byte, 10<<10)
n := runtime.Stack(buf, true)
t.Fatalf("timed out waiting for <-ctx.Done(); stacks:\n%s", buf[:n])
}
}
// Wait for all the cancel functions to return.
done := make(chan struct{})
go func() {
wg.Wait()
close(done)
}()
select {
case <-done:
case <-time.After(1 * time.Second):
buf := make([]byte, 10<<10)
n := runtime.Stack(buf, true)
t.Fatalf("timed out waiting for cancel functions; stacks:\n%s", buf[:n])
}
}
func TestInterlockedCancels(t *testing.T) {
parent, cancelParent := WithCancel(Background())
child, cancelChild := WithCancel(parent)
go func() {
parent.Done()
cancelChild()
}()
cancelParent()
select {
case <-child.Done():
case <-time.After(1 * time.Second):
buf := make([]byte, 10<<10)
n := runtime.Stack(buf, true)
t.Fatalf("timed out waiting for child.Done(); stacks:\n%s", buf[:n])
}
}
func TestLayersCancel(t *testing.T) {
testLayers(t, time.Now().UnixNano(), false)
}
func TestLayersTimeout(t *testing.T) {
testLayers(t, time.Now().UnixNano(), true)
}
func testLayers(t *testing.T, seed int64, testTimeout bool) {
rand.Seed(seed)
errorf := func(format string, a ...interface{}) {
t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...)
}
const (
timeout = 200 * time.Millisecond
minLayers = 30
)
type value int
var (
vals []*value
cancels []CancelFunc
numTimers int
ctx = Background()
)
for i := 0; i < minLayers || numTimers == 0 || len(cancels) == 0 || len(vals) == 0; i++ {
switch rand.Intn(3) {
case 0:
v := new(value)
ctx = WithValue(ctx, v, v)
vals = append(vals, v)
case 1:
var cancel CancelFunc
ctx, cancel = WithCancel(ctx)
cancels = append(cancels, cancel)
case 2:
var cancel CancelFunc
ctx, cancel = WithTimeout(ctx, timeout)
cancels = append(cancels, cancel)
numTimers++
}
}
checkValues := func(when string) {
for _, key := range vals {
if val := ctx.Value(key).(*value); key != val {
errorf("%s: ctx.Value(%p) = %p want %p", when, key, val, key)
}
}
}
select {
case <-ctx.Done():
errorf("ctx should not be canceled yet")
default:
}
if s, prefix := fmt.Sprint(ctx), "context.Background."; !strings.HasPrefix(s, prefix) {
t.Errorf("ctx.String() = %q want prefix %q", s, prefix)
}
t.Log(ctx)
checkValues("before cancel")
if testTimeout {
select {
case <-ctx.Done():
case <-time.After(timeout + 100*time.Millisecond):
errorf("ctx should have timed out")
}
checkValues("after timeout")
} else {
cancel := cancels[rand.Intn(len(cancels))]
cancel()
select {
case <-ctx.Done():
default:
errorf("ctx should be canceled")
}
checkValues("after cancel")
}
}
func TestCancelRemoves(t *testing.T) {
checkChildren := func(when string, ctx Context, want int) {
if got := len(ctx.(*cancelCtx).children); got != want {
t.Errorf("%s: context has %d children, want %d", when, got, want)
}
}
ctx, _ := WithCancel(Background())
checkChildren("after creation", ctx, 0)
_, cancel := WithCancel(ctx)
checkChildren("with WithCancel child ", ctx, 1)
cancel()
checkChildren("after cancelling WithCancel child", ctx, 0)
ctx, _ = WithCancel(Background())
checkChildren("after creation", ctx, 0)
_, cancel = WithTimeout(ctx, 60*time.Minute)
checkChildren("with WithTimeout child ", ctx, 1)
cancel()
checkChildren("after cancelling WithTimeout child", ctx, 0)
}

View file

@ -1,74 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.7
// Package ctxhttp provides helper functions for performing context-aware HTTP requests.
package ctxhttp // import "golang.org/x/net/context/ctxhttp"
import (
"io"
"net/http"
"net/url"
"strings"
"golang.org/x/net/context"
)
// Do sends an HTTP request with the provided http.Client and returns
// an HTTP response.
//
// If the client is nil, http.DefaultClient is used.
//
// The provided ctx must be non-nil. If it is canceled or times out,
// ctx.Err() will be returned.
func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
if client == nil {
client = http.DefaultClient
}
resp, err := client.Do(req.WithContext(ctx))
// If we got an error, and the context has been canceled,
// the context's error is probably more useful.
if err != nil {
select {
case <-ctx.Done():
err = ctx.Err()
default:
}
}
return resp, err
}
// Get issues a GET request via the Do function.
func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
return Do(ctx, client, req)
}
// Head issues a HEAD request via the Do function.
func Head(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
req, err := http.NewRequest("HEAD", url, nil)
if err != nil {
return nil, err
}
return Do(ctx, client, req)
}
// Post issues a POST request via the Do function.
func Post(ctx context.Context, client *http.Client, url string, bodyType string, body io.Reader) (*http.Response, error) {
req, err := http.NewRequest("POST", url, body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", bodyType)
return Do(ctx, client, req)
}
// PostForm issues a POST request via the Do function.
func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) {
return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
}

View file

@ -1,29 +0,0 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !plan9,go1.7
package ctxhttp
import (
"io"
"net/http"
"net/http/httptest"
"testing"
"context"
)
func TestGo17Context(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "ok")
}))
defer ts.Close()
ctx := context.Background()
resp, err := Get(ctx, http.DefaultClient, ts.URL)
if resp == nil || err != nil {
t.Fatalf("error received from client: %v %v", err, resp)
}
resp.Body.Close()
}

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