14
Godeps/Godeps.json
generated
14
Godeps/Godeps.json
generated
@@ -130,8 +130,8 @@
|
|||||||
"Rev": "aef70dacbc78771e35beb261bb3a72986adf7906"
|
"Rev": "aef70dacbc78771e35beb261bb3a72986adf7906"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/kr/text",
|
"ImportPath": "github.com/miekg/dns",
|
||||||
"Rev": "6807e777504f54ad073ecef66747de158294b639"
|
"Rev": "3f504e8dabd5d562e997d19ce0200aa41973e1b2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mitchellh/goamz/aws",
|
"ImportPath": "github.com/mitchellh/goamz/aws",
|
||||||
@@ -159,6 +159,11 @@
|
|||||||
"ImportPath": "github.com/skratchdot/open-golang/open",
|
"ImportPath": "github.com/skratchdot/open-golang/open",
|
||||||
"Rev": "ba570a111973b539baf23c918213059543b5bb6e"
|
"Rev": "ba570a111973b539baf23c918213059543b5bb6e"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/skynetservices/skydns/msg",
|
||||||
|
"Comment": "2.0.1d-2-g245a121",
|
||||||
|
"Rev": "245a1216be2a7f5377ea56e957fdfa0de6ecd067"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/spf13/cobra",
|
"ImportPath": "github.com/spf13/cobra",
|
||||||
"Rev": "e1e66f7b4e667751cf530ddb6e72b79d6eeb0235"
|
"Rev": "e1e66f7b4e667751cf530ddb6e72b79d6eeb0235"
|
||||||
@@ -179,11 +184,6 @@
|
|||||||
"ImportPath": "github.com/stretchr/testify/mock",
|
"ImportPath": "github.com/stretchr/testify/mock",
|
||||||
"Rev": "37614ac27794505bf7867ca93aac883cadb6a5f7"
|
"Rev": "37614ac27794505bf7867ca93aac883cadb6a5f7"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"ImportPath": "github.com/tonnerre/golang-pretty",
|
|
||||||
"Comment": "debian/0.0_git20130613-1-1-ge7fccc0",
|
|
||||||
"Rev": "e7fccc03e91bad289b96c21aa3312a220689bdd7"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/vaughan0/go-ini",
|
"ImportPath": "github.com/vaughan0/go-ini",
|
||||||
"Rev": "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1"
|
"Rev": "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1"
|
||||||
|
|||||||
19
Godeps/_workspace/src/github.com/kr/text/License
generated
vendored
19
Godeps/_workspace/src/github.com/kr/text/License
generated
vendored
@@ -1,19 +0,0 @@
|
|||||||
Copyright 2012 Keith Rarick
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
3
Godeps/_workspace/src/github.com/kr/text/Readme
generated
vendored
3
Godeps/_workspace/src/github.com/kr/text/Readme
generated
vendored
@@ -1,3 +0,0 @@
|
|||||||
This is a Go package for manipulating paragraphs of text.
|
|
||||||
|
|
||||||
See http://go.pkgdoc.org/github.com/kr/text for full documentation.
|
|
||||||
5
Godeps/_workspace/src/github.com/kr/text/colwriter/Readme
generated
vendored
5
Godeps/_workspace/src/github.com/kr/text/colwriter/Readme
generated
vendored
@@ -1,5 +0,0 @@
|
|||||||
Package colwriter provides a write filter that formats
|
|
||||||
input lines in multiple columns.
|
|
||||||
|
|
||||||
The package is a straightforward translation from
|
|
||||||
/src/cmd/draw/mc.c in Plan 9 from User Space.
|
|
||||||
147
Godeps/_workspace/src/github.com/kr/text/colwriter/column.go
generated
vendored
147
Godeps/_workspace/src/github.com/kr/text/colwriter/column.go
generated
vendored
@@ -1,147 +0,0 @@
|
|||||||
// Package colwriter provides a write filter that formats
|
|
||||||
// input lines in multiple columns.
|
|
||||||
//
|
|
||||||
// The package is a straightforward translation from
|
|
||||||
// /src/cmd/draw/mc.c in Plan 9 from User Space.
|
|
||||||
package colwriter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
tab = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Print each input line ending in a colon ':' separately.
|
|
||||||
BreakOnColon uint = 1 << iota
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Writer is a filter that arranges input lines in as many columns as will
|
|
||||||
// fit in its width. Tab '\t' chars in the input are translated to sequences
|
|
||||||
// of spaces ending at multiples of 4 positions.
|
|
||||||
//
|
|
||||||
// If BreakOnColon is set, each input line ending in a colon ':' is written
|
|
||||||
// separately.
|
|
||||||
//
|
|
||||||
// The Writer assumes that all Unicode code points have the same width; this
|
|
||||||
// may not be true in some fonts.
|
|
||||||
type Writer struct {
|
|
||||||
w io.Writer
|
|
||||||
buf []byte
|
|
||||||
width int
|
|
||||||
flag uint
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewWriter allocates and initializes a new Writer writing to w.
|
|
||||||
// Parameter width controls the total number of characters on each line
|
|
||||||
// across all columns.
|
|
||||||
func NewWriter(w io.Writer, width int, flag uint) *Writer {
|
|
||||||
return &Writer{
|
|
||||||
w: w,
|
|
||||||
width: width,
|
|
||||||
flag: flag,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write writes p to the writer w. The only errors returned are ones
|
|
||||||
// encountered while writing to the underlying output stream.
|
|
||||||
func (w *Writer) Write(p []byte) (n int, err error) {
|
|
||||||
var linelen int
|
|
||||||
var lastWasColon bool
|
|
||||||
for i, c := range p {
|
|
||||||
w.buf = append(w.buf, c)
|
|
||||||
linelen++
|
|
||||||
if c == '\t' {
|
|
||||||
w.buf[len(w.buf)-1] = ' '
|
|
||||||
for linelen%tab != 0 {
|
|
||||||
w.buf = append(w.buf, ' ')
|
|
||||||
linelen++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if w.flag&BreakOnColon != 0 && c == ':' {
|
|
||||||
lastWasColon = true
|
|
||||||
} else if lastWasColon {
|
|
||||||
if c == '\n' {
|
|
||||||
pos := bytes.LastIndex(w.buf[:len(w.buf)-1], []byte{'\n'})
|
|
||||||
if pos < 0 {
|
|
||||||
pos = 0
|
|
||||||
}
|
|
||||||
line := w.buf[pos:]
|
|
||||||
w.buf = w.buf[:pos]
|
|
||||||
if err = w.columnate(); err != nil {
|
|
||||||
if len(line) < i {
|
|
||||||
return i - len(line), err
|
|
||||||
}
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if n, err := w.w.Write(line); err != nil {
|
|
||||||
if r := len(line) - n; r < i {
|
|
||||||
return i - r, err
|
|
||||||
}
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastWasColon = false
|
|
||||||
}
|
|
||||||
if c == '\n' {
|
|
||||||
linelen = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return len(p), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush should be called after the last call to Write to ensure that any data
|
|
||||||
// buffered in the Writer is written to output.
|
|
||||||
func (w *Writer) Flush() error {
|
|
||||||
return w.columnate()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Writer) columnate() error {
|
|
||||||
words := bytes.Split(w.buf, []byte{'\n'})
|
|
||||||
w.buf = nil
|
|
||||||
if len(words[len(words)-1]) == 0 {
|
|
||||||
words = words[:len(words)-1]
|
|
||||||
}
|
|
||||||
maxwidth := 0
|
|
||||||
for _, wd := range words {
|
|
||||||
if n := utf8.RuneCount(wd); n > maxwidth {
|
|
||||||
maxwidth = n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
maxwidth++ // space char
|
|
||||||
wordsPerLine := w.width / maxwidth
|
|
||||||
if wordsPerLine <= 0 {
|
|
||||||
wordsPerLine = 1
|
|
||||||
}
|
|
||||||
nlines := (len(words) + wordsPerLine - 1) / wordsPerLine
|
|
||||||
for i := 0; i < nlines; i++ {
|
|
||||||
col := 0
|
|
||||||
endcol := 0
|
|
||||||
for j := i; j < len(words); j += nlines {
|
|
||||||
endcol += maxwidth
|
|
||||||
_, err := w.w.Write(words[j])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
col += utf8.RuneCount(words[j])
|
|
||||||
if j+nlines < len(words) {
|
|
||||||
for col < endcol {
|
|
||||||
_, err := w.w.Write([]byte{' '})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
col++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, err := w.w.Write([]byte{'\n'})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
90
Godeps/_workspace/src/github.com/kr/text/colwriter/column_test.go
generated
vendored
90
Godeps/_workspace/src/github.com/kr/text/colwriter/column_test.go
generated
vendored
@@ -1,90 +0,0 @@
|
|||||||
package colwriter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var src = `
|
|
||||||
.git
|
|
||||||
.gitignore
|
|
||||||
.godir
|
|
||||||
Procfile:
|
|
||||||
README.md
|
|
||||||
api.go
|
|
||||||
apps.go
|
|
||||||
auth.go
|
|
||||||
darwin.go
|
|
||||||
data.go
|
|
||||||
dyno.go:
|
|
||||||
env.go
|
|
||||||
git.go
|
|
||||||
help.go
|
|
||||||
hkdist
|
|
||||||
linux.go
|
|
||||||
ls.go
|
|
||||||
main.go
|
|
||||||
plugin.go
|
|
||||||
run.go
|
|
||||||
scale.go
|
|
||||||
ssh.go
|
|
||||||
tail.go
|
|
||||||
term
|
|
||||||
unix.go
|
|
||||||
update.go
|
|
||||||
version.go
|
|
||||||
windows.go
|
|
||||||
`[1:]
|
|
||||||
|
|
||||||
var tests = []struct{
|
|
||||||
wid int
|
|
||||||
flag uint
|
|
||||||
src string
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{80, 0, "", ""},
|
|
||||||
{80, 0, src, `
|
|
||||||
.git README.md darwin.go git.go ls.go scale.go unix.go
|
|
||||||
.gitignore api.go data.go help.go main.go ssh.go update.go
|
|
||||||
.godir apps.go dyno.go: hkdist plugin.go tail.go version.go
|
|
||||||
Procfile: auth.go env.go linux.go run.go term windows.go
|
|
||||||
`[1:]},
|
|
||||||
{80, BreakOnColon, src, `
|
|
||||||
.git .gitignore .godir
|
|
||||||
|
|
||||||
Procfile:
|
|
||||||
README.md api.go apps.go auth.go darwin.go data.go
|
|
||||||
|
|
||||||
dyno.go:
|
|
||||||
env.go hkdist main.go scale.go term version.go
|
|
||||||
git.go linux.go plugin.go ssh.go unix.go windows.go
|
|
||||||
help.go ls.go run.go tail.go update.go
|
|
||||||
`[1:]},
|
|
||||||
{20, 0, `
|
|
||||||
Hello
|
|
||||||
Γειά σου
|
|
||||||
안녕
|
|
||||||
今日は
|
|
||||||
`[1:], `
|
|
||||||
Hello 안녕
|
|
||||||
Γειά σου 今日は
|
|
||||||
`[1:]},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWriter(t *testing.T) {
|
|
||||||
for _, test := range tests {
|
|
||||||
b := new(bytes.Buffer)
|
|
||||||
w := NewWriter(b, test.wid, test.flag)
|
|
||||||
if _, err := w.Write([]byte(test.src)); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
if err := w.Flush(); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
if g := b.String(); test.want != g {
|
|
||||||
t.Log("\n" + test.want)
|
|
||||||
t.Log("\n" + g)
|
|
||||||
t.Errorf("%q != %q", test.want, g)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
3
Godeps/_workspace/src/github.com/kr/text/doc.go
generated
vendored
3
Godeps/_workspace/src/github.com/kr/text/doc.go
generated
vendored
@@ -1,3 +0,0 @@
|
|||||||
// Package text provides rudimentary functions for manipulating text in
|
|
||||||
// paragraphs.
|
|
||||||
package text
|
|
||||||
74
Godeps/_workspace/src/github.com/kr/text/indent.go
generated
vendored
74
Godeps/_workspace/src/github.com/kr/text/indent.go
generated
vendored
@@ -1,74 +0,0 @@
|
|||||||
package text
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Indent inserts prefix at the beginning of each non-empty line of s. The
|
|
||||||
// end-of-line marker is NL.
|
|
||||||
func Indent(s, prefix string) string {
|
|
||||||
return string(IndentBytes([]byte(s), []byte(prefix)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IndentBytes inserts prefix at the beginning of each non-empty line of b.
|
|
||||||
// The end-of-line marker is NL.
|
|
||||||
func IndentBytes(b, prefix []byte) []byte {
|
|
||||||
var res []byte
|
|
||||||
bol := true
|
|
||||||
for _, c := range b {
|
|
||||||
if bol && c != '\n' {
|
|
||||||
res = append(res, prefix...)
|
|
||||||
}
|
|
||||||
res = append(res, c)
|
|
||||||
bol = c == '\n'
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
// Writer indents each line of its input.
|
|
||||||
type indentWriter struct {
|
|
||||||
w io.Writer
|
|
||||||
bol bool
|
|
||||||
pre [][]byte
|
|
||||||
sel int
|
|
||||||
off int
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewIndentWriter makes a new write filter that indents the input
|
|
||||||
// lines. Each line is prefixed in order with the corresponding
|
|
||||||
// element of pre. If there are more lines than elements, the last
|
|
||||||
// element of pre is repeated for each subsequent line.
|
|
||||||
func NewIndentWriter(w io.Writer, pre ...[]byte) io.Writer {
|
|
||||||
return &indentWriter{
|
|
||||||
w: w,
|
|
||||||
pre: pre,
|
|
||||||
bol: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The only errors returned are from the underlying indentWriter.
|
|
||||||
func (w *indentWriter) Write(p []byte) (n int, err error) {
|
|
||||||
for _, c := range p {
|
|
||||||
if w.bol {
|
|
||||||
var i int
|
|
||||||
i, err = w.w.Write(w.pre[w.sel][w.off:])
|
|
||||||
w.off += i
|
|
||||||
if err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, err = w.w.Write([]byte{c})
|
|
||||||
if err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
n++
|
|
||||||
w.bol = c == '\n'
|
|
||||||
if w.bol {
|
|
||||||
w.off = 0
|
|
||||||
if w.sel < len(w.pre)-1 {
|
|
||||||
w.sel++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
119
Godeps/_workspace/src/github.com/kr/text/indent_test.go
generated
vendored
119
Godeps/_workspace/src/github.com/kr/text/indent_test.go
generated
vendored
@@ -1,119 +0,0 @@
|
|||||||
package text
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type T struct {
|
|
||||||
inp, exp, pre string
|
|
||||||
}
|
|
||||||
|
|
||||||
var tests = []T{
|
|
||||||
{
|
|
||||||
"The quick brown fox\njumps over the lazy\ndog.\nBut not quickly.\n",
|
|
||||||
"xxxThe quick brown fox\nxxxjumps over the lazy\nxxxdog.\nxxxBut not quickly.\n",
|
|
||||||
"xxx",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"The quick brown fox\njumps over the lazy\ndog.\n\nBut not quickly.",
|
|
||||||
"xxxThe quick brown fox\nxxxjumps over the lazy\nxxxdog.\n\nxxxBut not quickly.",
|
|
||||||
"xxx",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIndent(t *testing.T) {
|
|
||||||
for _, test := range tests {
|
|
||||||
got := Indent(test.inp, test.pre)
|
|
||||||
if got != test.exp {
|
|
||||||
t.Errorf("mismatch %q != %q", got, test.exp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type IndentWriterTest struct {
|
|
||||||
inp, exp string
|
|
||||||
pre []string
|
|
||||||
}
|
|
||||||
|
|
||||||
var ts = []IndentWriterTest{
|
|
||||||
{
|
|
||||||
`
|
|
||||||
The quick brown fox
|
|
||||||
jumps over the lazy
|
|
||||||
dog.
|
|
||||||
But not quickly.
|
|
||||||
`[1:],
|
|
||||||
`
|
|
||||||
xxxThe quick brown fox
|
|
||||||
xxxjumps over the lazy
|
|
||||||
xxxdog.
|
|
||||||
xxxBut not quickly.
|
|
||||||
`[1:],
|
|
||||||
[]string{"xxx"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`
|
|
||||||
The quick brown fox
|
|
||||||
jumps over the lazy
|
|
||||||
dog.
|
|
||||||
But not quickly.
|
|
||||||
`[1:],
|
|
||||||
`
|
|
||||||
xxaThe quick brown fox
|
|
||||||
xxxjumps over the lazy
|
|
||||||
xxxdog.
|
|
||||||
xxxBut not quickly.
|
|
||||||
`[1:],
|
|
||||||
[]string{"xxa", "xxx"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`
|
|
||||||
The quick brown fox
|
|
||||||
jumps over the lazy
|
|
||||||
dog.
|
|
||||||
But not quickly.
|
|
||||||
`[1:],
|
|
||||||
`
|
|
||||||
xxaThe quick brown fox
|
|
||||||
xxbjumps over the lazy
|
|
||||||
xxcdog.
|
|
||||||
xxxBut not quickly.
|
|
||||||
`[1:],
|
|
||||||
[]string{"xxa", "xxb", "xxc", "xxx"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`
|
|
||||||
The quick brown fox
|
|
||||||
jumps over the lazy
|
|
||||||
dog.
|
|
||||||
|
|
||||||
But not quickly.`[1:],
|
|
||||||
`
|
|
||||||
xxaThe quick brown fox
|
|
||||||
xxxjumps over the lazy
|
|
||||||
xxxdog.
|
|
||||||
xxx
|
|
||||||
xxxBut not quickly.`[1:],
|
|
||||||
[]string{"xxa", "xxx"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIndentWriter(t *testing.T) {
|
|
||||||
for _, test := range ts {
|
|
||||||
b := new(bytes.Buffer)
|
|
||||||
pre := make([][]byte, len(test.pre))
|
|
||||||
for i := range test.pre {
|
|
||||||
pre[i] = []byte(test.pre[i])
|
|
||||||
}
|
|
||||||
w := NewIndentWriter(b, pre...)
|
|
||||||
if _, err := w.Write([]byte(test.inp)); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
if got := b.String(); got != test.exp {
|
|
||||||
t.Errorf("mismatch %q != %q", got, test.exp)
|
|
||||||
t.Log(got)
|
|
||||||
t.Log(test.exp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
9
Godeps/_workspace/src/github.com/kr/text/mc/Readme
generated
vendored
9
Godeps/_workspace/src/github.com/kr/text/mc/Readme
generated
vendored
@@ -1,9 +0,0 @@
|
|||||||
Command mc prints in multiple columns.
|
|
||||||
|
|
||||||
Usage: mc [-] [-N] [file...]
|
|
||||||
|
|
||||||
Mc splits the input into as many columns as will fit in N
|
|
||||||
print positions. If the output is a tty, the default N is
|
|
||||||
the number of characters in a terminal line; otherwise the
|
|
||||||
default N is 80. Under option - each input line ending in
|
|
||||||
a colon ':' is printed separately.
|
|
||||||
62
Godeps/_workspace/src/github.com/kr/text/mc/mc.go
generated
vendored
62
Godeps/_workspace/src/github.com/kr/text/mc/mc.go
generated
vendored
@@ -1,62 +0,0 @@
|
|||||||
// Command mc prints in multiple columns.
|
|
||||||
//
|
|
||||||
// Usage: mc [-] [-N] [file...]
|
|
||||||
//
|
|
||||||
// Mc splits the input into as many columns as will fit in N
|
|
||||||
// print positions. If the output is a tty, the default N is
|
|
||||||
// the number of characters in a terminal line; otherwise the
|
|
||||||
// default N is 80. Under option - each input line ending in
|
|
||||||
// a colon ':' is printed separately.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/kr/pty"
|
|
||||||
"github.com/kr/text/colwriter"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var width int
|
|
||||||
var flag uint
|
|
||||||
args := os.Args[1:]
|
|
||||||
for len(args) > 0 && len(args[0]) > 0 && args[0][0] == '-' {
|
|
||||||
if len(args[0]) > 1 {
|
|
||||||
width, _ = strconv.Atoi(args[0][1:])
|
|
||||||
} else {
|
|
||||||
flag |= colwriter.BreakOnColon
|
|
||||||
}
|
|
||||||
args = args[1:]
|
|
||||||
}
|
|
||||||
if width < 1 {
|
|
||||||
_, width, _ = pty.Getsize(os.Stdout)
|
|
||||||
}
|
|
||||||
if width < 1 {
|
|
||||||
width = 80
|
|
||||||
}
|
|
||||||
|
|
||||||
w := colwriter.NewWriter(os.Stdout, width, flag)
|
|
||||||
if len(args) > 0 {
|
|
||||||
for _, s := range args {
|
|
||||||
if f, err := os.Open(s); err == nil {
|
|
||||||
copyin(w, f)
|
|
||||||
f.Close()
|
|
||||||
} else {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
copyin(w, os.Stdin)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyin(w *colwriter.Writer, r io.Reader) {
|
|
||||||
if _, err := io.Copy(w, r); err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
if err := w.Flush(); err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
86
Godeps/_workspace/src/github.com/kr/text/wrap.go
generated
vendored
86
Godeps/_workspace/src/github.com/kr/text/wrap.go
generated
vendored
@@ -1,86 +0,0 @@
|
|||||||
package text
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"math"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
nl = []byte{'\n'}
|
|
||||||
sp = []byte{' '}
|
|
||||||
)
|
|
||||||
|
|
||||||
const defaultPenalty = 1e5
|
|
||||||
|
|
||||||
// Wrap wraps s into a paragraph of lines of length lim, with minimal
|
|
||||||
// raggedness.
|
|
||||||
func Wrap(s string, lim int) string {
|
|
||||||
return string(WrapBytes([]byte(s), lim))
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapBytes wraps b into a paragraph of lines of length lim, with minimal
|
|
||||||
// raggedness.
|
|
||||||
func WrapBytes(b []byte, lim int) []byte {
|
|
||||||
words := bytes.Split(bytes.Replace(bytes.TrimSpace(b), nl, sp, -1), sp)
|
|
||||||
var lines [][]byte
|
|
||||||
for _, line := range WrapWords(words, 1, lim, defaultPenalty) {
|
|
||||||
lines = append(lines, bytes.Join(line, sp))
|
|
||||||
}
|
|
||||||
return bytes.Join(lines, nl)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapWords is the low-level line-breaking algorithm, useful if you need more
|
|
||||||
// control over the details of the text wrapping process. For most uses, either
|
|
||||||
// Wrap or WrapBytes will be sufficient and more convenient.
|
|
||||||
//
|
|
||||||
// WrapWords splits a list of words into lines with minimal "raggedness",
|
|
||||||
// treating each byte as one unit, accounting for spc units between adjacent
|
|
||||||
// words on each line, and attempting to limit lines to lim units. Raggedness
|
|
||||||
// is the total error over all lines, where error is the square of the
|
|
||||||
// difference of the length of the line and lim. Too-long lines (which only
|
|
||||||
// happen when a single word is longer than lim units) have pen penalty units
|
|
||||||
// added to the error.
|
|
||||||
func WrapWords(words [][]byte, spc, lim, pen int) [][][]byte {
|
|
||||||
n := len(words)
|
|
||||||
|
|
||||||
length := make([][]int, n)
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
length[i] = make([]int, n)
|
|
||||||
length[i][i] = len(words[i])
|
|
||||||
for j := i + 1; j < n; j++ {
|
|
||||||
length[i][j] = length[i][j-1] + spc + len(words[j])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nbrk := make([]int, n)
|
|
||||||
cost := make([]int, n)
|
|
||||||
for i := range cost {
|
|
||||||
cost[i] = math.MaxInt32
|
|
||||||
}
|
|
||||||
for i := n - 1; i >= 0; i-- {
|
|
||||||
if length[i][n-1] <= lim {
|
|
||||||
cost[i] = 0
|
|
||||||
nbrk[i] = n
|
|
||||||
} else {
|
|
||||||
for j := i + 1; j < n; j++ {
|
|
||||||
d := lim - length[i][j-1]
|
|
||||||
c := d*d + cost[j]
|
|
||||||
if length[i][j-1] > lim {
|
|
||||||
c += pen // too-long lines get a worse penalty
|
|
||||||
}
|
|
||||||
if c < cost[i] {
|
|
||||||
cost[i] = c
|
|
||||||
nbrk[i] = j
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var lines [][][]byte
|
|
||||||
i := 0
|
|
||||||
for i < n {
|
|
||||||
lines = append(lines, words[i:nbrk[i]])
|
|
||||||
i = nbrk[i]
|
|
||||||
}
|
|
||||||
return lines
|
|
||||||
}
|
|
||||||
44
Godeps/_workspace/src/github.com/kr/text/wrap_test.go
generated
vendored
44
Godeps/_workspace/src/github.com/kr/text/wrap_test.go
generated
vendored
@@ -1,44 +0,0 @@
|
|||||||
package text
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var text = "The quick brown fox jumps over the lazy dog."
|
|
||||||
|
|
||||||
func TestWrap(t *testing.T) {
|
|
||||||
exp := [][]string{
|
|
||||||
{"The", "quick", "brown", "fox"},
|
|
||||||
{"jumps", "over", "the", "lazy", "dog."},
|
|
||||||
}
|
|
||||||
words := bytes.Split([]byte(text), sp)
|
|
||||||
got := WrapWords(words, 1, 24, defaultPenalty)
|
|
||||||
if len(exp) != len(got) {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
for i := range exp {
|
|
||||||
if len(exp[i]) != len(got[i]) {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
for j := range exp[i] {
|
|
||||||
if exp[i][j] != string(got[i][j]) {
|
|
||||||
t.Fatal(i, exp[i][j], got[i][j])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWrapNarrow(t *testing.T) {
|
|
||||||
exp := "The\nquick\nbrown\nfox\njumps\nover\nthe\nlazy\ndog."
|
|
||||||
if Wrap(text, 5) != exp {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWrapOneLine(t *testing.T) {
|
|
||||||
exp := "The quick brown fox jumps over the lazy dog."
|
|
||||||
if Wrap(text, 500) != exp {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
4
Godeps/_workspace/src/github.com/miekg/dns/.gitignore
generated
vendored
Normal file
4
Godeps/_workspace/src/github.com/miekg/dns/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
*.6
|
||||||
|
tags
|
||||||
|
test.out
|
||||||
|
a.out
|
||||||
21
Godeps/_workspace/src/github.com/miekg/dns/.travis.yml
generated
vendored
Normal file
21
Godeps/_workspace/src/github.com/miekg/dns/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.2
|
||||||
|
- 1.3
|
||||||
|
env:
|
||||||
|
# "gvm update" resets GOOS and GOARCH environment variable, workaround it by setting
|
||||||
|
# BUILD_GOOS and BUILD_GOARCH and overriding GOARCH and GOOS in the build script
|
||||||
|
global:
|
||||||
|
- BUILD_GOARCH=amd64
|
||||||
|
matrix:
|
||||||
|
- BUILD_GOOS=linux
|
||||||
|
- BUILD_GOOS=darwin
|
||||||
|
- BUILD_GOOS=windows
|
||||||
|
script:
|
||||||
|
- gvm cross $BUILD_GOOS $BUILD_GOARCH
|
||||||
|
- GOARCH=$BUILD_GOARCH GOOS=$BUILD_GOOS go build
|
||||||
|
|
||||||
|
# only test on linux
|
||||||
|
# also specify -short; the crypto tests fail in weird ways *sometimes*
|
||||||
|
# See issue #151
|
||||||
|
- if [ $BUILD_GOOS == "linux" ]; then GOARCH=$BUILD_GOARCH GOOS=$BUILD_GOOS go test -short -bench=.; fi
|
||||||
1
Godeps/_workspace/src/github.com/miekg/dns/AUTHORS
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/miekg/dns/AUTHORS
generated
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Miek Gieben <miek@miek.nl>
|
||||||
9
Godeps/_workspace/src/github.com/miekg/dns/CONTRIBUTORS
generated
vendored
Normal file
9
Godeps/_workspace/src/github.com/miekg/dns/CONTRIBUTORS
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
Alex A. Skinner
|
||||||
|
Andrew Tunnell-Jones
|
||||||
|
Ask Bjørn Hansen
|
||||||
|
Dave Cheney
|
||||||
|
Dusty Wilson
|
||||||
|
Marek Majkowski
|
||||||
|
Peter van Dijk
|
||||||
|
Omri Bahumi
|
||||||
|
Alex Sergeyev
|
||||||
9
Godeps/_workspace/src/github.com/miekg/dns/COPYRIGHT
generated
vendored
Normal file
9
Godeps/_workspace/src/github.com/miekg/dns/COPYRIGHT
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
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
Godeps/_workspace/src/github.com/miekg/dns/LICENSE
generated
vendored
Normal file
32
Godeps/_workspace/src/github.com/miekg/dns/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
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.
|
||||||
|
|
||||||
140
Godeps/_workspace/src/github.com/miekg/dns/README.md
generated
vendored
Normal file
140
Godeps/_workspace/src/github.com/miekg/dns/README.md
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
[](https://travis-ci.org/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.
|
||||||
|
|
||||||
|
If you like this, you may also be interested in:
|
||||||
|
|
||||||
|
* https://github.com/miekg/unbound -- Go wrapper for the Unbound resolver.
|
||||||
|
|
||||||
|
# Goals
|
||||||
|
|
||||||
|
* KISS;
|
||||||
|
* Fast;
|
||||||
|
* Small API, if its 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/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/DevelopersPL/godnsagent
|
||||||
|
* https://github.com/duedil-ltd/discodns
|
||||||
|
|
||||||
|
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;
|
||||||
|
* AXFR/IXFR;
|
||||||
|
* TSIG, SIG(0);
|
||||||
|
* 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 dns.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
|
||||||
|
* 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 - Unkown 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
|
||||||
|
* 6891 - EDNS0 update
|
||||||
|
* 6895 - DNS IANA considerations
|
||||||
|
* 6975 - Algorithm Understanding in DNSSEC
|
||||||
|
* 7043 - EUI48/EUI64 records
|
||||||
|
* 7314 - DNS (EDNS) EXPIRE Option
|
||||||
|
* xxxx - URI record (draft)
|
||||||
|
* xxxx - EDNS0 DNS Update Lease (draft)
|
||||||
|
|
||||||
|
## Loosely based upon
|
||||||
|
|
||||||
|
* `ldns`
|
||||||
|
* `NSD`
|
||||||
|
* `Net::DNS`
|
||||||
|
* `GRONG`
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
* privatekey.Precompute() when signing?
|
||||||
|
* Last remaining RRs: APL, ATMA, A6 and NXT;
|
||||||
|
* Missing in parsing: ISDN, UNSPEC, ATMA;
|
||||||
|
* CAA parsing is broken;
|
||||||
|
* NSEC(3) cover/match/closest enclose;
|
||||||
|
* Replies with TC bit are not parsed to the end;
|
||||||
|
* Create IsMsg to validate a message before fully parsing it.
|
||||||
319
Godeps/_workspace/src/github.com/miekg/dns/client.go
generated
vendored
Normal file
319
Godeps/_workspace/src/github.com/miekg/dns/client.go
generated
vendored
Normal file
@@ -0,0 +1,319 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
// A client implementation.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const dnsTimeout time.Duration = 2 * 1e9
|
||||||
|
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" a TCP query will be initiated, otherwise an UDP one (default is "" for UDP)
|
||||||
|
UDPSize uint16 // minimum receive buffer for UDP messages
|
||||||
|
DialTimeout time.Duration // net.DialTimeout (ns), defaults to 2 * 1e9
|
||||||
|
ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections (ns), defaults to 2 * 1e9
|
||||||
|
WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections (ns), defaults to 2 * 1e9
|
||||||
|
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 an reply. Exchange does not retry a failed query, nor
|
||||||
|
// will it fall back to TCP in case of truncation.
|
||||||
|
// If you need to send a DNS message on an already existing connection, you can use the
|
||||||
|
// following:
|
||||||
|
//
|
||||||
|
// co := &dns.Conn{Conn: c} // c is your net.Conn
|
||||||
|
// co.WriteMsg(m)
|
||||||
|
// in, err := co.ReadMsg()
|
||||||
|
// co.Close()
|
||||||
|
//
|
||||||
|
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()
|
||||||
|
co.SetReadDeadline(time.Now().Add(dnsTimeout))
|
||||||
|
co.SetWriteDeadline(time.Now().Add(dnsTimeout))
|
||||||
|
if err = co.WriteMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r, err = co.ReadMsg()
|
||||||
|
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()
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exchange performs an synchronous query. It sends the message m to the address
|
||||||
|
// contained in a and waits for an 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.
|
||||||
|
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 err != nil {
|
||||||
|
return r, rtt, err
|
||||||
|
}
|
||||||
|
if shared {
|
||||||
|
return r.Copy(), rtt, nil
|
||||||
|
}
|
||||||
|
return r, rtt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
|
||||||
|
timeout := dnsTimeout
|
||||||
|
var co *Conn
|
||||||
|
if c.DialTimeout != 0 {
|
||||||
|
timeout = c.DialTimeout
|
||||||
|
}
|
||||||
|
if c.Net == "" {
|
||||||
|
co, err = DialTimeout("udp", a, timeout)
|
||||||
|
} else {
|
||||||
|
co, err = DialTimeout(c.Net, a, timeout)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
timeout = dnsTimeout
|
||||||
|
if c.ReadTimeout != 0 {
|
||||||
|
timeout = c.ReadTimeout
|
||||||
|
}
|
||||||
|
co.SetReadDeadline(time.Now().Add(timeout))
|
||||||
|
timeout = dnsTimeout
|
||||||
|
if c.WriteTimeout != 0 {
|
||||||
|
timeout = c.WriteTimeout
|
||||||
|
}
|
||||||
|
co.SetWriteDeadline(time.Now().Add(timeout))
|
||||||
|
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
|
||||||
|
if err = co.WriteMsg(m); err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
r, err = co.ReadMsg()
|
||||||
|
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) {
|
||||||
|
var p []byte
|
||||||
|
m := new(Msg)
|
||||||
|
if _, ok := co.Conn.(*net.TCPConn); ok {
|
||||||
|
p = make([]byte, MaxMsgSize)
|
||||||
|
} else {
|
||||||
|
if co.UDPSize >= 512 {
|
||||||
|
p = make([]byte, co.UDPSize)
|
||||||
|
} else {
|
||||||
|
p = make([]byte, MinMsgSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n, err := co.Read(p)
|
||||||
|
if err != nil && n == 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p = p[:n]
|
||||||
|
if err := m.Unpack(p); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
co.rtt = time.Since(co.t)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
if t, ok := co.Conn.(*net.TCPConn); ok {
|
||||||
|
n, err = t.Read(p[0:2])
|
||||||
|
if err != nil || n != 2 {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
l, _ := unpackUint16(p[0:2], 0)
|
||||||
|
if l == 0 {
|
||||||
|
return 0, ErrShortRead
|
||||||
|
}
|
||||||
|
if int(l) > len(p) {
|
||||||
|
return int(l), io.ErrShortBuffer
|
||||||
|
}
|
||||||
|
n, err = t.Read(p[:l])
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
i := n
|
||||||
|
for i < int(l) {
|
||||||
|
j, err := t.Read(p[i:int(l)])
|
||||||
|
if err != nil {
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
i += j
|
||||||
|
}
|
||||||
|
n = i
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
// UDP connection
|
||||||
|
n, err = co.Conn.Read(p)
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteMsg sends a message throught 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, allthough 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) {
|
||||||
|
if t, ok := co.Conn.(*net.TCPConn); ok {
|
||||||
|
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)
|
||||||
|
l[0], l[1] = packUint16(uint16(lp))
|
||||||
|
p = append(l, p...)
|
||||||
|
n, err := io.Copy(t, bytes.NewReader(p))
|
||||||
|
return int(n), err
|
||||||
|
}
|
||||||
|
n, err = co.Conn.(*net.UDPConn).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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close implements the net.Conn Close method.
|
||||||
|
func (co *Conn) Close() error { return co.Conn.Close() }
|
||||||
|
|
||||||
|
// LocalAddr implements the net.Conn LocalAddr method.
|
||||||
|
func (co *Conn) LocalAddr() net.Addr { return co.Conn.LocalAddr() }
|
||||||
|
|
||||||
|
// RemoteAddr implements the net.Conn RemoteAddr method.
|
||||||
|
func (co *Conn) RemoteAddr() net.Addr { return co.Conn.RemoteAddr() }
|
||||||
|
|
||||||
|
// SetDeadline implements the net.Conn SetDeadline method.
|
||||||
|
func (co *Conn) SetDeadline(t time.Time) error { return co.Conn.SetDeadline(t) }
|
||||||
|
|
||||||
|
// SetReadDeadline implements the net.Conn SetReadDeadline method.
|
||||||
|
func (co *Conn) SetReadDeadline(t time.Time) error { return co.Conn.SetReadDeadline(t) }
|
||||||
|
|
||||||
|
// SetWriteDeadline implements the net.Conn SetWriteDeadline method.
|
||||||
|
func (co *Conn) SetWriteDeadline(t time.Time) error { return co.Conn.SetWriteDeadline(t) }
|
||||||
195
Godeps/_workspace/src/github.com/miekg/dns/client_test.go
generated
vendored
Normal file
195
Godeps/_workspace/src/github.com/miekg/dns/client_test.go
generated
vendored
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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: %s", err)
|
||||||
|
}
|
||||||
|
defer s.Shutdown()
|
||||||
|
|
||||||
|
m := new(Msg)
|
||||||
|
m.SetQuestion("miek.nl.", TypeSOA)
|
||||||
|
|
||||||
|
c := new(Client)
|
||||||
|
r, _, e := c.Exchange(m, addrstr)
|
||||||
|
if e != nil {
|
||||||
|
t.Logf("failed to exchange: %s", e.Error())
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if r != nil && r.Rcode != RcodeSuccess {
|
||||||
|
t.Log("failed to get an valid answer")
|
||||||
|
t.Fail()
|
||||||
|
t.Logf("%v\n", r)
|
||||||
|
}
|
||||||
|
// And now with plain Exchange().
|
||||||
|
r, e = Exchange(m, addrstr)
|
||||||
|
if e != nil {
|
||||||
|
t.Logf("failed to exchange: %s", e.Error())
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if r != nil && r.Rcode != RcodeSuccess {
|
||||||
|
t.Log("failed to get an valid answer")
|
||||||
|
t.Fail()
|
||||||
|
t.Logf("%v\n", r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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: %s", err)
|
||||||
|
}
|
||||||
|
defer s.Shutdown()
|
||||||
|
|
||||||
|
m := new(Msg)
|
||||||
|
m.SetQuestion("miek.nl.", TypeDNSKEY)
|
||||||
|
|
||||||
|
m.SetEdns0(2048, true)
|
||||||
|
|
||||||
|
c := new(Client)
|
||||||
|
r, _, e := c.Exchange(m, addrstr)
|
||||||
|
if e != nil {
|
||||||
|
t.Logf("failed to exchange: %s", e.Error())
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
if r != nil && r.Rcode != RcodeSuccess {
|
||||||
|
t.Log("failed to get an valid answer")
|
||||||
|
t.Fail()
|
||||||
|
t.Logf("%v\n", r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSingleSingleInflight(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: %s", err)
|
||||||
|
}
|
||||||
|
defer s.Shutdown()
|
||||||
|
|
||||||
|
m := new(Msg)
|
||||||
|
m.SetQuestion("miek.nl.", TypeDNSKEY)
|
||||||
|
|
||||||
|
c := new(Client)
|
||||||
|
c.SingleInflight = true
|
||||||
|
nr := 10
|
||||||
|
ch := make(chan time.Duration)
|
||||||
|
for i := 0; i < nr; i++ {
|
||||||
|
go func() {
|
||||||
|
_, rtt, _ := c.Exchange(m, addrstr)
|
||||||
|
ch <- rtt
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
i := 0
|
||||||
|
var first time.Duration
|
||||||
|
// With inflight *all* rtt are identical, and by doing actual lookups
|
||||||
|
// the changes that this is a coincidence is small.
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case rtt := <-ch:
|
||||||
|
if i == 0 {
|
||||||
|
first = rtt
|
||||||
|
} else {
|
||||||
|
if first != rtt {
|
||||||
|
t.Log("all rtts should be equal")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
if i == 10 {
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func TestClientTsigAXFR(t *testing.T) {
|
||||||
|
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.Log("failed to setup axfr: " + err.Error())
|
||||||
|
t.Fatal()
|
||||||
|
} else {
|
||||||
|
for ex := range a {
|
||||||
|
if ex.Error != nil {
|
||||||
|
t.Logf("error %s\n", ex.Error.Error())
|
||||||
|
t.Fail()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for _, rr := range ex.RR {
|
||||||
|
t.Logf("%s\n", rr.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClientAXFRMultipleEnvelopes(t *testing.T) {
|
||||||
|
m := new(Msg)
|
||||||
|
m.SetAxfr("nlnetlabs.nl.")
|
||||||
|
|
||||||
|
tr := new(Transfer)
|
||||||
|
if a, err := tr.In(m, "213.154.224.1:53"); err != nil {
|
||||||
|
t.Log("Failed to setup axfr" + err.Error())
|
||||||
|
t.Fail()
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
for ex := range a {
|
||||||
|
if ex.Error != nil {
|
||||||
|
t.Logf("Error %s\n", ex.Error.Error())
|
||||||
|
t.Fail()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ExampleUpdateLeaseTSIG shows how to update a lease signed with TSIG.
|
||||||
|
func ExampleUpdateLeaseTSIG(t *testing.T) {
|
||||||
|
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)
|
||||||
|
|
||||||
|
lease_rr := new(OPT)
|
||||||
|
lease_rr.Hdr.Name = "."
|
||||||
|
lease_rr.Hdr.Rrtype = TypeOPT
|
||||||
|
e := new(EDNS0_UL)
|
||||||
|
e.Code = EDNS0UL
|
||||||
|
e.Lease = 120
|
||||||
|
lease_rr.Option = append(lease_rr.Option, e)
|
||||||
|
m.Extra = append(m.Extra, lease_rr)
|
||||||
|
|
||||||
|
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 {
|
||||||
|
t.Log(err.Error())
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
94
Godeps/_workspace/src/github.com/miekg/dns/clientconfig.go
generated
vendored
Normal file
94
Godeps/_workspace/src/github.com/miekg/dns/clientconfig.go
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Wraps the contents of the /etc/resolv.conf.
|
||||||
|
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)
|
||||||
|
b := bufio.NewReader(file)
|
||||||
|
c.Servers = make([]string, 0)
|
||||||
|
c.Search = make([]string, 0)
|
||||||
|
c.Port = "53"
|
||||||
|
c.Ndots = 1
|
||||||
|
c.Timeout = 5
|
||||||
|
c.Attempts = 2
|
||||||
|
for line, ok := b.ReadString('\n'); ok == nil; line, ok = b.ReadString('\n') {
|
||||||
|
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
|
||||||
|
}
|
||||||
242
Godeps/_workspace/src/github.com/miekg/dns/defaults.go
generated
vendored
Normal file
242
Godeps/_workspace/src/github.com/miekg/dns/defaults.go
generated
vendored
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
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.RecursionDesired = request.RecursionDesired // Copy rd bit
|
||||||
|
dns.Response = true
|
||||||
|
dns.Opcode = OpcodeQuery
|
||||||
|
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.
|
||||||
|
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.
|
||||||
|
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) *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
|
||||||
|
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, timesigned int64) *Msg {
|
||||||
|
t := new(TSIG)
|
||||||
|
t.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0}
|
||||||
|
t.Algorithm = algo
|
||||||
|
t.Fudge = 300
|
||||||
|
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 {
|
||||||
|
for _, r := range dns.Extra {
|
||||||
|
if r.Header().Rrtype == TypeOPT {
|
||||||
|
return r.(*OPT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsDomainName checks if s is a valid domainname, 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.
|
||||||
|
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. Both child and
|
||||||
|
// parent are *not* downcased before doing the comparison.
|
||||||
|
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] == '.'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fqdns 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))
|
||||||
|
}
|
||||||
193
Godeps/_workspace/src/github.com/miekg/dns/dns.go
generated
vendored
Normal file
193
Godeps/_workspace/src/github.com/miekg/dns/dns.go
generated
vendored
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
// 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.
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
year68 = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits.
|
||||||
|
DefaultMsgSize = 4096 // Standard default for larger than 512 bytes.
|
||||||
|
MinMsgSize = 512 // Minimal size of a DNS packet.
|
||||||
|
MaxMsgSize = 65536 // Largest possible DNS packet.
|
||||||
|
defaultTtl = 3600 // Default TTL.
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 octects) of the uncompressed RR in wire format.
|
||||||
|
len() int
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNS resource records.
|
||||||
|
// There are many types of RRs,
|
||||||
|
// but they all share the same header.
|
||||||
|
type RR_Header struct {
|
||||||
|
Name string `dns:"cdomain-name"`
|
||||||
|
Rrtype uint16
|
||||||
|
Class uint16
|
||||||
|
Ttl uint32
|
||||||
|
Rdlength uint16 // length of data after header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *RR_Header) Header() *RR_Header { return h }
|
||||||
|
|
||||||
|
// Just to imlement 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 := PackStruct(r, buf, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
buf = buf[:off]
|
||||||
|
rawSetRdlength(buf, 0, off)
|
||||||
|
_, err = UnpackStruct(rr, buf, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
511
Godeps/_workspace/src/github.com/miekg/dns/dns_test.go
generated
vendored
Normal file
511
Godeps/_workspace/src/github.com/miekg/dns/dns_test.go
generated
vendored
Normal file
@@ -0,0 +1,511 @@
|
|||||||
|
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.Log("failed to pack msg with DNSKEY")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
in := new(Msg)
|
||||||
|
if in.Unpack(msg) != nil {
|
||||||
|
t.Log("failed to unpack msg with DNSKEY")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Log("failed to pack msg with RRSIG")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.Unpack(msg) != nil {
|
||||||
|
t.Log("failed to unpack msg with RRSIG")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Log("Packing failed: " + err.Error())
|
||||||
|
t.Fail()
|
||||||
|
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.Log("packing failed: " + err.Error())
|
||||||
|
t.Fail()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var unpackMsg Msg
|
||||||
|
err = unpackMsg.Unpack(b)
|
||||||
|
if err != nil {
|
||||||
|
t.Log("unpacking failed")
|
||||||
|
t.Fail()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBailiwick(t *testing.T) {
|
||||||
|
yes := map[string]string{
|
||||||
|
"miek.nl": "ns.miek.nl",
|
||||||
|
".": "miek.nl",
|
||||||
|
}
|
||||||
|
for parent, child := range yes {
|
||||||
|
if !IsSubDomain(parent, child) {
|
||||||
|
t.Logf("%s should be child of %s\n", child, parent)
|
||||||
|
t.Logf("comparelabels %d", CompareDomainName(parent, child))
|
||||||
|
t.Logf("lenlabels %d %d", CountLabel(parent), CountLabel(child))
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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.Logf("%s should not be child of %s\n", child, parent)
|
||||||
|
t.Logf("comparelabels %d", CompareDomainName(parent, child))
|
||||||
|
t.Logf("lenlabels %d %d", CountLabel(parent), CountLabel(child))
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Logf("failed to create RR: %s\n", err.Error())
|
||||||
|
t.Fail()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, err := m.Pack(); err != nil {
|
||||||
|
t.Logf("packing failed: %s\n", err.Error())
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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.Log("packing should fail")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
x.Answer = make([]RR, 1)
|
||||||
|
x.Answer[0], err = NewRR(rr[0])
|
||||||
|
if _, err := x.Pack(); err == nil {
|
||||||
|
t.Log("packing should fail")
|
||||||
|
t.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.Log("packing should fail")
|
||||||
|
t.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.Logf("packing failed: %s", err.Error())
|
||||||
|
t.Logf("length %d, need more than %d\n", rr.len(), off)
|
||||||
|
t.Fail()
|
||||||
|
} else {
|
||||||
|
t.Logf("buf size needed: %d\n", 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)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if predicted < len(buf) {
|
||||||
|
t.Errorf("predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d\n",
|
||||||
|
msg.Question[0].Name, len(msg.Answer), predicted, len(buf))
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if predicted < len(buf) {
|
||||||
|
t.Errorf("predicted length is wrong: predicted %s (len=%d), actual %d\n",
|
||||||
|
msg.Question[0].Name, predicted, len(buf))
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
//println(m.String())
|
||||||
|
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 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 BenchmarkMsgPackBuffer(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.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, _ = msg.PackBuffer(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMsgUnpack(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)
|
||||||
|
msg_buf, _ := msg.Pack()
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = msg.Unpack(msg_buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoRdataPack(t *testing.T) {
|
||||||
|
data := make([]byte, 1024)
|
||||||
|
for typ, fn := range typeToRR {
|
||||||
|
if typ == TypeCAA {
|
||||||
|
continue // TODO(miek): known omission
|
||||||
|
}
|
||||||
|
r := fn()
|
||||||
|
*r.Header() = RR_Header{Name: "miek.nl.", Rrtype: typ, Class: ClassINET, Ttl: 3600}
|
||||||
|
_, e := PackRR(r, data, 0, nil, false)
|
||||||
|
if e != nil {
|
||||||
|
t.Logf("failed to pack RR with zero rdata: %s: %s\n", TypeToString[typ], e.Error())
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(miek): fix dns buffer too small errors this throws
|
||||||
|
func TestNoRdataUnpack(t *testing.T) {
|
||||||
|
data := make([]byte, 1024)
|
||||||
|
for typ, fn := range typeToRR {
|
||||||
|
if typ == TypeSOA || typ == TypeTSIG || typ == TypeWKS {
|
||||||
|
// SOA, TSIG will not be seen (like this) in dyn. updates?
|
||||||
|
// WKS is an bug, but...deprecated record.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r := fn()
|
||||||
|
*r.Header() = RR_Header{Name: "miek.nl.", Rrtype: typ, Class: ClassINET, Ttl: 3600}
|
||||||
|
off, e := PackRR(r, data, 0, nil, false)
|
||||||
|
if e != nil {
|
||||||
|
// Should always works, TestNoDataPack should have catched this
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rr, _, e := UnpackRR(data[:off], 0)
|
||||||
|
if e != nil {
|
||||||
|
t.Logf("failed to unpack RR with zero rdata: %s: %s\n", TypeToString[typ], e.Error())
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
t.Logf("%s\n", 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())
|
||||||
|
}
|
||||||
|
}
|
||||||
756
Godeps/_workspace/src/github.com/miekg/dns/dnssec.go
generated
vendored
Normal file
756
Godeps/_workspace/src/github.com/miekg/dns/dnssec.go
generated
vendored
Normal file
@@ -0,0 +1,756 @@
|
|||||||
|
// 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 an request.
|
||||||
|
//
|
||||||
|
// m := new(dns.Msg)
|
||||||
|
// m.SetEdns0(4096, true)
|
||||||
|
//
|
||||||
|
// Signature generation, signature verification and key generation are all supported.
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto"
|
||||||
|
"crypto/dsa"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha1"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/sha512"
|
||||||
|
"encoding/hex"
|
||||||
|
"hash"
|
||||||
|
"io"
|
||||||
|
"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
|
||||||
|
)
|
||||||
|
|
||||||
|
// DNSSEC hashing algorithm codes.
|
||||||
|
const (
|
||||||
|
_ uint8 = iota
|
||||||
|
SHA1 // RFC 4034
|
||||||
|
SHA256 // RFC 4509
|
||||||
|
GOST94 // RFC 5933
|
||||||
|
SHA384 // Experimental
|
||||||
|
SHA512 // Experimental
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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. Use this struct to easy
|
||||||
|
// the conversion (and re-use the pack/unpack functions).
|
||||||
|
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, _ := unpackUint16(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 := PackStruct(keywire, wire, 0)
|
||||||
|
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 := PackStruct(keywire, wire, 0)
|
||||||
|
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.
|
||||||
|
|
||||||
|
// digest buffer
|
||||||
|
digest := append(owner, wire...) // another copy
|
||||||
|
|
||||||
|
switch h {
|
||||||
|
case SHA1:
|
||||||
|
s := sha1.New()
|
||||||
|
io.WriteString(s, string(digest))
|
||||||
|
ds.Digest = hex.EncodeToString(s.Sum(nil))
|
||||||
|
case SHA256:
|
||||||
|
s := sha256.New()
|
||||||
|
io.WriteString(s, string(digest))
|
||||||
|
ds.Digest = hex.EncodeToString(s.Sum(nil))
|
||||||
|
case SHA384:
|
||||||
|
s := sha512.New384()
|
||||||
|
io.WriteString(s, string(digest))
|
||||||
|
ds.Digest = hex.EncodeToString(s.Sum(nil))
|
||||||
|
case GOST94:
|
||||||
|
/* I have no clue */
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ds
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 true when the signing went OK,
|
||||||
|
// otherwise false.
|
||||||
|
// 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 PrivateKey, 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 := PackStruct(sigwire, signdata, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
signdata = signdata[:n]
|
||||||
|
wire, err := rawSignatureData(rrset, rr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
signdata = append(signdata, wire...)
|
||||||
|
|
||||||
|
var sighash []byte
|
||||||
|
var h hash.Hash
|
||||||
|
var ch crypto.Hash // Only need for RSA
|
||||||
|
var intlen int
|
||||||
|
switch rr.Algorithm {
|
||||||
|
case DSA, DSANSEC3SHA1:
|
||||||
|
// Implicit in the ParameterSizes
|
||||||
|
case RSASHA1, RSASHA1NSEC3SHA1:
|
||||||
|
h = sha1.New()
|
||||||
|
ch = crypto.SHA1
|
||||||
|
case RSASHA256, ECDSAP256SHA256:
|
||||||
|
h = sha256.New()
|
||||||
|
ch = crypto.SHA256
|
||||||
|
intlen = 32
|
||||||
|
case ECDSAP384SHA384:
|
||||||
|
h = sha512.New384()
|
||||||
|
intlen = 48
|
||||||
|
case RSASHA512:
|
||||||
|
h = sha512.New()
|
||||||
|
ch = crypto.SHA512
|
||||||
|
case RSAMD5:
|
||||||
|
fallthrough // Deprecated in RFC 6725
|
||||||
|
default:
|
||||||
|
return ErrAlg
|
||||||
|
}
|
||||||
|
io.WriteString(h, string(signdata))
|
||||||
|
sighash = h.Sum(nil)
|
||||||
|
|
||||||
|
switch p := k.(type) {
|
||||||
|
case *dsa.PrivateKey:
|
||||||
|
r1, s1, err := dsa.Sign(rand.Reader, p, sighash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
signature := []byte{0x4D} // T value, here the ASCII M for Miek (not used in DNSSEC)
|
||||||
|
signature = append(signature, intToBytes(r1, 20)...)
|
||||||
|
signature = append(signature, intToBytes(s1, 20)...)
|
||||||
|
rr.Signature = toBase64(signature)
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
// We can use nil as rand.Reader here (says AGL)
|
||||||
|
signature, err := rsa.SignPKCS1v15(nil, p, ch, sighash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rr.Signature = toBase64(signature)
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
r1, s1, err := ecdsa.Sign(rand.Reader, p, sighash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
signature := intToBytes(r1, intlen)
|
||||||
|
signature = append(signature, intToBytes(s1, intlen)...)
|
||||||
|
rr.Signature = toBase64(signature)
|
||||||
|
default:
|
||||||
|
// Not given the correct key
|
||||||
|
return ErrKeyAlg
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 len(rrset) == 0 {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
for _, r := range rrset {
|
||||||
|
if r.Header().Class != rr.Hdr.Class {
|
||||||
|
return ErrRRset
|
||||||
|
}
|
||||||
|
if r.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 := PackStruct(sigwire, signeddata, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
signeddata = signeddata[:n]
|
||||||
|
wire, err := rawSignatureData(rrset, rr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
signeddata = append(signeddata, wire...)
|
||||||
|
|
||||||
|
sigbuf := rr.sigBuf() // Get the binary signature data
|
||||||
|
if rr.Algorithm == PRIVATEDNS { // PRIVATEOID
|
||||||
|
// TODO(mg)
|
||||||
|
// remove the domain name and assume its our
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
// Setup the hash as defined for this alg.
|
||||||
|
var h hash.Hash
|
||||||
|
var ch crypto.Hash
|
||||||
|
switch rr.Algorithm {
|
||||||
|
case RSAMD5:
|
||||||
|
h = md5.New()
|
||||||
|
ch = crypto.MD5
|
||||||
|
case RSASHA1, RSASHA1NSEC3SHA1:
|
||||||
|
h = sha1.New()
|
||||||
|
ch = crypto.SHA1
|
||||||
|
case RSASHA256:
|
||||||
|
h = sha256.New()
|
||||||
|
ch = crypto.SHA256
|
||||||
|
case RSASHA512:
|
||||||
|
h = sha512.New()
|
||||||
|
ch = crypto.SHA512
|
||||||
|
}
|
||||||
|
io.WriteString(h, string(signeddata))
|
||||||
|
sighash := h.Sum(nil)
|
||||||
|
return rsa.VerifyPKCS1v15(pubkey, ch, sighash, sigbuf)
|
||||||
|
case ECDSAP256SHA256, ECDSAP384SHA384:
|
||||||
|
pubkey := k.publicKeyCurve()
|
||||||
|
if pubkey == nil {
|
||||||
|
return ErrKey
|
||||||
|
}
|
||||||
|
var h hash.Hash
|
||||||
|
switch rr.Algorithm {
|
||||||
|
case ECDSAP256SHA256:
|
||||||
|
h = sha256.New()
|
||||||
|
case ECDSAP384SHA384:
|
||||||
|
h = sha512.New384()
|
||||||
|
}
|
||||||
|
io.WriteString(h, string(signeddata))
|
||||||
|
sighash := h.Sum(nil)
|
||||||
|
// Split sigbuf into the r and s coordinates
|
||||||
|
r := big.NewInt(0)
|
||||||
|
r.SetBytes(sigbuf[:len(sigbuf)/2])
|
||||||
|
s := big.NewInt(0)
|
||||||
|
s.SetBytes(sigbuf[len(sigbuf)/2:])
|
||||||
|
if ecdsa.Verify(pubkey, sighash, r, s) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ErrSig
|
||||||
|
}
|
||||||
|
// Unknown alg
|
||||||
|
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.
|
||||||
|
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 (s *RRSIG) sigBuf() []byte {
|
||||||
|
sigbuf, err := fromBase64([]byte(s.Signature))
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return sigbuf
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPublicKeyInPrivate sets the public key in the private key.
|
||||||
|
func (k *DNSKEY) setPublicKeyInPrivate(p PrivateKey) bool {
|
||||||
|
switch t := p.(type) {
|
||||||
|
case *dsa.PrivateKey:
|
||||||
|
x := k.publicKeyDSA()
|
||||||
|
if x == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
t.PublicKey = *x
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
x := k.publicKeyRSA()
|
||||||
|
if x == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
t.PublicKey = *x
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
x := k.publicKeyCurve()
|
||||||
|
if x == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
t.PublicKey = *x
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// publicKeyCurve returns the Curve public key from the DNSKEY record.
|
||||||
|
func (k *DNSKEY) publicKeyCurve() *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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) setPublicKeyCurve(_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))
|
||||||
|
if len(i.Bytes()) < 256 {
|
||||||
|
buf = make([]byte, 1)
|
||||||
|
buf[0] = uint8(len(i.Bytes()))
|
||||||
|
} else {
|
||||||
|
buf = make([]byte, 3)
|
||||||
|
buf[0] = 0
|
||||||
|
buf[1] = uint8(len(i.Bytes()) >> 8)
|
||||||
|
buf[2] = uint8(len(i.Bytes()))
|
||||||
|
}
|
||||||
|
buf = append(buf, i.Bytes()...)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
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 _, wire := range wires {
|
||||||
|
buf = append(buf, wire...)
|
||||||
|
}
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map for 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",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map of algorithm strings.
|
||||||
|
var StringToAlgorithm = reverseInt8(AlgorithmToString)
|
||||||
|
|
||||||
|
// Map for hash names.
|
||||||
|
var HashToString = map[uint8]string{
|
||||||
|
SHA1: "SHA1",
|
||||||
|
SHA256: "SHA256",
|
||||||
|
GOST94: "GOST94",
|
||||||
|
SHA384: "SHA384",
|
||||||
|
SHA512: "SHA512",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map of hash strings.
|
||||||
|
var StringToHash = reverseInt8(HashToString)
|
||||||
672
Godeps/_workspace/src/github.com/miekg/dns/dnssec_test.go
generated
vendored
Normal file
672
Godeps/_workspace/src/github.com/miekg/dns/dnssec_test.go
generated
vendored
Normal file
@@ -0,0 +1,672 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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.Logf("%s\n", key.String())
|
||||||
|
t.Logf("%s\n", 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.Logf("%s\n", key.String())
|
||||||
|
t.Logf("%s\n", 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.Logf("%s\n", key.String())
|
||||||
|
t.Logf("%s\n", 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 seperately, so this will keep on working
|
||||||
|
if sig.Verify(key, []RR{soa}) != nil {
|
||||||
|
t.Log("failure to validate")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Log("should not be valid")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Log("should be valid")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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."
|
||||||
|
|
||||||
|
// 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} {
|
||||||
|
if sig.Sign(privkey, []RR{r}) != nil {
|
||||||
|
t.Log("failure to sign the record")
|
||||||
|
t.Fail()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if sig.Verify(key, []RR{r}) != nil {
|
||||||
|
t.Log("failure to validate")
|
||||||
|
t.Fail()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.Logf("validated: %s\n", 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, []RR{t6}); err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
t.Log("failure to sign the TYPE65534 record")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if err := sig.Verify(key, []RR{t6}); err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
t.Log("failure to validate")
|
||||||
|
t.Fail()
|
||||||
|
} else {
|
||||||
|
t.Logf("validated: %s\n", t6.Header().Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDnskey(t *testing.T) {
|
||||||
|
// f, _ := os.Open("t/Kmiek.nl.+010+05240.key")
|
||||||
|
pubkey, _ := 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")
|
||||||
|
privkey, _ := pubkey.(*DNSKEY).ReadPrivateKey(strings.NewReader(`
|
||||||
|
Private-key-format: v1.2
|
||||||
|
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==
|
||||||
|
`), "Kmiek.nl.+010+05240.private")
|
||||||
|
if pubkey.(*DNSKEY).PublicKey != "AwEAAZuMCu2FdugHkTrXYgl5qixvcDw1aDDlvL46/xJKbHBAHY16fNUb2b65cwko2Js/aJxUYJbZk5dwCDZxYfrfbZVtDPQuc3o8QaChVxC7/JYz2AHc9qHvqQ1j4VrH71RWINlQo6VYjzN/BGpMhOZoZOEwzp1HfsOE3lNYcoWU1smL" {
|
||||||
|
t.Log("pubkey is not what we've read")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
// Coefficient looks fishy...
|
||||||
|
t.Logf("%s", 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.Logf("wrong key tag: %d for key %v\n", tag, key)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, []RR{soa}); err != nil {
|
||||||
|
t.Logf("failed to sign")
|
||||||
|
t.Fail()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := sig.Verify(key, []RR{soa}); err != nil {
|
||||||
|
t.Logf("failed to verify")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Logf("wrong DS digest for SHA1\n%v\n", ds)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Logf("%v\n", err)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
switch priv := p.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
if 65537 != priv.PublicKey.E {
|
||||||
|
t.Log("exponenent should be 65537")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Logf("we should have read an RSA key: %v", priv)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if k.KeyTag() != 37350 {
|
||||||
|
t.Logf("%d %v\n", k.KeyTag(), k)
|
||||||
|
t.Log("keytag should be 37350")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
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, []RR{soa})
|
||||||
|
if sig.Signature != "D5zsobpQcmMmYsUMLxCVEtgAdCvTu8V/IEeP4EyLBjqPJmjt96bwM9kqihsccofA5LIJ7DN91qkCORjWSTwNhzCv7bMyr2o5vBZElrlpnRzlvsFIoAZCD9xg6ZY7ZyzUJmU6IcTwG4v3xEYajcpbJJiyaw/RqR90MuRdKPiBzSo=" {
|
||||||
|
t.Log("signature is not correct")
|
||||||
|
t.Logf("%v\n", sig)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Error())
|
||||||
|
}
|
||||||
|
privkey, err := eckey.(*DNSKEY).NewPrivateKey(priv)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
// TODO: Create seperate 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, []RR{a}) != nil {
|
||||||
|
t.Fatal("failure to sign the record")
|
||||||
|
}
|
||||||
|
|
||||||
|
if e := sig.Verify(eckey.(*DNSKEY), []RR{a}); e != nil {
|
||||||
|
t.Logf("\n%s\n%s\n%s\n\n%s\n\n",
|
||||||
|
eckey.(*DNSKEY).String(),
|
||||||
|
a.String(),
|
||||||
|
sig.String(),
|
||||||
|
eckey.(*DNSKEY).PrivateKeyString(privkey),
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Fatalf("failure to validate: %s", e.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSignVerifyECDSA2(t *testing.T) {
|
||||||
|
srv1, err := NewRR("srv.miek.nl. IN SRV 1000 800 0 web1.miek.nl.")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(err.Error())
|
||||||
|
}
|
||||||
|
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, []RR{srv}) != nil {
|
||||||
|
t.Fatal("failure to sign the record")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sig.Verify(key, []RR{srv})
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("\n%s\n%s\n%s\n\n%s\n\n",
|
||||||
|
key.String(),
|
||||||
|
srv.String(),
|
||||||
|
sig.String(),
|
||||||
|
key.PrivateKeyString(privkey),
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Fatal("Failure to validate:", 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.Error())
|
||||||
|
}
|
||||||
|
priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
exDS := `example.net. 3600 IN DS 55648 13 2 (
|
||||||
|
b4c8c1fe2e7477127b27115656ad6256f424625bf5c1
|
||||||
|
e2770ce6d6e37df61d17 )`
|
||||||
|
rrDS, err := NewRR(exDS)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA256)
|
||||||
|
if !reflect.DeepEqual(ourDS, rrDS.(*DS)) {
|
||||||
|
t.Errorf("DS record differs:\n%v\n%v\n", 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.Error())
|
||||||
|
}
|
||||||
|
rrRRSIG, err := NewRR(exRRSIG)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
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, []RR{rrA})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
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\n", 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.Error())
|
||||||
|
}
|
||||||
|
priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
exDS := `example.net. 3600 IN DS 10771 14 4 (
|
||||||
|
72d7b62976ce06438e9c0bf319013cf801f09ecc84b8
|
||||||
|
d7e9495f27e305c6a9b0563a9b5f4d288405c3008a94
|
||||||
|
6df983d6 )`
|
||||||
|
rrDS, err := NewRR(exDS)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA384)
|
||||||
|
if !reflect.DeepEqual(ourDS, rrDS.(*DS)) {
|
||||||
|
t.Fatalf("DS record differs:\n%v\n%v\n", 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.Error())
|
||||||
|
}
|
||||||
|
rrRRSIG, err := NewRR(exRRSIG)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
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, []RR{rrA})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
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\n", ourRRSIG, rrRRSIG.(*RRSIG))
|
||||||
|
}
|
||||||
|
}
|
||||||
3
Godeps/_workspace/src/github.com/miekg/dns/dyn_test.go
generated
vendored
Normal file
3
Godeps/_workspace/src/github.com/miekg/dns/dyn_test.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
// Find better solution
|
||||||
501
Godeps/_workspace/src/github.com/miekg/dns/edns.go
generated
vendored
Normal file
501
Godeps/_workspace/src/github.com/miekg/dns/edns.go
generated
vendored
Normal file
@@ -0,0 +1,501 @@
|
|||||||
|
// 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.
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"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
|
||||||
|
EDNS0SUBNETDRAFT = 0x50fa // Don't use! Use EDNS0SUBNET
|
||||||
|
_DO = 1 << 15 // dnssec ok
|
||||||
|
)
|
||||||
|
|
||||||
|
type OPT struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
Option []EDNS0 `dns:"opt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *OPT) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
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_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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *OPT) len() int {
|
||||||
|
l := rr.Hdr.len()
|
||||||
|
for i := 0; i < len(rr.Option); i++ {
|
||||||
|
lo, _ := rr.Option[i].pack()
|
||||||
|
l += 2 + len(lo)
|
||||||
|
}
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *OPT) copy() RR {
|
||||||
|
return &OPT{*rr.Hdr.copyHeader(), rr.Option}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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() uint8 {
|
||||||
|
return uint8((rr.Hdr.Ttl & 0xFF000000) >> 24)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetExtendedRcode sets the EDNS extended RCODE field.
|
||||||
|
func (rr *OPT) SetExtendedRcode(v uint8) {
|
||||||
|
rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | (uint32(v) << 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.
|
||||||
|
func (rr *OPT) SetDo() {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// The nsid EDNS0 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) }
|
||||||
|
|
||||||
|
// The subnet EDNS0 option 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.NetMask = 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)
|
||||||
|
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)
|
||||||
|
b[0], b[1] = packUint16(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")
|
||||||
|
}
|
||||||
|
ip := make([]byte, net.IPv4len)
|
||||||
|
a := e.Address.To4().Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv4len*8))
|
||||||
|
for i := 0; i < net.IPv4len; i++ {
|
||||||
|
if i+1 > len(e.Address) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
ip[i] = a[i]
|
||||||
|
}
|
||||||
|
needLength := e.SourceNetmask / 8
|
||||||
|
if e.SourceNetmask%8 > 0 {
|
||||||
|
needLength++
|
||||||
|
}
|
||||||
|
ip = ip[:needLength]
|
||||||
|
b = append(b, ip...)
|
||||||
|
case 2:
|
||||||
|
if e.SourceNetmask > net.IPv6len*8 {
|
||||||
|
return nil, errors.New("dns: bad netmask")
|
||||||
|
}
|
||||||
|
ip := make([]byte, net.IPv6len)
|
||||||
|
a := e.Address.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv6len*8))
|
||||||
|
for i := 0; i < net.IPv6len; i++ {
|
||||||
|
if i+1 > len(e.Address) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
ip[i] = a[i]
|
||||||
|
}
|
||||||
|
needLength := e.SourceNetmask / 8
|
||||||
|
if e.SourceNetmask%8 > 0 {
|
||||||
|
needLength++
|
||||||
|
}
|
||||||
|
ip = ip[:needLength]
|
||||||
|
b = append(b, ip...)
|
||||||
|
default:
|
||||||
|
return nil, errors.New("dns: bad address family")
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EDNS0_SUBNET) unpack(b []byte) error {
|
||||||
|
lb := len(b)
|
||||||
|
if lb < 4 {
|
||||||
|
return ErrBuf
|
||||||
|
}
|
||||||
|
e.Family, _ = unpackUint16(b, 0)
|
||||||
|
e.SourceNetmask = b[2]
|
||||||
|
e.SourceScope = b[3]
|
||||||
|
switch e.Family {
|
||||||
|
case 1:
|
||||||
|
addr := make([]byte, 4)
|
||||||
|
for i := 0; i < int(e.SourceNetmask/8); i++ {
|
||||||
|
if i >= len(addr) || 4+i >= len(b) {
|
||||||
|
return ErrBuf
|
||||||
|
}
|
||||||
|
addr[i] = b[4+i]
|
||||||
|
}
|
||||||
|
e.Address = net.IPv4(addr[0], addr[1], addr[2], addr[3])
|
||||||
|
case 2:
|
||||||
|
addr := make([]byte, 16)
|
||||||
|
for i := 0; i < int(e.SourceNetmask/8); i++ {
|
||||||
|
if i >= len(addr) || 4+i >= len(b) {
|
||||||
|
return ErrBuf
|
||||||
|
}
|
||||||
|
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]}
|
||||||
|
}
|
||||||
|
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 UL (Update Lease) EDNS0 (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)
|
||||||
|
b[0] = byte(e.Lease >> 24)
|
||||||
|
b[1] = byte(e.Lease >> 16)
|
||||||
|
b[2] = byte(e.Lease >> 8)
|
||||||
|
b[3] = byte(e.Lease)
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EDNS0_UL) unpack(b []byte) error {
|
||||||
|
if len(b) < 4 {
|
||||||
|
return ErrBuf
|
||||||
|
}
|
||||||
|
e.Lease = uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
b[0], b[1] = packUint16(e.Version)
|
||||||
|
b[2], b[3] = packUint16(e.Opcode)
|
||||||
|
b[4], b[5] = packUint16(e.Error)
|
||||||
|
b[6] = byte(e.Id >> 56)
|
||||||
|
b[7] = byte(e.Id >> 48)
|
||||||
|
b[8] = byte(e.Id >> 40)
|
||||||
|
b[9] = byte(e.Id >> 32)
|
||||||
|
b[10] = byte(e.Id >> 24)
|
||||||
|
b[11] = byte(e.Id >> 16)
|
||||||
|
b[12] = byte(e.Id >> 8)
|
||||||
|
b[13] = byte(e.Id)
|
||||||
|
b[14] = byte(e.LeaseLife >> 24)
|
||||||
|
b[15] = byte(e.LeaseLife >> 16)
|
||||||
|
b[16] = byte(e.LeaseLife >> 8)
|
||||||
|
b[17] = byte(e.LeaseLife)
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EDNS0_LLQ) unpack(b []byte) error {
|
||||||
|
if len(b) < 18 {
|
||||||
|
return ErrBuf
|
||||||
|
}
|
||||||
|
e.Version, _ = unpackUint16(b, 0)
|
||||||
|
e.Opcode, _ = unpackUint16(b, 2)
|
||||||
|
e.Error, _ = unpackUint16(b, 4)
|
||||||
|
e.Id = uint64(b[6])<<56 | uint64(b[6+1])<<48 | uint64(b[6+2])<<40 |
|
||||||
|
uint64(b[6+3])<<32 | uint64(b[6+4])<<24 | uint64(b[6+5])<<16 | uint64(b[6+6])<<8 | uint64(b[6+7])
|
||||||
|
e.LeaseLife = uint32(b[14])<<24 | uint32(b[14+1])<<16 | uint32(b[14+2])<<8 | uint32(b[14+3])
|
||||||
|
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 = uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])
|
||||||
|
return nil
|
||||||
|
}
|
||||||
48
Godeps/_workspace/src/github.com/miekg/dns/edns_test.go
generated
vendored
Normal file
48
Godeps/_workspace/src/github.com/miekg/dns/edns_test.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestOPTTtl(t *testing.T) {
|
||||||
|
e := &OPT{}
|
||||||
|
e.Hdr.Name = "."
|
||||||
|
e.Hdr.Rrtype = TypeOPT
|
||||||
|
|
||||||
|
if e.Do() {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
e.SetDo()
|
||||||
|
if !e.Do() {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
oldTtl := e.Hdr.Ttl
|
||||||
|
|
||||||
|
if e.Version() != 0 {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
e.SetVersion(42)
|
||||||
|
if e.Version() != 42 {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
e.SetVersion(0)
|
||||||
|
if e.Hdr.Ttl != oldTtl {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.ExtendedRcode() != 0 {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
e.SetExtendedRcode(42)
|
||||||
|
if e.ExtendedRcode() != 42 {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
e.SetExtendedRcode(0)
|
||||||
|
if e.Hdr.Ttl != oldTtl {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
147
Godeps/_workspace/src/github.com/miekg/dns/example_test.go
generated
vendored
Normal file
147
Godeps/_workspace/src/github.com/miekg/dns/example_test.go
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
package dns_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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(zone string) {
|
||||||
|
config, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
|
||||||
|
c := new(dns.Client)
|
||||||
|
m := new(dns.Msg)
|
||||||
|
if zone == "" {
|
||||||
|
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
|
||||||
|
}
|
||||||
18
Godeps/_workspace/src/github.com/miekg/dns/idn/example_test.go
generated
vendored
Normal file
18
Godeps/_workspace/src/github.com/miekg/dns/idn/example_test.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package idn_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/miekg/dns/idn"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleToPunycode() {
|
||||||
|
name := "インターネット.テスト"
|
||||||
|
fmt.Printf("%s -> %s", name, idn.ToPunycode(name))
|
||||||
|
// Output: インターネット.テスト -> xn--eckucmux0ukc.xn--zckzah
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFromPunycode() {
|
||||||
|
name := "xn--mgbaja8a1hpac.xn--mgbachtv"
|
||||||
|
fmt.Printf("%s -> %s", name, idn.FromPunycode(name))
|
||||||
|
// Output: xn--mgbaja8a1hpac.xn--mgbachtv -> الانترنت.اختبار
|
||||||
|
}
|
||||||
268
Godeps/_workspace/src/github.com/miekg/dns/idn/punycode.go
generated
vendored
Normal file
268
Godeps/_workspace/src/github.com/miekg/dns/idn/punycode.go
generated
vendored
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
// Package idn implements encoding from and to punycode as speficied by RFC 3492.
|
||||||
|
package idn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Implementation idea from RFC itself and from from IDNA::Punycode created by
|
||||||
|
// Tatsuhiko Miyagawa <miyagawa@bulknews.net> and released under Perl Artistic
|
||||||
|
// License in 2002.
|
||||||
|
|
||||||
|
const (
|
||||||
|
_MIN rune = 1
|
||||||
|
_MAX rune = 26
|
||||||
|
_SKEW rune = 38
|
||||||
|
_BASE rune = 36
|
||||||
|
_BIAS rune = 72
|
||||||
|
_N rune = 128
|
||||||
|
_DAMP rune = 700
|
||||||
|
|
||||||
|
_DELIMITER = '-'
|
||||||
|
_PREFIX = "xn--"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ToPunycode converts unicode domain names to DNS-appropriate punycode names.
|
||||||
|
// This function would return incorrect result for strings for non-canonical
|
||||||
|
// unicode strings.
|
||||||
|
func ToPunycode(s string) string {
|
||||||
|
tokens := dns.SplitDomainName(s)
|
||||||
|
switch {
|
||||||
|
case s == "":
|
||||||
|
return ""
|
||||||
|
case tokens == nil: // s == .
|
||||||
|
return "."
|
||||||
|
case s[len(s)-1] == '.':
|
||||||
|
tokens = append(tokens, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range tokens {
|
||||||
|
tokens[i] = string(encode([]byte(tokens[i])))
|
||||||
|
}
|
||||||
|
return strings.Join(tokens, ".")
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromPunycode returns unicode domain name from provided punycode string.
|
||||||
|
func FromPunycode(s string) string {
|
||||||
|
tokens := dns.SplitDomainName(s)
|
||||||
|
switch {
|
||||||
|
case s == "":
|
||||||
|
return ""
|
||||||
|
case tokens == nil: // s == .
|
||||||
|
return "."
|
||||||
|
case s[len(s)-1] == '.':
|
||||||
|
tokens = append(tokens, "")
|
||||||
|
}
|
||||||
|
for i := range tokens {
|
||||||
|
tokens[i] = string(decode([]byte(tokens[i])))
|
||||||
|
}
|
||||||
|
return strings.Join(tokens, ".")
|
||||||
|
}
|
||||||
|
|
||||||
|
// digitval converts single byte into meaningful value that's used to calculate decoded unicode character.
|
||||||
|
const errdigit = 0xffff
|
||||||
|
|
||||||
|
func digitval(code rune) rune {
|
||||||
|
switch {
|
||||||
|
case code >= 'A' && code <= 'Z':
|
||||||
|
return code - 'A'
|
||||||
|
case code >= 'a' && code <= 'z':
|
||||||
|
return code - 'a'
|
||||||
|
case code >= '0' && code <= '9':
|
||||||
|
return code - '0' + 26
|
||||||
|
}
|
||||||
|
return errdigit
|
||||||
|
}
|
||||||
|
|
||||||
|
// lettercode finds BASE36 byte (a-z0-9) based on calculated number.
|
||||||
|
func lettercode(digit rune) rune {
|
||||||
|
switch {
|
||||||
|
case digit >= 0 && digit <= 25:
|
||||||
|
return digit + 'a'
|
||||||
|
case digit >= 26 && digit <= 36:
|
||||||
|
return digit - 26 + '0'
|
||||||
|
}
|
||||||
|
panic("dns: not reached")
|
||||||
|
}
|
||||||
|
|
||||||
|
// adapt calculates next bias to be used for next iteration delta.
|
||||||
|
func adapt(delta rune, numpoints int, firsttime bool) rune {
|
||||||
|
if firsttime {
|
||||||
|
delta /= _DAMP
|
||||||
|
} else {
|
||||||
|
delta /= 2
|
||||||
|
}
|
||||||
|
|
||||||
|
var k rune
|
||||||
|
for delta = delta + delta/rune(numpoints); delta > (_BASE-_MIN)*_MAX/2; k += _BASE {
|
||||||
|
delta /= _BASE - _MIN
|
||||||
|
}
|
||||||
|
|
||||||
|
return k + ((_BASE-_MIN+1)*delta)/(delta+_SKEW)
|
||||||
|
}
|
||||||
|
|
||||||
|
// next finds minimal rune (one with lowest codepoint value) that should be equal or above boundary.
|
||||||
|
func next(b []rune, boundary rune) rune {
|
||||||
|
if len(b) == 0 {
|
||||||
|
panic("dns: invalid set of runes to determine next one")
|
||||||
|
}
|
||||||
|
m := b[0]
|
||||||
|
for _, x := range b[1:] {
|
||||||
|
if x >= boundary && (m < boundary || x < m) {
|
||||||
|
m = x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// preprune converts unicode rune to lower case. At this time it's not
|
||||||
|
// supporting all things described in RFCs
|
||||||
|
func preprune(r rune) rune {
|
||||||
|
if unicode.IsUpper(r) {
|
||||||
|
r = unicode.ToLower(r)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// tfunc is a function that helps calculate each character weight
|
||||||
|
func tfunc(k, bias rune) rune {
|
||||||
|
switch {
|
||||||
|
case k <= bias:
|
||||||
|
return _MIN
|
||||||
|
case k >= bias+_MAX:
|
||||||
|
return _MAX
|
||||||
|
}
|
||||||
|
return k - bias
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode transforms Unicode input bytes (that represent DNS label) into punycode bytestream
|
||||||
|
func encode(input []byte) []byte {
|
||||||
|
n, bias := _N, _BIAS
|
||||||
|
|
||||||
|
b := bytes.Runes(input)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = preprune(b[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
basic := make([]byte, 0, len(b))
|
||||||
|
for _, ltr := range b {
|
||||||
|
if ltr <= 0x7f {
|
||||||
|
basic = append(basic, byte(ltr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
basiclen := len(basic)
|
||||||
|
fulllen := len(b)
|
||||||
|
if basiclen == fulllen {
|
||||||
|
return basic
|
||||||
|
}
|
||||||
|
|
||||||
|
var out bytes.Buffer
|
||||||
|
|
||||||
|
out.WriteString(_PREFIX)
|
||||||
|
if basiclen > 0 {
|
||||||
|
out.Write(basic)
|
||||||
|
out.WriteByte(_DELIMITER)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ltr, nextltr rune
|
||||||
|
delta, q rune // delta calculation (see rfc)
|
||||||
|
t, k, cp rune // weight and codepoint calculation
|
||||||
|
)
|
||||||
|
|
||||||
|
s := &bytes.Buffer{}
|
||||||
|
for h := basiclen; h < fulllen; n, delta = n+1, delta+1 {
|
||||||
|
nextltr = next(b, n)
|
||||||
|
s.Truncate(0)
|
||||||
|
s.WriteRune(nextltr)
|
||||||
|
delta, n = delta+(nextltr-n)*rune(h+1), nextltr
|
||||||
|
|
||||||
|
for _, ltr = range b {
|
||||||
|
if ltr < n {
|
||||||
|
delta++
|
||||||
|
}
|
||||||
|
if ltr == n {
|
||||||
|
q = delta
|
||||||
|
for k = _BASE; ; k += _BASE {
|
||||||
|
t = tfunc(k, bias)
|
||||||
|
if q < t {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
cp = t + ((q - t) % (_BASE - t))
|
||||||
|
out.WriteRune(lettercode(cp))
|
||||||
|
q = (q - t) / (_BASE - t)
|
||||||
|
}
|
||||||
|
|
||||||
|
out.WriteRune(lettercode(q))
|
||||||
|
|
||||||
|
bias = adapt(delta, h+1, h == basiclen)
|
||||||
|
h, delta = h+1, 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode transforms punycode input bytes (that represent DNS label) into Unicode bytestream
|
||||||
|
func decode(b []byte) []byte {
|
||||||
|
src := b // b would move and we need to keep it
|
||||||
|
|
||||||
|
n, bias := _N, _BIAS
|
||||||
|
if !bytes.HasPrefix(b, []byte(_PREFIX)) {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
out := make([]rune, 0, len(b))
|
||||||
|
b = b[len(_PREFIX):]
|
||||||
|
for pos, x := range b {
|
||||||
|
if x == _DELIMITER {
|
||||||
|
out = append(out, bytes.Runes(b[:pos])...)
|
||||||
|
b = b[pos+1:] // trim source string
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(b) == 0 {
|
||||||
|
return src
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
i, oldi, w rune
|
||||||
|
ch byte
|
||||||
|
t, digit rune
|
||||||
|
ln int
|
||||||
|
)
|
||||||
|
|
||||||
|
for i = 0; len(b) > 0; i++ {
|
||||||
|
oldi, w = i, 1
|
||||||
|
for k := _BASE; len(b) > 0; k += _BASE {
|
||||||
|
ch, b = b[0], b[1:]
|
||||||
|
digit = digitval(rune(ch))
|
||||||
|
if digit == errdigit {
|
||||||
|
return src
|
||||||
|
}
|
||||||
|
i += digit * w
|
||||||
|
|
||||||
|
t = tfunc(k, bias)
|
||||||
|
if digit < t {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
w *= _BASE - t
|
||||||
|
}
|
||||||
|
ln = len(out) + 1
|
||||||
|
bias = adapt(i-oldi, ln, oldi == 0)
|
||||||
|
n += i / rune(ln)
|
||||||
|
i = i % rune(ln)
|
||||||
|
// insert
|
||||||
|
out = append(out, 0)
|
||||||
|
copy(out[i+1:], out[i:])
|
||||||
|
out[i] = n
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret bytes.Buffer
|
||||||
|
for _, r := range out {
|
||||||
|
ret.WriteRune(r)
|
||||||
|
}
|
||||||
|
return ret.Bytes()
|
||||||
|
}
|
||||||
94
Godeps/_workspace/src/github.com/miekg/dns/idn/punycode_test.go
generated
vendored
Normal file
94
Godeps/_workspace/src/github.com/miekg/dns/idn/punycode_test.go
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
package idn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testcases = [][2]string{
|
||||||
|
{"", ""},
|
||||||
|
{"a", "a"},
|
||||||
|
{"A-B", "a-b"},
|
||||||
|
{"AbC", "abc"},
|
||||||
|
{"я", "xn--41a"},
|
||||||
|
{"zя", "xn--z-0ub"},
|
||||||
|
{"ЯZ", "xn--z-zub"},
|
||||||
|
{"إختبار", "xn--kgbechtv"},
|
||||||
|
{"آزمایشی", "xn--hgbk6aj7f53bba"},
|
||||||
|
{"测试", "xn--0zwm56d"},
|
||||||
|
{"測試", "xn--g6w251d"},
|
||||||
|
{"Испытание", "xn--80akhbyknj4f"},
|
||||||
|
{"परीक्षा", "xn--11b5bs3a9aj6g"},
|
||||||
|
{"δοκιμή", "xn--jxalpdlp"},
|
||||||
|
{"테스트", "xn--9t4b11yi5a"},
|
||||||
|
{"טעסט", "xn--deba0ad"},
|
||||||
|
{"テスト", "xn--zckzah"},
|
||||||
|
{"பரிட்சை", "xn--hlcj6aya9esc7a"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEncodeDecodePunycode(t *testing.T) {
|
||||||
|
for _, tst := range testcases {
|
||||||
|
enc := encode([]byte(tst[0]))
|
||||||
|
if string(enc) != tst[1] {
|
||||||
|
t.Errorf("%s encodeded as %s but should be %s", tst[0], enc, tst[1])
|
||||||
|
}
|
||||||
|
dec := decode([]byte(tst[1]))
|
||||||
|
if string(dec) != strings.ToLower(tst[0]) {
|
||||||
|
t.Errorf("%s decoded as %s but should be %s", tst[1], dec, strings.ToLower(tst[0]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToFromPunycode(t *testing.T) {
|
||||||
|
for _, tst := range testcases {
|
||||||
|
// assert unicode.com == punycode.com
|
||||||
|
full := ToPunycode(tst[0] + ".com")
|
||||||
|
if full != tst[1]+".com" {
|
||||||
|
t.Errorf("invalid result from string conversion to punycode, %s and should be %s.com", full, tst[1])
|
||||||
|
}
|
||||||
|
// assert punycode.punycode == unicode.unicode
|
||||||
|
decoded := FromPunycode(tst[1] + "." + tst[1])
|
||||||
|
if decoded != strings.ToLower(tst[0]+"."+tst[0]) {
|
||||||
|
t.Errorf("invalid result from string conversion to punycode, %s and should be %s.%s", decoded, tst[0], tst[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEncodeDecodeFinalPeriod(t *testing.T) {
|
||||||
|
for _, tst := range testcases {
|
||||||
|
// assert unicode.com. == punycode.com.
|
||||||
|
full := ToPunycode(tst[0] + ".")
|
||||||
|
if full != tst[1]+"." {
|
||||||
|
t.Errorf("invalid result from string conversion to punycode when period added at the end, %#v and should be %#v", full, tst[1]+".")
|
||||||
|
}
|
||||||
|
// assert punycode.com. == unicode.com.
|
||||||
|
decoded := FromPunycode(tst[1] + ".")
|
||||||
|
if decoded != strings.ToLower(tst[0]+".") {
|
||||||
|
t.Errorf("invalid result from string conversion to punycode when period added, %#v and should be %#v", decoded, tst[0]+".")
|
||||||
|
}
|
||||||
|
full = ToPunycode(tst[0])
|
||||||
|
if full != tst[1] {
|
||||||
|
t.Errorf("invalid result from string conversion to punycode when no period added at the end, %#v and should be %#v", full, tst[1]+".")
|
||||||
|
}
|
||||||
|
// assert punycode.com. == unicode.com.
|
||||||
|
decoded = FromPunycode(tst[1])
|
||||||
|
if decoded != strings.ToLower(tst[0]) {
|
||||||
|
t.Errorf("invalid result from string conversion to punycode when no period added, %#v and should be %#v", decoded, tst[0]+".")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var invalid = []string{
|
||||||
|
"xn--*",
|
||||||
|
"xn--",
|
||||||
|
"xn---",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidPunycode(t *testing.T) {
|
||||||
|
for _, d := range invalid {
|
||||||
|
s := FromPunycode(d)
|
||||||
|
if s != d {
|
||||||
|
t.Errorf("Changed invalid name %s to %#v", d, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
157
Godeps/_workspace/src/github.com/miekg/dns/keygen.go
generated
vendored
Normal file
157
Godeps/_workspace/src/github.com/miekg/dns/keygen.go
generated
vendored
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/dsa"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"math/big"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
const _FORMAT = "Private-key-format: v1.3\n"
|
||||||
|
|
||||||
|
// Empty interface that is used as a wrapper around all possible
|
||||||
|
// private key implementations from the crypto package.
|
||||||
|
type PrivateKey interface{}
|
||||||
|
|
||||||
|
// 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 (r *DNSKEY) Generate(bits int) (PrivateKey, error) {
|
||||||
|
switch r.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 r.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
|
||||||
|
}
|
||||||
|
r.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
|
||||||
|
}
|
||||||
|
r.setPublicKeyRSA(priv.PublicKey.E, priv.PublicKey.N)
|
||||||
|
return priv, nil
|
||||||
|
case ECDSAP256SHA256, ECDSAP384SHA384:
|
||||||
|
var c elliptic.Curve
|
||||||
|
switch r.Algorithm {
|
||||||
|
case ECDSAP256SHA256:
|
||||||
|
c = elliptic.P256()
|
||||||
|
case ECDSAP384SHA384:
|
||||||
|
c = elliptic.P384()
|
||||||
|
}
|
||||||
|
priv, err := ecdsa.GenerateKey(c, rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r.setPublicKeyCurve(priv.PublicKey.X, priv.PublicKey.Y)
|
||||||
|
return priv, nil
|
||||||
|
default:
|
||||||
|
return nil, ErrAlg
|
||||||
|
}
|
||||||
|
return nil, nil // Dummy return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 (hashing, keytag), so its a method of the DNSKEY.
|
||||||
|
func (r *DNSKEY) PrivateKeyString(p PrivateKey) (s string) {
|
||||||
|
switch t := p.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
algorithm := strconv.Itoa(int(r.Algorithm)) + " (" + AlgorithmToString[r.Algorithm] + ")"
|
||||||
|
modulus := toBase64(t.PublicKey.N.Bytes())
|
||||||
|
e := big.NewInt(int64(t.PublicKey.E))
|
||||||
|
publicExponent := toBase64(e.Bytes())
|
||||||
|
privateExponent := toBase64(t.D.Bytes())
|
||||||
|
prime1 := toBase64(t.Primes[0].Bytes())
|
||||||
|
prime2 := toBase64(t.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)
|
||||||
|
minusone := big.NewInt(-1)
|
||||||
|
p_1 := big.NewInt(0).Sub(t.Primes[0], one)
|
||||||
|
q_1 := big.NewInt(0).Sub(t.Primes[1], one)
|
||||||
|
exp1 := big.NewInt(0).Mod(t.D, p_1)
|
||||||
|
exp2 := big.NewInt(0).Mod(t.D, q_1)
|
||||||
|
coeff := big.NewInt(0).Exp(t.Primes[1], minusone, t.Primes[0])
|
||||||
|
|
||||||
|
exponent1 := toBase64(exp1.Bytes())
|
||||||
|
exponent2 := toBase64(exp2.Bytes())
|
||||||
|
coefficient := toBase64(coeff.Bytes())
|
||||||
|
|
||||||
|
s = _FORMAT +
|
||||||
|
"Algorithm: " + algorithm + "\n" +
|
||||||
|
"Modules: " + 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:
|
||||||
|
algorithm := strconv.Itoa(int(r.Algorithm)) + " (" + AlgorithmToString[r.Algorithm] + ")"
|
||||||
|
var intlen int
|
||||||
|
switch r.Algorithm {
|
||||||
|
case ECDSAP256SHA256:
|
||||||
|
intlen = 32
|
||||||
|
case ECDSAP384SHA384:
|
||||||
|
intlen = 48
|
||||||
|
}
|
||||||
|
private := toBase64(intToBytes(t.D, intlen))
|
||||||
|
s = _FORMAT +
|
||||||
|
"Algorithm: " + algorithm + "\n" +
|
||||||
|
"PrivateKey: " + private + "\n"
|
||||||
|
case *dsa.PrivateKey:
|
||||||
|
algorithm := strconv.Itoa(int(r.Algorithm)) + " (" + AlgorithmToString[r.Algorithm] + ")"
|
||||||
|
T := divRoundUp(divRoundUp(t.PublicKey.Parameters.G.BitLen(), 8)-64, 8)
|
||||||
|
prime := toBase64(intToBytes(t.PublicKey.Parameters.P, 64+T*8))
|
||||||
|
subprime := toBase64(intToBytes(t.PublicKey.Parameters.Q, 20))
|
||||||
|
base := toBase64(intToBytes(t.PublicKey.Parameters.G, 64+T*8))
|
||||||
|
priv := toBase64(intToBytes(t.X, 20))
|
||||||
|
pub := toBase64(intToBytes(t.PublicKey.Y, 64+T*8))
|
||||||
|
s = _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"
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
244
Godeps/_workspace/src/github.com/miekg/dns/kscan.go
generated
vendored
Normal file
244
Godeps/_workspace/src/github.com/miekg/dns/kscan.go
generated
vendored
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/dsa"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rsa"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (k *DNSKEY) NewPrivateKey(s string) (PrivateKey, error) {
|
||||||
|
if 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) (PrivateKey, error) {
|
||||||
|
m, e := parseKey(q, file)
|
||||||
|
if m == nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
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
|
||||||
|
switch m["algorithm"] {
|
||||||
|
case "3 (DSA)":
|
||||||
|
p, e := readPrivateKeyDSA(m)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
if !k.setPublicKeyInPrivate(p) {
|
||||||
|
return nil, ErrKey
|
||||||
|
}
|
||||||
|
return p, e
|
||||||
|
case "1 (RSAMD5)":
|
||||||
|
fallthrough
|
||||||
|
case "5 (RSASHA1)":
|
||||||
|
fallthrough
|
||||||
|
case "7 (RSASHA1NSEC3SHA1)":
|
||||||
|
fallthrough
|
||||||
|
case "8 (RSASHA256)":
|
||||||
|
fallthrough
|
||||||
|
case "10 (RSASHA512)":
|
||||||
|
p, e := readPrivateKeyRSA(m)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
if !k.setPublicKeyInPrivate(p) {
|
||||||
|
return nil, ErrKey
|
||||||
|
}
|
||||||
|
return p, e
|
||||||
|
case "12 (ECC-GOST)":
|
||||||
|
p, e := readPrivateKeyGOST(m)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
// setPublicKeyInPrivate(p)
|
||||||
|
return p, e
|
||||||
|
case "13 (ECDSAP256SHA256)":
|
||||||
|
fallthrough
|
||||||
|
case "14 (ECDSAP384SHA384)":
|
||||||
|
p, e := readPrivateKeyECDSA(m)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
if !k.setPublicKeyInPrivate(p) {
|
||||||
|
return nil, ErrKey
|
||||||
|
}
|
||||||
|
return p, e
|
||||||
|
}
|
||||||
|
return nil, ErrPrivKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read a private key (file) string and create a public key. Return the private key.
|
||||||
|
func readPrivateKeyRSA(m map[string]string) (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) (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) (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
|
||||||
|
}
|
||||||
|
|
||||||
|
func readPrivateKeyGOST(m map[string]string) (PrivateKey, error) {
|
||||||
|
// TODO(miek)
|
||||||
|
return nil, 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 _KEY:
|
||||||
|
k = l.token
|
||||||
|
case _VALUE:
|
||||||
|
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 = _KEY
|
||||||
|
c <- l
|
||||||
|
// Next token is a space, eat it
|
||||||
|
s.tokenText()
|
||||||
|
key = false
|
||||||
|
str = ""
|
||||||
|
} else {
|
||||||
|
l.value = _VALUE
|
||||||
|
}
|
||||||
|
case ';':
|
||||||
|
commt = true
|
||||||
|
case '\n':
|
||||||
|
if commt {
|
||||||
|
// Reset a comment
|
||||||
|
commt = false
|
||||||
|
}
|
||||||
|
l.value = _VALUE
|
||||||
|
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 = _VALUE
|
||||||
|
c <- l
|
||||||
|
}
|
||||||
|
}
|
||||||
162
Godeps/_workspace/src/github.com/miekg/dns/labels.go
generated
vendored
Normal file
162
Godeps/_workspace/src/github.com/miekg/dns/labels.go
generated
vendored
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
// 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"}
|
||||||
|
// The root label (.) returns nil. Note that using
|
||||||
|
// strings.Split(s) will work in most cases, but does not handle
|
||||||
|
// escaped dots (\.) for instance.
|
||||||
|
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
|
||||||
|
func CompareDomainName(s1, s2 string) (n int) {
|
||||||
|
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.
|
||||||
|
func CountLabel(s string) (labels int) {
|
||||||
|
if s == "." {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
off := 0
|
||||||
|
end := false
|
||||||
|
for {
|
||||||
|
off, end = NextLabel(s, off)
|
||||||
|
labels++
|
||||||
|
if end {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("dns: not reached")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 dns.SplitDomainName.
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
panic("dns: not reached")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
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.
|
||||||
|
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
|
||||||
|
}
|
||||||
214
Godeps/_workspace/src/github.com/miekg/dns/labels_test.go
generated
vendored
Normal file
214
Godeps/_workspace/src/github.com/miekg/dns/labels_test.go
generated
vendored
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
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.Logf("%s with %s should be %d", s1, s2, 2)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if CompareDomainName(s1, s3) != 1 {
|
||||||
|
t.Logf("%s with %s should be %d", s1, s3, 1)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if CompareDomainName(s3, s4) != 0 {
|
||||||
|
t.Logf("%s with %s should be %d", s3, s4, 0)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
// Non qualified tests
|
||||||
|
if CompareDomainName(s1, s5) != 1 {
|
||||||
|
t.Logf("%s with %s should be %d", s1, s5, 1)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if CompareDomainName(s1, s6) != 2 {
|
||||||
|
t.Logf("%s with %s should be %d", s1, s5, 2)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
if CompareDomainName(s1, ".") != 0 {
|
||||||
|
t.Logf("%s with %s should be %d", s1, s5, 0)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if CompareDomainName(".", ".") != 0 {
|
||||||
|
t.Logf("%s with %s should be %d", ".", ".", 0)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Logf("labels should be %d, got %d: %s %v\n", i, x, s, Split(s))
|
||||||
|
t.Fail()
|
||||||
|
} else {
|
||||||
|
t.Logf("%s %v\n", s, Split(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSplit2(t *testing.T) {
|
||||||
|
splitter := map[string][]int{
|
||||||
|
"www.miek.nl.": []int{0, 4, 9},
|
||||||
|
"www.miek.nl": []int{0, 4, 9},
|
||||||
|
"nl": []int{0},
|
||||||
|
}
|
||||||
|
for s, i := range splitter {
|
||||||
|
x := Split(s)
|
||||||
|
switch len(i) {
|
||||||
|
case 1:
|
||||||
|
if x[0] != i[0] {
|
||||||
|
t.Logf("labels should be %v, got %v: %s\n", i, x, s)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if x[0] != i[0] || x[1] != i[1] || x[2] != i[2] {
|
||||||
|
t.Logf("labels should be %v, got %v: %s\n", i, x, s)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Logf("label should be %d, got %d, %t: preving %d, %s\n", i, x, ok, s.int, s.string)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Logf("CountLabel should have %d, got %d\n", i, x)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSplitDomainName(t *testing.T) {
|
||||||
|
labels := map[string][]string{
|
||||||
|
"miek.nl": []string{"miek", "nl"},
|
||||||
|
".": nil,
|
||||||
|
"www.miek.nl.": []string{"www", "miek", "nl"},
|
||||||
|
"www.miek.nl": []string{"www", "miek", "nl"},
|
||||||
|
"www..miek.nl": []string{"www", "", "miek", "nl"},
|
||||||
|
`www\.miek.nl`: []string{`www\.miek`, "nl"},
|
||||||
|
`www\\.miek.nl`: []string{`www\\`, "miek", "nl"},
|
||||||
|
}
|
||||||
|
domainLoop:
|
||||||
|
for domain, splits := range labels {
|
||||||
|
parts := SplitDomainName(domain)
|
||||||
|
if len(parts) != len(splits) {
|
||||||
|
t.Logf("SplitDomainName returned %v for %s, expected %v", parts, domain, splits)
|
||||||
|
t.Fail()
|
||||||
|
continue domainLoop
|
||||||
|
}
|
||||||
|
for i := range parts {
|
||||||
|
if parts[i] != splits[i] {
|
||||||
|
t.Logf("SplitDomainName returned %v for %s, expected %v", parts, domain, splits)
|
||||||
|
t.Fail()
|
||||||
|
continue domainLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsDomainName(t *testing.T) {
|
||||||
|
type ret struct {
|
||||||
|
ok bool
|
||||||
|
lab int
|
||||||
|
}
|
||||||
|
names := map[string]*ret{
|
||||||
|
"..": &ret{false, 1},
|
||||||
|
"@.": &ret{true, 1},
|
||||||
|
"www.example.com": &ret{true, 3},
|
||||||
|
"www.e%ample.com": &ret{true, 3},
|
||||||
|
"www.example.com.": &ret{true, 3},
|
||||||
|
"mi\\k.nl.": &ret{true, 2},
|
||||||
|
"mi\\k.nl": &ret{true, 2},
|
||||||
|
}
|
||||||
|
for d, ok := range names {
|
||||||
|
l, k := IsDomainName(d)
|
||||||
|
if ok.ok != k || ok.lab != l {
|
||||||
|
t.Logf(" got %v %d for %s ", k, l, d)
|
||||||
|
t.Logf("have %v %d for %s ", ok.ok, ok.lab, d)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
CompareDomainName("www.example.com", "aa.example.com")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkIsSubDomain(b *testing.B) {
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
1899
Godeps/_workspace/src/github.com/miekg/dns/msg.go
generated
vendored
Normal file
1899
Godeps/_workspace/src/github.com/miekg/dns/msg.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
110
Godeps/_workspace/src/github.com/miekg/dns/nsecx.go
generated
vendored
Normal file
110
Godeps/_workspace/src/github.com/miekg/dns/nsecx.go
generated
vendored
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha1"
|
||||||
|
"hash"
|
||||||
|
"io"
|
||||||
|
"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 := PackStruct(saltwire, wire, 0)
|
||||||
|
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
|
||||||
|
name = append(name, wire...)
|
||||||
|
io.WriteString(s, string(name))
|
||||||
|
nsec3 := s.Sum(nil)
|
||||||
|
// k > 0
|
||||||
|
for k := uint16(0); k < iter; k++ {
|
||||||
|
s.Reset()
|
||||||
|
nsec3 = append(nsec3, wire...)
|
||||||
|
io.WriteString(s, string(nsec3))
|
||||||
|
nsec3 = s.Sum(nil)
|
||||||
|
}
|
||||||
|
return toBase32(nsec3)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Denialer interface {
|
||||||
|
// Cover will check if the (unhashed) name is being covered by this NSEC or NSEC3.
|
||||||
|
Cover(name string) bool
|
||||||
|
// Match will check if the ownername matches the (unhashed) name for this NSEC3 or NSEC3.
|
||||||
|
Match(name string) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cover implements the Denialer interface.
|
||||||
|
func (rr *NSEC) Cover(name string) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match implements the Denialer interface.
|
||||||
|
func (rr *NSEC) Match(name string) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cover implements the Denialer interface.
|
||||||
|
func (rr *NSEC3) Cover(name string) bool {
|
||||||
|
// FIXME(miek): check if the zones match
|
||||||
|
// FIXME(miek): check if we're not dealing with parent nsec3
|
||||||
|
hname := HashName(name, rr.Hash, rr.Iterations, rr.Salt)
|
||||||
|
labels := Split(rr.Hdr.Name)
|
||||||
|
if len(labels) < 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
hash := strings.ToUpper(rr.Hdr.Name[labels[0] : labels[1]-1]) // -1 to remove the dot
|
||||||
|
if hash == rr.NextDomain {
|
||||||
|
return false // empty interval
|
||||||
|
}
|
||||||
|
if hash > rr.NextDomain { // last name, points to apex
|
||||||
|
// hname > hash
|
||||||
|
// hname > rr.NextDomain
|
||||||
|
// TODO(miek)
|
||||||
|
}
|
||||||
|
if hname <= hash {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if hname >= rr.NextDomain {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match implements the Denialer interface.
|
||||||
|
func (rr *NSEC3) Match(name string) bool {
|
||||||
|
// FIXME(miek): Check if we are in the same zone
|
||||||
|
hname := HashName(name, rr.Hash, rr.Iterations, rr.Salt)
|
||||||
|
labels := Split(rr.Hdr.Name)
|
||||||
|
if len(labels) < 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
hash := strings.ToUpper(rr.Hdr.Name[labels[0] : labels[1]-1]) // -1 to remove the .
|
||||||
|
if hash == hname {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
33
Godeps/_workspace/src/github.com/miekg/dns/nsecx_test.go
generated
vendored
Normal file
33
Godeps/_workspace/src/github.com/miekg/dns/nsecx_test.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPackNsec3(t *testing.T) {
|
||||||
|
nsec3 := HashName("dnsex.nl.", SHA1, 0, "DEAD")
|
||||||
|
if nsec3 != "ROCCJAE8BJJU7HN6T7NG3TNM8ACRS87J" {
|
||||||
|
t.Logf("%v\n", nsec3)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
nsec3 = HashName("a.b.c.example.org.", SHA1, 2, "DEAD")
|
||||||
|
if nsec3 != "6LQ07OAHBTOOEU2R9ANI2AT70K5O0RCG" {
|
||||||
|
t.Logf("%v\n", nsec3)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNsec3(t *testing.T) {
|
||||||
|
// examples taken from .nl
|
||||||
|
nsec3, _ := NewRR("39p91242oslggest5e6a7cci4iaeqvnk.nl. IN NSEC3 1 1 5 F10E9F7EA83FC8F3 39P99DCGG0MDLARTCRMCF6OFLLUL7PR6 NS DS RRSIG")
|
||||||
|
if !nsec3.(*NSEC3).Cover("snasajsksasasa.nl.") { // 39p94jrinub66hnpem8qdpstrec86pg3
|
||||||
|
t.Logf("39p94jrinub66hnpem8qdpstrec86pg3. should be covered by 39p91242oslggest5e6a7cci4iaeqvnk.nl. - 39P99DCGG0MDLARTCRMCF6OFLLUL7PR6")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
nsec3, _ = NewRR("sk4e8fj94u78smusb40o1n0oltbblu2r.nl. IN NSEC3 1 1 5 F10E9F7EA83FC8F3 SK4F38CQ0ATIEI8MH3RGD0P5I4II6QAN NS SOA TXT RRSIG DNSKEY NSEC3PARAM")
|
||||||
|
if !nsec3.(*NSEC3).Match("nl.") { // sk4e8fj94u78smusb40o1n0oltbblu2r.nl.
|
||||||
|
t.Logf("sk4e8fj94u78smusb40o1n0oltbblu2r.nl. should match sk4e8fj94u78smusb40o1n0oltbblu2r.nl.")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
1276
Godeps/_workspace/src/github.com/miekg/dns/parse_test.go
generated
vendored
Normal file
1276
Godeps/_workspace/src/github.com/miekg/dns/parse_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
122
Godeps/_workspace/src/github.com/miekg/dns/privaterr.go
generated
vendored
Normal file
122
Godeps/_workspace/src/github.com/miekg/dns/privaterr.go
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
PRIVATE RR
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
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 _NEWLINE, _EOF:
|
||||||
|
break FETCH
|
||||||
|
case _STRING:
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
169
Godeps/_workspace/src/github.com/miekg/dns/privaterr_test.go
generated
vendored
Normal file
169
Godeps/_workspace/src/github.com/miekg/dns/privaterr_test.go
generated
vendored
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
package dns_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const TypeISBN uint16 = 0x0F01
|
||||||
|
|
||||||
|
// 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: %s", 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: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = 0x0F02
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
95
Godeps/_workspace/src/github.com/miekg/dns/rawmsg.go
generated
vendored
Normal file
95
Godeps/_workspace/src/github.com/miekg/dns/rawmsg.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
// These raw* functions do not use reflection, they directly set the values
|
||||||
|
// in the buffer. There are faster than their reflection counterparts.
|
||||||
|
|
||||||
|
// RawSetId sets the message id in buf.
|
||||||
|
func rawSetId(msg []byte, i uint16) bool {
|
||||||
|
if len(msg) < 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
msg[0], msg[1] = packUint16(i)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// rawSetQuestionLen sets the length of the question section.
|
||||||
|
func rawSetQuestionLen(msg []byte, i uint16) bool {
|
||||||
|
if len(msg) < 6 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
msg[4], msg[5] = packUint16(i)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// rawSetAnswerLen sets the lenght of the answer section.
|
||||||
|
func rawSetAnswerLen(msg []byte, i uint16) bool {
|
||||||
|
if len(msg) < 8 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
msg[6], msg[7] = packUint16(i)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// rawSetsNsLen sets the lenght of the authority section.
|
||||||
|
func rawSetNsLen(msg []byte, i uint16) bool {
|
||||||
|
if len(msg) < 10 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
msg[8], msg[9] = packUint16(i)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// rawSetExtraLen sets the lenght of the additional section.
|
||||||
|
func rawSetExtraLen(msg []byte, i uint16) bool {
|
||||||
|
if len(msg) < 12 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
msg[10], msg[11] = packUint16(i)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
msg[off], msg[off+1] = packUint16(uint16(rdatalen))
|
||||||
|
return true
|
||||||
|
}
|
||||||
43
Godeps/_workspace/src/github.com/miekg/dns/scanner.go
generated
vendored
Normal file
43
Godeps/_workspace/src/github.com/miekg/dns/scanner.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
626
Godeps/_workspace/src/github.com/miekg/dns/server.go
generated
vendored
Normal file
626
Godeps/_workspace/src/github.com/miekg/dns/server.go
generated
vendored
Normal file
@@ -0,0 +1,626 @@
|
|||||||
|
// DNS server implementation.
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
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.TCPConn // i/o connection if TCP was used
|
||||||
|
udpSession *sessionUDP // oob data to get egress interface right
|
||||||
|
remoteAddr net.Addr // address of the client
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
// ServerDNS calls f(w, r)
|
||||||
|
func (f HandlerFunc) ServeDNS(w ResponseWriter, r *Msg) {
|
||||||
|
f(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FailedHandler 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 addresss and network speficied. 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
} else {
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle adds a handler 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)
|
||||||
|
}
|
||||||
|
// don't need a mutex here, because deleting is OK, even if the
|
||||||
|
// entry is note there.
|
||||||
|
delete(mux.z, Fqdn(pattern))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Server defines parameters for running an DNS server.
|
||||||
|
type Server struct {
|
||||||
|
// Address to listen on, ":dns" if empty.
|
||||||
|
Addr string
|
||||||
|
// if "tcp" 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
|
||||||
|
// 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 specfically not check if the query has the QR bit not set.
|
||||||
|
Unsafe bool
|
||||||
|
// If NotifyStartedFunc is set is is called, once the server has started listening.
|
||||||
|
NotifyStartedFunc func()
|
||||||
|
|
||||||
|
// For graceful shutdown.
|
||||||
|
stopUDP chan bool
|
||||||
|
stopTCP chan bool
|
||||||
|
wgUDP sync.WaitGroup
|
||||||
|
wgTCP sync.WaitGroup
|
||||||
|
|
||||||
|
// make start/shutdown not racy
|
||||||
|
lock sync.Mutex
|
||||||
|
started bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenAndServe starts a nameserver on the configured address in *Server.
|
||||||
|
func (srv *Server) ListenAndServe() error {
|
||||||
|
srv.lock.Lock()
|
||||||
|
if srv.started {
|
||||||
|
return &Error{err: "server already started"}
|
||||||
|
}
|
||||||
|
srv.stopUDP, srv.stopTCP = make(chan bool), make(chan bool)
|
||||||
|
srv.started = true
|
||||||
|
srv.lock.Unlock()
|
||||||
|
addr := srv.Addr
|
||||||
|
if addr == "" {
|
||||||
|
addr = ":domain"
|
||||||
|
}
|
||||||
|
if srv.UDPSize == 0 {
|
||||||
|
srv.UDPSize = MinMsgSize
|
||||||
|
}
|
||||||
|
switch srv.Net {
|
||||||
|
case "tcp", "tcp4", "tcp6":
|
||||||
|
a, e := net.ResolveTCPAddr(srv.Net, addr)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
l, e := net.ListenTCP(srv.Net, a)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return srv.serveTCP(l)
|
||||||
|
case "udp", "udp4", "udp6":
|
||||||
|
a, e := net.ResolveUDPAddr(srv.Net, addr)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
l, e := net.ListenUDP(srv.Net, a)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
if e := setUDPSocketOptions(l); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return srv.serveUDP(l)
|
||||||
|
}
|
||||||
|
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()
|
||||||
|
if srv.started {
|
||||||
|
return &Error{err: "server already started"}
|
||||||
|
}
|
||||||
|
srv.stopUDP, srv.stopTCP = make(chan bool), make(chan bool)
|
||||||
|
srv.started = true
|
||||||
|
srv.lock.Unlock()
|
||||||
|
if srv.PacketConn != nil {
|
||||||
|
if srv.UDPSize == 0 {
|
||||||
|
srv.UDPSize = MinMsgSize
|
||||||
|
}
|
||||||
|
if t, ok := srv.PacketConn.(*net.UDPConn); ok {
|
||||||
|
if e := setUDPSocketOptions(t); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return srv.serveUDP(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if srv.Listener != nil {
|
||||||
|
if t, ok := srv.Listener.(*net.TCPListener); ok {
|
||||||
|
return srv.serveTCP(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 and error
|
||||||
|
// is returned.
|
||||||
|
func (srv *Server) Shutdown() error {
|
||||||
|
srv.lock.Lock()
|
||||||
|
if !srv.started {
|
||||||
|
return &Error{err: "server not started"}
|
||||||
|
}
|
||||||
|
srv.started = false
|
||||||
|
srv.lock.Unlock()
|
||||||
|
net, addr := srv.Net, srv.Addr
|
||||||
|
switch {
|
||||||
|
case srv.Listener != nil:
|
||||||
|
a := srv.Listener.Addr()
|
||||||
|
net, addr = a.Network(), a.String()
|
||||||
|
case srv.PacketConn != nil:
|
||||||
|
a := srv.PacketConn.LocalAddr()
|
||||||
|
net, addr = a.Network(), a.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
fin := make(chan bool)
|
||||||
|
switch net {
|
||||||
|
case "tcp", "tcp4", "tcp6":
|
||||||
|
go func() {
|
||||||
|
srv.stopTCP <- true
|
||||||
|
srv.wgTCP.Wait()
|
||||||
|
fin <- true
|
||||||
|
}()
|
||||||
|
|
||||||
|
case "udp", "udp4", "udp6":
|
||||||
|
go func() {
|
||||||
|
srv.stopUDP <- true
|
||||||
|
srv.wgUDP.Wait()
|
||||||
|
fin <- true
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
c := &Client{Net: net}
|
||||||
|
go c.Exchange(new(Msg), addr) // extra query to help ReadXXX loop to pass
|
||||||
|
|
||||||
|
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 seperate goroutine.
|
||||||
|
func (srv *Server) serveTCP(l *net.TCPListener) error {
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
if srv.NotifyStartedFunc != nil {
|
||||||
|
srv.NotifyStartedFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
handler := srv.Handler
|
||||||
|
if handler == nil {
|
||||||
|
handler = DefaultServeMux
|
||||||
|
}
|
||||||
|
rtimeout := srv.getReadTimeout()
|
||||||
|
// deadline is not used here
|
||||||
|
for {
|
||||||
|
rw, e := l.AcceptTCP()
|
||||||
|
if e != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
m, e := srv.readTCP(rw, rtimeout)
|
||||||
|
select {
|
||||||
|
case <-srv.stopTCP:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
if e != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
srv.wgTCP.Add(1)
|
||||||
|
go srv.serve(rw.RemoteAddr(), handler, m, nil, nil, rw)
|
||||||
|
}
|
||||||
|
panic("dns: not reached")
|
||||||
|
}
|
||||||
|
|
||||||
|
// serveUDP starts a UDP listener for the server.
|
||||||
|
// Each request is handled in a seperate goroutine.
|
||||||
|
func (srv *Server) serveUDP(l *net.UDPConn) error {
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
if srv.NotifyStartedFunc != nil {
|
||||||
|
srv.NotifyStartedFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
handler := srv.Handler
|
||||||
|
if handler == nil {
|
||||||
|
handler = DefaultServeMux
|
||||||
|
}
|
||||||
|
rtimeout := srv.getReadTimeout()
|
||||||
|
// deadline is not used here
|
||||||
|
for {
|
||||||
|
m, s, e := srv.readUDP(l, rtimeout)
|
||||||
|
select {
|
||||||
|
case <-srv.stopUDP:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
if e != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
srv.wgUDP.Add(1)
|
||||||
|
go srv.serve(s.RemoteAddr(), handler, m, l, s, nil)
|
||||||
|
}
|
||||||
|
panic("dns: not reached")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve a new connection.
|
||||||
|
func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, s *sessionUDP, t *net.TCPConn) {
|
||||||
|
w := &response{tsigSecret: srv.TsigSecret, udp: u, tcp: t, remoteAddr: a, udpSession: s}
|
||||||
|
q := 0
|
||||||
|
defer func() {
|
||||||
|
if u != nil {
|
||||||
|
srv.wgUDP.Done()
|
||||||
|
}
|
||||||
|
if t != nil {
|
||||||
|
srv.wgTCP.Done()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
Redo:
|
||||||
|
// Ideally we want use isMsg here before we allocate memory to actually parse the packet.
|
||||||
|
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.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, e := srv.readTCP(w.tcp, idleTimeout)
|
||||||
|
if e == nil {
|
||||||
|
q++
|
||||||
|
// TODO(miek): make this number configurable?
|
||||||
|
if q > 128 { // close socket after this many queries
|
||||||
|
w.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
goto Redo
|
||||||
|
}
|
||||||
|
w.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *Server) readTCP(conn *net.TCPConn, 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, _ := unpackUint16(l, 0)
|
||||||
|
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, e := readFromSessionUDP(conn, m)
|
||||||
|
if e != nil || n == 0 {
|
||||||
|
if e != nil {
|
||||||
|
return nil, nil, e
|
||||||
|
}
|
||||||
|
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.Write(data)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data, err = m.Pack()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = w.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)
|
||||||
|
l[0], l[1] = packUint16(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
|
||||||
|
}
|
||||||
401
Godeps/_workspace/src/github.com/miekg/dns/server_test.go
generated
vendored
Normal file
401
Godeps/_workspace/src/github.com/miekg/dns/server_test.go
generated
vendored
Normal file
@@ -0,0 +1,401 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
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 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) {
|
||||||
|
pc, err := net.ListenPacket("udp", laddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
server := &Server{PacketConn: pc}
|
||||||
|
|
||||||
|
waitLock := sync.Mutex{}
|
||||||
|
waitLock.Lock()
|
||||||
|
server.NotifyStartedFunc = waitLock.Unlock
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
server.ActivateAndServe()
|
||||||
|
pc.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
waitLock.Lock()
|
||||||
|
return server, pc.LocalAddr().String(), 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}
|
||||||
|
|
||||||
|
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}
|
||||||
|
|
||||||
|
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: %s", 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.Log("failed to exchange miek.nl", err)
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
txt := r.Extra[0].(*TXT).Txt[0]
|
||||||
|
if txt != "Hello world" {
|
||||||
|
t.Log("Unexpected result for miek.nl", txt, "!= Hello world")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
m.SetQuestion("example.com.", TypeTXT)
|
||||||
|
r, _, err = c.Exchange(m, addrstr)
|
||||||
|
if err != nil {
|
||||||
|
t.Log("failed to exchange example.com", err)
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
txt = r.Extra[0].(*TXT).Txt[0]
|
||||||
|
if txt != "Hello example" {
|
||||||
|
t.Log("Unexpected result for example.com", txt, "!= Hello example")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Mixes cased as noticed by Ask.
|
||||||
|
m.SetQuestion("eXaMplE.cOm.", TypeTXT)
|
||||||
|
r, _, err = c.Exchange(m, addrstr)
|
||||||
|
if err != nil {
|
||||||
|
t.Log("failed to exchange eXaMplE.cOm", err)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
txt = r.Extra[0].(*TXT).Txt[0]
|
||||||
|
if txt != "Hello example" {
|
||||||
|
t.Log("Unexpected result for example.com", txt, "!= Hello example")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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: %s", 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: %s", 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: %s", 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: %s", 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.Logf("failed to exchange: %s", err.Error())
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
// This must fail
|
||||||
|
M.Lock()
|
||||||
|
M.max = 20
|
||||||
|
M.Unlock()
|
||||||
|
_, _, err = c.Exchange(m, addrstr)
|
||||||
|
if err == nil {
|
||||||
|
t.Logf("failed to fail exchange, this should generate packet error")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
// But this must work again
|
||||||
|
c.UDPSize = 7000
|
||||||
|
_, _, err = c.Exchange(m, addrstr)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("failed to exchange: %s", err.Error())
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := new(Client)
|
||||||
|
m := new(Msg)
|
||||||
|
m.SetQuestion("miek.nl.", TypeTXT)
|
||||||
|
m.Response = false
|
||||||
|
_, _, err = c.Exchange(m, addrstr)
|
||||||
|
if err != nil {
|
||||||
|
t.Log("failed to exchange", err)
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
m.Response = true
|
||||||
|
_, _, err = c.Exchange(m, addrstr)
|
||||||
|
if err == nil {
|
||||||
|
t.Log("exchanged response message")
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Shutdown()
|
||||||
|
s, addrstr, err = RunLocalUDPServerUnsafe("127.0.0.1:0")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to run test server: %s", err)
|
||||||
|
}
|
||||||
|
defer s.Shutdown()
|
||||||
|
|
||||||
|
m.Response = true
|
||||||
|
_, _, err = c.Exchange(m, addrstr)
|
||||||
|
if err != nil {
|
||||||
|
t.Log("could exchanged response message in Unsafe mode")
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShutdownTCP(t *testing.T) {
|
||||||
|
s, _, err := RunLocalTCPServer("127.0.0.1:0")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to run test server: %s", err)
|
||||||
|
}
|
||||||
|
err = s.Shutdown()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Could not shutdown test TCP server, %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShutdownUDP(t *testing.T) {
|
||||||
|
s, _, err := RunLocalUDPServer("127.0.0.1:0")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to run test server: %s", err)
|
||||||
|
}
|
||||||
|
err = s.Shutdown()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Could not shutdown test UDP server, %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
262
Godeps/_workspace/src/github.com/miekg/dns/sig0.go
generated
vendored
Normal file
262
Godeps/_workspace/src/github.com/miekg/dns/sig0.go
generated
vendored
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/dsa"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"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 PrivateKey, 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)]
|
||||||
|
var hash crypto.Hash
|
||||||
|
var intlen int
|
||||||
|
switch rr.Algorithm {
|
||||||
|
case DSA, RSASHA1:
|
||||||
|
hash = crypto.SHA1
|
||||||
|
case RSASHA256, ECDSAP256SHA256:
|
||||||
|
hash = crypto.SHA256
|
||||||
|
intlen = 32
|
||||||
|
case ECDSAP384SHA384:
|
||||||
|
hash = crypto.SHA384
|
||||||
|
intlen = 48
|
||||||
|
case RSASHA512:
|
||||||
|
hash = crypto.SHA512
|
||||||
|
default:
|
||||||
|
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)])
|
||||||
|
hashed := hasher.Sum(nil)
|
||||||
|
|
||||||
|
var sig []byte
|
||||||
|
switch p := k.(type) {
|
||||||
|
case *dsa.PrivateKey:
|
||||||
|
t := divRoundUp(divRoundUp(p.PublicKey.Y.BitLen(), 8)-64, 8)
|
||||||
|
r1, s1, err := dsa.Sign(rand.Reader, p, hashed)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sig = append(sig, byte(t))
|
||||||
|
sig = append(sig, intToBytes(r1, 20)...)
|
||||||
|
sig = append(sig, intToBytes(s1, 20)...)
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
sig, err = rsa.SignPKCS1v15(rand.Reader, p, hash, hashed)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
r1, s1, err := ecdsa.Sign(rand.Reader, p, hashed)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sig = intToBytes(r1, intlen)
|
||||||
|
sig = append(sig, intToBytes(s1, intlen)...)
|
||||||
|
default:
|
||||||
|
return nil, ErrAlg
|
||||||
|
}
|
||||||
|
rr.Signature = toBase64(sig)
|
||||||
|
buf = append(buf, sig...)
|
||||||
|
if len(buf) > int(^uint16(0)) {
|
||||||
|
return nil, ErrBuf
|
||||||
|
}
|
||||||
|
// Adjust sig data length
|
||||||
|
rdoff := len(mbuf) + 1 + 2 + 2 + 4
|
||||||
|
rdlen, _ := unpackUint16(buf, rdoff)
|
||||||
|
rdlen += uint16(len(sig))
|
||||||
|
buf[rdoff], buf[rdoff+1] = packUint16(rdlen)
|
||||||
|
// Adjust additional count
|
||||||
|
adc, _ := unpackUint16(buf, 10)
|
||||||
|
adc += 1
|
||||||
|
buf[10], buf[11] = packUint16(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, _ := unpackUint16(buf, 4)
|
||||||
|
anc, _ := unpackUint16(buf, 6)
|
||||||
|
auc, _ := unpackUint16(buf, 8)
|
||||||
|
adc, offset := unpackUint16(buf, 10)
|
||||||
|
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, offset = unpackUint16(buf, offset)
|
||||||
|
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 := uint32(buf[offset])<<24 | uint32(buf[offset+1])<<16 | uint32(buf[offset+2])<<8 | uint32(buf[offset+3])
|
||||||
|
offset += 4
|
||||||
|
incept := uint32(buf[offset])<<24 | uint32(buf[offset+1])<<16 | uint32(buf[offset+2])<<8 | uint32(buf[offset+3])
|
||||||
|
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.publicKeyCurve()
|
||||||
|
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
|
||||||
|
}
|
||||||
96
Godeps/_workspace/src/github.com/miekg/dns/sig0_test.go
generated
vendored
Normal file
96
Godeps/_workspace/src/github.com/miekg/dns/sig0_test.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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{DSA, 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.Logf("Failed to generate key for “%s”: %v", algstr, err)
|
||||||
|
t.Fail()
|
||||||
|
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, m)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Failed to sign message using “%s”: %v", algstr, err)
|
||||||
|
t.Fail()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
m := new(Msg)
|
||||||
|
if err := m.Unpack(mb); err != nil {
|
||||||
|
t.Logf("Failed to unpack message signed using “%s”: %v", algstr, err)
|
||||||
|
t.Fail()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(m.Extra) != 1 {
|
||||||
|
t.Logf("Missing SIG for message signed using “%s”", algstr)
|
||||||
|
t.Fail()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var sigrrwire *SIG
|
||||||
|
switch rr := m.Extra[0].(type) {
|
||||||
|
case *SIG:
|
||||||
|
sigrrwire = rr
|
||||||
|
default:
|
||||||
|
t.Logf("Expected SIG RR, instead: %v", rr)
|
||||||
|
t.Fail()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, rr := range []*SIG{sigrr, sigrrwire} {
|
||||||
|
id := "sigrr"
|
||||||
|
if rr == sigrrwire {
|
||||||
|
id = "sigrrwire"
|
||||||
|
}
|
||||||
|
if err := rr.Verify(keyrr, mb); err != nil {
|
||||||
|
t.Logf("Failed to verify “%s” signed SIG(%s): %v", algstr, id, err)
|
||||||
|
t.Fail()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mb[13]++
|
||||||
|
if err := sigrr.Verify(keyrr, mb); err == nil {
|
||||||
|
t.Logf("Verify succeeded on an altered message using “%s”", algstr)
|
||||||
|
t.Fail()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sigrr.Expiration = 2
|
||||||
|
sigrr.Inception = 1
|
||||||
|
mb, _ = sigrr.Sign(pk, m)
|
||||||
|
if err := sigrr.Verify(keyrr, mb); err == nil {
|
||||||
|
t.Logf("Verify succeeded on an expired message using “%s”", algstr)
|
||||||
|
t.Fail()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
57
Godeps/_workspace/src/github.com/miekg/dns/singleinflight.go
generated
vendored
Normal file
57
Godeps/_workspace/src/github.com/miekg/dns/singleinflight.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// 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
|
||||||
|
}
|
||||||
84
Godeps/_workspace/src/github.com/miekg/dns/tlsa.go
generated
vendored
Normal file
84
Godeps/_workspace/src/github.com/miekg/dns/tlsa.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/sha512"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CertificateToDANE converts a certificate to a hex string as used in the TLSA record.
|
||||||
|
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:
|
||||||
|
return hex.EncodeToString(cert.Raw), nil
|
||||||
|
case 1:
|
||||||
|
io.WriteString(h, string(cert.RawSubjectPublicKeyInfo))
|
||||||
|
return hex.EncodeToString(h.Sum(nil)), nil
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
h := sha512.New()
|
||||||
|
switch selector {
|
||||||
|
case 0:
|
||||||
|
return hex.EncodeToString(cert.Raw), nil
|
||||||
|
case 1:
|
||||||
|
io.WriteString(h, string(cert.RawSubjectPublicKeyInfo))
|
||||||
|
return hex.EncodeToString(h.Sum(nil)), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", errors.New("dns: bad TLSA MatchingType or TLSA Selector")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, e := net.LookupPort(network, service)
|
||||||
|
if e != nil {
|
||||||
|
return "", e
|
||||||
|
}
|
||||||
|
return "_" + strconv.Itoa(p) + "_" + network + "." + name, nil
|
||||||
|
}
|
||||||
378
Godeps/_workspace/src/github.com/miekg/dns/tsig.go
generated
vendored
Normal file
378
Godeps/_workspace/src/github.com/miekg/dns/tsig.go
generated
vendored
Normal file
@@ -0,0 +1,378 @@
|
|||||||
|
// TRANSACTION SIGNATURE
|
||||||
|
//
|
||||||
|
// An TSIG or transaction signature adds a HMAC TSIG record to each message sent.
|
||||||
|
// The supported algorithms include: HmacMD5, HmacSHA1 and HmacSHA256.
|
||||||
|
//
|
||||||
|
// 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 { /* r.RR */ }
|
||||||
|
//
|
||||||
|
// 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(Msg)
|
||||||
|
// m.SetReply(r)
|
||||||
|
// if r.IsTsig() {
|
||||||
|
// 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)
|
||||||
|
// }
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/sha1"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"hash"
|
||||||
|
"io"
|
||||||
|
"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."
|
||||||
|
)
|
||||||
|
|
||||||
|
type TSIG struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
Algorithm string `dns:"domain-name"`
|
||||||
|
TimeSigned uint64 `dns:"uint48"`
|
||||||
|
Fudge uint16
|
||||||
|
MACSize uint16
|
||||||
|
MAC string `dns:"size-hex"`
|
||||||
|
OrigId uint16
|
||||||
|
Error uint16
|
||||||
|
OtherLen uint16
|
||||||
|
OtherData string `dns:"size-hex"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *TSIG) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *TSIG) len() int {
|
||||||
|
return rr.Hdr.len() + len(rr.Algorithm) + 1 + 6 +
|
||||||
|
4 + len(rr.MAC)/2 + 1 + 6 + len(rr.OtherData)/2 + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
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}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 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))
|
||||||
|
default:
|
||||||
|
return nil, "", ErrKeyAlg
|
||||||
|
}
|
||||||
|
io.WriteString(h, string(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...)
|
||||||
|
rawSetExtraLen(mbuf, 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 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)
|
||||||
|
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, _ := PackStruct(m, buf, 0)
|
||||||
|
buf = buf[:n]
|
||||||
|
}
|
||||||
|
|
||||||
|
tsigvar := make([]byte, DefaultMsgSize)
|
||||||
|
if timersOnly {
|
||||||
|
tsig := new(timerWireFmt)
|
||||||
|
tsig.TimeSigned = rr.TimeSigned
|
||||||
|
tsig.Fudge = rr.Fudge
|
||||||
|
n, _ := PackStruct(tsig, tsigvar, 0)
|
||||||
|
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, _ := PackStruct(tsig, tsigvar, 0)
|
||||||
|
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.
|
||||||
|
var dh Header
|
||||||
|
var err error
|
||||||
|
dns := new(Msg)
|
||||||
|
rr := new(TSIG)
|
||||||
|
off := 0
|
||||||
|
tsigoff := 0
|
||||||
|
if off, err = UnpackStruct(&dh, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arrays.
|
||||||
|
dns.Question = make([]Question, dh.Qdcount)
|
||||||
|
dns.Answer = make([]RR, dh.Ancount)
|
||||||
|
dns.Ns = make([]RR, dh.Nscount)
|
||||||
|
dns.Extra = make([]RR, dh.Arcount)
|
||||||
|
|
||||||
|
for i := 0; i < len(dns.Question); i++ {
|
||||||
|
off, err = UnpackStruct(&dns.Question[i], msg, off)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := 0; i < len(dns.Answer); i++ {
|
||||||
|
dns.Answer[i], off, err = UnpackRR(msg, off)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := 0; i < len(dns.Ns); i++ {
|
||||||
|
dns.Ns[i], off, err = UnpackRR(msg, off)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := 0; i < len(dns.Extra); i++ {
|
||||||
|
tsigoff = off
|
||||||
|
dns.Extra[i], off, err = UnpackRR(msg, off)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if dns.Extra[i].Header().Rrtype == TypeTSIG {
|
||||||
|
rr = dns.Extra[i].(*TSIG)
|
||||||
|
// Adjust Arcount.
|
||||||
|
arcount, _ := unpackUint16(msg, 10)
|
||||||
|
msg[10], msg[11] = packUint16(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")
|
||||||
|
}
|
||||||
1697
Godeps/_workspace/src/github.com/miekg/dns/types.go
generated
vendored
Normal file
1697
Godeps/_workspace/src/github.com/miekg/dns/types.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
42
Godeps/_workspace/src/github.com/miekg/dns/types_test.go
generated
vendored
Normal file
42
Godeps/_workspace/src/github.com/miekg/dns/types_test.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
55
Godeps/_workspace/src/github.com/miekg/dns/udp.go
generated
vendored
Normal file
55
Godeps/_workspace/src/github.com/miekg/dns/udp.go
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sessionUDP struct {
|
||||||
|
raddr *net.UDPAddr
|
||||||
|
context []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sessionUDP) RemoteAddr() net.Addr { return s.raddr }
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
63
Godeps/_workspace/src/github.com/miekg/dns/udp_linux.go
generated
vendored
Normal file
63
Godeps/_workspace/src/github.com/miekg/dns/udp_linux.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return v6only == 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUDPSocketName(conn *net.UDPConn) (syscall.Sockaddr, error) {
|
||||||
|
file, err := conn.File()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return syscall.Getsockname(int(file.Fd()))
|
||||||
|
}
|
||||||
17
Godeps/_workspace/src/github.com/miekg/dns/udp_other.go
generated
vendored
Normal file
17
Godeps/_workspace/src/github.com/miekg/dns/udp_other.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// +build !linux
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 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 }
|
||||||
|
func getUDPSocketName(conn *net.UDPConn) (syscall.Sockaddr, error) { return nil, nil }
|
||||||
34
Godeps/_workspace/src/github.com/miekg/dns/udp_windows.go
generated
vendored
Normal file
34
Godeps/_workspace/src/github.com/miekg/dns/udp_windows.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
type sessionUDP struct {
|
||||||
|
raddr *net.UDPAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sessionUDP) RemoteAddr() net.Addr { return s.raddr }
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
138
Godeps/_workspace/src/github.com/miekg/dns/update.go
generated
vendored
Normal file
138
Godeps/_workspace/src/github.com/miekg/dns/update.go
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
// 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
|
||||||
|
//
|
||||||
|
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) {
|
||||||
|
u.Answer = make([]RR, len(rr))
|
||||||
|
for i, r := range rr {
|
||||||
|
u.Answer[i] = &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) {
|
||||||
|
u.Answer = make([]RR, len(rr))
|
||||||
|
for i, r := range rr {
|
||||||
|
u.Answer[i] = &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")
|
||||||
|
}
|
||||||
|
u.Answer = make([]RR, len(rr))
|
||||||
|
for i, r := range rr {
|
||||||
|
u.Answer[i] = r
|
||||||
|
u.Answer[i].Header().Class = u.Question[0].Qclass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
u.Answer = make([]RR, len(rr))
|
||||||
|
for i, r := range rr {
|
||||||
|
u.Answer[i] = r
|
||||||
|
u.Answer[i].Header().Class = ClassANY
|
||||||
|
u.Answer[i].Header().Ttl = 0
|
||||||
|
u.Answer[i].Header().Rdlength = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
u.Answer = make([]RR, len(rr))
|
||||||
|
for i, r := range rr {
|
||||||
|
u.Answer[i] = r
|
||||||
|
u.Answer[i].Header().Class = ClassNONE
|
||||||
|
u.Answer[i].Header().Rdlength = 0
|
||||||
|
u.Answer[i].Header().Ttl = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
|
u.Ns = make([]RR, len(rr))
|
||||||
|
for i, r := range rr {
|
||||||
|
u.Ns[i] = r
|
||||||
|
u.Ns[i].Header().Class = u.Question[0].Qclass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveRRset creates a dynamic update packet that deletes an RRset, see RFC 2136 section 2.5.2.
|
||||||
|
func (u *Msg) RemoveRRset(rr []RR) {
|
||||||
|
m := make(map[RR_Header]struct{})
|
||||||
|
u.Ns = make([]RR, 0, len(rr))
|
||||||
|
for _, r := range rr {
|
||||||
|
h := *r.Header().copyHeader()
|
||||||
|
h.Class = ClassANY
|
||||||
|
h.Ttl = 0
|
||||||
|
h.Rdlength = 0
|
||||||
|
if _, ok := m[h]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
m[h] = struct{}{}
|
||||||
|
u.Ns = append(u.Ns, &ANY{h})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
u.Ns = make([]RR, len(rr))
|
||||||
|
for i, r := range rr {
|
||||||
|
u.Ns[i] = &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove creates a dynamic update packet deletes RR from the RRSset, see RFC 2136 section 2.5.4
|
||||||
|
func (u *Msg) Remove(rr []RR) {
|
||||||
|
u.Ns = make([]RR, len(rr))
|
||||||
|
for i, r := range rr {
|
||||||
|
u.Ns[i] = r
|
||||||
|
u.Ns[i].Header().Class = ClassNONE
|
||||||
|
u.Ns[i].Header().Ttl = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
105
Godeps/_workspace/src/github.com/miekg/dns/update_test.go
generated
vendored
Normal file
105
Godeps/_workspace/src/github.com/miekg/dns/update_test.go
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDynamicUpdateParsing(t *testing.T) {
|
||||||
|
prefix := "example.com. IN "
|
||||||
|
for _, typ := range TypeToString {
|
||||||
|
if typ == "CAA" || typ == "OPT" || typ == "AXFR" || typ == "IXFR" || typ == "ANY" || typ == "TKEY" ||
|
||||||
|
typ == "TSIG" || typ == "ISDN" || typ == "UNSPEC" || typ == "NULL" || typ == "ATMA" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r, e := NewRR(prefix + typ)
|
||||||
|
if e != nil {
|
||||||
|
t.Log("failure to parse: " + prefix + typ)
|
||||||
|
t.Fail()
|
||||||
|
} 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.Log("failed to unpack: " + err.Error() + "\n" + msg.String())
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Logf("failed to pack %s: %v", s, err)
|
||||||
|
t.Fail()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := new(Msg).Unpack(bytes); err != nil {
|
||||||
|
t.Logf("failed to unpack %s: %v", s, err)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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", err)
|
||||||
|
}
|
||||||
|
t.Logf("Expected msg:\n%s", expectstr)
|
||||||
|
t.Logf("Actual msg:\n%v", tmp)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Ns = nil
|
||||||
|
m.RemoveRRset([]RR{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", err)
|
||||||
|
}
|
||||||
|
t.Logf("Expected msg:\n%v", expectstr)
|
||||||
|
t.Logf("Actual msg:\n%v", tmp)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
236
Godeps/_workspace/src/github.com/miekg/dns/xfr.go
generated
vendored
Normal file
236
Godeps/_workspace/src/github.com/miekg/dns/xfr.go
generated
vendored
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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 (ns), defaults to 2 * 1e9
|
||||||
|
ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections (ns), defaults to 2 * 1e9
|
||||||
|
WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections (ns), defaults to 2 * 1e9
|
||||||
|
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.
|
||||||
|
func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) {
|
||||||
|
timeout := dnsTimeout
|
||||||
|
if t.DialTimeout != 0 {
|
||||||
|
timeout = t.DialTimeout
|
||||||
|
}
|
||||||
|
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 !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}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("dns: not reached")
|
||||||
|
}
|
||||||
|
|
||||||
|
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{in.Answer, err}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if id != in.Id {
|
||||||
|
c <- &Envelope{in.Answer, ErrId}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if first {
|
||||||
|
// 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)
|
||||||
|
// tr.Out(w, r, ch)
|
||||||
|
// c <- &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 {
|
||||||
|
r := new(Msg)
|
||||||
|
// Compress?
|
||||||
|
r.SetReply(q)
|
||||||
|
r.Authoritative = true
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for x := range ch {
|
||||||
|
// assume it fits TODO(miek): fix
|
||||||
|
r.Answer = append(r.Answer, x.RR...)
|
||||||
|
if err := w.WriteMsg(r); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.TsigTimersOnly(true)
|
||||||
|
r.Answer = nil
|
||||||
|
}()
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
157
Godeps/_workspace/src/github.com/miekg/dns/zgenerate.go
generated
vendored
Normal file
157
Godeps/_workspace/src/github.com/miekg/dns/zgenerate.go
generated
vendored
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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* occurences
|
||||||
|
// 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, e := strconv.Atoi(l.token[i+1:]); e != nil {
|
||||||
|
return "bad step in $GENERATE range"
|
||||||
|
} else {
|
||||||
|
if s < 0 {
|
||||||
|
return "bad step in $GENERATE range"
|
||||||
|
}
|
||||||
|
step = s
|
||||||
|
}
|
||||||
|
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 != _NEWLINE && l.value != _EOF {
|
||||||
|
s += l.token
|
||||||
|
goto BuildRR
|
||||||
|
}
|
||||||
|
for i := start; i <= end; i += step {
|
||||||
|
var (
|
||||||
|
escape bool
|
||||||
|
dom string
|
||||||
|
mod string
|
||||||
|
err string
|
||||||
|
offset int
|
||||||
|
)
|
||||||
|
|
||||||
|
for j := 0; j < len(s); j++ { // No 'range' because we need to jump around
|
||||||
|
switch s[j] {
|
||||||
|
case '\\':
|
||||||
|
if escape {
|
||||||
|
dom += "\\"
|
||||||
|
escape = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
escape = true
|
||||||
|
case '$':
|
||||||
|
mod = "%d"
|
||||||
|
offset = 0
|
||||||
|
if escape {
|
||||||
|
dom += "$"
|
||||||
|
escape = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
escape = false
|
||||||
|
if j+1 >= len(s) { // End of the string
|
||||||
|
dom += fmt.Sprintf(mod, i+offset)
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
if s[j+1] == '$' {
|
||||||
|
dom += "$"
|
||||||
|
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 != "" {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
j += 2 + sep // Jump to it
|
||||||
|
}
|
||||||
|
dom += fmt.Sprintf(mod, i+offset)
|
||||||
|
default:
|
||||||
|
if escape { // Pretty useless here
|
||||||
|
escape = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dom += string(s[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Re-parse the RR and send it on the current channel t
|
||||||
|
rx, e := NewRR("$ORIGIN " + o + "\n" + dom)
|
||||||
|
if e != nil {
|
||||||
|
return e.(*ParseError).err
|
||||||
|
}
|
||||||
|
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, string) {
|
||||||
|
xs := strings.SplitN(s, ",", 3)
|
||||||
|
if len(xs) != 3 {
|
||||||
|
return "", 0, "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, "bad base in $GENERATE"
|
||||||
|
}
|
||||||
|
offset, err := strconv.Atoi(xs[0])
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, "bad offset in $GENERATE"
|
||||||
|
}
|
||||||
|
width, err := strconv.Atoi(xs[1])
|
||||||
|
if err != nil {
|
||||||
|
return "", offset, "bad width in $GENERATE"
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case width < 0:
|
||||||
|
return "", offset, "bad width in $GENERATE"
|
||||||
|
case width == 0:
|
||||||
|
return "%" + xs[1] + xs[2], offset, ""
|
||||||
|
}
|
||||||
|
return "%0" + xs[1] + xs[2], offset, ""
|
||||||
|
}
|
||||||
956
Godeps/_workspace/src/github.com/miekg/dns/zscan.go
generated
vendored
Normal file
956
Godeps/_workspace/src/github.com/miekg/dns/zscan.go
generated
vendored
Normal file
@@ -0,0 +1,956 @@
|
|||||||
|
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
|
||||||
|
_EOF = iota
|
||||||
|
_STRING
|
||||||
|
_BLANK
|
||||||
|
_QUOTE
|
||||||
|
_NEWLINE
|
||||||
|
_RRTYPE
|
||||||
|
_OWNER
|
||||||
|
_CLASS
|
||||||
|
_DIRORIGIN // $ORIGIN
|
||||||
|
_DIRTTL // $TTL
|
||||||
|
_DIRINCLUDE // $INCLUDE
|
||||||
|
_DIRGENERATE // $GENERATE
|
||||||
|
|
||||||
|
// Privatekey file
|
||||||
|
_VALUE
|
||||||
|
_KEY
|
||||||
|
|
||||||
|
_EXPECT_OWNER_DIR // Ownername
|
||||||
|
_EXPECT_OWNER_BL // Whitespace after the ownername
|
||||||
|
_EXPECT_ANY // Expect rrtype, ttl or class
|
||||||
|
_EXPECT_ANY_NOCLASS // Expect rrtype or ttl
|
||||||
|
_EXPECT_ANY_NOCLASS_BL // The whitespace after _EXPECT_ANY_NOCLASS
|
||||||
|
_EXPECT_ANY_NOTTL // Expect rrtype or class
|
||||||
|
_EXPECT_ANY_NOTTL_BL // Whitespace after _EXPECT_ANY_NOTTL
|
||||||
|
_EXPECT_RRTYPE // Expect rrtype
|
||||||
|
_EXPECT_RRTYPE_BL // Whitespace BEFORE rrtype
|
||||||
|
_EXPECT_RDATA // The first element of the rdata
|
||||||
|
_EXPECT_DIRTTL_BL // Space after directive $TTL
|
||||||
|
_EXPECT_DIRTTL // Directive $TTL
|
||||||
|
_EXPECT_DIRORIGIN_BL // Space after directive $ORIGIN
|
||||||
|
_EXPECT_DIRORIGIN // Directive $ORIGIN
|
||||||
|
_EXPECT_DIRINCLUDE_BL // Space after directive $INCLUDE
|
||||||
|
_EXPECT_DIRINCLUDE // Directive $INCLUDE
|
||||||
|
_EXPECT_DIRGENERATE // Directive $GENERATE
|
||||||
|
_EXPECT_DIRGENERATE_BL // Space after directive $GENERATE
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseError is a parsing error. It contains the parse error and the location in the io.Reader
|
||||||
|
// where the error occured.
|
||||||
|
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 // lenght of the token
|
||||||
|
err bool // when true, token text has lexer error
|
||||||
|
value uint8 // value: _STRING, _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
|
||||||
|
}
|
||||||
|
|
||||||
|
// *Tokens are returned when a zone file is parsed.
|
||||||
|
type Token struct {
|
||||||
|
RR // the scanned resource record when error is not nil
|
||||||
|
Error *ParseError // when an error occured, this has the error specifics
|
||||||
|
Comment string // a potential comment positioned after the RR and on the same line
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRR reads the RR contained in the string s. Only the first RR is returned.
|
||||||
|
// 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 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.Error != nil {
|
||||||
|
return nil, r.Error
|
||||||
|
}
|
||||||
|
return r.RR, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseZone reads a RFC 1035 style one 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 {
|
||||||
|
// // 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, 1000)
|
||||||
|
// Start the lexer
|
||||||
|
go zlexer(s, c)
|
||||||
|
// 6 possible beginnings of a line, _ is a space
|
||||||
|
// 0. _RRTYPE -> all omitted until the rrtype
|
||||||
|
// 1. _OWNER _ _RRTYPE -> class/ttl omitted
|
||||||
|
// 2. _OWNER _ _STRING _ _RRTYPE -> class omitted
|
||||||
|
// 3. _OWNER _ _STRING _ _CLASS _ _RRTYPE -> ttl/class
|
||||||
|
// 4. _OWNER _ _CLASS _ _RRTYPE -> ttl omitted
|
||||||
|
// 5. _OWNER _ _CLASS _ _STRING _ _RRTYPE -> class/ttl (reversed)
|
||||||
|
// After detecting these, we know the _RRTYPE 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 := _EXPECT_OWNER_DIR // 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 _EXPECT_OWNER_DIR:
|
||||||
|
// We can also expect a directive, like $TTL or $ORIGIN
|
||||||
|
h.Ttl = defttl
|
||||||
|
h.Class = ClassINET
|
||||||
|
switch l.value {
|
||||||
|
case _NEWLINE: // Empty line
|
||||||
|
st = _EXPECT_OWNER_DIR
|
||||||
|
case _OWNER:
|
||||||
|
h.Name = l.token
|
||||||
|
if l.token[0] == '@' {
|
||||||
|
h.Name = origin
|
||||||
|
prevName = h.Name
|
||||||
|
st = _EXPECT_OWNER_BL
|
||||||
|
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 = _EXPECT_OWNER_BL
|
||||||
|
case _DIRTTL:
|
||||||
|
st = _EXPECT_DIRTTL_BL
|
||||||
|
case _DIRORIGIN:
|
||||||
|
st = _EXPECT_DIRORIGIN_BL
|
||||||
|
case _DIRINCLUDE:
|
||||||
|
st = _EXPECT_DIRINCLUDE_BL
|
||||||
|
case _DIRGENERATE:
|
||||||
|
st = _EXPECT_DIRGENERATE_BL
|
||||||
|
case _RRTYPE: // Everthing has been omitted, this is the first thing on the line
|
||||||
|
h.Name = prevName
|
||||||
|
h.Rrtype = l.torc
|
||||||
|
st = _EXPECT_RDATA
|
||||||
|
case _CLASS: // First thing on the line is the class
|
||||||
|
h.Name = prevName
|
||||||
|
h.Class = l.torc
|
||||||
|
st = _EXPECT_ANY_NOCLASS_BL
|
||||||
|
case _BLANK:
|
||||||
|
// Discard, can happen when there is nothing on the
|
||||||
|
// line except the RR type
|
||||||
|
case _STRING: // First thing on the is the ttl
|
||||||
|
if ttl, ok := stringToTtl(l.token); !ok {
|
||||||
|
t <- &Token{Error: &ParseError{f, "not a TTL", l}}
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
h.Ttl = ttl
|
||||||
|
// Don't about the defttl, we should take the $TTL value
|
||||||
|
// defttl = ttl
|
||||||
|
}
|
||||||
|
st = _EXPECT_ANY_NOTTL_BL
|
||||||
|
|
||||||
|
default:
|
||||||
|
t <- &Token{Error: &ParseError{f, "syntax error at beginning", l}}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case _EXPECT_DIRINCLUDE_BL:
|
||||||
|
if l.value != _BLANK {
|
||||||
|
t <- &Token{Error: &ParseError{f, "no blank after $INCLUDE-directive", l}}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
st = _EXPECT_DIRINCLUDE
|
||||||
|
case _EXPECT_DIRINCLUDE:
|
||||||
|
if l.value != _STRING {
|
||||||
|
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 _BLANK:
|
||||||
|
l := <-c
|
||||||
|
if l.value == _STRING {
|
||||||
|
if _, ok := IsDomainName(l.token); !ok {
|
||||||
|
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 _NEWLINE, _EOF:
|
||||||
|
// 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 = _EXPECT_OWNER_DIR
|
||||||
|
case _EXPECT_DIRTTL_BL:
|
||||||
|
if l.value != _BLANK {
|
||||||
|
t <- &Token{Error: &ParseError{f, "no blank after $TTL-directive", l}}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
st = _EXPECT_DIRTTL
|
||||||
|
case _EXPECT_DIRTTL:
|
||||||
|
if l.value != _STRING {
|
||||||
|
t <- &Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if e, _ := slurpRemainder(c, f); e != nil {
|
||||||
|
t <- &Token{Error: e}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ttl, ok := stringToTtl(l.token); !ok {
|
||||||
|
t <- &Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}}
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
defttl = ttl
|
||||||
|
}
|
||||||
|
st = _EXPECT_OWNER_DIR
|
||||||
|
case _EXPECT_DIRORIGIN_BL:
|
||||||
|
if l.value != _BLANK {
|
||||||
|
t <- &Token{Error: &ParseError{f, "no blank after $ORIGIN-directive", l}}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
st = _EXPECT_DIRORIGIN
|
||||||
|
case _EXPECT_DIRORIGIN:
|
||||||
|
if l.value != _STRING {
|
||||||
|
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 = _EXPECT_OWNER_DIR
|
||||||
|
case _EXPECT_DIRGENERATE_BL:
|
||||||
|
if l.value != _BLANK {
|
||||||
|
t <- &Token{Error: &ParseError{f, "no blank after $GENERATE-directive", l}}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
st = _EXPECT_DIRGENERATE
|
||||||
|
case _EXPECT_DIRGENERATE:
|
||||||
|
if l.value != _STRING {
|
||||||
|
t <- &Token{Error: &ParseError{f, "expecting $GENERATE value, not this...", l}}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if e := generate(l, c, t, origin); e != "" {
|
||||||
|
t <- &Token{Error: &ParseError{f, e, l}}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
st = _EXPECT_OWNER_DIR
|
||||||
|
case _EXPECT_OWNER_BL:
|
||||||
|
if l.value != _BLANK {
|
||||||
|
t <- &Token{Error: &ParseError{f, "no blank after owner", l}}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
st = _EXPECT_ANY
|
||||||
|
case _EXPECT_ANY:
|
||||||
|
switch l.value {
|
||||||
|
case _RRTYPE:
|
||||||
|
h.Rrtype = l.torc
|
||||||
|
st = _EXPECT_RDATA
|
||||||
|
case _CLASS:
|
||||||
|
h.Class = l.torc
|
||||||
|
st = _EXPECT_ANY_NOCLASS_BL
|
||||||
|
case _STRING: // TTL is this case
|
||||||
|
if ttl, ok := stringToTtl(l.token); !ok {
|
||||||
|
t <- &Token{Error: &ParseError{f, "not a TTL", l}}
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
h.Ttl = ttl
|
||||||
|
// defttl = ttl // don't set the defttl here
|
||||||
|
}
|
||||||
|
st = _EXPECT_ANY_NOTTL_BL
|
||||||
|
default:
|
||||||
|
t <- &Token{Error: &ParseError{f, "expecting RR type, TTL or class, not this...", l}}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case _EXPECT_ANY_NOCLASS_BL:
|
||||||
|
if l.value != _BLANK {
|
||||||
|
t <- &Token{Error: &ParseError{f, "no blank before class", l}}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
st = _EXPECT_ANY_NOCLASS
|
||||||
|
case _EXPECT_ANY_NOTTL_BL:
|
||||||
|
if l.value != _BLANK {
|
||||||
|
t <- &Token{Error: &ParseError{f, "no blank before TTL", l}}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
st = _EXPECT_ANY_NOTTL
|
||||||
|
case _EXPECT_ANY_NOTTL:
|
||||||
|
switch l.value {
|
||||||
|
case _CLASS:
|
||||||
|
h.Class = l.torc
|
||||||
|
st = _EXPECT_RRTYPE_BL
|
||||||
|
case _RRTYPE:
|
||||||
|
h.Rrtype = l.torc
|
||||||
|
st = _EXPECT_RDATA
|
||||||
|
default:
|
||||||
|
t <- &Token{Error: &ParseError{f, "expecting RR type or class, not this...", l}}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case _EXPECT_ANY_NOCLASS:
|
||||||
|
switch l.value {
|
||||||
|
case _STRING: // TTL
|
||||||
|
if ttl, ok := stringToTtl(l.token); !ok {
|
||||||
|
t <- &Token{Error: &ParseError{f, "not a TTL", l}}
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
h.Ttl = ttl
|
||||||
|
// defttl = ttl // don't set the def ttl anymore
|
||||||
|
}
|
||||||
|
st = _EXPECT_RRTYPE_BL
|
||||||
|
case _RRTYPE:
|
||||||
|
h.Rrtype = l.torc
|
||||||
|
st = _EXPECT_RDATA
|
||||||
|
default:
|
||||||
|
t <- &Token{Error: &ParseError{f, "expecting RR type or TTL, not this...", l}}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case _EXPECT_RRTYPE_BL:
|
||||||
|
if l.value != _BLANK {
|
||||||
|
t <- &Token{Error: &ParseError{f, "no blank before RR type", l}}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
st = _EXPECT_RRTYPE
|
||||||
|
case _EXPECT_RRTYPE:
|
||||||
|
if l.value != _RRTYPE {
|
||||||
|
t <- &Token{Error: &ParseError{f, "unknown RR type", l}}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h.Rrtype = l.torc
|
||||||
|
st = _EXPECT_RDATA
|
||||||
|
case _EXPECT_RDATA:
|
||||||
|
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 = _EXPECT_OWNER_DIR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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 = _OWNER
|
||||||
|
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 = _DIRTTL
|
||||||
|
case "$ORIGIN":
|
||||||
|
l.value = _DIRORIGIN
|
||||||
|
case "$INCLUDE":
|
||||||
|
l.value = _DIRINCLUDE
|
||||||
|
case "$GENERATE":
|
||||||
|
l.value = _DIRGENERATE
|
||||||
|
}
|
||||||
|
debug.Printf("[7 %+v]", l.token)
|
||||||
|
c <- l
|
||||||
|
} else {
|
||||||
|
l.value = _STRING
|
||||||
|
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 = _RRTYPE
|
||||||
|
l.torc = t
|
||||||
|
rrtype = true
|
||||||
|
} else {
|
||||||
|
if strings.HasPrefix(l.tokenUpper, "TYPE") {
|
||||||
|
if t, ok := typeToInt(l.token); !ok {
|
||||||
|
l.token = "unknown RR type"
|
||||||
|
l.err = true
|
||||||
|
c <- l
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
l.value = _RRTYPE
|
||||||
|
l.torc = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t, ok := StringToClass[l.tokenUpper]; ok {
|
||||||
|
l.value = _CLASS
|
||||||
|
l.torc = t
|
||||||
|
} else {
|
||||||
|
if strings.HasPrefix(l.tokenUpper, "CLASS") {
|
||||||
|
if t, ok := classToInt(l.token); !ok {
|
||||||
|
l.token = "unknown class"
|
||||||
|
l.err = true
|
||||||
|
c <- l
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
l.value = _CLASS
|
||||||
|
l.torc = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug.Printf("[6 %+v]", l.token)
|
||||||
|
c <- l
|
||||||
|
}
|
||||||
|
stri = 0
|
||||||
|
// I reverse space stuff here
|
||||||
|
if !space && !commt {
|
||||||
|
l.value = _BLANK
|
||||||
|
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 = _STRING
|
||||||
|
l.token = string(str[:stri])
|
||||||
|
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 = _NEWLINE
|
||||||
|
l.token = "\n"
|
||||||
|
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 = _STRING
|
||||||
|
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 = _RRTYPE
|
||||||
|
l.torc = t
|
||||||
|
rrtype = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug.Printf("[2 %+v]", l.token)
|
||||||
|
c <- l
|
||||||
|
}
|
||||||
|
l.value = _NEWLINE
|
||||||
|
l.token = "\n"
|
||||||
|
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 = _STRING
|
||||||
|
l.token = string(str[:stri])
|
||||||
|
l.length = stri
|
||||||
|
|
||||||
|
debug.Printf("[%+v]", l.token)
|
||||||
|
c <- l
|
||||||
|
stri = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// send quote itself as separate token
|
||||||
|
l.value = _QUOTE
|
||||||
|
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.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.length = stri
|
||||||
|
l.value = _STRING
|
||||||
|
debug.Printf("[%+v]", l.token)
|
||||||
|
c <- l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the class number from CLASSxx
|
||||||
|
func classToInt(token string) (uint16, bool) {
|
||||||
|
class, ok := strconv.Atoi(token[5:])
|
||||||
|
if ok != nil || class > maxUint16 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return uint16(class), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the rr number from TYPExxx
|
||||||
|
func typeToInt(token string) (uint16, bool) {
|
||||||
|
typ, ok := strconv.Atoi(token[4:])
|
||||||
|
if ok != nil || typ > maxUint16 {
|
||||||
|
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 _BLANK:
|
||||||
|
l = <-c
|
||||||
|
com = l.comment
|
||||||
|
if l.value != _NEWLINE && l.value != _EOF {
|
||||||
|
return &ParseError{f, "garbage after rdata", l}, ""
|
||||||
|
}
|
||||||
|
case _NEWLINE:
|
||||||
|
com = l.comment
|
||||||
|
case _EOF:
|
||||||
|
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, e := strconv.ParseUint(s, 16, 64)
|
||||||
|
if e != nil {
|
||||||
|
return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
|
||||||
|
}
|
||||||
|
return u, nil
|
||||||
|
}
|
||||||
2155
Godeps/_workspace/src/github.com/miekg/dns/zscan_rr.go
generated
vendored
Normal file
2155
Godeps/_workspace/src/github.com/miekg/dns/zscan_rr.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
95
Godeps/_workspace/src/github.com/skynetservices/skydns/msg/service.go
generated
vendored
Normal file
95
Godeps/_workspace/src/github.com/skynetservices/skydns/msg/service.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by The MIT License (MIT) that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
package msg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This *is* the rdata from a SRV record, but with a twist.
|
||||||
|
// Host (Target in SRV) must be a domain name, but if it looks like an IP
|
||||||
|
// address (4/6), we will treat it like an IP address.
|
||||||
|
type Service struct {
|
||||||
|
Host string `json:"host,omitempty"`
|
||||||
|
Port int `json:"port,omitempty"`
|
||||||
|
Priority int `json:"priority,omitempty"`
|
||||||
|
Weight int `json:"weight,omitempty"`
|
||||||
|
Ttl uint32 `json:"ttl,omitempty"`
|
||||||
|
// etcd key where we found this service and ignore from json un-/marshalling
|
||||||
|
Key string `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSRV returns a new SRV record based on the Service.
|
||||||
|
func (s *Service) NewSRV(name string, weight uint16) *dns.SRV {
|
||||||
|
return &dns.SRV{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeSRV, Class: dns.ClassINET, Ttl: s.Ttl},
|
||||||
|
Priority: uint16(s.Priority), Weight: weight, Port: uint16(s.Port), Target: dns.Fqdn(s.Host)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewA returns a new A record based on the Service.
|
||||||
|
func (s *Service) NewA(name string, ip net.IP) *dns.A {
|
||||||
|
return &dns.A{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: s.Ttl}, A: ip}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAAAA returns a new AAAA record based on the Service.
|
||||||
|
func (s *Service) NewAAAA(name string, ip net.IP) *dns.AAAA {
|
||||||
|
return &dns.AAAA{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: s.Ttl}, AAAA: ip}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCNAME returns a new CNAME record based on the Service.
|
||||||
|
func (s *Service) NewCNAME(name string, target string) *dns.CNAME {
|
||||||
|
return &dns.CNAME{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: s.Ttl}, Target: target}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNS returns a new NS record based on the Service.
|
||||||
|
func (s *Service) NewNS(name string, target string) *dns.NS {
|
||||||
|
return &dns.NS{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: s.Ttl}, Ns: target}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPTR returns a new PTR record based on the Service.
|
||||||
|
func (s *Service) NewPTR(name string, ttl uint32) *dns.PTR {
|
||||||
|
return &dns.PTR{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: ttl}, Ptr: dns.Fqdn(s.Host)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// As Path, but
|
||||||
|
// if a name contains wildcards (*), the name will be chopped of before the (first) wildcard, and
|
||||||
|
// we do a highler evel search and later find the matching names.
|
||||||
|
// So service.*.skydns.local, will look for all services under skydns.local and will later check
|
||||||
|
// for names that match service.*.skydns.local. If a wildcard is found the returned bool is true.
|
||||||
|
func PathWithWildcard(s string) (string, bool) {
|
||||||
|
l := dns.SplitDomainName(s)
|
||||||
|
for i, j := 0, len(l)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
l[i], l[j] = l[j], l[i]
|
||||||
|
}
|
||||||
|
for i, k := range l {
|
||||||
|
if k == "*" {
|
||||||
|
return path.Join(append([]string{"/skydns/"}, l[:i]...)...), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path.Join(append([]string{"/skydns/"}, l...)...), false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path converts a domainname to an etcd path. If s looks like service.staging.skydns.local.,
|
||||||
|
// the resulting key will be /skydns/local/skydns/staging/service .
|
||||||
|
func Path(s string) string {
|
||||||
|
l := dns.SplitDomainName(s)
|
||||||
|
for i, j := 0, len(l)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
l[i], l[j] = l[j], l[i]
|
||||||
|
}
|
||||||
|
return path.Join(append([]string{"/skydns/"}, l...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Domain is the opposite of Path.
|
||||||
|
func Domain(s string) string {
|
||||||
|
l := strings.Split(s, "/")
|
||||||
|
// start with 1, to strip /skydns
|
||||||
|
for i, j := 1, len(l)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
l[i], l[j] = l[j], l[i]
|
||||||
|
}
|
||||||
|
return dns.Fqdn(strings.Join(l[1:len(l)-1], "."))
|
||||||
|
}
|
||||||
4
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/.gitignore
generated
vendored
4
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/.gitignore
generated
vendored
@@ -1,4 +0,0 @@
|
|||||||
[568].out
|
|
||||||
_go*
|
|
||||||
_test*
|
|
||||||
_obj
|
|
||||||
19
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/License
generated
vendored
19
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/License
generated
vendored
@@ -1,19 +0,0 @@
|
|||||||
Copyright 2012 Keith Rarick
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
9
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/Readme
generated
vendored
9
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/Readme
generated
vendored
@@ -1,9 +0,0 @@
|
|||||||
package pretty
|
|
||||||
|
|
||||||
import "github.com/kr/pretty"
|
|
||||||
|
|
||||||
Package pretty provides pretty-printing for Go values.
|
|
||||||
|
|
||||||
Documentation
|
|
||||||
|
|
||||||
http://godoc.org/github.com/kr/pretty
|
|
||||||
5
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/debian/changelog
generated
vendored
5
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/debian/changelog
generated
vendored
@@ -1,5 +0,0 @@
|
|||||||
golang-pretty (0.0~git20130613-1) unstable; urgency=low
|
|
||||||
|
|
||||||
* Initial release. Closes: #722983
|
|
||||||
|
|
||||||
-- Tonnerre Lombard <tonnerre@ancient-solutions.com> Wed, 11 Sep 2013 02:36:12 +0200
|
|
||||||
1
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/debian/compat
generated
vendored
1
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/debian/compat
generated
vendored
@@ -1 +0,0 @@
|
|||||||
9
|
|
||||||
22
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/debian/control
generated
vendored
22
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/debian/control
generated
vendored
@@ -1,22 +0,0 @@
|
|||||||
Source: golang-pretty
|
|
||||||
Section: devel
|
|
||||||
Priority: extra
|
|
||||||
Maintainer: Tonnerre Lombard <tonnerre@ancient-solutions.com>
|
|
||||||
Build-Depends: debhelper (>= 9), golang-go, dh-golang,
|
|
||||||
golang-text-dev
|
|
||||||
Standards-Version: 3.9.4
|
|
||||||
Homepage: https://github.com/kr/pretty/
|
|
||||||
Vcs-Git: git://anonscm.debian.org/pkg-go/packages/golang-pretty.git
|
|
||||||
Vcs-Browser: http://anonscm.debian.org/gitweb/?p=pkg-go/packages/golang-pretty.git;a=summary
|
|
||||||
|
|
||||||
Package: golang-pretty-dev
|
|
||||||
Architecture: all
|
|
||||||
Depends: ${shlibs:Depends}, ${misc:Depends}, golang-text-dev
|
|
||||||
Description: Pretty printing for go values
|
|
||||||
Package pretty provides pretty-printing for Go values. This is useful
|
|
||||||
during debugging, to avoid wrapping long output lines in the
|
|
||||||
terminal.
|
|
||||||
.
|
|
||||||
It provides a function, Formatter, that can be used with any function
|
|
||||||
that accepts a format string. It also provides convenience wrappers
|
|
||||||
for functions in packages fmt and log.
|
|
||||||
30
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/debian/copyright
generated
vendored
30
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/debian/copyright
generated
vendored
@@ -1,30 +0,0 @@
|
|||||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
|
||||||
Upstream-Name: golang-pretty
|
|
||||||
Source: https://github.com/kr/pretty/
|
|
||||||
|
|
||||||
Files: *
|
|
||||||
Copyright: 2011, 2012, 2013 Keith Rarick <kr@xph.us>
|
|
||||||
License: Expat
|
|
||||||
|
|
||||||
Files: debian/*
|
|
||||||
Copyright: 2013 Tonnerre Lombard <tonnerre@ancient-solutions.com>
|
|
||||||
License: Expat
|
|
||||||
|
|
||||||
License: Expat
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
.
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
.
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
1
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/debian/docs
generated
vendored
1
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/debian/docs
generated
vendored
@@ -1 +0,0 @@
|
|||||||
Readme
|
|
||||||
11
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/debian/rules
generated
vendored
11
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/debian/rules
generated
vendored
@@ -1,11 +0,0 @@
|
|||||||
#!/usr/bin/make -f
|
|
||||||
|
|
||||||
# Uncomment this to turn on verbose mode.
|
|
||||||
export DH_VERBOSE=1
|
|
||||||
|
|
||||||
# DH_GOPKG is the upstream path which you would normally “go get”.
|
|
||||||
# Using it allows us to build applications without patching locations.
|
|
||||||
export DH_GOPKG := github.com/kr/pretty
|
|
||||||
|
|
||||||
%:
|
|
||||||
dh $@ --buildsystem=golang --with=golang
|
|
||||||
1
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/debian/source/format
generated
vendored
1
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/debian/source/format
generated
vendored
@@ -1 +0,0 @@
|
|||||||
3.0 (quilt)
|
|
||||||
148
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/diff.go
generated
vendored
148
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/diff.go
generated
vendored
@@ -1,148 +0,0 @@
|
|||||||
package pretty
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
type sbuf []string
|
|
||||||
|
|
||||||
func (s *sbuf) Write(b []byte) (int, error) {
|
|
||||||
*s = append(*s, string(b))
|
|
||||||
return len(b), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Diff returns a slice where each element describes
|
|
||||||
// a difference between a and b.
|
|
||||||
func Diff(a, b interface{}) (desc []string) {
|
|
||||||
Fdiff((*sbuf)(&desc), a, b)
|
|
||||||
return desc
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fdiff writes to w a description of the differences between a and b.
|
|
||||||
func Fdiff(w io.Writer, a, b interface{}) {
|
|
||||||
diffWriter{w: w}.diff(reflect.ValueOf(a), reflect.ValueOf(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
type diffWriter struct {
|
|
||||||
w io.Writer
|
|
||||||
l string // label
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w diffWriter) printf(f string, a ...interface{}) {
|
|
||||||
var l string
|
|
||||||
if w.l != "" {
|
|
||||||
l = w.l + ": "
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w.w, l+f, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w diffWriter) diff(av, bv reflect.Value) {
|
|
||||||
if !av.IsValid() && bv.IsValid() {
|
|
||||||
w.printf("nil != %#v", bv.Interface())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if av.IsValid() && !bv.IsValid() {
|
|
||||||
w.printf("%#v != nil", av.Interface())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !av.IsValid() && !bv.IsValid() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
at := av.Type()
|
|
||||||
bt := bv.Type()
|
|
||||||
if at != bt {
|
|
||||||
w.printf("%v != %v", at, bt)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// numeric types, including bool
|
|
||||||
if at.Kind() < reflect.Array {
|
|
||||||
a, b := av.Interface(), bv.Interface()
|
|
||||||
if a != b {
|
|
||||||
w.printf("%#v != %#v", a, b)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch at.Kind() {
|
|
||||||
case reflect.String:
|
|
||||||
a, b := av.Interface(), bv.Interface()
|
|
||||||
if a != b {
|
|
||||||
w.printf("%q != %q", a, b)
|
|
||||||
}
|
|
||||||
case reflect.Ptr:
|
|
||||||
switch {
|
|
||||||
case av.IsNil() && !bv.IsNil():
|
|
||||||
w.printf("nil != %v", bv.Interface())
|
|
||||||
case !av.IsNil() && bv.IsNil():
|
|
||||||
w.printf("%v != nil", av.Interface())
|
|
||||||
case !av.IsNil() && !bv.IsNil():
|
|
||||||
w.diff(av.Elem(), bv.Elem())
|
|
||||||
}
|
|
||||||
case reflect.Struct:
|
|
||||||
for i := 0; i < av.NumField(); i++ {
|
|
||||||
w.relabel(at.Field(i).Name).diff(av.Field(i), bv.Field(i))
|
|
||||||
}
|
|
||||||
case reflect.Map:
|
|
||||||
ak, both, bk := keyDiff(av.MapKeys(), bv.MapKeys())
|
|
||||||
for _, k := range ak {
|
|
||||||
w := w.relabel(fmt.Sprintf("[%#v]", k.Interface()))
|
|
||||||
w.printf("%q != (missing)", av.MapIndex(k))
|
|
||||||
}
|
|
||||||
for _, k := range both {
|
|
||||||
w := w.relabel(fmt.Sprintf("[%#v]", k.Interface()))
|
|
||||||
w.diff(av.MapIndex(k), bv.MapIndex(k))
|
|
||||||
}
|
|
||||||
for _, k := range bk {
|
|
||||||
w := w.relabel(fmt.Sprintf("[%#v]", k.Interface()))
|
|
||||||
w.printf("(missing) != %q", bv.MapIndex(k))
|
|
||||||
}
|
|
||||||
case reflect.Interface:
|
|
||||||
w.diff(reflect.ValueOf(av.Interface()), reflect.ValueOf(bv.Interface()))
|
|
||||||
default:
|
|
||||||
if !reflect.DeepEqual(av.Interface(), bv.Interface()) {
|
|
||||||
w.printf("%# v != %# v", Formatter(av.Interface()), Formatter(bv.Interface()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d diffWriter) relabel(name string) (d1 diffWriter) {
|
|
||||||
d1 = d
|
|
||||||
if d.l != "" && name[0] != '[' {
|
|
||||||
d1.l += "."
|
|
||||||
}
|
|
||||||
d1.l += name
|
|
||||||
return d1
|
|
||||||
}
|
|
||||||
|
|
||||||
func keyDiff(a, b []reflect.Value) (ak, both, bk []reflect.Value) {
|
|
||||||
for _, av := range a {
|
|
||||||
inBoth := false
|
|
||||||
for _, bv := range b {
|
|
||||||
if reflect.DeepEqual(av.Interface(), bv.Interface()) {
|
|
||||||
inBoth = true
|
|
||||||
both = append(both, av)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !inBoth {
|
|
||||||
ak = append(ak, av)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, bv := range b {
|
|
||||||
inBoth := false
|
|
||||||
for _, av := range a {
|
|
||||||
if reflect.DeepEqual(av.Interface(), bv.Interface()) {
|
|
||||||
inBoth = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !inBoth {
|
|
||||||
bk = append(bk, bv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
73
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/diff_test.go
generated
vendored
73
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/diff_test.go
generated
vendored
@@ -1,73 +0,0 @@
|
|||||||
package pretty
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type difftest struct {
|
|
||||||
a interface{}
|
|
||||||
b interface{}
|
|
||||||
exp []string
|
|
||||||
}
|
|
||||||
|
|
||||||
type S struct {
|
|
||||||
A int
|
|
||||||
S *S
|
|
||||||
I interface{}
|
|
||||||
C []int
|
|
||||||
}
|
|
||||||
|
|
||||||
var diffs = []difftest{
|
|
||||||
{a: nil, b: nil},
|
|
||||||
{a: S{A: 1}, b: S{A: 1}},
|
|
||||||
|
|
||||||
{0, "", []string{`int != string`}},
|
|
||||||
{0, 1, []string{`0 != 1`}},
|
|
||||||
{S{}, new(S), []string{`pretty.S != *pretty.S`}},
|
|
||||||
{"a", "b", []string{`"a" != "b"`}},
|
|
||||||
{S{}, S{A: 1}, []string{`A: 0 != 1`}},
|
|
||||||
{new(S), &S{A: 1}, []string{`A: 0 != 1`}},
|
|
||||||
{S{S: new(S)}, S{S: &S{A: 1}}, []string{`S.A: 0 != 1`}},
|
|
||||||
{S{}, S{I: 0}, []string{`I: nil != 0`}},
|
|
||||||
{S{I: 1}, S{I: "x"}, []string{`I: int != string`}},
|
|
||||||
{S{}, S{C: []int{1}}, []string{`C: []int(nil) != []int{1}`}},
|
|
||||||
{S{C: []int{}}, S{C: []int{1}}, []string{`C: []int{} != []int{1}`}},
|
|
||||||
{S{}, S{A: 1, S: new(S)}, []string{`A: 0 != 1`, `S: nil != &{0 <nil> <nil> []}`}},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDiff(t *testing.T) {
|
|
||||||
for _, tt := range diffs {
|
|
||||||
got := Diff(tt.a, tt.b)
|
|
||||||
eq := len(got) == len(tt.exp)
|
|
||||||
if eq {
|
|
||||||
for i := range got {
|
|
||||||
eq = eq && got[i] == tt.exp[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !eq {
|
|
||||||
t.Errorf("diffing % #v", tt.a)
|
|
||||||
t.Errorf("with % #v", tt.b)
|
|
||||||
diffdiff(t, got, tt.exp)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func diffdiff(t *testing.T, got, exp []string) {
|
|
||||||
minus(t, "unexpected:", got, exp)
|
|
||||||
minus(t, "missing:", exp, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
func minus(t *testing.T, s string, a, b []string) {
|
|
||||||
var i, j int
|
|
||||||
for i = 0; i < len(a); i++ {
|
|
||||||
for j = 0; j < len(b); j++ {
|
|
||||||
if a[i] == b[j] {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if j == len(b) {
|
|
||||||
t.Error(s, a[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
20
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/example_test.go
generated
vendored
20
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/example_test.go
generated
vendored
@@ -1,20 +0,0 @@
|
|||||||
package pretty_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/kr/pretty"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Example() {
|
|
||||||
type myType struct {
|
|
||||||
a, b int
|
|
||||||
}
|
|
||||||
var x = []myType{{1, 2}, {3, 4}, {5, 6}}
|
|
||||||
fmt.Printf("%# v", pretty.Formatter(x))
|
|
||||||
// output:
|
|
||||||
// []pretty_test.myType{
|
|
||||||
// {a:1, b:2},
|
|
||||||
// {a:3, b:4},
|
|
||||||
// {a:5, b:6},
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
300
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/formatter.go
generated
vendored
300
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/formatter.go
generated
vendored
@@ -1,300 +0,0 @@
|
|||||||
package pretty
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/kr/text"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"text/tabwriter"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
limit = 50
|
|
||||||
)
|
|
||||||
|
|
||||||
type formatter struct {
|
|
||||||
x interface{}
|
|
||||||
force bool
|
|
||||||
quote bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Formatter makes a wrapper, f, that will format x as go source with line
|
|
||||||
// breaks and tabs. Object f responds to the "%v" formatting verb when both the
|
|
||||||
// "#" and " " (space) flags are set, for example:
|
|
||||||
//
|
|
||||||
// fmt.Sprintf("%# v", Formatter(x))
|
|
||||||
//
|
|
||||||
// If one of these two flags is not set, or any other verb is used, f will
|
|
||||||
// format x according to the usual rules of package fmt.
|
|
||||||
// In particular, if x satisfies fmt.Formatter, then x.Format will be called.
|
|
||||||
func Formatter(x interface{}) (f fmt.Formatter) {
|
|
||||||
return formatter{x: x, quote: true}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fo formatter) String() string {
|
|
||||||
return fmt.Sprint(fo.x) // unwrap it
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fo formatter) passThrough(f fmt.State, c rune) {
|
|
||||||
s := "%"
|
|
||||||
for i := 0; i < 128; i++ {
|
|
||||||
if f.Flag(i) {
|
|
||||||
s += string(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if w, ok := f.Width(); ok {
|
|
||||||
s += fmt.Sprintf("%d", w)
|
|
||||||
}
|
|
||||||
if p, ok := f.Precision(); ok {
|
|
||||||
s += fmt.Sprintf(".%d", p)
|
|
||||||
}
|
|
||||||
s += string(c)
|
|
||||||
fmt.Fprintf(f, s, fo.x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fo formatter) Format(f fmt.State, c rune) {
|
|
||||||
if fo.force || c == 'v' && f.Flag('#') && f.Flag(' ') {
|
|
||||||
w := tabwriter.NewWriter(f, 4, 4, 1, ' ', 0)
|
|
||||||
p := &printer{tw: w, Writer: w}
|
|
||||||
p.printValue(reflect.ValueOf(fo.x), true, fo.quote)
|
|
||||||
w.Flush()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fo.passThrough(f, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
type printer struct {
|
|
||||||
io.Writer
|
|
||||||
tw *tabwriter.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *printer) indent() *printer {
|
|
||||||
q := *p
|
|
||||||
q.tw = tabwriter.NewWriter(p.Writer, 4, 4, 1, ' ', 0)
|
|
||||||
q.Writer = text.NewIndentWriter(q.tw, []byte{'\t'})
|
|
||||||
return &q
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *printer) printInline(v reflect.Value, x interface{}, showType bool) {
|
|
||||||
if showType {
|
|
||||||
io.WriteString(p, v.Type().String())
|
|
||||||
fmt.Fprintf(p, "(%#v)", x)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(p, "%#v", x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *printer) printValue(v reflect.Value, showType, quote bool) {
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
p.printInline(v, v.Bool(), showType)
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
p.printInline(v, v.Int(), showType)
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
||||||
p.printInline(v, v.Uint(), showType)
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
p.printInline(v, v.Float(), showType)
|
|
||||||
case reflect.Complex64, reflect.Complex128:
|
|
||||||
fmt.Fprintf(p, "%#v", v.Complex())
|
|
||||||
case reflect.String:
|
|
||||||
p.fmtString(v.String(), quote)
|
|
||||||
case reflect.Map:
|
|
||||||
t := v.Type()
|
|
||||||
if showType {
|
|
||||||
io.WriteString(p, t.String())
|
|
||||||
}
|
|
||||||
writeByte(p, '{')
|
|
||||||
if nonzero(v) {
|
|
||||||
expand := !canInline(v.Type())
|
|
||||||
pp := p
|
|
||||||
if expand {
|
|
||||||
writeByte(p, '\n')
|
|
||||||
pp = p.indent()
|
|
||||||
}
|
|
||||||
keys := v.MapKeys()
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
showTypeInStruct := true
|
|
||||||
k := keys[i]
|
|
||||||
mv := v.MapIndex(k)
|
|
||||||
pp.printValue(k, false, true)
|
|
||||||
writeByte(pp, ':')
|
|
||||||
if expand {
|
|
||||||
writeByte(pp, '\t')
|
|
||||||
}
|
|
||||||
showTypeInStruct = t.Elem().Kind() == reflect.Interface
|
|
||||||
pp.printValue(mv, showTypeInStruct, true)
|
|
||||||
if expand {
|
|
||||||
io.WriteString(pp, ",\n")
|
|
||||||
} else if i < v.Len()-1 {
|
|
||||||
io.WriteString(pp, ", ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if expand {
|
|
||||||
pp.tw.Flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeByte(p, '}')
|
|
||||||
case reflect.Struct:
|
|
||||||
t := v.Type()
|
|
||||||
if showType {
|
|
||||||
io.WriteString(p, t.String())
|
|
||||||
}
|
|
||||||
writeByte(p, '{')
|
|
||||||
if nonzero(v) {
|
|
||||||
expand := !canInline(v.Type())
|
|
||||||
pp := p
|
|
||||||
if expand {
|
|
||||||
writeByte(p, '\n')
|
|
||||||
pp = p.indent()
|
|
||||||
}
|
|
||||||
for i := 0; i < v.NumField(); i++ {
|
|
||||||
showTypeInStruct := true
|
|
||||||
if f := t.Field(i); f.Name != "" {
|
|
||||||
io.WriteString(pp, f.Name)
|
|
||||||
writeByte(pp, ':')
|
|
||||||
if expand {
|
|
||||||
writeByte(pp, '\t')
|
|
||||||
}
|
|
||||||
showTypeInStruct = f.Type.Kind() == reflect.Interface
|
|
||||||
}
|
|
||||||
pp.printValue(getField(v, i), showTypeInStruct, true)
|
|
||||||
if expand {
|
|
||||||
io.WriteString(pp, ",\n")
|
|
||||||
} else if i < v.NumField()-1 {
|
|
||||||
io.WriteString(pp, ", ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if expand {
|
|
||||||
pp.tw.Flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeByte(p, '}')
|
|
||||||
case reflect.Interface:
|
|
||||||
switch e := v.Elem(); {
|
|
||||||
case e.Kind() == reflect.Invalid:
|
|
||||||
io.WriteString(p, "nil")
|
|
||||||
case e.IsValid():
|
|
||||||
p.printValue(e, showType, true)
|
|
||||||
default:
|
|
||||||
io.WriteString(p, v.Type().String())
|
|
||||||
io.WriteString(p, "(nil)")
|
|
||||||
}
|
|
||||||
case reflect.Array, reflect.Slice:
|
|
||||||
t := v.Type()
|
|
||||||
if showType {
|
|
||||||
io.WriteString(p, t.String())
|
|
||||||
}
|
|
||||||
if v.Kind() == reflect.Slice && v.IsNil() && showType {
|
|
||||||
io.WriteString(p, "(nil)")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if v.Kind() == reflect.Slice && v.IsNil() {
|
|
||||||
io.WriteString(p, "nil")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
writeByte(p, '{')
|
|
||||||
expand := !canInline(v.Type())
|
|
||||||
pp := p
|
|
||||||
if expand {
|
|
||||||
writeByte(p, '\n')
|
|
||||||
pp = p.indent()
|
|
||||||
}
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
showTypeInSlice := t.Elem().Kind() == reflect.Interface
|
|
||||||
pp.printValue(v.Index(i), showTypeInSlice, true)
|
|
||||||
if expand {
|
|
||||||
io.WriteString(pp, ",\n")
|
|
||||||
} else if i < v.Len()-1 {
|
|
||||||
io.WriteString(pp, ", ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if expand {
|
|
||||||
pp.tw.Flush()
|
|
||||||
}
|
|
||||||
writeByte(p, '}')
|
|
||||||
case reflect.Ptr:
|
|
||||||
e := v.Elem()
|
|
||||||
if !e.IsValid() {
|
|
||||||
writeByte(p, '(')
|
|
||||||
io.WriteString(p, v.Type().String())
|
|
||||||
io.WriteString(p, ")(nil)")
|
|
||||||
} else {
|
|
||||||
writeByte(p, '&')
|
|
||||||
p.printValue(e, true, true)
|
|
||||||
}
|
|
||||||
case reflect.Chan:
|
|
||||||
x := v.Pointer()
|
|
||||||
if showType {
|
|
||||||
writeByte(p, '(')
|
|
||||||
io.WriteString(p, v.Type().String())
|
|
||||||
fmt.Fprintf(p, ")(%#v)", x)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(p, "%#v", x)
|
|
||||||
}
|
|
||||||
case reflect.Func:
|
|
||||||
io.WriteString(p, v.Type().String())
|
|
||||||
io.WriteString(p, " {...}")
|
|
||||||
case reflect.UnsafePointer:
|
|
||||||
p.printInline(v, v.Pointer(), showType)
|
|
||||||
case reflect.Invalid:
|
|
||||||
io.WriteString(p, "nil")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func canInline(t reflect.Type) bool {
|
|
||||||
switch t.Kind() {
|
|
||||||
case reflect.Map:
|
|
||||||
return !canExpand(t.Elem())
|
|
||||||
case reflect.Struct:
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
|
||||||
if canExpand(t.Field(i).Type) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
case reflect.Interface:
|
|
||||||
return false
|
|
||||||
case reflect.Array, reflect.Slice:
|
|
||||||
return !canExpand(t.Elem())
|
|
||||||
case reflect.Ptr:
|
|
||||||
return false
|
|
||||||
case reflect.Chan, reflect.Func, reflect.UnsafePointer:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func canExpand(t reflect.Type) bool {
|
|
||||||
switch t.Kind() {
|
|
||||||
case reflect.Map, reflect.Struct,
|
|
||||||
reflect.Interface, reflect.Array, reflect.Slice,
|
|
||||||
reflect.Ptr:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *printer) fmtString(s string, quote bool) {
|
|
||||||
if quote {
|
|
||||||
s = strconv.Quote(s)
|
|
||||||
}
|
|
||||||
io.WriteString(p, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func tryDeepEqual(a, b interface{}) bool {
|
|
||||||
defer func() { recover() }()
|
|
||||||
return reflect.DeepEqual(a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeByte(w io.Writer, b byte) {
|
|
||||||
w.Write([]byte{b})
|
|
||||||
}
|
|
||||||
|
|
||||||
func getField(v reflect.Value, i int) reflect.Value {
|
|
||||||
val := v.Field(i)
|
|
||||||
if val.Kind() == reflect.Interface && !val.IsNil() {
|
|
||||||
val = val.Elem()
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
146
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/formatter_test.go
generated
vendored
146
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/formatter_test.go
generated
vendored
@@ -1,146 +0,0 @@
|
|||||||
package pretty
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"testing"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
type test struct {
|
|
||||||
v interface{}
|
|
||||||
s string
|
|
||||||
}
|
|
||||||
|
|
||||||
type LongStructTypeName struct {
|
|
||||||
longFieldName interface{}
|
|
||||||
otherLongFieldName interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type SA struct {
|
|
||||||
t *T
|
|
||||||
}
|
|
||||||
|
|
||||||
type T struct {
|
|
||||||
x, y int
|
|
||||||
}
|
|
||||||
|
|
||||||
type F int
|
|
||||||
|
|
||||||
func (f F) Format(s fmt.State, c rune) {
|
|
||||||
fmt.Fprintf(s, "F(%d)", int(f))
|
|
||||||
}
|
|
||||||
|
|
||||||
var long = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|
||||||
|
|
||||||
var gosyntax = []test{
|
|
||||||
{nil, `nil`},
|
|
||||||
{"", `""`},
|
|
||||||
{"a", `"a"`},
|
|
||||||
{1, "int(1)"},
|
|
||||||
{1.0, "float64(1)"},
|
|
||||||
{[]int(nil), "[]int(nil)"},
|
|
||||||
{[0]int{}, "[0]int{}"},
|
|
||||||
{complex(1, 0), "(1+0i)"},
|
|
||||||
//{make(chan int), "(chan int)(0x1234)"},
|
|
||||||
{unsafe.Pointer(uintptr(1)), "unsafe.Pointer(0x1)"},
|
|
||||||
{func(int) {}, "func(int) {...}"},
|
|
||||||
{map[int]int{1: 1}, "map[int]int{1:1}"},
|
|
||||||
{int32(1), "int32(1)"},
|
|
||||||
{io.EOF, `&errors.errorString{s:"EOF"}`},
|
|
||||||
{[]string{"a"}, `[]string{"a"}`},
|
|
||||||
{
|
|
||||||
[]string{long},
|
|
||||||
`[]string{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"}`,
|
|
||||||
},
|
|
||||||
{F(5), "pretty.F(5)"},
|
|
||||||
{
|
|
||||||
SA{&T{1, 2}},
|
|
||||||
`pretty.SA{
|
|
||||||
t: &pretty.T{x:1, y:2},
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
map[int][]byte{1: []byte{}},
|
|
||||||
`map[int][]uint8{
|
|
||||||
1: {},
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
map[int]T{1: T{}},
|
|
||||||
`map[int]pretty.T{
|
|
||||||
1: {},
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
long,
|
|
||||||
`"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
LongStructTypeName{
|
|
||||||
longFieldName: LongStructTypeName{},
|
|
||||||
otherLongFieldName: long,
|
|
||||||
},
|
|
||||||
`pretty.LongStructTypeName{
|
|
||||||
longFieldName: pretty.LongStructTypeName{},
|
|
||||||
otherLongFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
&LongStructTypeName{
|
|
||||||
longFieldName: &LongStructTypeName{},
|
|
||||||
otherLongFieldName: (*LongStructTypeName)(nil),
|
|
||||||
},
|
|
||||||
`&pretty.LongStructTypeName{
|
|
||||||
longFieldName: &pretty.LongStructTypeName{},
|
|
||||||
otherLongFieldName: (*pretty.LongStructTypeName)(nil),
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[]LongStructTypeName{
|
|
||||||
{nil, nil},
|
|
||||||
{3, 3},
|
|
||||||
{long, nil},
|
|
||||||
},
|
|
||||||
`[]pretty.LongStructTypeName{
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
longFieldName: int(3),
|
|
||||||
otherLongFieldName: int(3),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
longFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
|
|
||||||
otherLongFieldName: nil,
|
|
||||||
},
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[]interface{}{
|
|
||||||
LongStructTypeName{nil, nil},
|
|
||||||
[]byte{1, 2, 3},
|
|
||||||
T{3, 4},
|
|
||||||
LongStructTypeName{long, nil},
|
|
||||||
},
|
|
||||||
`[]interface {}{
|
|
||||||
pretty.LongStructTypeName{},
|
|
||||||
[]uint8{0x1, 0x2, 0x3},
|
|
||||||
pretty.T{x:3, y:4},
|
|
||||||
pretty.LongStructTypeName{
|
|
||||||
longFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
|
|
||||||
otherLongFieldName: nil,
|
|
||||||
},
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGoSyntax(t *testing.T) {
|
|
||||||
for _, tt := range gosyntax {
|
|
||||||
s := fmt.Sprintf("%# v", Formatter(tt.v))
|
|
||||||
if tt.s != s {
|
|
||||||
t.Errorf("expected %q", tt.s)
|
|
||||||
t.Errorf("got %q", s)
|
|
||||||
t.Errorf("expraw\n%s", tt.s)
|
|
||||||
t.Errorf("gotraw\n%s", s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
98
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/pretty.go
generated
vendored
98
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/pretty.go
generated
vendored
@@ -1,98 +0,0 @@
|
|||||||
// Package pretty provides pretty-printing for Go values. This is
|
|
||||||
// useful during debugging, to avoid wrapping long output lines in
|
|
||||||
// the terminal.
|
|
||||||
//
|
|
||||||
// It provides a function, Formatter, that can be used with any
|
|
||||||
// function that accepts a format string. It also provides
|
|
||||||
// convenience wrappers for functions in packages fmt and log.
|
|
||||||
package pretty
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Errorf is a convenience wrapper for fmt.Errorf.
|
|
||||||
//
|
|
||||||
// Calling Errorf(f, x, y) is equivalent to
|
|
||||||
// fmt.Errorf(f, Formatter(x), Formatter(y)).
|
|
||||||
func Errorf(format string, a ...interface{}) error {
|
|
||||||
return fmt.Errorf(format, wrap(a, false)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fprintf is a convenience wrapper for fmt.Fprintf.
|
|
||||||
//
|
|
||||||
// Calling Fprintf(w, f, x, y) is equivalent to
|
|
||||||
// fmt.Fprintf(w, f, Formatter(x), Formatter(y)).
|
|
||||||
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, error error) {
|
|
||||||
return fmt.Fprintf(w, format, wrap(a, false)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log is a convenience wrapper for log.Printf.
|
|
||||||
//
|
|
||||||
// Calling Log(x, y) is equivalent to
|
|
||||||
// log.Print(Formatter(x), Formatter(y)), but each operand is
|
|
||||||
// formatted with "%# v".
|
|
||||||
func Log(a ...interface{}) {
|
|
||||||
log.Print(wrap(a, true)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logf is a convenience wrapper for log.Printf.
|
|
||||||
//
|
|
||||||
// Calling Logf(f, x, y) is equivalent to
|
|
||||||
// log.Printf(f, Formatter(x), Formatter(y)).
|
|
||||||
func Logf(format string, a ...interface{}) {
|
|
||||||
log.Printf(format, wrap(a, false)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logln is a convenience wrapper for log.Printf.
|
|
||||||
//
|
|
||||||
// Calling Logln(x, y) is equivalent to
|
|
||||||
// log.Println(Formatter(x), Formatter(y)), but each operand is
|
|
||||||
// formatted with "%# v".
|
|
||||||
func Logln(a ...interface{}) {
|
|
||||||
log.Println(wrap(a, true)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print pretty-prints its operands and writes to standard output.
|
|
||||||
//
|
|
||||||
// Calling Print(x, y) is equivalent to
|
|
||||||
// fmt.Print(Formatter(x), Formatter(y)), but each operand is
|
|
||||||
// formatted with "%# v".
|
|
||||||
func Print(a ...interface{}) (n int, errno error) {
|
|
||||||
return fmt.Print(wrap(a, true)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Printf is a convenience wrapper for fmt.Printf.
|
|
||||||
//
|
|
||||||
// Calling Printf(f, x, y) is equivalent to
|
|
||||||
// fmt.Printf(f, Formatter(x), Formatter(y)).
|
|
||||||
func Printf(format string, a ...interface{}) (n int, errno error) {
|
|
||||||
return fmt.Printf(format, wrap(a, false)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Println pretty-prints its operands and writes to standard output.
|
|
||||||
//
|
|
||||||
// Calling Print(x, y) is equivalent to
|
|
||||||
// fmt.Println(Formatter(x), Formatter(y)), but each operand is
|
|
||||||
// formatted with "%# v".
|
|
||||||
func Println(a ...interface{}) (n int, errno error) {
|
|
||||||
return fmt.Println(wrap(a, true)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sprintf is a convenience wrapper for fmt.Sprintf.
|
|
||||||
//
|
|
||||||
// Calling Sprintf(f, x, y) is equivalent to
|
|
||||||
// fmt.Sprintf(f, Formatter(x), Formatter(y)).
|
|
||||||
func Sprintf(format string, a ...interface{}) string {
|
|
||||||
return fmt.Sprintf(format, wrap(a, false)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func wrap(a []interface{}, force bool) []interface{} {
|
|
||||||
w := make([]interface{}, len(a))
|
|
||||||
for i, x := range a {
|
|
||||||
w[i] = formatter{x: x, force: force}
|
|
||||||
}
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
41
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/zero.go
generated
vendored
41
Godeps/_workspace/src/github.com/tonnerre/golang-pretty/zero.go
generated
vendored
@@ -1,41 +0,0 @@
|
|||||||
package pretty
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
func nonzero(v reflect.Value) bool {
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
return v.Bool()
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
return v.Int() != 0
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
||||||
return v.Uint() != 0
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return v.Float() != 0
|
|
||||||
case reflect.Complex64, reflect.Complex128:
|
|
||||||
return v.Complex() != complex(0, 0)
|
|
||||||
case reflect.String:
|
|
||||||
return v.String() != ""
|
|
||||||
case reflect.Struct:
|
|
||||||
for i := 0; i < v.NumField(); i++ {
|
|
||||||
if nonzero(getField(v, i)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
case reflect.Array:
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
if nonzero(v.Index(i)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
case reflect.Map, reflect.Interface, reflect.Slice, reflect.Ptr, reflect.Chan, reflect.Func:
|
|
||||||
return !v.IsNil()
|
|
||||||
case reflect.UnsafePointer:
|
|
||||||
return v.Pointer() != 0
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user