mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	
							
								
								
									
										14
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										14
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							@@ -130,8 +130,8 @@
 | 
			
		||||
			"Rev": "aef70dacbc78771e35beb261bb3a72986adf7906"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/kr/text",
 | 
			
		||||
			"Rev": "6807e777504f54ad073ecef66747de158294b639"
 | 
			
		||||
			"ImportPath": "github.com/miekg/dns",
 | 
			
		||||
			"Rev": "3f504e8dabd5d562e997d19ce0200aa41973e1b2"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/mitchellh/goamz/aws",
 | 
			
		||||
@@ -159,6 +159,11 @@
 | 
			
		||||
			"ImportPath": "github.com/skratchdot/open-golang/open",
 | 
			
		||||
			"Rev": "ba570a111973b539baf23c918213059543b5bb6e"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/skynetservices/skydns/msg",
 | 
			
		||||
			"Comment": "2.0.1d-2-g245a121",
 | 
			
		||||
			"Rev": "245a1216be2a7f5377ea56e957fdfa0de6ecd067"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/spf13/cobra",
 | 
			
		||||
			"Rev": "e1e66f7b4e667751cf530ddb6e72b79d6eeb0235"
 | 
			
		||||
@@ -179,11 +184,6 @@
 | 
			
		||||
			"ImportPath": "github.com/stretchr/testify/mock",
 | 
			
		||||
			"Rev": "37614ac27794505bf7867ca93aac883cadb6a5f7"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/tonnerre/golang-pretty",
 | 
			
		||||
			"Comment": "debian/0.0_git20130613-1-1-ge7fccc0",
 | 
			
		||||
			"Rev": "e7fccc03e91bad289b96c21aa3312a220689bdd7"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/vaughan0/go-ini",
 | 
			
		||||
			"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