Browse Source

Merge branch 'master' into 3.0

Ask Bjørn Hansen 9 years ago
parent
commit
f3f23e546b
100 changed files with 1572 additions and 960 deletions
  1. 5 0
      .gitignore
  2. 8 6
      .travis.yml
  3. 28 7
      Godeps/Godeps.json
  4. 0 2
      Godeps/_workspace/.gitignore
  5. 0 57
      Godeps/_workspace/src/code.google.com/p/gcfg/LICENSE
  6. 0 7
      Godeps/_workspace/src/code.google.com/p/gcfg/README
  7. 0 94
      Godeps/_workspace/src/github.com/miekg/dns/update.go
  8. 0 84
      Godeps/_workspace/src/github.com/miekg/dns/update_test.go
  9. 0 109
      Godeps/_workspace/src/gopkg.in/check.v1/printer_test.go
  10. 6 0
      Makefile
  11. 13 2
      README.md
  12. 7 4
      config.go
  13. 11 0
      countries/countries.go
  14. 65 57
      countries/regiongroups.go
  15. 20 0
      dns/1.168.192.in-addr.arpa.json
  16. 8 0
      dns/geodns.conf.sample
  17. 31 9
      geodns.go
  18. 1 1
      geoip.go
  19. 2 1
      healthtest.go
  20. 1 1
      metrics.go
  21. 2 2
      monitor.go
  22. 4 2
      monitor_test.go
  23. 1 1
      picker.go
  24. 50 0
      querylog/querylog.go
  25. 53 29
      serve.go
  26. 39 8
      serve_test.go
  27. 68 0
      server.go
  28. 5 2
      service/run
  29. 2 2
      stathat.go
  30. 1 1
      targeting_test.go
  31. 46 16
      templates.go
  32. 0 0
      vendor/github.com/abh/errorutil/README.md
  33. 0 0
      vendor/github.com/abh/errorutil/highlight.go
  34. 0 0
      vendor/github.com/abh/geoip/.gitignore
  35. 0 0
      vendor/github.com/abh/geoip/.travis.yml
  36. 0 0
      vendor/github.com/abh/geoip/LICENSE
  37. 0 0
      vendor/github.com/abh/geoip/README.md
  38. 0 0
      vendor/github.com/abh/geoip/const.go
  39. 0 0
      vendor/github.com/abh/geoip/db/.gitignore
  40. 0 0
      vendor/github.com/abh/geoip/db/download
  41. 1 1
      vendor/github.com/abh/geoip/ex/geoip-demo.go
  42. 0 0
      vendor/github.com/abh/geoip/geoip.go
  43. 1 1
      vendor/github.com/abh/geoip/geoip_test.go
  44. 0 0
      vendor/github.com/abh/geoip/test-db/GeoIP.dat
  45. 0 0
      vendor/github.com/abh/geoip/test-db/GeoIPCity.dat
  46. 0 0
      vendor/github.com/abh/geoip/test-db/GeoIPRegion.dat
  47. 0 0
      vendor/github.com/miekg/dns/.gitignore
  48. 2 2
      vendor/github.com/miekg/dns/.travis.yml
  49. 0 0
      vendor/github.com/miekg/dns/AUTHORS
  50. 0 0
      vendor/github.com/miekg/dns/CONTRIBUTORS
  51. 0 0
      vendor/github.com/miekg/dns/COPYRIGHT
  52. 0 0
      vendor/github.com/miekg/dns/LICENSE
  53. 12 3
      vendor/github.com/miekg/dns/README.md
  54. 90 32
      vendor/github.com/miekg/dns/client.go
  55. 191 58
      vendor/github.com/miekg/dns/client_test.go
  56. 0 0
      vendor/github.com/miekg/dns/clientconfig.go
  57. 4 9
      vendor/github.com/miekg/dns/clientconfig_test.go
  58. 8 5
      vendor/github.com/miekg/dns/defaults.go
  59. 0 0
      vendor/github.com/miekg/dns/dns.go
  60. 2 2
      vendor/github.com/miekg/dns/dns_test.go
  61. 15 10
      vendor/github.com/miekg/dns/dnssec.go
  62. 0 0
      vendor/github.com/miekg/dns/dnssec_keygen.go
  63. 0 0
      vendor/github.com/miekg/dns/dnssec_keyscan.go
  64. 0 0
      vendor/github.com/miekg/dns/dnssec_privkey.go
  65. 21 10
      vendor/github.com/miekg/dns/dnssec_test.go
  66. 79 0
      vendor/github.com/miekg/dns/dnsutil/util.go
  67. 130 0
      vendor/github.com/miekg/dns/dnsutil/util_test.go
  68. 3 3
      vendor/github.com/miekg/dns/doc.go
  69. 0 0
      vendor/github.com/miekg/dns/dyn_test.go
  70. 0 9
      vendor/github.com/miekg/dns/edns.go
  71. 0 0
      vendor/github.com/miekg/dns/edns_test.go
  72. 4 5
      vendor/github.com/miekg/dns/example_test.go
  73. 0 0
      vendor/github.com/miekg/dns/format.go
  74. 0 0
      vendor/github.com/miekg/dns/fuzz_test.go
  75. 0 0
      vendor/github.com/miekg/dns/idn/code_points.go
  76. 1 1
      vendor/github.com/miekg/dns/idn/example_test.go
  77. 1 2
      vendor/github.com/miekg/dns/idn/punycode.go
  78. 0 0
      vendor/github.com/miekg/dns/idn/punycode_test.go
  79. 23 0
      vendor/github.com/miekg/dns/issue_test.go
  80. 7 1
      vendor/github.com/miekg/dns/labels.go
  81. 18 19
      vendor/github.com/miekg/dns/labels_test.go
  82. 53 138
      vendor/github.com/miekg/dns/msg.go
  83. 0 0
      vendor/github.com/miekg/dns/nsecx.go
  84. 0 0
      vendor/github.com/miekg/dns/nsecx_test.go
  85. 10 10
      vendor/github.com/miekg/dns/parse_test.go
  86. 4 4
      vendor/github.com/miekg/dns/privaterr.go
  87. 3 3
      vendor/github.com/miekg/dns/privaterr_test.go
  88. 3 3
      vendor/github.com/miekg/dns/rawmsg.go
  89. 0 0
      vendor/github.com/miekg/dns/remote_test.go
  90. 0 0
      vendor/github.com/miekg/dns/sanitize.go
  91. 6 6
      vendor/github.com/miekg/dns/sanitize_test.go
  92. 0 0
      vendor/github.com/miekg/dns/scanner.go
  93. 106 75
      vendor/github.com/miekg/dns/server.go
  94. 249 20
      vendor/github.com/miekg/dns/server_test.go
  95. 0 0
      vendor/github.com/miekg/dns/sig0.go
  96. 8 8
      vendor/github.com/miekg/dns/sig0_test.go
  97. 0 0
      vendor/github.com/miekg/dns/singleinflight.go
  98. 1 1
      vendor/github.com/miekg/dns/tlsa.go
  99. 2 15
      vendor/github.com/miekg/dns/tsig.go
  100. 37 0
      vendor/github.com/miekg/dns/tsig_test.go

+ 5 - 0
.gitignore

@@ -1,2 +1,7 @@
 *~
 .DS_Store
+/geodns
+/REVISION
+/run
+.idea
+/dns/geodns.conf

+ 8 - 6
.travis.yml

@@ -1,20 +1,22 @@
 language: go
 go:
-  - 1.4
-  - 1.5
+  - 1.5.4
+  - 1.6.2
   - tip
+
+env:
+  global:
+  - GO15VENDOREXPERIMENT='1'
+
 before_install:
   - sudo apt-get install libgeoip-dev bzr
+
 install:
   - mkdir -p $TRAVIS_BUILD_DIR/db
   - curl -s http://geodns.bitnames.com/geoip/GeoLiteCity.dat.gz | gzip -cd > $TRAVIS_BUILD_DIR/db/GeoIPCity.dat
   - curl -s http://download.maxmind.com/download/geoip/database/asnum/GeoIPASNum.dat.gz | gzip -cd > $TRAVIS_BUILD_DIR/db/GeoIPASNum.dat
   - echo [geodns] >> dns/geodns.conf
   - echo Directory=$TRAVIS_BUILD_DIR/db >> dns/geodns.conf
-  - go get github.com/abh/dns
-  - go get github.com/abh/geoip
-  - go get gopkg.in/check.v1
-  - go get -v
   - go build -v
   - go install
 

+ 28 - 7
Godeps/Godeps.json

@@ -1,11 +1,8 @@
 {
 	"ImportPath": "github.com/abh/geodns",
-	"GoVersion": "go1.4.2",
+	"GoVersion": "go1.7",
+	"GodepVersion": "v74",
 	"Deps": [
-		{
-			"ImportPath": "code.google.com/p/gcfg",
-			"Rev": "c2d3050044d05357eaf6c3547249ba57c5e235cb"
-		},
 		{
 			"ImportPath": "github.com/abh/errorutil",
 			"Rev": "f9bd360d00b902548fbb80837aef90dca2c8285e"
@@ -16,7 +13,7 @@
 		},
 		{
 			"ImportPath": "github.com/miekg/dns",
-			"Rev": "8395762c3490507cf5a27405fcd0e3d3dc547109"
+			"Rev": "b9171237b0642de1d8e8004f16869970e065f46b"
 		},
 		{
 			"ImportPath": "github.com/pborman/uuid",
@@ -24,7 +21,7 @@
 		},
 		{
 			"ImportPath": "github.com/rcrowley/go-metrics",
-			"Rev": "1ce93efbc8f9c568886b2ef85ce305b2217b3de3"
+			"Rev": "eeba7bd0dd01ace6e690fa833b3f22aaec29af43"
 		},
 		{
 			"ImportPath": "github.com/stathat/go",
@@ -42,6 +39,30 @@
 			"ImportPath": "gopkg.in/fsnotify.v1",
 			"Comment": "v1.2.0",
 			"Rev": "96c060f6a6b7e0d6f75fddd10efeaca3e5d1bcb0"
+		},
+		{
+			"ImportPath": "gopkg.in/gcfg.v1",
+			"Rev": "083575c3955c85df16fe9590cceab64d03f5eb6e"
+		},
+		{
+			"ImportPath": "gopkg.in/gcfg.v1/scanner",
+			"Comment": "v1.0.0",
+			"Rev": "083575c3955c85df16fe9590cceab64d03f5eb6e"
+		},
+		{
+			"ImportPath": "gopkg.in/gcfg.v1/token",
+			"Comment": "v1.0.0",
+			"Rev": "083575c3955c85df16fe9590cceab64d03f5eb6e"
+		},
+		{
+			"ImportPath": "gopkg.in/gcfg.v1/types",
+			"Comment": "v1.0.0",
+			"Rev": "083575c3955c85df16fe9590cceab64d03f5eb6e"
+		},
+		{
+			"ImportPath": "gopkg.in/natefinch/lumberjack.v2",
+			"Comment": "v1.0-21-g514cbda",
+			"Rev": "514cbda263a734ae8caac038dadf05f8f3f9f738"
 		}
 	]
 }

+ 0 - 2
Godeps/_workspace/.gitignore

@@ -1,2 +0,0 @@
-/pkg
-/bin

+ 0 - 57
Godeps/_workspace/src/code.google.com/p/gcfg/LICENSE

@@ -1,57 +0,0 @@
-Copyright (c) 2012 Péter Surányi. 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.
-
-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.
-
-----------------------------------------------------------------------
-Portions of gcfg's source code have been derived from Go, and are
-covered by the following license:
-----------------------------------------------------------------------
-
-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.

+ 0 - 7
Godeps/_workspace/src/code.google.com/p/gcfg/README

@@ -1,7 +0,0 @@
-Gcfg reads INI-style configuration files into Go structs;
-supports user-defined types and subsections.
-
-Project page: https://code.google.com/p/gcfg
-Package docs: http://godoc.org/code.google.com/p/gcfg
-
-My other projects: https://speter.net

+ 0 - 94
Godeps/_workspace/src/github.com/miekg/dns/update.go

@@ -1,94 +0,0 @@
-package dns
-
-// NameUsed sets the RRs in the prereq section to
-// "Name is in use" RRs. RFC 2136 section 2.4.4.
-func (u *Msg) NameUsed(rr []RR) {
-	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) {
-	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: r.Header().Rrtype, Class: ClassANY}}
-	}
-}
-
-// RemoveName creates a dynamic update packet that deletes all RRsets of a name, see RFC 2136 section 2.5.3
-func (u *Msg) RemoveName(rr []RR) {
-	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
-	}
-}

+ 0 - 84
Godeps/_workspace/src/github.com/miekg/dns/update_test.go

@@ -1,84 +0,0 @@
-package dns
-
-import (
-	"bytes"
-	"testing"
-)
-
-func TestDynamicUpdateParsing(t *testing.T) {
-	prefix := "example.com. IN "
-	for _, typ := range TypeToString {
-		if typ == "OPT" || typ == "AXFR" || typ == "IXFR" || typ == "ANY" || typ == "TKEY" ||
-			typ == "TSIG" || typ == "ISDN" || typ == "UNSPEC" || typ == "NULL" || typ == "ATMA" {
-			continue
-		}
-		r, err := NewRR(prefix + typ)
-		if err != nil {
-			t.Errorf("failure to parse: %s %s: %v", prefix, typ, err)
-		} else {
-			t.Logf("parsed: %s", r.String())
-		}
-	}
-}
-
-func TestDynamicUpdateUnpack(t *testing.T) {
-	// From https://github.com/miekg/dns/issues/150#issuecomment-62296803
-	// It should be an update message for the zone "example.",
-	// deleting the A RRset "example." and then adding an A record at "example.".
-	// class ANY, TYPE A
-	buf := []byte{171, 68, 40, 0, 0, 1, 0, 0, 0, 2, 0, 0, 7, 101, 120, 97, 109, 112, 108, 101, 0, 0, 6, 0, 1, 192, 12, 0, 1, 0, 255, 0, 0, 0, 0, 0, 0, 192, 12, 0, 1, 0, 1, 0, 0, 0, 0, 0, 4, 127, 0, 0, 1}
-	msg := new(Msg)
-	err := msg.Unpack(buf)
-	if err != nil {
-		t.Errorf("failed to unpack: %v\n%s", err, msg.String())
-	}
-}
-
-func TestDynamicUpdateZeroRdataUnpack(t *testing.T) {
-	m := new(Msg)
-	rr := &RR_Header{Name: ".", Rrtype: 0, Class: 1, Ttl: ^uint32(0), Rdlength: 0}
-	m.Answer = []RR{rr, rr, rr, rr, rr}
-	m.Ns = m.Answer
-	for n, s := range TypeToString {
-		rr.Rrtype = n
-		bytes, err := m.Pack()
-		if err != nil {
-			t.Errorf("failed to pack %s: %v", s, err)
-			continue
-		}
-		if err := new(Msg).Unpack(bytes); err != nil {
-			t.Errorf("failed to unpack %s: %v", s, err)
-		}
-	}
-}
-
-func TestRemoveRRset(t *testing.T) {
-	// Should add a zero data RR in Class ANY with a TTL of 0
-	// for each set mentioned in the RRs provided to it.
-	rr, err := NewRR(". 100 IN A 127.0.0.1")
-	if err != nil {
-		t.Fatalf("Error constructing RR: %v", err)
-	}
-	m := new(Msg)
-	m.Ns = []RR{&RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY, Ttl: 0, Rdlength: 0}}
-	expectstr := m.String()
-	expect, err := m.Pack()
-	if err != nil {
-		t.Fatalf("Error packing expected msg: %v", err)
-	}
-
-	m.Ns = nil
-	m.RemoveRRset([]RR{rr})
-	actual, err := m.Pack()
-	if err != nil {
-		t.Fatalf("Error packing actual msg: %v", err)
-	}
-	if !bytes.Equal(actual, expect) {
-		tmp := new(Msg)
-		if err := tmp.Unpack(actual); err != nil {
-			t.Fatalf("Error unpacking actual msg: %v", err)
-		}
-		t.Errorf("Expected msg:\n%s", expectstr)
-		t.Errorf("Actual msg:\n%v", tmp)
-	}
-}

+ 0 - 109
Godeps/_workspace/src/gopkg.in/check.v1/printer_test.go

@@ -1,109 +0,0 @@
-package check_test
-
-import (
-	. "github.com/abh/geodns/Godeps/_workspace/src/gopkg.in/check.v1"
-)
-
-var _ = Suite(&PrinterS{})
-
-type PrinterS struct{}
-
-func (s *PrinterS) TestCountSuite(c *C) {
-	suitesRun += 1
-}
-
-var printTestFuncLine int
-
-func init() {
-	printTestFuncLine = getMyLine() + 3
-}
-
-func printTestFunc() {
-	println(1)  // Comment1
-	if 2 == 2 { // Comment2
-		println(3) // Comment3
-	}
-	switch 5 {
-	case 6:
-		println(6) // Comment6
-		println(7)
-	}
-	switch interface{}(9).(type) { // Comment9
-	case int:
-		println(10)
-		println(11)
-	}
-	select {
-	case <-(chan bool)(nil):
-		println(14)
-		println(15)
-	default:
-		println(16)
-		println(17)
-	}
-	println(19,
-		20)
-	_ = func() {
-		println(21)
-		println(22)
-	}
-	println(24, func() {
-		println(25)
-	})
-	// Leading comment
-	// with multiple lines.
-	println(29) // Comment29
-}
-
-var printLineTests = []struct {
-	line   int
-	output string
-}{
-	{1, "println(1) // Comment1"},
-	{2, "if 2 == 2 { // Comment2\n    ...\n}"},
-	{3, "println(3) // Comment3"},
-	{5, "switch 5 {\n...\n}"},
-	{6, "case 6:\n    println(6) // Comment6\n    ..."},
-	{7, "println(7)"},
-	{9, "switch interface{}(9).(type) { // Comment9\n...\n}"},
-	{10, "case int:\n    println(10)\n    ..."},
-	{14, "case <-(chan bool)(nil):\n    println(14)\n    ..."},
-	{15, "println(15)"},
-	{16, "default:\n    println(16)\n    ..."},
-	{17, "println(17)"},
-	{19, "println(19,\n    20)"},
-	{20, "println(19,\n    20)"},
-	{21, "_ = func() {\n    println(21)\n    println(22)\n}"},
-	{22, "println(22)"},
-	{24, "println(24, func() {\n    println(25)\n})"},
-	{25, "println(25)"},
-	{26, "println(24, func() {\n    println(25)\n})"},
-	{29, "// Leading comment\n// with multiple lines.\nprintln(29) // Comment29"},
-}
-
-func (s *PrinterS) TestPrintLine(c *C) {
-	for _, test := range printLineTests {
-		output, err := PrintLine("printer_test.go", printTestFuncLine+test.line)
-		c.Assert(err, IsNil)
-		c.Assert(output, Equals, test.output)
-	}
-}
-
-var indentTests = []struct {
-	in, out string
-}{
-	{"", ""},
-	{"\n", "\n"},
-	{"a", ">>>a"},
-	{"a\n", ">>>a\n"},
-	{"a\nb", ">>>a\n>>>b"},
-	{" ", ">>> "},
-}
-
-func (s *PrinterS) TestIndent(c *C) {
-	for _, test := range indentTests {
-		out := Indent(test.in, ">>>")
-		c.Assert(out, Equals, test.out)
-	}
-
-}

+ 6 - 0
Makefile

@@ -4,5 +4,11 @@ all: templates.go
 templates.go: templates/*.html monitor.go
 	go generate
 
+test:
+	go test -race $(go list ./... | grep -v /vendor/)
+
 devel:
 	go build -tags devel
+
+bench:
+	go test -check.b -check.bmem

+ 13 - 2
README.md

@@ -49,6 +49,14 @@ To test the responses run
 
 `dig -t a test.example.com @127.1 -p 5053`
 
+or
+
+`dig -t ptr 2.1.168.192.IN-ADDR.ARPA. @127.1 -p 5053`
+
+or more simply put
+
+`dig -x 192.168.1.2 @127.1 -p 5053`
+
 The binary can be moved to /usr/local/bin, /opt/geodns/ or wherever you find appropriate.
 
 ### Command options
@@ -136,10 +144,13 @@ with `max_hosts` 2 then .4 will be returned about 4 times more often than .1.
 ## Configuration file
 
 The geodns.conf file allows you to specify a specific directory for the GeoIP
-data files and enable posting metrics to StatHat. See the `geodns.conf.sample`
-file for example configuration.
+data files and other options. See the `geodns.conf.sample` file for example
+configuration.
+
+The global configuration file is not reloaded at runtime.
 
 Most of the configuration is "per zone" and done in the zone .json files.
+The zone configuration files are automatically reloaded when they change.
 
 ## Zone format
 

+ 7 - 4
config.go

@@ -7,8 +7,8 @@ import (
 	"sync"
 	"time"
 
-	"github.com/abh/geodns/Godeps/_workspace/src/code.google.com/p/gcfg"
-	"github.com/abh/geodns/Godeps/_workspace/src/gopkg.in/fsnotify.v1"
+	"gopkg.in/fsnotify.v1"
+	"gopkg.in/gcfg.v1"
 )
 
 type AppConfig struct {
@@ -25,6 +25,11 @@ type AppConfig struct {
 		User     string
 		Password string
 	}
+	QueryLog struct {
+		Path    string
+		MaxSize int
+		Keep    int
+	}
 	Nodeping struct {
 		Token string
 	}
@@ -60,8 +65,6 @@ func (conf *AppConfig) GeoIPDirectory() string {
 
 func configWatcher(fileName string) {
 
-	configReader(fileName)
-
 	watcher, err := fsnotify.NewWatcher()
 	if err != nil {
 		fmt.Println(err)

+ 11 - 0
countries/countries.go

@@ -251,3 +251,14 @@ var CountryContinent = map[string]string{
 	"zm": "africa",
 	"zw": "africa",
 }
+
+var ContinentCountries = map[string][]string{}
+
+func init() {
+	for cc, co := range CountryContinent {
+		if _, ok := ContinentCountries[co]; !ok {
+			ContinentCountries[co] = []string{}
+		}
+		ContinentCountries[co] = append(ContinentCountries[co], cc)
+	}
+}

+ 65 - 57
countries/regiongroups.go

@@ -4,72 +4,80 @@ import (
 	"log"
 )
 
-func CountryRegionGroup(country, region string) string {
+var RegionGroups = map[string]string{
+	"us-ak": "us-west",
+	"us-az": "us-west",
+	"us-ca": "us-west",
+	"us-co": "us-west",
+	"us-hi": "us-west",
+	"us-id": "us-west",
+	"us-mt": "us-west",
+	"us-nm": "us-west",
+	"us-nv": "us-west",
+	"us-or": "us-west",
+	"us-ut": "us-west",
+	"us-wa": "us-west",
+	"us-wy": "us-west",
 
-	if country != "us" {
-		return ""
-	}
+	"us-ar": "us-central",
+	"us-ia": "us-central",
+	"us-il": "us-central",
+	"us-in": "us-central",
+	"us-ks": "us-central",
+	"us-la": "us-central",
+	"us-mn": "us-central",
+	"us-mo": "us-central",
+	"us-nd": "us-central",
+	"us-ne": "us-central",
+	"us-ok": "us-central",
+	"us-sd": "us-central",
+	"us-tx": "us-central",
+	"us-wi": "us-central",
+
+	"us-al": "us-east",
+	"us-ct": "us-east",
+	"us-dc": "us-east",
+	"us-de": "us-east",
+	"us-fl": "us-east",
+	"us-ga": "us-east",
+	"us-ky": "us-east",
+	"us-ma": "us-east",
+	"us-md": "us-east",
+	"us-me": "us-east",
+	"us-mi": "us-east",
+	"us-ms": "us-east",
+	"us-nc": "us-east",
+	"us-nh": "us-east",
+	"us-nj": "us-east",
+	"us-ny": "us-east",
+	"us-oh": "us-east",
+	"us-pa": "us-east",
+	"us-ri": "us-east",
+	"us-sc": "us-east",
+	"us-tn": "us-east",
+	"us-va": "us-east",
+	"us-vt": "us-east",
+	"us-wv": "us-east",
+}
 
-	regions := map[string]string{
-		"us-ak": "us-west",
-		"us-az": "us-west",
-		"us-ca": "us-west",
-		"us-co": "us-west",
-		"us-hi": "us-west",
-		"us-id": "us-west",
-		"us-mt": "us-west",
-		"us-nm": "us-west",
-		"us-nv": "us-west",
-		"us-or": "us-west",
-		"us-ut": "us-west",
-		"us-wa": "us-west",
-		"us-wy": "us-west",
+var RegionGroupRegions = map[string][]string{}
 
-		"us-ar": "us-central",
-		"us-ia": "us-central",
-		"us-il": "us-central",
-		"us-in": "us-central",
-		"us-ks": "us-central",
-		"us-la": "us-central",
-		"us-mn": "us-central",
-		"us-mo": "us-central",
-		"us-nd": "us-central",
-		"us-ne": "us-central",
-		"us-ok": "us-central",
-		"us-sd": "us-central",
-		"us-tx": "us-central",
-		"us-wi": "us-central",
+func CountryRegionGroup(country, region string) string {
 
-		"us-al": "us-east",
-		"us-ct": "us-east",
-		"us-dc": "us-east",
-		"us-de": "us-east",
-		"us-fl": "us-east",
-		"us-ga": "us-east",
-		"us-ky": "us-east",
-		"us-ma": "us-east",
-		"us-md": "us-east",
-		"us-me": "us-east",
-		"us-mi": "us-east",
-		"us-ms": "us-east",
-		"us-nc": "us-east",
-		"us-nh": "us-east",
-		"us-nj": "us-east",
-		"us-ny": "us-east",
-		"us-oh": "us-east",
-		"us-pa": "us-east",
-		"us-ri": "us-east",
-		"us-sc": "us-east",
-		"us-tn": "us-east",
-		"us-va": "us-east",
-		"us-vt": "us-east",
-		"us-wv": "us-east",
+	if country != "us" {
+		return ""
 	}
 
-	if group, ok := regions[region]; ok {
+	if group, ok := RegionGroups[region]; ok {
 		return group
 	}
 
 	log.Printf("Did not find a region group for '%s'/'%s'", country, region)
 	return ""
 }
+
+func init() {
+	for ccrc, rg := range RegionGroups {
+		RegionGroupRegions[rg] = append(RegionGroupRegions[rg], ccrc)
+	}
+}

+ 20 - 0
dns/1.168.192.in-addr.arpa.json

@@ -0,0 +1,20 @@
+{ "serial": 3,
+  "ttl":    600,
+  "max_hosts": 2,
+  "origin": "1.168.192.IN-ADDR.ARPA.",
+  "logging": {
+    "stathat": true,
+    "stathat_api": "abc-test"
+  },
+  "targeting": "country continent @ regiongroup region ip asn",
+  "contact": "support.bitnames.com",
+  "data" : {
+    "":  {
+      "ns": { "ns1.example.net.": null, "ns2.example.net.": null }
+    },
+    "2": {
+        "ptr": [ [ "bar.example.com." ] ],
+        "ttl": "601"
+    }
+  }
+}

+ 8 - 0
dns/geodns.conf.sample

@@ -7,6 +7,14 @@
 ;; Directory containing the GeoIP .dat database files
 ;directory=/usr/local/share/GeoIP/
 
+[querylog]
+;; directory to save query logs; disabled if not specified
+path = log/queries.log
+;; max size per file in megabytes before rotating (default 200)
+; maxsize = 100
+;; keep up to this many rotated log files (default 1)
+; keep = 2
+
 [stathat]
 ;; Add an API key to send query counts and other metrics to stathat
 ;apikey=abc123

+ 31 - 9
geodns.go

@@ -29,11 +29,12 @@ import (
 	"strings"
 	"time"
 
-	"github.com/abh/geodns/Godeps/_workspace/src/github.com/pborman/uuid"
+	"github.com/abh/geodns/querylog"
+	"github.com/pborman/uuid"
 )
 
 // VERSION is the current version of GeoDNS
-var VERSION string = "2.6.0"
+var VERSION string = "2.7.0"
 var buildTime string
 var gitVersion string
 
@@ -52,6 +53,7 @@ var timeStarted = time.Now()
 
 var (
 	flagconfig       = flag.String("config", "./dns/", "directory of zone files")
+	flagconfigfile   = flag.String("configfile", "geodns.conf", "filename of config file (in 'config' directory)")
 	flagcheckconfig  = flag.Bool("checkconfig", false, "check configuration and exit")
 	flagidentifier   = flag.String("identifier", "", "identifier (hostname, pop name or similar)")
 	flaginter        = flag.String("interface", "*", "set the listener address")
@@ -94,6 +96,8 @@ func main() {
 		os.Exit(0)
 	}
 
+	srv := Server{}
+
 	if len(*flagLogFile) > 0 {
 		logToFileOpen(*flagLogFile)
 	}
@@ -106,7 +110,13 @@ func main() {
 		}
 	}
 
-	configFileName := filepath.Clean(*flagconfig + "/geodns.conf")
+	var configFileName string
+
+	if filepath.IsAbs(*flagconfigfile) {
+		configFileName = *flagconfigfile
+	} else {
+		configFileName = filepath.Clean(filepath.Join(*flagconfig, *flagconfigfile))
+	}
 
 	if *flagcheckconfig {
 		dirName := *flagconfig
@@ -118,8 +128,8 @@ func main() {
 		}
 
 		Zones := make(Zones)
-		setupPgeodnsZone(Zones)
-		err = zonesReadDir(dirName, Zones)
+		srv.setupPgeodnsZone(Zones)
+		err = srv.zonesReadDir(dirName, Zones)
 		if err != nil {
 			log.Println("Errors reading zones", err)
 			os.Exit(2)
@@ -152,11 +162,23 @@ func main() {
 		}()
 	}
 
+	// load geodns.conf config
+	configReader(configFileName)
+
+	// load (and re-load) zone data
 	go configWatcher(configFileName)
 
 	metrics := NewMetrics()
 	go metrics.Updater()
 
+	if qlc := Config.QueryLog; len(qlc.Path) > 0 {
+		ql, err := querylog.NewFileLogger(qlc.Path, qlc.MaxSize, qlc.Keep)
+		if err != nil {
+			log.Fatalf("Could not start file query logger: %s", err)
+		}
+		srv.SetQueryLogger(ql)
+	}
+
 	if *flaginter == "*" {
 		addrs, _ := net.InterfaceAddrs()
 		ips := make([]string, 0)
@@ -182,14 +204,14 @@ func main() {
 	go monitor(Zones)
 	go Zones.statHatPoster()
 
-	setupRootZone()
-	setupPgeodnsZone(Zones)
+	srv.setupRootZone()
+	srv.setupPgeodnsZone(Zones)
 
 	dirName := *flagconfig
-	go zonesReader(dirName, Zones)
+	go srv.zonesReader(dirName, Zones)
 
 	for _, host := range inter {
-		go listenAndServe(host)
+		go srv.listenAndServe(host)
 	}
 
 	terminate := make(chan os.Signal)

+ 1 - 1
geoip.go

@@ -1,8 +1,8 @@
 package main
 
 import (
-	"github.com/abh/geodns/Godeps/_workspace/src/github.com/abh/geoip"
 	"github.com/abh/geodns/countries"
+	"github.com/abh/geoip"
 	"github.com/golang/geo/s2"
 	"log"
 	"math"

+ 2 - 1
healthtest.go

@@ -2,12 +2,13 @@ package main
 
 import (
 	"fmt"
-	"github.com/abh/geodns/Godeps/_workspace/src/github.com/miekg/dns"
 	"log"
 	"math/rand"
 	"net"
 	"sync"
 	"time"
+
+	"github.com/miekg/dns"
 )
 
 var (

+ 1 - 1
metrics.go

@@ -4,7 +4,7 @@ import (
 	"runtime"
 	"time"
 
-	metrics "github.com/abh/geodns/Godeps/_workspace/src/github.com/rcrowley/go-metrics"
+	metrics "github.com/rcrowley/go-metrics"
 )
 
 type ServerMetrics struct {

+ 2 - 2
monitor.go

@@ -15,8 +15,8 @@ import (
 	"strconv"
 	"time"
 
-	"github.com/abh/geodns/Godeps/_workspace/src/github.com/rcrowley/go-metrics"
-	"github.com/abh/geodns/Godeps/_workspace/src/golang.org/x/net/websocket"
+	"github.com/rcrowley/go-metrics"
+	"golang.org/x/net/websocket"
 )
 
 // Initial status message on websocket

+ 4 - 2
monitor_test.go

@@ -2,11 +2,12 @@ package main
 
 import (
 	"fmt"
-	. "github.com/abh/geodns/Godeps/_workspace/src/gopkg.in/check.v1"
 	"io/ioutil"
 	"net/http"
 	"strings"
 	"time"
+
+	. "gopkg.in/check.v1"
 )
 
 type MonitorSuite struct {
@@ -28,7 +29,8 @@ func (s *MonitorSuite) SetUpSuite(c *C) {
 	// TODO: use httptest
 	// https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/Jk785WB7F8I
 
-	zonesReadDir("dns", s.zones)
+	srv := Server{}
+	srv.zonesReadDir("dns", s.zones)
 	go httpHandler(s.zones)
 	time.Sleep(500 * time.Millisecond)
 }

+ 1 - 1
picker.go

@@ -3,7 +3,7 @@ package main
 import (
 	"math/rand"
 
-	"github.com/abh/geodns/Godeps/_workspace/src/github.com/miekg/dns"
+	"github.com/miekg/dns"
 )
 
 func (label *Label) Picker(qtype uint16, max int, location *Location) Records {

+ 50 - 0
querylog/querylog.go

@@ -0,0 +1,50 @@
+package querylog
+
+import (
+	"encoding/json"
+
+	"gopkg.in/natefinch/lumberjack.v2"
+)
+
+type QueryLogger interface {
+	Write(*Entry) error
+}
+
+// easyjson:json
+type Entry struct {
+	Time       int64
+	Origin     string
+	Name       string
+	Qtype      uint16
+	Rcode      int
+	Answers    int
+	Targets    []string
+	LabelName  string
+	RemoteAddr string
+	ClientAddr string
+	HasECS     bool
+}
+
+type FileLogger struct {
+	logger lumberjack.Logger
+}
+
+func NewFileLogger(filename string, maxsize int, keep int) (*FileLogger, error) {
+	fl := &FileLogger{}
+	fl.logger = lumberjack.Logger{
+		Filename:   filename,
+		MaxSize:    maxsize, // megabytes
+		MaxBackups: keep,
+	}
+	return fl, nil
+}
+
+func (l *FileLogger) Write(e *Entry) error {
+	js, err := json.Marshal(e)
+	if err != nil {
+		return err
+	}
+	js = append(js, []byte("\n")...)
+	_, err = l.logger.Write(js)
+	return err
+}

+ 53 - 29
serve.go

@@ -10,8 +10,9 @@ import (
 	"strings"
 	"time"
 
-	"github.com/abh/geodns/Godeps/_workspace/src/github.com/miekg/dns"
-	"github.com/abh/geodns/Godeps/_workspace/src/github.com/rcrowley/go-metrics"
+	"github.com/abh/geodns/querylog"
+	"github.com/miekg/dns"
+	"github.com/rcrowley/go-metrics"
 )
 
 func getQuestionName(z *Zone, req *dns.Msg) string {
@@ -20,11 +21,24 @@ func getQuestionName(z *Zone, req *dns.Msg) string {
 	return strings.ToLower(strings.Join(ql, "."))
 }
 
-func serve(w dns.ResponseWriter, req *dns.Msg, z *Zone) {
+func (srv *Server) serve(w dns.ResponseWriter, req *dns.Msg, z *Zone) {
 
+	qname := req.Question[0].Name
 	qtype := req.Question[0].Qtype
 
-	logPrintf("[zone %s] incoming  %s %s (id %d) from %s\n", z.Origin, req.Question[0].Name,
+	var qle *querylog.Entry
+
+	if srv.queryLogger != nil {
+		qle = &querylog.Entry{
+			Time:   time.Now().UnixNano(),
+			Origin: z.Origin,
+			Name:   qname,
+			Qtype:  qtype,
+		}
+		defer srv.queryLogger.Write(qle)
+	}
+
+	logPrintf("[zone %s] incoming  %s %s (id %d) from %s\n", z.Origin, qname,
 		dns.TypeToString[qtype], req.Id, w.RemoteAddr())
 
 	// Global meter
@@ -49,6 +63,9 @@ func serve(w dns.ResponseWriter, req *dns.Msg, z *Zone) {
 		realIP = make(net.IP, len(addr.IP))
 		copy(realIP, addr.IP)
 	}
+	if qle != nil {
+		qle.RemoteAddr = realIP.String()
+	}
 
 	z.Metrics.ClientStats.Add(realIP.String())
 
@@ -71,6 +88,11 @@ func serve(w dns.ResponseWriter, req *dns.Msg, z *Zone) {
 					if e.Address != nil {
 						edns = e
 						ip = e.Address
+
+						if qle != nil {
+							qle.HasECS = true
+							qle.ClientAddr = fmt.Sprintf("%s/%d", ip, e.SourceNetmask)
+						}
 					}
 				}
 			}
@@ -79,11 +101,26 @@ func serve(w dns.ResponseWriter, req *dns.Msg, z *Zone) {
 
 	if len(ip) == 0 { // no edns subnet
 		ip = realIP
+		if qle != nil {
+			qle.ClientAddr = fmt.Sprintf("%s/%d", ip, len(ip)*8)
+		}
 	}
 
 	targets, netmask, location := z.Options.Targeting.GetTargets(ip, z.HasClosest)
 
+	if qle != nil {
+		qle.Targets = targets
+	}
+
 	m := new(dns.Msg)
+
+	if qle != nil {
+		defer func() {
+			qle.Rcode = m.Rcode
+			qle.Answers = len(m.Answer)
+		}()
+	}
+
 	m.SetReply(req)
 	if e := m.IsEdns0(); e != nil {
 		m.SetEdns0(4096, e.Do())
@@ -112,6 +149,10 @@ func serve(w dns.ResponseWriter, req *dns.Msg, z *Zone) {
 
 		firstLabel := (strings.Split(label, "."))[0]
 
+		if qle != nil {
+			qle.LabelName = firstLabel
+		}
+
 		if permitDebug && firstLabel == "_status" {
 			if qtype == dns.TypeANY || qtype == dns.TypeTXT {
 				m.Answer = statusRR(label + "." + z.Origin + ".")
@@ -164,6 +205,7 @@ func serve(w dns.ResponseWriter, req *dns.Msg, z *Zone) {
 			}
 
 			m.Authoritative = true
+
 			w.WriteMsg(m)
 			return
 		}
@@ -186,18 +228,24 @@ func serve(w dns.ResponseWriter, req *dns.Msg, z *Zone) {
 		var rrs []dns.RR
 		for _, record := range servers {
 			rr := dns.Copy(record.RR)
-			rr.Header().Name = req.Question[0].Name
+			rr.Header().Name = qname
 			rrs = append(rrs, rr)
 		}
 		m.Answer = rrs
 	}
 
 	if len(m.Answer) == 0 {
+		// Return a SOA so the NOERROR answer gets cached
 		m.Ns = append(m.Ns, z.SoaRR())
 	}
 
 	logPrintln(m)
 
+	if qle != nil {
+		qle.LabelName = labels.Label
+		qle.Answers = len(m.Answer)
+		qle.Rcode = m.Rcode
+	}
 	err := w.WriteMsg(m)
 	if err != nil {
 		// if Pack'ing fails the Write fails. Return SERVFAIL.
@@ -252,27 +300,3 @@ func (z *Zone) healthRR(label string, baseLabel string) []dns.RR {
 
 	return []dns.RR{&dns.TXT{Hdr: h, Txt: []string{string(js)}}}
 }
-
-func setupServerFunc(Zone *Zone) func(dns.ResponseWriter, *dns.Msg) {
-	return func(w dns.ResponseWriter, r *dns.Msg) {
-		serve(w, r, Zone)
-	}
-}
-
-func listenAndServe(ip string) {
-
-	prots := []string{"udp", "tcp"}
-
-	for _, prot := range prots {
-		go func(p string) {
-			server := &dns.Server{Addr: ip, Net: p}
-
-			log.Printf("Opening on %s %s", ip, p)
-			if err := server.ListenAndServe(); err != nil {
-				log.Fatalf("geodns: failed to setup %s %s: %s", ip, p, err)
-			}
-			log.Fatalf("geodns: ListenAndServe unexpectedly returned")
-		}(prot)
-	}
-
-}

+ 39 - 8
serve_test.go

@@ -1,12 +1,14 @@
 package main
 
 import (
+	"math/rand"
 	"net"
 	"strings"
 	"sync"
+	"time"
 
-	"github.com/abh/geodns/Godeps/_workspace/src/github.com/miekg/dns"
-	. "github.com/abh/geodns/Godeps/_workspace/src/gopkg.in/check.v1"
+	"github.com/miekg/dns"
+	. "gopkg.in/check.v1"
 )
 
 const (
@@ -24,14 +26,19 @@ func (s *ServeSuite) SetUpSuite(c *C) {
 	metrics := NewMetrics()
 	go metrics.Updater()
 
+	srv := Server{}
+
 	Zones := make(Zones)
-	setupPgeodnsZone(Zones)
-	setupRootZone()
-	zonesReadDir("dns", Zones)
+	srv.setupPgeodnsZone(Zones)
+	srv.setupRootZone()
+	srv.zonesReadDir("dns", Zones)
 
-	// listenAndServe returns after listning on udp + tcp, so just
+	// listenAndServe returns after listening on udp + tcp, so just
 	// wait for it before continuing
-	listenAndServe(PORT)
+	srv.listenAndServe(PORT)
+
+	// ensure service has properly started before we query it
+	time.Sleep(200 * time.Millisecond)
 }
 
 func (s *ServeSuite) TestServing(c *C) {
@@ -125,6 +132,14 @@ func (s *ServeSuite) TestServing(c *C) {
 	r = exchange(c, "one.test.example.com.", dns.TypeA)
 	ip = r.Answer[0].(*dns.A).A
 	c.Check(ip.String(), Equals, "192.168.1.6")
+
+	// PTR
+	r = exchange(c, "2.1.168.192.IN-ADDR.ARPA.", dns.TypePTR)
+	c.Check(r.Answer, HasLen, 1)
+	// NOERROR for PTR request
+	c.Check(r.Rcode, Equals, dns.RcodeSuccess)
+	name := r.Answer[0].(*dns.PTR).Ptr
+	c.Check(name, Equals, "bar.example.com.")
 }
 
 func (s *ServeSuite) TestServingMixedCase(c *C) {
@@ -222,12 +237,28 @@ func (s *ServeSuite) TestServeRace(c *C) {
 	wg.Wait()
 }
 
-func (s *ServeSuite) BenchmarkServing(c *C) {
+func (s *ServeSuite) BenchmarkServingCountryDebug(c *C) {
 	for i := 0; i < c.N; i++ {
 		exchange(c, "_country.foo.pgeodns.", dns.TypeTXT)
 	}
 }
 
+func (s *ServeSuite) BenchmarkServing(c *C) {
+
+	// a deterministic seed is the default anyway, but let's be explicit we want it here.
+	rnd := rand.NewSource(1)
+
+	testNames := []string{"foo.test.example.com.", "one.test.example.com.",
+		"weight.test.example.com.", "three.two.one.test.example.com.",
+		"bar.test.example.com.", "0-alias.test.example.com.",
+	}
+
+	for i := 0; i < c.N; i++ {
+		name := testNames[rnd.Int63()%int64(len(testNames))]
+		exchange(c, name, dns.TypeA)
+	}
+}
+
 func exchangeSubnet(c *C, name string, dnstype uint16, ip string) *dns.Msg {
 	msg := new(dns.Msg)
 

+ 68 - 0
server.go

@@ -0,0 +1,68 @@
+package main
+
+import (
+	"log"
+	"time"
+
+	"github.com/abh/geodns/querylog"
+	"github.com/miekg/dns"
+)
+
+type Server struct {
+	queryLogger querylog.QueryLogger
+}
+
+func NewServer() *Server {
+	return &Server{}
+}
+
+// Setup the QueryLogger. For now it only supports writing to a file (and all
+// zones get logged to the same file).
+func (srv *Server) SetQueryLogger(logger querylog.QueryLogger) {
+	srv.queryLogger = logger
+}
+
+func (srv *Server) setupServerFunc(Zone *Zone) func(dns.ResponseWriter, *dns.Msg) {
+	return func(w dns.ResponseWriter, r *dns.Msg) {
+		srv.serve(w, r, Zone)
+	}
+}
+
+func (srv *Server) listenAndServe(ip string) {
+
+	prots := []string{"udp", "tcp"}
+
+	for _, prot := range prots {
+		go func(p string) {
+			server := &dns.Server{Addr: ip, Net: p}
+
+			log.Printf("Opening on %s %s", ip, p)
+			if err := server.ListenAndServe(); err != nil {
+				log.Fatalf("geodns: failed to setup %s %s: %s", ip, p, err)
+			}
+			log.Fatalf("geodns: ListenAndServe unexpectedly returned")
+		}(prot)
+	}
+}
+
+func (srv *Server) addHandler(zones Zones, name string, config *Zone) {
+	oldZone := zones[name]
+	// across the recconfiguration keep a reference to all healthchecks to ensure
+	// the global map doesn't get destroyed
+	healthTestRunner.refAllGlobalHealthChecks(name, true)
+	defer healthTestRunner.refAllGlobalHealthChecks(name, false)
+	if oldZone != nil {
+		oldZone.StartStopHealthChecks(false, nil)
+	}
+	config.SetupMetrics(oldZone)
+	zones[name] = config
+	config.StartStopHealthChecks(true, oldZone)
+	dns.HandleFunc(name, srv.setupServerFunc(config))
+}
+
+func (srv *Server) zonesReader(dirName string, zones Zones) {
+	for {
+		srv.zonesReadDir(dirName, zones)
+		time.Sleep(5 * time.Second)
+	}
+}

+ 5 - 2
service/run

@@ -20,12 +20,15 @@ if [ -e env/ID ]; then
   fi
 fi
 
-
 CONFIG=dns
 if [ -e env/CONFIG ]; then
   CONFIG=`head -1 env/CONFIG`
 fi
 
+if [ -e env/ARGS ]; then
+  XARGS=`cat env/ARGS`
+fi
+
 ulimit -n 64000
 
-exec softlimit -d500000000 ./geodns $INTERFACE $ID --config="$CONFIG"
+exec softlimit -d500000000 ./geodns $INTERFACE $ID --config="$CONFIG" $XARGS

+ 2 - 2
stathat.go

@@ -6,8 +6,8 @@ import (
 	"strings"
 	"time"
 
-	"github.com/abh/geodns/Godeps/_workspace/src/github.com/rcrowley/go-metrics"
-	"github.com/abh/geodns/Godeps/_workspace/src/github.com/stathat/go"
+	"github.com/rcrowley/go-metrics"
+	"github.com/stathat/go"
 )
 
 func (zs *Zones) statHatPoster() {

+ 1 - 1
targeting_test.go

@@ -1,7 +1,7 @@
 package main
 
 import (
-	. "github.com/abh/geodns/Godeps/_workspace/src/gopkg.in/check.v1"
+	. "gopkg.in/check.v1"
 	"net"
 )
 

+ 46 - 16
templates.go

@@ -3,6 +3,7 @@ package main
 import (
 	"bytes"
 	"compress/gzip"
+	"encoding/base64"
 	"io/ioutil"
 	"net/http"
 	"os"
@@ -19,14 +20,20 @@ type _escStaticFS struct{}
 
 var _escStatic _escStaticFS
 
+type _escDirectory struct {
+	fs   http.FileSystem
+	name string
+}
+
 type _escFile struct {
 	compressed string
 	size       int64
+	modtime    int64
 	local      string
 	isDir      bool
 
-	data []byte
 	once sync.Once
+	data []byte
 	name string
 }
 
@@ -50,7 +57,8 @@ func (_escStaticFS) prepare(name string) (*_escFile, error) {
 			return
 		}
 		var gr *gzip.Reader
-		gr, err = gzip.NewReader(bytes.NewBufferString(f.compressed))
+		b64 := base64.NewDecoder(base64.StdEncoding, bytes.NewBufferString(f.compressed))
+		gr, err = gzip.NewReader(b64)
 		if err != nil {
 			return
 		}
@@ -70,6 +78,10 @@ func (fs _escStaticFS) Open(name string) (http.File, error) {
 	return f.File()
 }
 
+func (dir _escDirectory) Open(name string) (http.File, error) {
+	return dir.fs.Open(dir.name + name)
+}
+
 func (f *_escFile) File() (http.File, error) {
 	type httpFile struct {
 		*bytes.Reader
@@ -106,7 +118,7 @@ func (f *_escFile) Mode() os.FileMode {
 }
 
 func (f *_escFile) ModTime() time.Time {
-	return time.Time{}
+	return time.Unix(f.modtime, 0)
 }
 
 func (f *_escFile) IsDir() bool {
@@ -126,6 +138,15 @@ func FS(useLocal bool) http.FileSystem {
 	return _escStatic
 }
 
+// Dir returns a http.Filesystem for the embedded assets on a given prefix dir.
+// If useLocal is true, the filesystem's contents are instead used.
+func Dir(useLocal bool, name string) http.FileSystem {
+	if useLocal {
+		return _escDirectory{fs: _escLocal, name: name}
+	}
+	return _escDirectory{fs: _escStatic, name: name}
+}
+
 // FSByte returns the named file from the embedded assets. If useLocal is
 // true, the filesystem's contents are instead used.
 func FSByte(useLocal bool, name string) ([]byte, error) {
@@ -134,7 +155,9 @@ func FSByte(useLocal bool, name string) ([]byte, error) {
 		if err != nil {
 			return nil, err
 		}
-		return ioutil.ReadAll(f)
+		b, err := ioutil.ReadAll(f)
+		f.Close()
+		return b, err
 	}
 	f, err := _escStatic.prepare(name)
 	if err != nil {
@@ -166,18 +189,25 @@ func FSMustString(useLocal bool, name string) string {
 var _escData = map[string]*_escFile{
 
 	"/templates/status.html": {
-		local: "templates/status.html",
-		size:  3181,
-		compressed: "" +
-			"\x1f\x8b\b\x00\x00\tn\x88\x00\xff\xb4WMS\xeb6\x14]'\xbfB\xe3)\xbb\"\x13Jf\x1a\xeax\x03\f]4\x14\x9a\xb6\x8b\xb7S\xac\x9b\xd8\xf3\x1c)OR\b\xbcL\xfe\xfb\xbb\x92?b;&\x04\xbfa\x83\xe5#\x9d+\x9d{O\xaeL\x10\x9be\x1a\x0610\x1e\x06&1)\x84\xf7 o\x1f\xa6d\xbb%\xf4\u007fP:\x91\x82" +
-			"\xecv\x81\x9fM\xf6\x834\x11_I\xac`>\xf6|_\x80\xe1\x82љ\x94F\x1b\xc5V\x11\x174\x92K\xdfl\x12c@\x9d\x97\x13\xfe%\xfd\x8d\x0e\xfcHk\xbf\xc4\xceq\xe5,\x11\xc0\xe92A\x9a\xd6\x1eQ\x90\x8e=m^S\xd01\x80\xf1N\xdd\xcf\x01\x1bf\xa2\xb8\xd8\b\xd4:\x05&\xf6\xbb\x1d\xddĽ\x85\xfd\x9e\xe1\xf4\xbb\x14" +
-			" \xd8\x12~űM\v(\xb2%s)\xcc\xf9\x06\x92El\xae\xc9L\xa6\xfc\x0f\xb2\xeb\a~N\vf\x92\xbf\x86\xfd~\xc0\x93g\x12\xa5L\xeb\xb1\x17!\x83\xa18\xe5ىx\x10ާr\xc6\xd2\xc0\xc7a}\xa5\x92\x1b\xbb\xa6W\xc5\xf4\x8a\x89\xdf\x1d\xda\v\f\x9b\xa5PLd/\xee/&W\xe1\xe9\x80篸#\a\xa1\x81#" +
-			"\xafgy\xca=q\xc0\vv\"\xe6\x92d\xa2\xbc\xfc@D\x1bf4\x96\x97\x97\xab\xc3\x01\xc1\\ա\xe1!4h\xc1&\x98\xf2\x12\xc1\x81r\x12jg\t\x9f֠\x12hl\xb9ݮT\"̜xg\xf4r\xee\x11\x9a\x9d\x8e\xe6\x8b\xe9?\xcc\xc0\xc0\xd9\xf0\x83\xa4a\x17Ҡ\x13ˊ\xaf\xf02\xf9\xeei\xeb\xf3\xb9\xc5\xfc3\xd1" +
-			"F.\x14[6\xf2:9Z!\x87\x8c.\xce\x1a\xc0\xe8\x00\xa0Mh\xc2^\xea\xc0\xd4\xf0[x~\xaf\xf6\xb9羽c\x01\xee\x11R\xa6\xb8\x94FQ̩u\xa9\x90\xeaU9\x91\xf5\x18\x99\xd1\x05\xe9\xc6\x1bu\xe5\x1d%\xbe\x95\x13\xf6\xf2\xf1ݲb\x91\x03\xb3\xd6\xdd\x1a\xf8ؑ\xda:\xd3\xd5Ou\xa6\xc2\x10\x15+\x17.\xfeo" +
-			"e\x92%\xec\x0f\xe5\xc4\xd0\f\xa5\xb7\xecujP\xd8b\u007f\xec\xf2'\xf6v\xc8ǔ\x99\xb9T\xcbf\xd0\x02o\tv\x98\x81\xf2\x89\xcd\xfb\v\xde\x0f:k\xe3\x9d\x12\xe0\xce\x1a\x988\xc4}\xe2l\xe4\xfa\xed\xda\xc0\x1e\x19戮,j\xc1\xac\xb7\xc9\xd3\xe34G\xb2\x1a\xe2\xc5\xfd\x8b\x91\xab\xbf1kxu_\x8f\t\xfd\xb7x\xdb\xed" +
-			"\xec\xb4bb\x01\xe8%\xa7\x84X\xcc&\xb0\xdaV\xf2:a2ej\v>\x1e\x16\xb3\xc5\xed\xe8\xd9\x1c>\xe0\xa0\xcc_ᠠ(\xc9A\xa7o\xf5\xe6\x04\xb0\xa6\x91~\xabџ\xcc\x19v\xe0\f\xba\x90j\xfd\xa4E\xf4\x9d\xfdx\xfa\x88\xf2;.t\x17\xf5Mީb\x0e\xf6\xebJl̈́u_$\xd7\xc2hg\xbd\x82\xfb\x17\x9b" +
-			"A:\xb5\x9f\x1a֍7ق\x8aM\x9d\v\x91\x9b\xccK:B\xbdVc֝\x19\xbaк\xf9+.l^\x8bָG\xf3\x10W\xd6\xcb.J\xae&o\x11\xee\x94-\x17\xfav\v\x82\xdbx\xc5 W\x9d&Д}\xe3\xb0Su\xe7\x01N\x15\x9e\x05?\xa2\xbc\x1aﳤ\xf7\xcbѾo\xee\xdbf\xf6a\x8c\xdd\xd2\xfe\x97\xd1" +
-			"\xff\x11\x00\x00\xff\xff\xb0\x9dR\xc9m\f\x00\x00",
+		local:   "templates/status.html",
+		size:    3181,
+		modtime: 1442049075,
+		compressed: `
+H4sIAAAJbogA/7RXTY/bNhA9W7+CEJpbl4rSGKhTWpdskB7qdFO3PfRGiWOLqES65HidreD/XpD6sCxr
+Ha+KXCzpkW/IN/M0lFmOZZGwHLhIGEosIPkI+v7TmlQVoX+CsVIrcjyyqB4MWCHV3yQ3sFmGUaQAheI0
+1RotGr7LhKKZLiM8SEQwd91A9Ib+QOMoszbqsLtMl6lUIGgpFc2sDYmBYhlafCrA5gAY3rqeBw4cs7xd
+CMy+AK5Oq11dxD8lwQwF/VcrULyE71FQlxYwpCIbrfDuAHKb4zuS6kL8RI4BixoaS7V4SoKACflIsoJb
+uwwzrZBLBSZ0A3mcfCx0ygsW5fFgptEHN2fWx+yOqx89OmPI0wLagfrB/96l2ggwIJrHTCsByoIIk2Dm
+eMZfZwxFy5Zqo0ktKmw2RCxytCxC0c1OYlJKdQ7NL6F4BFsBPyEsclsY7iX5vAcjYbBkVe2MVLgh4Sv6
+ZhMSWu+ONpPpbxwh9jZ8IWk+hRRPYjnxPV4t319dfb5tMX+WFvXW8HKQ19XVCnlk8frVAFhcAHQIrfiX
+c2CN4h4ev1b7xnP/fMUCIiSkS3Enja6kurUuPdJ5VW5kPWS4eE2m8RZTeVeJz+WEf3n5anWxyIVZz93K
+IiEfxzrT2//VmVpD9KzcuviPHcoSTpvyYmiN0nv+tEYj1fa07e4Vez7kQ8Fxo005DNriI8EuM9Bd8zj5
+SyvnXNfGJyXA75VhnrAI8/rO99s9wgmZN4jtTRrBnLfJ54d1g9Q1rCryHerdrzt0R/e7JaG/t0/Hoxs2
+XG2BEOqVEIe5BPbbSlMnQTJduIIv5+1oezqGLoefeAld/loHsbYkF51+1JsrQCMz+1yjv5kzn8CJp5DO
++smI6A/u4+klyj8IZaeoH/JuFXOx3lTiaCac+zK9V2i99VruLzyFYu0+NZwb39cTejb1LqwqIjcd/Xis
+3+wLY547M/Gh7fAtbm1+Fm1wjjYh3jov+yiNmqZF+F2OHOhVBUq4eO1No7qQMJT93mO36m4C3Cq8Dn5F
+eT/et5IedHenvnlqm/WHMYv8v4zgvwAAAP//sJ1SyW0MAAA=
+`,
 	},
 
 	"/": {

+ 0 - 0
Godeps/_workspace/src/github.com/abh/errorutil/README.md → vendor/github.com/abh/errorutil/README.md


+ 0 - 0
Godeps/_workspace/src/github.com/abh/errorutil/highlight.go → vendor/github.com/abh/errorutil/highlight.go


+ 0 - 0
Godeps/_workspace/src/github.com/abh/geoip/.gitignore → vendor/github.com/abh/geoip/.gitignore


+ 0 - 0
Godeps/_workspace/src/github.com/abh/geoip/.travis.yml → vendor/github.com/abh/geoip/.travis.yml


+ 0 - 0
Godeps/_workspace/src/github.com/abh/geoip/LICENSE → vendor/github.com/abh/geoip/LICENSE


+ 0 - 0
Godeps/_workspace/src/github.com/abh/geoip/README.md → vendor/github.com/abh/geoip/README.md


+ 0 - 0
Godeps/_workspace/src/github.com/abh/geoip/const.go → vendor/github.com/abh/geoip/const.go


+ 0 - 0
Godeps/_workspace/src/github.com/abh/geoip/db/.gitignore → vendor/github.com/abh/geoip/db/.gitignore


+ 0 - 0
Godeps/_workspace/src/github.com/abh/geoip/db/download → vendor/github.com/abh/geoip/db/download


+ 1 - 1
Godeps/_workspace/src/github.com/abh/geoip/ex/geoip-demo.go → vendor/github.com/abh/geoip/ex/geoip-demo.go

@@ -2,7 +2,7 @@ package main
 
 import (
 	"fmt"
-	"github.com/abh/geodns/Godeps/_workspace/src/github.com/abh/geoip"
+	"github.com/abh/geoip"
 )
 
 func main() {

+ 0 - 0
Godeps/_workspace/src/github.com/abh/geoip/geoip.go → vendor/github.com/abh/geoip/geoip.go


+ 1 - 1
Godeps/_workspace/src/github.com/abh/geoip/geoip_test.go → vendor/github.com/abh/geoip/geoip_test.go

@@ -4,7 +4,7 @@ import (
 	"fmt"
 	"testing"
 
-	. "github.com/abh/geodns/Godeps/_workspace/src/gopkg.in/check.v1"
+	. "gopkg.in/check.v1"
 )
 
 // Hook up gocheck into the gotest runner.

+ 0 - 0
Godeps/_workspace/src/github.com/abh/geoip/test-db/GeoIP.dat → vendor/github.com/abh/geoip/test-db/GeoIP.dat


+ 0 - 0
Godeps/_workspace/src/github.com/abh/geoip/test-db/GeoIPCity.dat → vendor/github.com/abh/geoip/test-db/GeoIPCity.dat


+ 0 - 0
Godeps/_workspace/src/github.com/abh/geoip/test-db/GeoIPRegion.dat → vendor/github.com/abh/geoip/test-db/GeoIPRegion.dat


+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/.gitignore → vendor/github.com/miekg/dns/.gitignore


+ 2 - 2
Godeps/_workspace/src/github.com/miekg/dns/.travis.yml → vendor/github.com/miekg/dns/.travis.yml

@@ -1,7 +1,7 @@
 language: go
 sudo: false
 go:
-  - 1.4
   - 1.5
+  - 1.6
 script:
-  - go test -race -bench=.
+  - go test -race -v -bench=.

+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/AUTHORS → vendor/github.com/miekg/dns/AUTHORS


+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/CONTRIBUTORS → vendor/github.com/miekg/dns/CONTRIBUTORS


+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/COPYRIGHT → vendor/github.com/miekg/dns/COPYRIGHT


+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/LICENSE → vendor/github.com/miekg/dns/LICENSE


+ 12 - 3
Godeps/_workspace/src/github.com/miekg/dns/README.md → vendor/github.com/miekg/dns/README.md

@@ -10,9 +10,9 @@ 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.
+We try to keep the "master" branch as sane as possible and at the bleeding edge
+of standards, avoiding breaking changes wherever reasonable. We support the last
+two versions of Go, currently: 1.4 and 1.5.
 
 # Goals
 
@@ -33,6 +33,7 @@ A not-so-up-to-date-list-that-may-be-actually-current:
 * https://github.com/fcambus/rrda
 * https://github.com/kenshinx/godns
 * https://github.com/skynetservices/skydns
+* https://github.com/hashicorp/consul
 * https://github.com/DevelopersPL/godnsagent
 * https://github.com/duedil-ltd/discodns
 * https://github.com/StalkR/dns-reverse-proxy
@@ -42,6 +43,11 @@ A not-so-up-to-date-list-that-may-be-actually-current:
 * https://play.google.com/store/apps/details?id=com.turbobytes.dig
 * https://github.com/fcambus/statzone
 * https://github.com/benschw/dns-clb-go
+* https://github.com/corny/dnscheck for http://public-dns.info/
+* https://namesmith.io
+* https://github.com/miekg/unbound
+* https://github.com/miekg/exdns
+* https://dnslookup.org
 
 Send pull request if you want to be listed here.
 
@@ -58,6 +64,7 @@ Send pull request if you want to be listed here.
 * EDNS0, NSID;
 * AXFR/IXFR;
 * TSIG, SIG(0);
+* DNS over TLS: optional encrypted connection between client and server;
 * DNS name compression;
 * Depends only on the standard library.
 
@@ -123,6 +130,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
 * 6605 - ECDSA
 * 6725 - IANA Registry Update
 * 6742 - ILNP DNS
+* 6840 - Clarifications and Implementation Notes for DNS Security
 * 6844 - CAA record
 * 6891 - EDNS0 update
 * 6895 - DNS IANA considerations
@@ -131,6 +139,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
 * 7314 - DNS (EDNS) EXPIRE Option
 * 7553 - URI record
 * xxxx - EDNS0 DNS Update Lease (draft)
+* yyyy - DNS over TLS: Initiation and Performance Considerations (draft)
 
 ## Loosely based upon
 

+ 90 - 32
Godeps/_workspace/src/github.com/miekg/dns/client.go → vendor/github.com/miekg/dns/client.go

@@ -4,6 +4,7 @@ package dns
 
 import (
 	"bytes"
+	"crypto/tls"
 	"io"
 	"net"
 	"time"
@@ -24,8 +25,9 @@ type Conn struct {
 
 // 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)
+	Net            string            // if "tcp" or "tcp-tls" (DNS over TLS) a TCP query will be initiated, otherwise an UDP one (default is "" for UDP)
 	UDPSize        uint16            // minimum receive buffer for UDP messages
+	TLSConfig      *tls.Config       // TLS connection configuration
 	DialTimeout    time.Duration     // net.DialTimeout, defaults to 2 seconds
 	ReadTimeout    time.Duration     // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds
 	WriteTimeout   time.Duration     // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds
@@ -37,14 +39,7 @@ type Client struct {
 // 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()
-//
+// See client.Exchange for more information on setting larger buffer sizes.
 func Exchange(m *Msg, a string) (r *Msg, err error) {
 	var co *Conn
 	co, err = DialTimeout("udp", a, dnsTimeout)
@@ -53,8 +48,6 @@ func Exchange(m *Msg, a string) (r *Msg, err error) {
 	}
 
 	defer co.Close()
-	co.SetReadDeadline(time.Now().Add(dnsTimeout))
-	co.SetWriteDeadline(time.Now().Add(dnsTimeout))
 
 	opt := m.IsEdns0()
 	// If EDNS0 is used use that for size.
@@ -62,9 +55,12 @@ func Exchange(m *Msg, a string) (r *Msg, err error) {
 		co.UDPSize = opt.UDPSize()
 	}
 
+	co.SetWriteDeadline(time.Now().Add(dnsTimeout))
 	if err = co.WriteMsg(m); err != nil {
 		return nil, err
 	}
+
+	co.SetReadDeadline(time.Now().Add(dnsTimeout))
 	r, err = co.ReadMsg()
 	if err == nil && r.Id != m.Id {
 		err = ErrId
@@ -103,6 +99,10 @@ func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) {
 //
 // Exchange does not retry a failed query, nor will it fall back to TCP in
 // case of truncation.
+// It is up to the caller to create a message that allows for larger responses to be
+// returned. Specifically this means adding an EDNS0 OPT RR that will advertise a larger
+// buffer, see SetEdns0. Messsages without an OPT RR will fallback to the historic limit
+// of 512 bytes.
 func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
 	if !c.SingleInflight {
 		return c.exchange(m, a)
@@ -151,11 +151,31 @@ func (c *Client) writeTimeout() time.Duration {
 
 func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
 	var co *Conn
-	if c.Net == "" {
-		co, err = DialTimeout("udp", a, c.dialTimeout())
+	network := "udp"
+	tls := false
+
+	switch c.Net {
+	case "tcp-tls":
+		network = "tcp"
+		tls = true
+	case "tcp4-tls":
+		network = "tcp4"
+		tls = true
+	case "tcp6-tls":
+		network = "tcp6"
+		tls = true
+	default:
+		if c.Net != "" {
+			network = c.Net
+		}
+	}
+
+	if tls {
+		co, err = DialTimeoutWithTLS(network, a, c.TLSConfig, c.dialTimeout())
 	} else {
-		co, err = DialTimeout(c.Net, a, c.dialTimeout())
+		co, err = DialTimeout(network, a, c.dialTimeout())
 	}
+
 	if err != nil {
 		return nil, 0, err
 	}
@@ -171,13 +191,13 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro
 		co.UDPSize = c.UDPSize
 	}
 
-	co.SetReadDeadline(time.Now().Add(c.readTimeout()))
-	co.SetWriteDeadline(time.Now().Add(c.writeTimeout()))
-
 	co.TsigSecret = c.TsigSecret
+	co.SetWriteDeadline(time.Now().Add(c.writeTimeout()))
 	if err = co.WriteMsg(m); err != nil {
 		return nil, 0, err
 	}
+
+	co.SetReadDeadline(time.Now().Add(c.readTimeout()))
 	r, err = co.ReadMsg()
 	if err == nil && r.Id != m.Id {
 		err = ErrId
@@ -196,6 +216,12 @@ func (co *Conn) ReadMsg() (*Msg, error) {
 
 	m := new(Msg)
 	if err := m.Unpack(p); err != nil {
+		// If ErrTruncated was returned, we still want to allow the user to use
+		// the message, but naively they can just check err if they don't want
+		// to use a truncated message
+		if err == ErrTruncated {
+			return m, err
+		}
 		return nil, err
 	}
 	if t := m.IsTsig(); t != nil {
@@ -218,21 +244,26 @@ func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) {
 		err error
 	)
 
-	if t, ok := co.Conn.(*net.TCPConn); ok {
+	switch t := co.Conn.(type) {
+	case *net.TCPConn, *tls.Conn:
+		r := t.(io.Reader)
+
 		// First two bytes specify the length of the entire message.
-		l, err := tcpMsgLen(t)
+		l, err := tcpMsgLen(r)
 		if err != nil {
 			return nil, err
 		}
 		p = make([]byte, l)
-		n, err = tcpRead(t, p)
-	} else {
+		n, err = tcpRead(r, p)
+		co.rtt = time.Since(co.t)
+	default:
 		if co.UDPSize > MinMsgSize {
 			p = make([]byte, co.UDPSize)
 		} else {
 			p = make([]byte, MinMsgSize)
 		}
 		n, err = co.Read(p)
+		co.rtt = time.Since(co.t)
 	}
 
 	if err != nil {
@@ -251,7 +282,7 @@ func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) {
 }
 
 // tcpMsgLen is a helper func to read first two bytes of stream as uint16 packet length.
-func tcpMsgLen(t *net.TCPConn) (int, error) {
+func tcpMsgLen(t io.Reader) (int, error) {
 	p := []byte{0, 0}
 	n, err := t.Read(p)
 	if err != nil {
@@ -268,7 +299,7 @@ func tcpMsgLen(t *net.TCPConn) (int, error) {
 }
 
 // tcpRead calls TCPConn.Read enough times to fill allocated buffer.
-func tcpRead(t *net.TCPConn, p []byte) (int, error) {
+func tcpRead(t io.Reader, p []byte) (int, error) {
 	n, err := t.Read(p)
 	if err != nil {
 		return n, err
@@ -291,27 +322,28 @@ func (co *Conn) Read(p []byte) (n int, err error) {
 	if len(p) < 2 {
 		return 0, io.ErrShortBuffer
 	}
-	if t, ok := co.Conn.(*net.TCPConn); ok {
-		l, err := tcpMsgLen(t)
+	switch t := co.Conn.(type) {
+	case *net.TCPConn, *tls.Conn:
+		r := t.(io.Reader)
+
+		l, err := tcpMsgLen(r)
 		if err != nil {
 			return 0, err
 		}
 		if l > len(p) {
 			return int(l), io.ErrShortBuffer
 		}
-		return tcpRead(t, p[:l])
+		return tcpRead(r, p[:l])
 	}
 	// UDP connection
 	n, err = co.Conn.Read(p)
 	if err != nil {
 		return n, err
 	}
-
-	co.rtt = time.Since(co.t)
 	return n, err
 }
 
-// WriteMsg sends a message throught the connection co.
+// WriteMsg sends a message through the connection co.
 // If the message m contains a TSIG record the transaction
 // signature is calculated.
 func (co *Conn) WriteMsg(m *Msg) (err error) {
@@ -322,7 +354,7 @@ func (co *Conn) WriteMsg(m *Msg) (err error) {
 			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
+		// Set for the next read, although only used in zone transfers
 		co.tsigRequestMAC = mac
 	} else {
 		out, err = m.Pack()
@@ -339,7 +371,10 @@ func (co *Conn) WriteMsg(m *Msg) (err error) {
 
 // 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 {
+	switch t := co.Conn.(type) {
+	case *net.TCPConn, *tls.Conn:
+		w := t.(io.Writer)
+
 		lp := len(p)
 		if lp < 2 {
 			return 0, io.ErrShortBuffer
@@ -350,7 +385,7 @@ func (co *Conn) Write(p []byte) (n int, err error) {
 		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))
+		n, err := io.Copy(w, bytes.NewReader(p))
 		return int(n), err
 	}
 	n, err = co.Conn.(*net.UDPConn).Write(p)
@@ -376,3 +411,26 @@ func DialTimeout(network, address string, timeout time.Duration) (conn *Conn, er
 	}
 	return conn, nil
 }
+
+// DialWithTLS connects to the address on the named network with TLS.
+func DialWithTLS(network, address string, tlsConfig *tls.Config) (conn *Conn, err error) {
+	conn = new(Conn)
+	conn.Conn, err = tls.Dial(network, address, tlsConfig)
+	if err != nil {
+		return nil, err
+	}
+	return conn, nil
+}
+
+// DialTimeoutWithTLS acts like DialWithTLS but takes a timeout.
+func DialTimeoutWithTLS(network, address string, tlsConfig *tls.Config, timeout time.Duration) (conn *Conn, err error) {
+	var dialer net.Dialer
+	dialer.Timeout = timeout
+
+	conn = new(Conn)
+	conn.Conn, err = tls.DialWithDialer(&dialer, network, address, tlsConfig)
+	if err != nil {
+		return nil, err
+	}
+	return conn, nil
+}

+ 191 - 58
Godeps/_workspace/src/github.com/miekg/dns/client_test.go → vendor/github.com/miekg/dns/client_test.go

@@ -1,6 +1,9 @@
 package dns
 
 import (
+	"crypto/tls"
+	"fmt"
+	"net"
 	"strconv"
 	"testing"
 	"time"
@@ -12,7 +15,7 @@ func TestClientSync(t *testing.T) {
 
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	if err != nil {
-		t.Fatalf("Unable to run test server: %v", err)
+		t.Fatalf("unable to run test server: %v", err)
 	}
 	defer s.Shutdown()
 
@@ -37,13 +40,50 @@ func TestClientSync(t *testing.T) {
 	}
 }
 
+func TestClientTLSSync(t *testing.T) {
+	HandleFunc("miek.nl.", HelloServer)
+	defer HandleRemove("miek.nl.")
+
+	cert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock)
+	if err != nil {
+		t.Fatalf("unable to build certificate: %v", err)
+	}
+
+	config := tls.Config{
+		Certificates: []tls.Certificate{cert},
+	}
+
+	s, addrstr, err := RunLocalTLSServer("127.0.0.1:0", &config)
+	if err != nil {
+		t.Fatalf("unable to run test server: %v", err)
+	}
+	defer s.Shutdown()
+
+	m := new(Msg)
+	m.SetQuestion("miek.nl.", TypeSOA)
+
+	c := new(Client)
+	c.Net = "tcp-tls"
+	c.TLSConfig = &tls.Config{
+		InsecureSkipVerify: true,
+	}
+
+	r, _, err := c.Exchange(m, addrstr)
+	if err != nil {
+		t.Errorf("failed to exchange: %v", err)
+	}
+	if r != nil && r.Rcode != RcodeSuccess {
+		t.Errorf("failed to get an valid answer\n%v", r)
+	}
+}
+
 func TestClientSyncBadId(t *testing.T) {
 	HandleFunc("miek.nl.", HelloServerBadId)
 	defer HandleRemove("miek.nl.")
 
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	if err != nil {
-		t.Fatalf("Unable to run test server: %v", err)
+		t.Fatalf("unable to run test server: %v", err)
 	}
 	defer s.Shutdown()
 
@@ -66,7 +106,7 @@ func TestClientEDNS0(t *testing.T) {
 
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	if err != nil {
-		t.Fatalf("Unable to run test server: %v", err)
+		t.Fatalf("unable to run test server: %v", err)
 	}
 	defer s.Shutdown()
 
@@ -113,7 +153,7 @@ func TestClientEDNS0Local(t *testing.T) {
 
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	if err != nil {
-		t.Fatalf("Unable to run test server: %s", err)
+		t.Fatalf("unable to run test server: %s", err)
 	}
 	defer s.Shutdown()
 
@@ -161,58 +201,8 @@ func TestClientEDNS0Local(t *testing.T) {
 	}
 }
 
-func TestSingleInflight(t *testing.T) {
-	// Test is inherently racy, because queries might actually be returned before the test
-	// is over, leading to multiple queries even with SingleInflight. This ofcourse then
-	// leads to diff. rrts and the test fails. Number of tests is now 3, to lower the chance
-	// for the race to hit.
-	HandleFunc("miek.nl.", HelloServer)
-	defer HandleRemove("miek.nl.")
-
-	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
-	if err != nil {
-		t.Fatalf("Unable to run test server: %v", err)
-	}
-	defer s.Shutdown()
-
-	m := new(Msg)
-	m.SetQuestion("miek.nl.", TypeDNSKEY)
-
-	c := new(Client)
-	c.SingleInflight = true
-	nr := 3
-	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 chances that this is a coincidence is small.
-Loop:
-	for {
-		select {
-		case rtt := <-ch:
-			if i == 0 {
-				first = rtt
-			} else {
-				if first != rtt {
-					t.Errorf("all rtts should be equal, got %d want %d", rtt, first)
-				}
-			}
-			i++
-			if i == nr {
-				break Loop
-			}
-		}
-	}
-}
-
-// ExampleUpdateLeaseTSIG shows how to update a lease signed with TSIG.
-func ExampleUpdateLeaseTSIG(t *testing.T) {
+// ExampleTsigSecret_updateLeaseTSIG shows how to update a lease signed with TSIG
+func ExampleTsigSecret_updateLeaseTSIG() {
 	m := new(Msg)
 	m.SetUpdate("t.local.ip6.io.")
 	rr, _ := NewRR("t.local.ip6.io. 30 A 127.0.0.1")
@@ -235,7 +225,7 @@ func ExampleUpdateLeaseTSIG(t *testing.T) {
 
 	_, _, err := c.Exchange(m, "127.0.0.1:53")
 	if err != nil {
-		t.Error(err)
+		panic(err)
 	}
 }
 
@@ -246,7 +236,7 @@ func TestClientConn(t *testing.T) {
 	// This uses TCP just to make it slightly different than TestClientSync
 	s, addrstr, err := RunLocalTCPServer("127.0.0.1:0")
 	if err != nil {
-		t.Fatalf("Unable to run test server: %v", err)
+		t.Fatalf("unable to run test server: %v", err)
 	}
 	defer s.Shutdown()
 
@@ -286,3 +276,146 @@ func TestClientConn(t *testing.T) {
 		t.Errorf("unable to unpack message fully: %v", err)
 	}
 }
+
+func TestTruncatedMsg(t *testing.T) {
+	m := new(Msg)
+	m.SetQuestion("miek.nl.", TypeSRV)
+	cnt := 10
+	for i := 0; i < cnt; i++ {
+		r := &SRV{
+			Hdr:    RR_Header{Name: m.Question[0].Name, Rrtype: TypeSRV, Class: ClassINET, Ttl: 0},
+			Port:   uint16(i + 8000),
+			Target: "target.miek.nl.",
+		}
+		m.Answer = append(m.Answer, r)
+
+		re := &A{
+			Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeA, Class: ClassINET, Ttl: 0},
+			A:   net.ParseIP(fmt.Sprintf("127.0.0.%d", i)).To4(),
+		}
+		m.Extra = append(m.Extra, re)
+	}
+	buf, err := m.Pack()
+	if err != nil {
+		t.Errorf("failed to pack: %v", err)
+	}
+
+	r := new(Msg)
+	if err = r.Unpack(buf); err != nil {
+		t.Errorf("unable to unpack message: %v", err)
+	}
+	if len(r.Answer) != cnt {
+		t.Logf("answer count after regular unpack doesn't match: %d", len(r.Answer))
+		t.Fail()
+	}
+	if len(r.Extra) != cnt {
+		t.Logf("extra count after regular unpack doesn't match: %d", len(r.Extra))
+		t.Fail()
+	}
+
+	m.Truncated = true
+	buf, err = m.Pack()
+	if err != nil {
+		t.Errorf("failed to pack truncated: %v", err)
+	}
+
+	r = new(Msg)
+	if err = r.Unpack(buf); err != nil && err != ErrTruncated {
+		t.Errorf("unable to unpack truncated message: %v", err)
+	}
+	if !r.Truncated {
+		t.Log("truncated message wasn't unpacked as truncated")
+		t.Fail()
+	}
+	if len(r.Answer) != cnt {
+		t.Logf("answer count after truncated unpack doesn't match: %d", len(r.Answer))
+		t.Fail()
+	}
+	if len(r.Extra) != cnt {
+		t.Logf("extra count after truncated unpack doesn't match: %d", len(r.Extra))
+		t.Fail()
+	}
+
+	// Now we want to remove almost all of the extra records
+	// We're going to loop over the extra to get the count of the size of all
+	// of them
+	off := 0
+	buf1 := make([]byte, m.Len())
+	for i := 0; i < len(m.Extra); i++ {
+		off, err = PackRR(m.Extra[i], buf1, off, nil, m.Compress)
+		if err != nil {
+			t.Errorf("failed to pack extra: %v", err)
+		}
+	}
+
+	// Remove all of the extra bytes but 10 bytes from the end of buf
+	off -= 10
+	buf1 = buf[:len(buf)-off]
+
+	r = new(Msg)
+	if err = r.Unpack(buf1); err != nil && err != ErrTruncated {
+		t.Errorf("unable to unpack cutoff message: %v", err)
+	}
+	if !r.Truncated {
+		t.Log("truncated cutoff message wasn't unpacked as truncated")
+		t.Fail()
+	}
+	if len(r.Answer) != cnt {
+		t.Logf("answer count after cutoff unpack doesn't match: %d", len(r.Answer))
+		t.Fail()
+	}
+	if len(r.Extra) != 0 {
+		t.Logf("extra count after cutoff unpack is not zero: %d", len(r.Extra))
+		t.Fail()
+	}
+
+	// Now we want to remove almost all of the answer records too
+	buf1 = make([]byte, m.Len())
+	as := 0
+	for i := 0; i < len(m.Extra); i++ {
+		off1 := off
+		off, err = PackRR(m.Extra[i], buf1, off, nil, m.Compress)
+		as = off - off1
+		if err != nil {
+			t.Errorf("failed to pack extra: %v", err)
+		}
+	}
+
+	// Keep exactly one answer left
+	// This should still cause Answer to be nil
+	off -= as
+	buf1 = buf[:len(buf)-off]
+
+	r = new(Msg)
+	if err = r.Unpack(buf1); err != nil && err != ErrTruncated {
+		t.Errorf("unable to unpack cutoff message: %v", err)
+	}
+	if !r.Truncated {
+		t.Log("truncated cutoff message wasn't unpacked as truncated")
+		t.Fail()
+	}
+	if len(r.Answer) != 0 {
+		t.Logf("answer count after second cutoff unpack is not zero: %d", len(r.Answer))
+		t.Fail()
+	}
+
+	// Now leave only 1 byte of the question
+	// Since the header is always 12 bytes, we just need to keep 13
+	buf1 = buf[:13]
+
+	r = new(Msg)
+	err = r.Unpack(buf1)
+	if err == nil || err == ErrTruncated {
+		t.Logf("error should not be ErrTruncated from question cutoff unpack: %v", err)
+		t.Fail()
+	}
+
+	// Finally, if we only have the header, we should still return an error
+	buf1 = buf[:12]
+
+	r = new(Msg)
+	if err = r.Unpack(buf1); err == nil || err != ErrTruncated {
+		t.Logf("error not ErrTruncated from header-only unpack: %v", err)
+		t.Fail()
+	}
+}

+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/clientconfig.go → vendor/github.com/miekg/dns/clientconfig.go


+ 4 - 9
Godeps/_workspace/src/github.com/miekg/dns/clientconfig_test.go → vendor/github.com/miekg/dns/clientconfig_test.go

@@ -22,13 +22,13 @@ nameserver 11.28.10.1` // <- NOTE: NO newline.
 func testConfig(t *testing.T, data string) {
 	tempDir, err := ioutil.TempDir("", "")
 	if err != nil {
-		t.Fatalf("TempDir: %v", err)
+		t.Fatalf("tempDir: %v", err)
 	}
 	defer os.RemoveAll(tempDir)
 
 	path := filepath.Join(tempDir, "resolv.conf")
 	if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil {
-		t.Fatalf("WriteFile: %v", err)
+		t.Fatalf("writeFile: %v", err)
 	}
 	cc, err := ClientConfigFromFile(path)
 	if err != nil {
@@ -46,10 +46,5 @@ func testConfig(t *testing.T, data string) {
 	}
 }
 
-func TestNameserver(t *testing.T) {
-	testConfig(t, normal)
-}
-
-func TestMissingFinalNewLine(t *testing.T) {
-	testConfig(t, missingNewline)
-}
+func TestNameserver(t *testing.T)          { testConfig(t, normal) }
+func TestMissingFinalNewLine(t *testing.T) { testConfig(t, missingNewline) }

+ 8 - 5
Godeps/_workspace/src/github.com/miekg/dns/defaults.go → vendor/github.com/miekg/dns/defaults.go

@@ -150,11 +150,14 @@ func (dns *Msg) IsEdns0() *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.
+// IsDomainName checks if s is a valid domain name, it returns the number of
+// labels and true, when a domain name is valid.  Note that non fully qualified
+// domain name is considered valid, in this case the last label is counted in
+// the number of labels.  When false is returned the number of labels is not
+// defined.  Also note that this function is extremely liberal; almost any
+// string is a valid domain name as the DNS is 8 bit protocol. It checks if each
+// label fits in 63 characters, but there is no length check for the entire
+// string s. I.e.  a domain name longer than 255 characters is considered valid.
 func IsDomainName(s string) (labels int, ok bool) {
 	_, labels, err := packDomainName(s, nil, 0, nil, false)
 	return labels, err == nil

+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/dns.go → vendor/github.com/miekg/dns/dns.go


+ 2 - 2
Godeps/_workspace/src/github.com/miekg/dns/dns_test.go → vendor/github.com/miekg/dns/dns_test.go

@@ -428,7 +428,7 @@ func TestToRFC3597(t *testing.T) {
 
 func TestNoRdataPack(t *testing.T) {
 	data := make([]byte, 1024)
-	for typ, fn := range typeToRR {
+	for typ, fn := range TypeToRR {
 		r := fn()
 		*r.Header() = RR_Header{Name: "miek.nl.", Rrtype: typ, Class: ClassINET, Ttl: 3600}
 		_, err := PackRR(r, data, 0, nil, false)
@@ -441,7 +441,7 @@ func TestNoRdataPack(t *testing.T) {
 // TODO(miek): fix dns buffer too small errors this throws
 func TestNoRdataUnpack(t *testing.T) {
 	data := make([]byte, 1024)
-	for typ, fn := range typeToRR {
+	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.

+ 15 - 10
Godeps/_workspace/src/github.com/miekg/dns/dnssec.go → vendor/github.com/miekg/dns/dnssec.go

@@ -104,7 +104,7 @@ const (
 )
 
 // The RRSIG needs to be converted to wireformat with some of
-// the rdata (the signature) missing. Use this struct to easy
+// the rdata (the signature) missing. Use this struct to ease
 // the conversion (and re-use the pack/unpack functions).
 type rrsigWireFmt struct {
 	TypeCovered uint16
@@ -248,13 +248,12 @@ func (d *DS) ToCDS() *CDS {
 	return c
 }
 
-// Sign signs an RRSet. The signature needs to be filled in with
-// the values: Inception, Expiration, KeyTag, SignerName and Algorithm.
-// The rest is copied from the RRset. Sign returns 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.
+// Sign signs an RRSet. The signature needs to be filled in with the values:
+// Inception, Expiration, KeyTag, SignerName and Algorithm.  The rest is copied
+// from the RRset. Sign returns a non-nill error when the signing went OK.
+// There is no check if RRSet is a proper (RFC 2181) RRSet.  If OrigTTL is non
+// zero, it is used as-is, otherwise the TTL of the RRset is used as the
+// OrigTTL.
 func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
 	if k == nil {
 		return ErrPrivKey
@@ -421,8 +420,8 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
 
 	sigbuf := rr.sigBuf()           // Get the binary signature data
 	if rr.Algorithm == PRIVATEDNS { // PRIVATEOID
-		// TODO(mg)
-		// remove the domain name and assume its our
+		// TODO(miek)
+		// remove the domain name and assume its ours?
 	}
 
 	hash, ok := AlgorithmToHash[rr.Algorithm]
@@ -609,6 +608,12 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) {
 		//   NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
 		//   HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
 		//   SRV, DNAME, A6
+		//
+		// RFC 6840 - Clarifications and Implementation Notes for DNS Security (DNSSEC):
+		//	Section 6.2 of [RFC4034] also erroneously lists HINFO as a record
+		//	that needs conversion to lowercase, and twice at that.  Since HINFO
+		//	records contain no domain names, they are not subject to case
+		//	conversion.
 		switch x := r1.(type) {
 		case *NS:
 			x.Ns = strings.ToLower(x.Ns)

+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/dnssec_keygen.go → vendor/github.com/miekg/dns/dnssec_keygen.go


+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/dnssec_keyscan.go → vendor/github.com/miekg/dns/dnssec_keyscan.go


+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/dnssec_privkey.go → vendor/github.com/miekg/dns/dnssec_privkey.go


+ 21 - 10
Godeps/_workspace/src/github.com/miekg/dns/dnssec_test.go → vendor/github.com/miekg/dns/dnssec_test.go

@@ -171,6 +171,17 @@ func TestSignVerify(t *testing.T) {
 	srv.Weight = 800
 	srv.Target = "web1.miek.nl."
 
+	hinfo := &HINFO{
+		Hdr: RR_Header{
+			Name:   "miek.nl.",
+			Rrtype: TypeHINFO,
+			Class:  ClassINET,
+			Ttl:    3789,
+		},
+		Cpu: "X",
+		Os:  "Y",
+	}
+
 	// With this key
 	key := new(DNSKEY)
 	key.Hdr.Rrtype = TypeDNSKEY
@@ -194,12 +205,12 @@ func TestSignVerify(t *testing.T) {
 	sig.SignerName = key.Hdr.Name
 	sig.Algorithm = RSASHA256
 
-	for _, r := range []RR{soa, soa1, srv} {
-		if sig.Sign(privkey.(*rsa.PrivateKey), []RR{r}) != nil {
-			t.Error("failure to sign the record")
+	for _, r := range []RR{soa, soa1, srv, hinfo} {
+		if err := sig.Sign(privkey.(*rsa.PrivateKey), []RR{r}); err != nil {
+			t.Error("failure to sign the record:", err)
 			continue
 		}
-		if sig.Verify(key, []RR{r}) != nil {
+		if err := sig.Verify(key, []RR{r}); err != nil {
 			t.Error("failure to validate")
 			continue
 		}
@@ -451,7 +462,7 @@ PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
 	}
 
 	if err := sig.Verify(eckey.(*DNSKEY), []RR{a}); err != nil {
-		t.Fatalf("Failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v",
+		t.Fatalf("failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v",
 			eckey.(*DNSKEY).String(),
 			a.String(),
 			sig.String(),
@@ -500,7 +511,7 @@ func TestSignVerifyECDSA2(t *testing.T) {
 
 	err = sig.Verify(key, []RR{srv})
 	if err != nil {
-		t.Logf("Failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v",
+		t.Logf("failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v",
 			key.String(),
 			srv.String(),
 			sig.String(),
@@ -554,7 +565,7 @@ PrivateKey: GU6SnQ/Ou+xC5RumuIUIuJZteXT2z0O/ok1s38Et6mQ=`
 		t.Fatal(err)
 	}
 	if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
-		t.Errorf("Failure to validate the spec RRSIG: %v", err)
+		t.Errorf("failure to validate the spec RRSIG: %v", err)
 	}
 
 	ourRRSIG := &RRSIG{
@@ -573,7 +584,7 @@ PrivateKey: GU6SnQ/Ou+xC5RumuIUIuJZteXT2z0O/ok1s38Et6mQ=`
 	}
 
 	if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
-		t.Errorf("Failure to validate our RRSIG: %v", err)
+		t.Errorf("failure to validate our RRSIG: %v", err)
 	}
 
 	// Signatures are randomized
@@ -630,7 +641,7 @@ PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
 		t.Fatal(err)
 	}
 	if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
-		t.Errorf("Failure to validate the spec RRSIG: %v", err)
+		t.Errorf("failure to validate the spec RRSIG: %v", err)
 	}
 
 	ourRRSIG := &RRSIG{
@@ -649,7 +660,7 @@ PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
 	}
 
 	if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
-		t.Errorf("Failure to validate our RRSIG: %v", err)
+		t.Errorf("failure to validate our RRSIG: %v", err)
 	}
 
 	// Signatures are randomized

+ 79 - 0
vendor/github.com/miekg/dns/dnsutil/util.go

@@ -0,0 +1,79 @@
+// Package dnsutil contains higher-level methods useful with the dns
+// package.  While package dns implements the DNS protocols itself,
+// these functions are related but not directly required for protocol
+// processing.  They are often useful in preparing input/output of the
+// functions in package dns.
+package dnsutil
+
+import (
+	"strings"
+
+	"github.com/miekg/dns"
+)
+
+// AddDomain adds origin to s if s is not already a FQDN.
+// Note that the result may not be a FQDN.  If origin does not end
+// with a ".", the result won't either.
+// This implements the zonefile convention (specified in RFC 1035,
+// Section "5.1. Format") that "@" represents the
+// apex (bare) domain. i.e. AddOrigin("@", "foo.com.") returns "foo.com.".
+func AddOrigin(s, origin string) string {
+	// ("foo.", "origin.") -> "foo." (already a FQDN)
+	// ("foo", "origin.") -> "foo.origin."
+	// ("foo"), "origin" -> "foo.origin"
+	// ("@", "origin.") -> "origin." (@ represents the apex (bare) domain)
+	// ("", "origin.") -> "origin." (not obvious)
+	// ("foo", "") -> "foo" (not obvious)
+
+	if dns.IsFqdn(s) {
+		return s // s is already a FQDN, no need to mess with it.
+	}
+	if len(origin) == 0 {
+		return s // Nothing to append.
+	}
+	if s == "@" || len(s) == 0 {
+		return origin // Expand apex.
+	}
+
+	if origin == "." {
+		return s + origin // AddOrigin(s, ".") is an expensive way to add a ".".
+	}
+
+	return s + "." + origin // The simple case.
+}
+
+// TrimDomainName trims origin from s if s is a subdomain.
+// This function will never return "", but returns "@" instead (@ represents the apex (bare) domain).
+func TrimDomainName(s, origin string) string {
+	// An apex (bare) domain is always returned as "@".
+	// If the return value ends in a ".", the domain was not the suffix.
+	// origin can end in "." or not. Either way the results should be the same.
+
+	if len(s) == 0 {
+		return "@" // Return the apex (@) rather than "".
+	}
+	// Someone is using TrimDomainName(s, ".") to remove a dot if it exists.
+	if origin == "." {
+		return strings.TrimSuffix(s, origin)
+	}
+
+	// Dude, you aren't even if the right subdomain!
+	if !dns.IsSubDomain(origin, s) {
+		return s
+	}
+
+	slabels := dns.Split(s)
+	olabels := dns.Split(origin)
+	m := dns.CompareDomainName(s, origin)
+	if len(olabels) == m {
+		if len(olabels) == len(slabels) {
+			return "@" // origin == s
+		}
+		if (s[0] == '.') && (len(slabels) == (len(olabels) + 1)) {
+			return "@" // TrimDomainName(".foo.", "foo.")
+		}
+	}
+
+	// Return the first (len-m) labels:
+	return s[:slabels[len(slabels)-m]-1]
+}

+ 130 - 0
vendor/github.com/miekg/dns/dnsutil/util_test.go

@@ -0,0 +1,130 @@
+package dnsutil
+
+import "testing"
+
+func TestAddOrigin(t *testing.T) {
+	var tests = []struct{ e1, e2, expected string }{
+		{"@", "example.com", "example.com"},
+		{"foo", "example.com", "foo.example.com"},
+		{"foo.", "example.com", "foo."},
+		{"@", "example.com.", "example.com."},
+		{"foo", "example.com.", "foo.example.com."},
+		{"foo.", "example.com.", "foo."},
+		// Oddball tests:
+		// In general origin should not be "" or "." but at least
+		// these tests verify we don't crash and will keep results
+		// from changing unexpectedly.
+		{"*.", "", "*."},
+		{"@", "", "@"},
+		{"foobar", "", "foobar"},
+		{"foobar.", "", "foobar."},
+		{"*.", ".", "*."},
+		{"@", ".", "."},
+		{"foobar", ".", "foobar."},
+		{"foobar.", ".", "foobar."},
+	}
+	for _, test := range tests {
+		actual := AddOrigin(test.e1, test.e2)
+		if test.expected != actual {
+			t.Errorf("AddOrigin(%#v, %#v) expected %#v, go %#v\n", test.e1, test.e2, test.expected, actual)
+		}
+	}
+}
+
+func TestTrimDomainName(t *testing.T) {
+
+	// Basic tests.
+	// Try trimming "example.com" and "example.com." from typical use cases.
+	var tests_examplecom = []struct{ experiment, expected string }{
+		{"foo.example.com", "foo"},
+		{"foo.example.com.", "foo"},
+		{".foo.example.com", ".foo"},
+		{".foo.example.com.", ".foo"},
+		{"*.example.com", "*"},
+		{"example.com", "@"},
+		{"example.com.", "@"},
+		{"com.", "com."},
+		{"foo.", "foo."},
+		{"serverfault.com.", "serverfault.com."},
+		{"serverfault.com", "serverfault.com"},
+		{".foo.ronco.com", ".foo.ronco.com"},
+		{".foo.ronco.com.", ".foo.ronco.com."},
+	}
+	for _, dom := range []string{"example.com", "example.com."} {
+		for i, test := range tests_examplecom {
+			actual := TrimDomainName(test.experiment, dom)
+			if test.expected != actual {
+				t.Errorf("%d TrimDomainName(%#v, %#v): expected (%v) got (%v)\n", i, test.experiment, dom, test.expected, actual)
+			}
+		}
+	}
+
+	// Paranoid tests.
+	// These test shouldn't be needed but I was weary of off-by-one errors.
+	// In theory, these can't happen because there are no single-letter TLDs,
+	// but it is good to exercize the code this way.
+	var tests = []struct{ experiment, expected string }{
+		{"", "@"},
+		{".", "."},
+		{"a.b.c.d.e.f.", "a.b.c.d.e"},
+		{"b.c.d.e.f.", "b.c.d.e"},
+		{"c.d.e.f.", "c.d.e"},
+		{"d.e.f.", "d.e"},
+		{"e.f.", "e"},
+		{"f.", "@"},
+		{".a.b.c.d.e.f.", ".a.b.c.d.e"},
+		{".b.c.d.e.f.", ".b.c.d.e"},
+		{".c.d.e.f.", ".c.d.e"},
+		{".d.e.f.", ".d.e"},
+		{".e.f.", ".e"},
+		{".f.", "@"},
+		{"a.b.c.d.e.f", "a.b.c.d.e"},
+		{"a.b.c.d.e.", "a.b.c.d.e."},
+		{"a.b.c.d.e", "a.b.c.d.e"},
+		{"a.b.c.d.", "a.b.c.d."},
+		{"a.b.c.d", "a.b.c.d"},
+		{"a.b.c.", "a.b.c."},
+		{"a.b.c", "a.b.c"},
+		{"a.b.", "a.b."},
+		{"a.b", "a.b"},
+		{"a.", "a."},
+		{"a", "a"},
+		{".a.b.c.d.e.f", ".a.b.c.d.e"},
+		{".a.b.c.d.e.", ".a.b.c.d.e."},
+		{".a.b.c.d.e", ".a.b.c.d.e"},
+		{".a.b.c.d.", ".a.b.c.d."},
+		{".a.b.c.d", ".a.b.c.d"},
+		{".a.b.c.", ".a.b.c."},
+		{".a.b.c", ".a.b.c"},
+		{".a.b.", ".a.b."},
+		{".a.b", ".a.b"},
+		{".a.", ".a."},
+		{".a", ".a"},
+	}
+	for _, dom := range []string{"f", "f."} {
+		for i, test := range tests {
+			actual := TrimDomainName(test.experiment, dom)
+			if test.expected != actual {
+				t.Errorf("%d TrimDomainName(%#v, %#v): expected (%v) got (%v)\n", i, test.experiment, dom, test.expected, actual)
+			}
+		}
+	}
+
+	// Test cases for bugs found in the wild.
+	// These test cases provide both origin, s, and the expected result.
+	// If you find a bug in the while, this is probably the easiest place
+	// to add it as a test case.
+	var tests_wild = []struct{ e1, e2, expected string }{
+		{"mathoverflow.net.", ".", "mathoverflow.net"},
+		{"mathoverflow.net", ".", "mathoverflow.net"},
+		{"", ".", "@"},
+		{"@", ".", "@"},
+	}
+	for i, test := range tests_wild {
+		actual := TrimDomainName(test.e1, test.e2)
+		if test.expected != actual {
+			t.Errorf("%d TrimDomainName(%#v, %#v): expected (%v) got (%v)\n", i, test.e1, test.e2, test.expected, actual)
+		}
+	}
+
+}

+ 3 - 3
Godeps/_workspace/src/github.com/miekg/dns/doc.go → vendor/github.com/miekg/dns/doc.go

@@ -101,7 +101,7 @@ 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.
+to a request.
 
      m := new(dns.Msg)
      m.SetEdns0(4096, true)
@@ -184,9 +184,9 @@ Basic use pattern validating and replying to a message that has TSIG set.
 	dns.HandleFunc(".", handleRequest)
 
 	func handleRequest(w dns.ResponseWriter, r *dns.Msg) {
-		m := new(Msg)
+		m := new(dns.Msg)
 		m.SetReply(r)
-		if r.IsTsig() {
+		if r.IsTsig() != nil {
 			if w.TsigStatus() == nil {
 				// *Msg r has an TSIG record and it was validated
 				m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())

+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/dyn_test.go → vendor/github.com/miekg/dns/dyn_test.go


+ 0 - 9
Godeps/_workspace/src/github.com/miekg/dns/edns.go → vendor/github.com/miekg/dns/edns.go

@@ -30,11 +30,6 @@ type OPT struct {
 	Option []EDNS0 `dns:"opt"`
 }
 
-// Header implements the RR interface.
-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() {
@@ -88,10 +83,6 @@ func (rr *OPT) len() int {
 	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.

+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/edns_test.go → vendor/github.com/miekg/dns/edns_test.go


+ 4 - 5
Godeps/_workspace/src/github.com/miekg/dns/example_test.go → vendor/github.com/miekg/dns/example_test.go

@@ -3,9 +3,10 @@ package dns_test
 import (
 	"errors"
 	"fmt"
-	"github.com/abh/geodns/Godeps/_workspace/src/github.com/miekg/dns"
 	"log"
 	"net"
+
+	"github.com/miekg/dns"
 )
 
 // Retrieve the MX records for miek.nl.
@@ -31,13 +32,11 @@ func ExampleMX() {
 
 // Retrieve the DNSKEY records of a zone and convert them
 // to DS records for SHA1, SHA256 and SHA384.
-func ExampleDS(zone string) {
+func ExampleDS() {
 	config, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
 	c := new(dns.Client)
 	m := new(dns.Msg)
-	if zone == "" {
-		zone = "miek.nl"
-	}
+	zone := "miek.nl"
 	m.SetQuestion(dns.Fqdn(zone), dns.TypeDNSKEY)
 	m.SetEdns0(4096, true)
 	r, _, err := c.Exchange(m, config.Servers[0]+":"+config.Port)

+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/format.go → vendor/github.com/miekg/dns/format.go


+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/fuzz_test.go → vendor/github.com/miekg/dns/fuzz_test.go


+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/idn/code_points.go → vendor/github.com/miekg/dns/idn/code_points.go


+ 1 - 1
Godeps/_workspace/src/github.com/miekg/dns/idn/example_test.go → vendor/github.com/miekg/dns/idn/example_test.go

@@ -2,7 +2,7 @@ package idn_test
 
 import (
 	"fmt"
-	"github.com/abh/geodns/Godeps/_workspace/src/github.com/miekg/dns/idn"
+	"github.com/miekg/dns/idn"
 )
 
 func ExampleToPunycode() {

+ 1 - 2
Godeps/_workspace/src/github.com/miekg/dns/idn/punycode.go → vendor/github.com/miekg/dns/idn/punycode.go

@@ -7,7 +7,7 @@ import (
 	"unicode"
 	"unicode/utf8"
 
-	"github.com/abh/geodns/Godeps/_workspace/src/github.com/miekg/dns"
+	"github.com/miekg/dns"
 )
 
 // Implementation idea from RFC itself and from from IDNA::Punycode created by
@@ -199,7 +199,6 @@ func needFromPunycode(s string) bool {
 			return true
 		}
 	}
-	panic("dns: not reached")
 }
 
 // encode transforms Unicode input bytes (that represent DNS label) into

+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/idn/punycode_test.go → vendor/github.com/miekg/dns/idn/punycode_test.go


+ 23 - 0
vendor/github.com/miekg/dns/issue_test.go

@@ -0,0 +1,23 @@
+package dns
+
+// Tests that solve that an specific issue.
+
+import "testing"
+
+func TestTCPRtt(t *testing.T) {
+	m := new(Msg)
+	m.RecursionDesired = true
+	m.SetQuestion("example.org.", TypeA)
+
+	c := &Client{}
+	for _, proto := range []string{"udp", "tcp"} {
+		c.Net = proto
+		_, rtt, err := c.Exchange(m, "8.8.4.4:53")
+		if err != nil {
+			t.Fatal(err)
+		}
+		if rtt == 0 {
+			t.Fatalf("expecting non zero rtt %s, got zero", c.Net)
+		}
+	}
+}

+ 7 - 1
Godeps/_workspace/src/github.com/miekg/dns/labels.go → vendor/github.com/miekg/dns/labels.go

@@ -4,9 +4,11 @@ package dns
 
 // SplitDomainName splits a name string into it's labels.
 // www.miek.nl. returns []string{"www", "miek", "nl"}
+// .www.miek.nl. returns []string{"", "www", "miek", "nl"},
 // The root label (.) returns nil. Note that using
 // strings.Split(s) will work in most cases, but does not handle
 // escaped dots (\.) for instance.
+// s must be a syntactically valid domain name, see IsDomainName.
 func SplitDomainName(s string) (labels []string) {
 	if len(s) == 0 {
 		return nil
@@ -45,6 +47,8 @@ func SplitDomainName(s string) (labels []string) {
 //
 // www.miek.nl. and miek.nl. have two labels in common: miek and nl
 // www.miek.nl. and www.bla.nl. have one label in common: nl
+//
+// s1 and s2 must be syntactically valid domain names.
 func CompareDomainName(s1, s2 string) (n int) {
 	s1 = Fqdn(s1)
 	s2 = Fqdn(s2)
@@ -85,6 +89,7 @@ func CompareDomainName(s1, s2 string) (n int) {
 }
 
 // CountLabel counts the the number of labels in the string s.
+// s must be a syntactically valid domain name.
 func CountLabel(s string) (labels int) {
 	if s == "." {
 		return
@@ -102,7 +107,8 @@ func CountLabel(s string) (labels int) {
 
 // Split splits a name s into its label indexes.
 // www.miek.nl. returns []int{0, 4, 9}, www.miek.nl also returns []int{0, 4, 9}.
-// The root name (.) returns nil. Also see SplitDomainName.
+// The root name (.) returns nil. Also see SplitDomainName. 
+// s must be a syntactically valid domain name.
 func Split(s string) []int {
 	if s == "." {
 		return nil

+ 18 - 19
Godeps/_workspace/src/github.com/miekg/dns/labels_test.go → vendor/github.com/miekg/dns/labels_test.go

@@ -1,8 +1,6 @@
 package dns
 
-import (
-	"testing"
-)
+import "testing"
 
 func TestCompareDomainName(t *testing.T) {
 	s1 := "www.miek.nl."
@@ -61,9 +59,9 @@ func TestSplit(t *testing.T) {
 
 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},
+		"www.miek.nl.": {0, 4, 9},
+		"www.miek.nl":  {0, 4, 9},
+		"nl":           {0},
 	}
 	for s, i := range splitter {
 		x := Split(s)
@@ -125,13 +123,14 @@ func TestCountLabel(t *testing.T) {
 
 func TestSplitDomainName(t *testing.T) {
 	labels := map[string][]string{
-		"miek.nl":       []string{"miek", "nl"},
+		"miek.nl":       {"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"},
+		"www.miek.nl.":  {"www", "miek", "nl"},
+		"www.miek.nl":   {"www", "miek", "nl"},
+		"www..miek.nl":  {"www", "", "miek", "nl"},
+		`www\.miek.nl`:  {`www\.miek`, "nl"},
+		`www\\.miek.nl`: {`www\\`, "miek", "nl"},
+		".www.miek.nl.": {"", "www", "miek", "nl"},
 	}
 domainLoop:
 	for domain, splits := range labels {
@@ -155,13 +154,13 @@ func TestIsDomainName(t *testing.T) {
 		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},
+		"..":               {false, 1},
+		"@.":               {true, 1},
+		"www.example.com":  {true, 3},
+		"www.e%ample.com":  {true, 3},
+		"www.example.com.": {true, 3},
+		"mi\\k.nl.":        {true, 2},
+		"mi\\k.nl":         {true, 2},
 	}
 	for d, ok := range names {
 		l, k := IsDomainName(d)

+ 53 - 138
Godeps/_workspace/src/github.com/miekg/dns/msg.go → vendor/github.com/miekg/dns/msg.go

@@ -54,6 +54,9 @@ var (
 	ErrSoa error = &Error{err: "no SOA"}
 	// ErrTime indicates a timing error in TSIG authentication.
 	ErrTime error = &Error{err: "bad time"}
+	// ErrTruncated indicates that we failed to unpack a truncated message.
+	// We unpacked as much as we had so Msg can still be used, if desired.
+	ErrTruncated error = &Error{err: "failed to unpack truncated message"}
 )
 
 // Id, by default, returns a 16 bits random number to be used as a
@@ -89,84 +92,6 @@ type Msg struct {
 	Extra    []RR       // Holds the RR(s) of the additional section.
 }
 
-// TypeToString is a map of strings for each RR wire type.
-var TypeToString = map[uint16]string{
-	TypeA:          "A",
-	TypeAAAA:       "AAAA",
-	TypeAFSDB:      "AFSDB",
-	TypeANY:        "ANY", // Meta RR
-	TypeATMA:       "ATMA",
-	TypeAXFR:       "AXFR", // Meta RR
-	TypeCAA:        "CAA",
-	TypeCDNSKEY:    "CDNSKEY",
-	TypeCDS:        "CDS",
-	TypeCERT:       "CERT",
-	TypeCNAME:      "CNAME",
-	TypeDHCID:      "DHCID",
-	TypeDLV:        "DLV",
-	TypeDNAME:      "DNAME",
-	TypeDNSKEY:     "DNSKEY",
-	TypeDS:         "DS",
-	TypeEID:        "EID",
-	TypeEUI48:      "EUI48",
-	TypeEUI64:      "EUI64",
-	TypeGID:        "GID",
-	TypeGPOS:       "GPOS",
-	TypeHINFO:      "HINFO",
-	TypeHIP:        "HIP",
-	TypeIPSECKEY:   "IPSECKEY",
-	TypeISDN:       "ISDN",
-	TypeIXFR:       "IXFR", // Meta RR
-	TypeKEY:        "KEY",
-	TypeKX:         "KX",
-	TypeL32:        "L32",
-	TypeL64:        "L64",
-	TypeLOC:        "LOC",
-	TypeLP:         "LP",
-	TypeMB:         "MB",
-	TypeMD:         "MD",
-	TypeMF:         "MF",
-	TypeMG:         "MG",
-	TypeMINFO:      "MINFO",
-	TypeMR:         "MR",
-	TypeMX:         "MX",
-	TypeNAPTR:      "NAPTR",
-	TypeNID:        "NID",
-	TypeNINFO:      "NINFO",
-	TypeNIMLOC:     "NIMLOC",
-	TypeNS:         "NS",
-	TypeNSAPPTR:    "NSAP-PTR",
-	TypeNSEC3:      "NSEC3",
-	TypeNSEC3PARAM: "NSEC3PARAM",
-	TypeNSEC:       "NSEC",
-	TypeNULL:       "NULL",
-	TypeOPT:        "OPT",
-	TypeOPENPGPKEY: "OPENPGPKEY",
-	TypePTR:        "PTR",
-	TypeRKEY:       "RKEY",
-	TypeRP:         "RP",
-	TypeRRSIG:      "RRSIG",
-	TypeRT:         "RT",
-	TypeSIG:        "SIG",
-	TypeSOA:        "SOA",
-	TypeSPF:        "SPF",
-	TypeSRV:        "SRV",
-	TypeSSHFP:      "SSHFP",
-	TypeTA:         "TA",
-	TypeTALINK:     "TALINK",
-	TypeTKEY:       "TKEY", // Meta RR
-	TypeTLSA:       "TLSA",
-	TypeTSIG:       "TSIG", // Meta RR
-	TypeTXT:        "TXT",
-	TypePX:         "PX",
-	TypeUID:        "UID",
-	TypeUINFO:      "UINFO",
-	TypeUNSPEC:     "UNSPEC",
-	TypeURI:        "URI",
-	TypeWKS:        "WKS",
-	TypeX25:        "X25",
-}
-
 // StringToType is the reverse of TypeToString, needed for string parsing.
 var StringToType = reverseInt16(TypeToString)
 
@@ -332,7 +257,7 @@ func packDomainName(s string, msg []byte, off int, compression map[string]int, c
 				roBs = string(bs)
 				bsFresh = true
 			}
-			// Dont try to compress '.'
+			// Don't try to compress '.'
 			if compress && roBs[begin:] != "." {
 				if p, ok := compression[roBs[begin:]]; !ok {
 					// Only offsets smaller than this can be used.
@@ -1316,8 +1241,8 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
 						continue
 					}
 				}
-				if off == lenmsg {
-					// zero rdata foo, OK for dyn. updates
+				if off == lenmsg && int(val.FieldByName("Hdr").FieldByName("Rdlength").Uint()) == 0 {
+					// zero rdata is ok for dyn updates, but only if rdlength is 0
 					break
 				}
 				s, off, err = UnpackDomainName(msg, off)
@@ -1461,7 +1386,7 @@ func UnpackRR(msg []byte, off int) (rr RR, off1 int, err error) {
 	}
 	end := off + int(h.Rdlength)
 	// make an rr of that type and re-unpack.
-	mk, known := typeToRR[h.Rrtype]
+	mk, known := TypeToRR[h.Rrtype]
 	if !known {
 		rr = new(RFC3597)
 	} else {
@@ -1474,6 +1399,32 @@ func UnpackRR(msg []byte, off int) (rr RR, off1 int, err error) {
 	return rr, off, err
 }
 
+// unpackRRslice unpacks msg[off:] into an []RR.
+// If we cannot unpack the whole array, then it will return nil
+func unpackRRslice(l int, msg []byte, off int) (dst1 []RR, off1 int, err error) {
+	var r RR
+	// Optimistically make dst be the length that was sent
+	dst := make([]RR, 0, l)
+	for i := 0; i < l; i++ {
+		off1 := off
+		r, off, err = UnpackRR(msg, off)
+		if err != nil {
+			off = len(msg)
+			break
+		}
+		// If offset does not increase anymore, l is a lie
+		if off1 == off {
+			l = i
+			break
+		}
+		dst = append(dst, r)
+	}
+	if err != nil && off == len(msg) {
+		dst = nil
+	}
+	return dst, off, err
+}
+
 // Reverse a map
 func reverseInt8(m map[uint8]string) map[string]uint8 {
 	n := make(map[string]uint8)
@@ -1672,84 +1623,48 @@ func (dns *Msg) Unpack(msg []byte) (err error) {
 	dns.CheckingDisabled = (dh.Bits & _CD) != 0
 	dns.Rcode = int(dh.Bits & 0xF)
 
-	// Don't pre-alloc these arrays, the incoming lengths are from the network.
-	dns.Question = make([]Question, 0, 1)
-	dns.Answer = make([]RR, 0, 10)
-	dns.Ns = make([]RR, 0, 10)
-	dns.Extra = make([]RR, 0, 10)
+	// Optimistically use the count given to us in the header
+	dns.Question = make([]Question, 0, int(dh.Qdcount))
 
 	var q Question
 	for i := 0; i < int(dh.Qdcount); i++ {
 		off1 := off
 		off, err = UnpackStruct(&q, msg, off)
 		if err != nil {
+			// Even if Truncated is set, we only will set ErrTruncated if we
+			// actually got the questions
 			return err
 		}
 		if off1 == off { // Offset does not increase anymore, dh.Qdcount is a lie!
 			dh.Qdcount = uint16(i)
 			break
 		}
-
 		dns.Question = append(dns.Question, q)
-
-	}
-	// If we see a TC bit being set we return here, without
-	// an error, because technically it isn't an error. So return
-	// without parsing the potentially corrupt packet and hitting an error.
-	// TODO(miek): this isn't the best strategy!
-	// Better stragey would be: set boolean indicating truncated message, go forth and parse
-	// until we hit an error, return the message without the latest parsed rr if this boolean
-	// is true.
-	if dns.Truncated {
-		dns.Answer = nil
-		dns.Ns = nil
-		dns.Extra = nil
-		return nil
 	}
 
-	var r RR
-	for i := 0; i < int(dh.Ancount); i++ {
-		off1 := off
-		r, off, err = UnpackRR(msg, off)
-		if err != nil {
-			return err
-		}
-		if off1 == off { // Offset does not increase anymore, dh.Ancount is a lie!
-			dh.Ancount = uint16(i)
-			break
-		}
-		dns.Answer = append(dns.Answer, r)
+	dns.Answer, off, err = unpackRRslice(int(dh.Ancount), msg, off)
+	// The header counts might have been wrong so we need to update it
+	dh.Ancount = uint16(len(dns.Answer))
+	if err == nil {
+		dns.Ns, off, err = unpackRRslice(int(dh.Nscount), msg, off)
 	}
-	for i := 0; i < int(dh.Nscount); i++ {
-		off1 := off
-		r, off, err = UnpackRR(msg, off)
-		if err != nil {
-			return err
-		}
-		if off1 == off { // Offset does not increase anymore, dh.Nscount is a lie!
-			dh.Nscount = uint16(i)
-			break
-		}
-		dns.Ns = append(dns.Ns, r)
-	}
-	for i := 0; i < int(dh.Arcount); i++ {
-		off1 := off
-		r, off, err = UnpackRR(msg, off)
-		if err != nil {
-			return err
-		}
-		if off1 == off { // Offset does not increase anymore, dh.Arcount is a lie!
-			dh.Arcount = uint16(i)
-			break
-		}
-		dns.Extra = append(dns.Extra, r)
+	// The header counts might have been wrong so we need to update it
+	dh.Nscount = uint16(len(dns.Ns))
+	if err == nil {
+		dns.Extra, off, err = unpackRRslice(int(dh.Arcount), msg, off)
 	}
+	// The header counts might have been wrong so we need to update it
+	dh.Arcount = uint16(len(dns.Extra))
 	if off != len(msg) {
 		// TODO(miek) make this an error?
 		// use PackOpt to let people tell how detailed the error reporting should be?
 		// println("dns: extra bytes in dns packet", off, "<", len(msg))
+	} else if dns.Truncated {
+		// Whether we ran into a an error or not, we want to return that it
+		// was truncated
+		err = ErrTruncated
 	}
-	return nil
+	return err
 }
 
 // Convert a complete message to a string with dig-like output.

+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/nsecx.go → vendor/github.com/miekg/dns/nsecx.go


+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/nsecx_test.go → vendor/github.com/miekg/dns/nsecx_test.go


+ 10 - 10
Godeps/_workspace/src/github.com/miekg/dns/parse_test.go → vendor/github.com/miekg/dns/parse_test.go

@@ -145,13 +145,13 @@ func TestTXTEscapeParsing(t *testing.T) {
 	for _, s := range test {
 		rr, err := NewRR(fmt.Sprintf("example.com. IN TXT %v", s[0]))
 		if err != nil {
-			t.Errorf("Could not parse %v TXT: %s", s[0], err)
+			t.Errorf("could not parse %v TXT: %s", s[0], err)
 			continue
 		}
 
 		txt := sprintTxt(rr.(*TXT).Txt)
 		if txt != s[1] {
-			t.Errorf("Mismatch after parsing `%v` TXT record: `%v` != `%v`", s[0], txt, s[1])
+			t.Errorf("mismatch after parsing `%v` TXT record: `%v` != `%v`", s[0], txt, s[1])
 		}
 	}
 }
@@ -569,7 +569,7 @@ test                          IN CNAME test.a.example.com.
 	t.Logf("%d RRs parsed in %.2f s (%.2f RR/s)", i, float32(delta)/1e9, float32(i)/(float32(delta)/1e9))
 }
 
-func ExampleZone() {
+func ExampleParseZone() {
 	zone := `$ORIGIN .
 $TTL 3600       ; 1 hour
 name                    IN SOA  a6.nstld.com. hostmaster.nic.name. (
@@ -768,7 +768,7 @@ func TestRfc1982(t *testing.T) {
 }
 
 func TestEmpty(t *testing.T) {
-	for _ = range ParseZone(strings.NewReader(""), "", "") {
+	for range ParseZone(strings.NewReader(""), "", "") {
 		t.Errorf("should be empty")
 	}
 }
@@ -799,7 +799,7 @@ func TestLowercaseTokens(t *testing.T) {
 	}
 }
 
-func ExampleGenerate() {
+func ExampleParseZone_generate() {
 	// From the manual: http://www.bind9.net/manual/bind/9.3.2/Bv9ARM.ch06.html#id2566761
 	zone := "$GENERATE 1-2 0 NS SERVER$.EXAMPLE.\n$GENERATE 1-8 $ CNAME $.0"
 	to := ParseZone(strings.NewReader(zone), "0.0.192.IN-ADDR.ARPA.", "")
@@ -1208,11 +1208,11 @@ func TestNewPrivateKey(t *testing.T) {
 		t.Skip("skipping test in short mode.")
 	}
 	algorithms := []algorithm{
-		algorithm{ECDSAP256SHA256, 256},
-		algorithm{ECDSAP384SHA384, 384},
-		algorithm{RSASHA1, 1024},
-		algorithm{RSASHA256, 2048},
-		algorithm{DSA, 1024},
+		{ECDSAP256SHA256, 256},
+		{ECDSAP384SHA384, 384},
+		{RSASHA1, 1024},
+		{RSASHA256, 2048},
+		{DSA, 1024},
 	}
 
 	for _, algo := range algorithms {

+ 4 - 4
Godeps/_workspace/src/github.com/miekg/dns/privaterr.go → vendor/github.com/miekg/dns/privaterr.go

@@ -33,7 +33,7 @@ type PrivateRR struct {
 
 func mkPrivateRR(rrtype uint16) *PrivateRR {
 	// Panics if RR is not an instance of PrivateRR.
-	rrfunc, ok := typeToRR[rrtype]
+	rrfunc, ok := TypeToRR[rrtype]
 	if !ok {
 		panic(fmt.Sprintf("dns: invalid operation with Private RR type %d", rrtype))
 	}
@@ -43,7 +43,7 @@ func mkPrivateRR(rrtype uint16) *PrivateRR {
 	case *PrivateRR:
 		return rr
 	}
-	panic(fmt.Sprintf("dns: RR is not a PrivateRR, typeToRR[%d] generator returned %T", rrtype, anyrr))
+	panic(fmt.Sprintf("dns: RR is not a PrivateRR, TypeToRR[%d] generator returned %T", rrtype, anyrr))
 }
 
 // Header return the RR header of r.
@@ -71,7 +71,7 @@ func (r *PrivateRR) copy() RR {
 func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata) {
 	rtypestr = strings.ToUpper(rtypestr)
 
-	typeToRR[rtype] = func() RR { return &PrivateRR{RR_Header{}, generator()} }
+	TypeToRR[rtype] = func() RR { return &PrivateRR{RR_Header{}, generator()} }
 	TypeToString[rtype] = rtypestr
 	StringToType[rtypestr] = rtype
 
@@ -108,7 +108,7 @@ func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata)
 func PrivateHandleRemove(rtype uint16) {
 	rtypestr, ok := TypeToString[rtype]
 	if ok {
-		delete(typeToRR, rtype)
+		delete(TypeToRR, rtype)
 		delete(TypeToString, rtype)
 		delete(typeToparserFunc, rtype)
 		delete(StringToType, rtypestr)

+ 3 - 3
Godeps/_workspace/src/github.com/miekg/dns/privaterr_test.go → vendor/github.com/miekg/dns/privaterr_test.go

@@ -4,7 +4,7 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/abh/geodns/Godeps/_workspace/src/github.com/miekg/dns"
+	"github.com/miekg/dns"
 )
 
 const TypeISBN uint16 = 0x0F01
@@ -90,11 +90,11 @@ func TestPrivateByteSlice(t *testing.T) {
 	}
 
 	if off1 != off {
-		t.Errorf("Offset after unpacking differs: %d != %d", 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)
+		t.Errorf("record string representation did not match original %#v != %#v", rr1.String(), testrecord)
 	} else {
 		t.Log(rr1.String())
 	}

+ 3 - 3
Godeps/_workspace/src/github.com/miekg/dns/rawmsg.go → vendor/github.com/miekg/dns/rawmsg.go

@@ -21,7 +21,7 @@ func rawSetQuestionLen(msg []byte, i uint16) bool {
 	return true
 }
 
-// rawSetAnswerLen sets the lenght of the answer section.
+// rawSetAnswerLen sets the length of the answer section.
 func rawSetAnswerLen(msg []byte, i uint16) bool {
 	if len(msg) < 8 {
 		return false
@@ -30,7 +30,7 @@ func rawSetAnswerLen(msg []byte, i uint16) bool {
 	return true
 }
 
-// rawSetsNsLen sets the lenght of the authority section.
+// rawSetsNsLen sets the length of the authority section.
 func rawSetNsLen(msg []byte, i uint16) bool {
 	if len(msg) < 10 {
 		return false
@@ -39,7 +39,7 @@ func rawSetNsLen(msg []byte, i uint16) bool {
 	return true
 }
 
-// rawSetExtraLen sets the lenght of the additional section.
+// rawSetExtraLen sets the length of the additional section.
 func rawSetExtraLen(msg []byte, i uint16) bool {
 	if len(msg) < 12 {
 		return false

+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/remote_test.go → vendor/github.com/miekg/dns/remote_test.go


+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/sanitize.go → vendor/github.com/miekg/dns/sanitize.go


+ 6 - 6
Godeps/_workspace/src/github.com/miekg/dns/sanitize_test.go → vendor/github.com/miekg/dns/sanitize_test.go

@@ -9,24 +9,24 @@ func TestDedup(t *testing.T) {
 			newRR(t, "mIek.nl. IN A 127.0.0.1"),
 			newRR(t, "mieK.nl. IN A 127.0.0.1"),
 			newRR(t, "miek.Nl. IN A 127.0.0.1"),
-		}: []string{"mIek.nl.\t3600\tIN\tA\t127.0.0.1"},
+		}: {"mIek.nl.\t3600\tIN\tA\t127.0.0.1"},
 		[...]RR{
 			newRR(t, "miEk.nl. 2000 IN A 127.0.0.1"),
 			newRR(t, "mieK.Nl. 1000 IN A 127.0.0.1"),
 			newRR(t, "Miek.nL. 500 IN A 127.0.0.1"),
-		}: []string{"miEk.nl.\t500\tIN\tA\t127.0.0.1"},
+		}: {"miEk.nl.\t500\tIN\tA\t127.0.0.1"},
 		[...]RR{
 			newRR(t, "miek.nl. IN A 127.0.0.1"),
 			newRR(t, "miek.nl. CH A 127.0.0.1"),
 			newRR(t, "miek.nl. IN A 127.0.0.1"),
-		}: []string{"miek.nl.\t3600\tIN\tA\t127.0.0.1",
+		}: {"miek.nl.\t3600\tIN\tA\t127.0.0.1",
 			"miek.nl.\t3600\tCH\tA\t127.0.0.1",
 		},
 		[...]RR{
 			newRR(t, "miek.nl. CH A 127.0.0.1"),
 			newRR(t, "miek.nl. IN A 127.0.0.1"),
 			newRR(t, "miek.de. IN A 127.0.0.1"),
-		}: []string{"miek.nl.\t3600\tCH\tA\t127.0.0.1",
+		}: {"miek.nl.\t3600\tCH\tA\t127.0.0.1",
 			"miek.nl.\t3600\tIN\tA\t127.0.0.1",
 			"miek.de.\t3600\tIN\tA\t127.0.0.1",
 		},
@@ -34,7 +34,7 @@ func TestDedup(t *testing.T) {
 			newRR(t, "miek.de. IN A 127.0.0.1"),
 			newRR(t, "miek.nl. 200 IN A 127.0.0.1"),
 			newRR(t, "miek.nl. 300 IN A 127.0.0.1"),
-		}: []string{"miek.de.\t3600\tIN\tA\t127.0.0.1",
+		}: {"miek.de.\t3600\tIN\tA\t127.0.0.1",
 			"miek.nl.\t200\tIN\tA\t127.0.0.1",
 		},
 	}
@@ -57,7 +57,7 @@ func BenchmarkDedup(b *testing.B) {
 	}
 	m := make(map[string]RR)
 	for i := 0; i < b.N; i++ {
-		Dedup(rrs,m )
+		Dedup(rrs, m)
 	}
 }
 

+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/scanner.go → vendor/github.com/miekg/dns/scanner.go


+ 106 - 75
Godeps/_workspace/src/github.com/miekg/dns/server.go → vendor/github.com/miekg/dns/server.go

@@ -4,6 +4,7 @@ package dns
 
 import (
 	"bytes"
+	"crypto/tls"
 	"io"
 	"net"
 	"sync"
@@ -47,7 +48,7 @@ type response struct {
 	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
+	tcp            net.Conn          // i/o connection if TCP was used
 	udpSession     *SessionUDP       // oob data to get egress interface right
 	remoteAddr     net.Addr          // address of the client
 	writer         Writer            // writer to output the raw DNS bits
@@ -92,13 +93,35 @@ func HandleFailed(w ResponseWriter, r *Msg) {
 
 func failedHandler() Handler { return HandlerFunc(HandleFailed) }
 
-// ListenAndServe Starts a server on addresss and network speficied. Invoke handler
+// ListenAndServe Starts a server on address and network specified Invoke handler
 // for incoming queries.
 func ListenAndServe(addr string, network string, handler Handler) error {
 	server := &Server{Addr: addr, Net: network, Handler: handler}
 	return server.ListenAndServe()
 }
 
+// ListenAndServeTLS acts like http.ListenAndServeTLS, more information in
+// http://golang.org/pkg/net/http/#ListenAndServeTLS
+func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error {
+	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
+	if err != nil {
+		return err
+	}
+
+	config := tls.Config{
+		Certificates: []tls.Certificate{cert},
+	}
+
+	server := &Server{
+		Addr:      addr,
+		Net:       "tcp-tls",
+		TLSConfig: &config,
+		Handler:   handler,
+	}
+
+	return server.ListenAndServe()
+}
+
 // ActivateAndServe activates a server with a listener from systemd,
 // l and p should not both be non-nil.
 // If both l and p are not nil only p will be used.
@@ -210,7 +233,7 @@ type Writer interface {
 type Reader interface {
 	// ReadTCP reads a raw message from a TCP connection. Implementations may alter
 	// connection properties, for example the read-deadline.
-	ReadTCP(conn *net.TCPConn, timeout time.Duration) ([]byte, error)
+	ReadTCP(conn net.Conn, timeout time.Duration) ([]byte, error)
 	// ReadUDP reads a raw message from a UDP connection. Implementations may alter
 	// connection properties, for example the read-deadline.
 	ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error)
@@ -222,7 +245,7 @@ type defaultReader struct {
 	*Server
 }
 
-func (dr *defaultReader) ReadTCP(conn *net.TCPConn, timeout time.Duration) ([]byte, error) {
+func (dr *defaultReader) ReadTCP(conn net.Conn, timeout time.Duration) ([]byte, error) {
 	return dr.readTCP(conn, timeout)
 }
 
@@ -242,10 +265,12 @@ type DecorateWriter func(Writer) Writer
 type Server struct {
 	// Address to listen on, ":dns" if empty.
 	Addr string
-	// if "tcp" it will invoke a TCP listener, otherwise an UDP one.
+	// if "tcp" or "tcp-tls" (DNS over TLS) it will invoke a TCP listener, otherwise an UDP one
 	Net string
 	// TCP Listener to use, this is to aid in systemd's socket activation.
 	Listener net.Listener
+	// TLS connection configuration
+	TLSConfig *tls.Config
 	// UDP "Listener" to use, this is to aid in systemd's socket activation.
 	PacketConn net.PacketConn
 	// Handler to invoke, dns.DefaultServeMux if nil.
@@ -262,7 +287,7 @@ type Server struct {
 	// 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.
+	// the handler. It will specifically not check if the query has the QR bit not set.
 	Unsafe bool
 	// If NotifyStartedFunc is set it is called once the server has started listening.
 	NotifyStartedFunc func()
@@ -271,26 +296,21 @@ type Server struct {
 	// DecorateWriter is optional, allows customization of the process that writes raw DNS messages.
 	DecorateWriter DecorateWriter
 
-	// For graceful shutdown.
-	stopUDP chan bool
-	stopTCP chan bool
-	wgUDP   sync.WaitGroup
-	wgTCP   sync.WaitGroup
+	// Graceful shutdown handling
+
+	inFlight sync.WaitGroup
 
-	// make start/shutdown not racy
-	lock    sync.Mutex
+	lock    sync.RWMutex
 	started bool
 }
 
 // ListenAndServe starts a nameserver on the configured address in *Server.
 func (srv *Server) ListenAndServe() error {
 	srv.lock.Lock()
+	defer srv.lock.Unlock()
 	if srv.started {
-		srv.lock.Unlock()
 		return &Error{err: "server already started"}
 	}
-	srv.stopUDP, srv.stopTCP = make(chan bool), make(chan bool)
-	srv.started = true
 	addr := srv.Addr
 	if addr == "" {
 		addr = ":domain"
@@ -309,8 +329,29 @@ func (srv *Server) ListenAndServe() error {
 			return e
 		}
 		srv.Listener = l
+		srv.started = true
 		srv.lock.Unlock()
-		return srv.serveTCP(l)
+		e = srv.serveTCP(l)
+		srv.lock.Lock() // to satisfy the defer at the top
+		return e
+	case "tcp-tls", "tcp4-tls", "tcp6-tls":
+		network := "tcp"
+		if srv.Net == "tcp4-tls" {
+			network = "tcp4"
+		} else if srv.Net == "tcp6" {
+			network = "tcp6"
+		}
+
+		l, e := tls.Listen(network, addr, srv.TLSConfig)
+		if e != nil {
+			return e
+		}
+		srv.Listener = l
+		srv.started = true
+		srv.lock.Unlock()
+		e = srv.serveTCP(l)
+		srv.lock.Lock() // to satisfy the defer at the top
+		return e
 	case "udp", "udp4", "udp6":
 		a, e := net.ResolveUDPAddr(srv.Net, addr)
 		if e != nil {
@@ -324,10 +365,12 @@ func (srv *Server) ListenAndServe() error {
 			return e
 		}
 		srv.PacketConn = l
+		srv.started = true
 		srv.lock.Unlock()
-		return srv.serveUDP(l)
+		e = srv.serveUDP(l)
+		srv.lock.Lock() // to satisfy the defer at the top
+		return e
 	}
-	srv.lock.Unlock()
 	return &Error{err: "bad network"}
 }
 
@@ -335,15 +378,12 @@ func (srv *Server) ListenAndServe() error {
 // configured in *Server. Its main use is to start a server from systemd.
 func (srv *Server) ActivateAndServe() error {
 	srv.lock.Lock()
+	defer srv.lock.Unlock()
 	if srv.started {
-		srv.lock.Unlock()
 		return &Error{err: "server already started"}
 	}
-	srv.stopUDP, srv.stopTCP = make(chan bool), make(chan bool)
-	srv.started = true
 	pConn := srv.PacketConn
 	l := srv.Listener
-	srv.lock.Unlock()
 	if pConn != nil {
 		if srv.UDPSize == 0 {
 			srv.UDPSize = MinMsgSize
@@ -352,13 +392,19 @@ func (srv *Server) ActivateAndServe() error {
 			if e := setUDPSocketOptions(t); e != nil {
 				return e
 			}
-			return srv.serveUDP(t)
+			srv.started = true
+			srv.lock.Unlock()
+			e := srv.serveUDP(t)
+			srv.lock.Lock() // to satisfy the defer at the top
+			return e
 		}
 	}
 	if l != nil {
-		if t, ok := l.(*net.TCPListener); ok {
-			return srv.serveTCP(t)
-		}
+		srv.started = true
+		srv.lock.Unlock()
+		e := srv.serveTCP(l)
+		srv.lock.Lock() // to satisfy the defer at the top
+		return e
 	}
 	return &Error{err: "bad listeners"}
 }
@@ -374,36 +420,20 @@ func (srv *Server) Shutdown() error {
 		return &Error{err: "server not started"}
 	}
 	srv.started = false
-	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()
-	}
 	srv.lock.Unlock()
 
-	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
-		}()
+	if srv.PacketConn != nil {
+		srv.PacketConn.Close()
+	}
+	if srv.Listener != nil {
+		srv.Listener.Close()
 	}
 
-	c := &Client{Net: net}
-	go c.Exchange(new(Msg), addr) // extra query to help ReadXXX loop to pass
+	fin := make(chan bool)
+	go func() {
+		srv.inFlight.Wait()
+		fin <- true
+	}()
 
 	select {
 	case <-time.After(srv.getReadTimeout()):
@@ -424,7 +454,7 @@ func (srv *Server) getReadTimeout() time.Duration {
 
 // serveTCP starts a TCP listener for the server.
 // Each request is handled in a separate goroutine.
-func (srv *Server) serveTCP(l *net.TCPListener) error {
+func (srv *Server) serveTCP(l net.Listener) error {
 	defer l.Close()
 
 	if srv.NotifyStartedFunc != nil {
@@ -443,20 +473,24 @@ func (srv *Server) serveTCP(l *net.TCPListener) error {
 	rtimeout := srv.getReadTimeout()
 	// deadline is not used here
 	for {
-		rw, e := l.AcceptTCP()
+		rw, e := l.Accept()
 		if e != nil {
-			continue
+			if neterr, ok := e.(net.Error); ok && neterr.Temporary() {
+				continue
+			}
+			return e
 		}
 		m, e := reader.ReadTCP(rw, rtimeout)
-		select {
-		case <-srv.stopTCP:
+		srv.lock.RLock()
+		if !srv.started {
+			srv.lock.RUnlock()
 			return nil
-		default:
 		}
+		srv.lock.RUnlock()
 		if e != nil {
 			continue
 		}
-		srv.wgTCP.Add(1)
+		srv.inFlight.Add(1)
 		go srv.serve(rw.RemoteAddr(), handler, m, nil, nil, rw)
 	}
 }
@@ -483,21 +517,24 @@ func (srv *Server) serveUDP(l *net.UDPConn) error {
 	// deadline is not used here
 	for {
 		m, s, e := reader.ReadUDP(l, rtimeout)
-		select {
-		case <-srv.stopUDP:
+		srv.lock.RLock()
+		if !srv.started {
+			srv.lock.RUnlock()
 			return nil
-		default:
 		}
+		srv.lock.RUnlock()
 		if e != nil {
 			continue
 		}
-		srv.wgUDP.Add(1)
+		srv.inFlight.Add(1)
 		go srv.serve(s.RemoteAddr(), handler, m, l, s, nil)
 	}
 }
 
 // Serve a new connection.
-func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, s *SessionUDP, t *net.TCPConn) {
+func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, s *SessionUDP, t net.Conn) {
+	defer srv.inFlight.Done()
+
 	w := &response{tsigSecret: srv.TsigSecret, udp: u, tcp: t, remoteAddr: a, udpSession: s}
 	if srv.DecorateWriter != nil {
 		w.writer = srv.DecorateWriter(w)
@@ -507,15 +544,6 @@ func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, s *Ses
 
 	q := 0 // counter for the amount of TCP queries we get
 
-	defer func() {
-		if u != nil {
-			srv.wgUDP.Done()
-		}
-		if t != nil {
-			srv.wgTCP.Done()
-		}
-	}()
-
 	reader := Reader(&defaultReader{srv})
 	if srv.DecorateReader != nil {
 		reader = srv.DecorateReader(reader)
@@ -548,6 +576,9 @@ Redo:
 	h.ServeDNS(w, req) // Writes back to the client
 
 Exit:
+	if w.tcp == nil {
+		return
+	}
 	// TODO(miek): make this number configurable?
 	if q > maxTCPQueries { // close socket after this many queries
 		w.Close()
@@ -574,7 +605,7 @@ Exit:
 	return
 }
 
-func (srv *Server) readTCP(conn *net.TCPConn, timeout time.Duration) ([]byte, error) {
+func (srv *Server) readTCP(conn net.Conn, timeout time.Duration) ([]byte, error) {
 	conn.SetReadDeadline(time.Now().Add(timeout))
 	l := make([]byte, 2)
 	n, err := conn.Read(l)

+ 249 - 20
Godeps/_workspace/src/github.com/miekg/dns/server_test.go → vendor/github.com/miekg/dns/server_test.go

@@ -1,11 +1,14 @@
 package dns
 
 import (
+	"crypto/tls"
 	"fmt"
+	"io"
 	"net"
 	"runtime"
 	"sync"
 	"testing"
+	"time"
 )
 
 func HelloServer(w ResponseWriter, req *Msg) {
@@ -37,23 +40,32 @@ func AnotherHelloServer(w ResponseWriter, req *Msg) {
 }
 
 func RunLocalUDPServer(laddr string) (*Server, string, error) {
+	server, l, _, err := RunLocalUDPServerWithFinChan(laddr)
+
+	return server, l, err
+}
+
+func RunLocalUDPServerWithFinChan(laddr string) (*Server, string, chan struct{}, error) {
 	pc, err := net.ListenPacket("udp", laddr)
 	if err != nil {
-		return nil, "", err
+		return nil, "", nil, err
 	}
-	server := &Server{PacketConn: pc}
+	server := &Server{PacketConn: pc, ReadTimeout: time.Hour, WriteTimeout: time.Hour}
 
 	waitLock := sync.Mutex{}
 	waitLock.Lock()
 	server.NotifyStartedFunc = waitLock.Unlock
 
+	fin := make(chan struct{}, 0)
+
 	go func() {
 		server.ActivateAndServe()
+		close(fin)
 		pc.Close()
 	}()
 
 	waitLock.Lock()
-	return server, pc.LocalAddr().String(), nil
+	return server, pc.LocalAddr().String(), fin, nil
 }
 
 func RunLocalUDPServerUnsafe(laddr string) (*Server, string, error) {
@@ -61,7 +73,8 @@ func RunLocalUDPServerUnsafe(laddr string) (*Server, string, error) {
 	if err != nil {
 		return nil, "", err
 	}
-	server := &Server{PacketConn: pc, Unsafe: true}
+	server := &Server{PacketConn: pc, Unsafe: true,
+		ReadTimeout: time.Hour, WriteTimeout: time.Hour}
 
 	waitLock := sync.Mutex{}
 	waitLock.Lock()
@@ -82,7 +95,28 @@ func RunLocalTCPServer(laddr string) (*Server, string, error) {
 		return nil, "", err
 	}
 
-	server := &Server{Listener: l}
+	server := &Server{Listener: l, ReadTimeout: time.Hour, WriteTimeout: time.Hour}
+
+	waitLock := sync.Mutex{}
+	waitLock.Lock()
+	server.NotifyStartedFunc = waitLock.Unlock
+
+	go func() {
+		server.ActivateAndServe()
+		l.Close()
+	}()
+
+	waitLock.Lock()
+	return server, l.Addr().String(), nil
+}
+
+func RunLocalTLSServer(laddr string, config *tls.Config) (*Server, string, error) {
+	l, err := tls.Listen("tcp", laddr, config)
+	if err != nil {
+		return nil, "", err
+	}
+
+	server := &Server{Listener: l, ReadTimeout: time.Hour, WriteTimeout: time.Hour}
 
 	waitLock := sync.Mutex{}
 	waitLock.Lock()
@@ -105,7 +139,7 @@ func TestServing(t *testing.T) {
 
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	if err != nil {
-		t.Fatalf("Unable to run test server: %v", err)
+		t.Fatalf("unable to run test server: %v", err)
 	}
 	defer s.Shutdown()
 
@@ -118,7 +152,7 @@ func TestServing(t *testing.T) {
 	}
 	txt := r.Extra[0].(*TXT).Txt[0]
 	if txt != "Hello world" {
-		t.Error("Unexpected result for miek.nl", txt, "!= Hello world")
+		t.Error("unexpected result for miek.nl", txt, "!= Hello world")
 	}
 
 	m.SetQuestion("example.com.", TypeTXT)
@@ -128,7 +162,7 @@ func TestServing(t *testing.T) {
 	}
 	txt = r.Extra[0].(*TXT).Txt[0]
 	if txt != "Hello example" {
-		t.Error("Unexpected result for example.com", txt, "!= Hello example")
+		t.Error("unexpected result for example.com", txt, "!= Hello example")
 	}
 
 	// Test Mixes cased as noticed by Ask.
@@ -139,7 +173,67 @@ func TestServing(t *testing.T) {
 	}
 	txt = r.Extra[0].(*TXT).Txt[0]
 	if txt != "Hello example" {
-		t.Error("Unexpected result for example.com", txt, "!= Hello example")
+		t.Error("unexpected result for example.com", txt, "!= Hello example")
+	}
+}
+
+func TestServingTLS(t *testing.T) {
+	HandleFunc("miek.nl.", HelloServer)
+	HandleFunc("example.com.", AnotherHelloServer)
+	defer HandleRemove("miek.nl.")
+	defer HandleRemove("example.com.")
+
+	cert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock)
+	if err != nil {
+		t.Fatalf("unable to build certificate: %v", err)
+	}
+
+	config := tls.Config{
+		Certificates: []tls.Certificate{cert},
+	}
+
+	s, addrstr, err := RunLocalTLSServer("127.0.0.1:0", &config)
+	if err != nil {
+		t.Fatalf("unable to run test server: %v", err)
+	}
+	defer s.Shutdown()
+
+	c := new(Client)
+	c.Net = "tcp-tls"
+	c.TLSConfig = &tls.Config{
+		InsecureSkipVerify: true,
+	}
+
+	m := new(Msg)
+	m.SetQuestion("miek.nl.", TypeTXT)
+	r, _, err := c.Exchange(m, addrstr)
+	if err != nil || len(r.Extra) == 0 {
+		t.Fatal("failed to exchange miek.nl", err)
+	}
+	txt := r.Extra[0].(*TXT).Txt[0]
+	if txt != "Hello world" {
+		t.Error("unexpected result for miek.nl", txt, "!= Hello world")
+	}
+
+	m.SetQuestion("example.com.", TypeTXT)
+	r, _, err = c.Exchange(m, addrstr)
+	if err != nil {
+		t.Fatal("failed to exchange example.com", err)
+	}
+	txt = r.Extra[0].(*TXT).Txt[0]
+	if txt != "Hello example" {
+		t.Error("unexpected result for example.com", txt, "!= Hello example")
+	}
+
+	// Test Mixes cased as noticed by Ask.
+	m.SetQuestion("eXaMplE.cOm.", TypeTXT)
+	r, _, err = c.Exchange(m, addrstr)
+	if err != nil {
+		t.Error("failed to exchange eXaMplE.cOm", err)
+	}
+	txt = r.Extra[0].(*TXT).Txt[0]
+	if txt != "Hello example" {
+		t.Error("unexpected result for example.com", txt, "!= Hello example")
 	}
 }
 
@@ -151,7 +245,7 @@ func BenchmarkServe(b *testing.B) {
 
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	if err != nil {
-		b.Fatalf("Unable to run test server: %v", err)
+		b.Fatalf("unable to run test server: %v", err)
 	}
 	defer s.Shutdown()
 
@@ -173,7 +267,7 @@ func benchmarkServe6(b *testing.B) {
 	a := runtime.GOMAXPROCS(4)
 	s, addrstr, err := RunLocalUDPServer("[::1]:0")
 	if err != nil {
-		b.Fatalf("Unable to run test server: %v", err)
+		b.Fatalf("unable to run test server: %v", err)
 	}
 	defer s.Shutdown()
 
@@ -204,7 +298,7 @@ func BenchmarkServeCompress(b *testing.B) {
 	a := runtime.GOMAXPROCS(4)
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	if err != nil {
-		b.Fatalf("Unable to run test server: %v", err)
+		b.Fatalf("unable to run test server: %v", err)
 	}
 	defer s.Shutdown()
 
@@ -305,7 +399,7 @@ func TestServingLargeResponses(t *testing.T) {
 
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	if err != nil {
-		t.Fatalf("Unable to run test server: %v", err)
+		t.Fatalf("unable to run test server: %v", err)
 	}
 	defer s.Shutdown()
 
@@ -345,7 +439,7 @@ func TestServingResponse(t *testing.T) {
 	HandleFunc("miek.nl.", HelloServer)
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	if err != nil {
-		t.Fatalf("Unable to run test server: %v", err)
+		t.Fatalf("unable to run test server: %v", err)
 	}
 
 	c := new(Client)
@@ -365,7 +459,7 @@ func TestServingResponse(t *testing.T) {
 	s.Shutdown()
 	s, addrstr, err = RunLocalUDPServerUnsafe("127.0.0.1:0")
 	if err != nil {
-		t.Fatalf("Unable to run test server: %v", err)
+		t.Fatalf("unable to run test server: %v", err)
 	}
 	defer s.Shutdown()
 
@@ -379,22 +473,104 @@ func TestServingResponse(t *testing.T) {
 func TestShutdownTCP(t *testing.T) {
 	s, _, err := RunLocalTCPServer("127.0.0.1:0")
 	if err != nil {
-		t.Fatalf("Unable to run test server: %v", err)
+		t.Fatalf("unable to run test server: %v", err)
+	}
+	err = s.Shutdown()
+	if err != nil {
+		t.Errorf("could not shutdown test TCP server, %v", err)
+	}
+}
+
+func TestShutdownTLS(t *testing.T) {
+	cert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock)
+	if err != nil {
+		t.Fatalf("unable to build certificate: %v", err)
+	}
+
+	config := tls.Config{
+		Certificates: []tls.Certificate{cert},
+	}
+
+	s, _, err := RunLocalTLSServer("127.0.0.1:0", &config)
+	if err != nil {
+		t.Fatalf("unable to run test server: %v", err)
 	}
 	err = s.Shutdown()
 	if err != nil {
-		t.Errorf("Could not shutdown test TCP server, %v", err)
+		t.Errorf("could not shutdown test TLS server, %v", err)
+	}
+}
+
+type trigger struct {
+	done bool
+	sync.RWMutex
+}
+
+func (t *trigger) Set() {
+	t.Lock()
+	defer t.Unlock()
+	t.done = true
+}
+func (t *trigger) Get() bool {
+	t.RLock()
+	defer t.RUnlock()
+	return t.done
+}
+
+func TestHandlerCloseTCP(t *testing.T) {
+
+	ln, err := net.Listen("tcp", "127.0.0.1:0")
+	if err != nil {
+		panic(err)
+	}
+	addr := ln.Addr().String()
+
+	server := &Server{Addr: addr, Net: "tcp", Listener: ln}
+
+	hname := "testhandlerclosetcp."
+	triggered := &trigger{}
+	HandleFunc(hname, func(w ResponseWriter, r *Msg) {
+		triggered.Set()
+		w.Close()
+	})
+	defer HandleRemove(hname)
+
+	go func() {
+		defer server.Shutdown()
+		c := &Client{Net: "tcp"}
+		m := new(Msg).SetQuestion(hname, 1)
+		tries := 0
+	exchange:
+		_, _, err := c.Exchange(m, addr)
+		if err != nil && err != io.EOF {
+			t.Logf("exchange failed: %s\n", err)
+			if tries == 3 {
+				return
+			}
+			time.Sleep(time.Second / 10)
+			tries += 1
+			goto exchange
+		}
+	}()
+	server.ActivateAndServe()
+	if !triggered.Get() {
+		t.Fatalf("handler never called")
 	}
 }
 
 func TestShutdownUDP(t *testing.T) {
-	s, _, err := RunLocalUDPServer("127.0.0.1:0")
+	s, _, fin, err := RunLocalUDPServerWithFinChan("127.0.0.1:0")
 	if err != nil {
-		t.Fatalf("Unable to run test server: %v", err)
+		t.Fatalf("unable to run test server: %v", err)
 	}
 	err = s.Shutdown()
 	if err != nil {
-		t.Errorf("Could not shutdown test UDP server, %v", err)
+		t.Errorf("could not shutdown test UDP server, %v", err)
+	}
+	select {
+	case <-fin:
+	case <-time.After(2 * time.Second):
+		t.Error("Could not shutdown test UDP server. Gave up waiting")
 	}
 }
 
@@ -422,6 +598,7 @@ func ExampleDecorateWriter() {
 	server := &Server{
 		PacketConn:     pc,
 		DecorateWriter: wf,
+		ReadTimeout:    time.Hour, WriteTimeout: time.Hour,
 	}
 
 	waitLock := sync.Mutex{}
@@ -448,3 +625,55 @@ func ExampleDecorateWriter() {
 	}
 	// Output: writing raw DNS message of length 56
 }
+
+var (
+	// CertPEMBlock is a X509 data used to test TLS servers (used with tls.X509KeyPair)
+	CertPEMBlock = []byte(`-----BEGIN CERTIFICATE-----
+MIIDAzCCAeugAwIBAgIRAJFYMkcn+b8dpU15wjf++GgwDQYJKoZIhvcNAQELBQAw
+EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNjAxMDgxMjAzNTNaFw0xNzAxMDcxMjAz
+NTNaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDXjqO6skvP03k58CNjQggd9G/mt+Wa+xRU+WXiKCCHttawM8x+slq5
+yfsHCwxlwsGn79HmJqecNqgHb2GWBXAvVVokFDTcC1hUP4+gp2gu9Ny27UHTjlLm
+O0l/xZ5MN8tfKyYlFw18tXu3fkaPyHj8v/D1RDkuo4ARdFvGSe8TqisbhLk2+9ow
+xfIGbEM9Fdiw8qByC2+d+FfvzIKz3GfQVwn0VoRom8L6NBIANq1IGrB5JefZB6nv
+DnfuxkBmY7F1513HKuEJ8KsLWWZWV9OPU4j4I4Rt+WJNlKjbD2srHxyrS2RDsr91
+8nCkNoWVNO3sZq0XkWKecdc921vL4ginAgMBAAGjVDBSMA4GA1UdDwEB/wQEAwIC
+pDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MBoGA1UdEQQT
+MBGCCWxvY2FsaG9zdIcEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEAGcU3iyLBIVZj
+aDzSvEDHUd1bnLBl1C58Xu/CyKlPqVU7mLfK0JcgEaYQTSX6fCJVNLbbCrcGLsPJ
+fbjlBbyeLjTV413fxPVuona62pBFjqdtbli2Qe8FRH2KBdm41JUJGdo+SdsFu7nc
+BFOcubdw6LLIXvsTvwndKcHWx1rMX709QU1Vn1GAIsbJV/DWI231Jyyb+lxAUx/C
+8vce5uVxiKcGS+g6OjsN3D3TtiEQGSXLh013W6Wsih8td8yMCMZ3w8LQ38br1GUe
+ahLIgUJ9l6HDguM17R7kGqxNvbElsMUHfTtXXP7UDQUiYXDakg8xDP6n9DCDhJ8Y
+bSt7OLB7NQ==
+-----END CERTIFICATE-----`)
+
+	// KeyPEMBlock is a X509 data used to test TLS servers (used with tls.X509KeyPair)
+	KeyPEMBlock = []byte(`-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEA146jurJLz9N5OfAjY0IIHfRv5rflmvsUVPll4iggh7bWsDPM
+frJaucn7BwsMZcLBp+/R5iannDaoB29hlgVwL1VaJBQ03AtYVD+PoKdoLvTctu1B
+045S5jtJf8WeTDfLXysmJRcNfLV7t35Gj8h4/L/w9UQ5LqOAEXRbxknvE6orG4S5
+NvvaMMXyBmxDPRXYsPKgcgtvnfhX78yCs9xn0FcJ9FaEaJvC+jQSADatSBqweSXn
+2Qep7w537sZAZmOxdeddxyrhCfCrC1lmVlfTj1OI+COEbfliTZSo2w9rKx8cq0tk
+Q7K/dfJwpDaFlTTt7GatF5FinnHXPdtby+IIpwIDAQABAoIBAAJK4RDmPooqTJrC
+JA41MJLo+5uvjwCT9QZmVKAQHzByUFw1YNJkITTiognUI0CdzqNzmH7jIFs39ZeG
+proKusO2G6xQjrNcZ4cV2fgyb5g4QHStl0qhs94A+WojduiGm2IaumAgm6Mc5wDv
+ld6HmknN3Mku/ZCyanVFEIjOVn2WB7ZQLTBs6ZYaebTJG2Xv6p9t2YJW7pPQ9Xce
+s9ohAWohyM4X/OvfnfnLtQp2YLw/BxwehBsCR5SXM3ibTKpFNtxJC8hIfTuWtxZu
+2ywrmXShYBRB1WgtZt5k04bY/HFncvvcHK3YfI1+w4URKtwdaQgPUQRbVwDwuyBn
+flfkCJECgYEA/eWt01iEyE/lXkGn6V9lCocUU7lCU6yk5UT8VXVUc5If4KZKPfCk
+p4zJDOqwn2eM673aWz/mG9mtvAvmnugaGjcaVCyXOp/D/GDmKSoYcvW5B/yjfkLy
+dK6Yaa5LDRVYlYgyzcdCT5/9Qc626NzFwKCZNI4ncIU8g7ViATRxWJ8CgYEA2Ver
+vZ0M606sfgC0H3NtwNBxmuJ+lIF5LNp/wDi07lDfxRR1rnZMX5dnxjcpDr/zvm8J
+WtJJX3xMgqjtHuWKL3yKKony9J5ZPjichSbSbhrzfovgYIRZLxLLDy4MP9L3+CX/
+yBXnqMWuSnFX+M5fVGxdDWiYF3V+wmeOv9JvavkCgYEAiXAPDFzaY+R78O3xiu7M
+r0o3wqqCMPE/wav6O/hrYrQy9VSO08C0IM6g9pEEUwWmzuXSkZqhYWoQFb8Lc/GI
+T7CMXAxXQLDDUpbRgG79FR3Wr3AewHZU8LyiXHKwxcBMV4WGmsXGK3wbh8fyU1NO
+6NsGk+BvkQVOoK1LBAPzZ1kCgYEAsBSmD8U33T9s4dxiEYTrqyV0lH3g/SFz8ZHH
+pAyNEPI2iC1ONhyjPWKlcWHpAokiyOqeUpVBWnmSZtzC1qAydsxYB6ShT+sl9BHb
+RMix/QAauzBJhQhUVJ3OIys0Q1UBDmqCsjCE8SfOT4NKOUnA093C+YT+iyrmmktZ
+zDCJkckCgYEAndqM5KXGk5xYo+MAA1paZcbTUXwaWwjLU+XSRSSoyBEi5xMtfvUb
+7+a1OMhLwWbuz+pl64wFKrbSUyimMOYQpjVE/1vk/kb99pxbgol27hdKyTH1d+ov
+kFsxKCqxAnBVGEWAvVZAiiTOxleQFjz5RnL0BQp9Lg2cQe+dvuUmIAA=
+-----END RSA PRIVATE KEY-----`)
+)

+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/sig0.go → vendor/github.com/miekg/dns/sig0.go


+ 8 - 8
Godeps/_workspace/src/github.com/miekg/dns/sig0_test.go → vendor/github.com/miekg/dns/sig0_test.go

@@ -28,7 +28,7 @@ func TestSIG0(t *testing.T) {
 		}
 		pk, err := keyrr.Generate(keysize)
 		if err != nil {
-			t.Errorf("Failed to generate key for “%s”: %v", algstr, err)
+			t.Errorf("failed to generate key for “%s”: %v", algstr, err)
 			continue
 		}
 		now := uint32(time.Now().Unix())
@@ -43,16 +43,16 @@ func TestSIG0(t *testing.T) {
 		sigrr.SignerName = keyrr.Hdr.Name
 		mb, err := sigrr.Sign(pk.(crypto.Signer), m)
 		if err != nil {
-			t.Errorf("Failed to sign message using “%s”: %v", algstr, err)
+			t.Errorf("failed to sign message using “%s”: %v", algstr, err)
 			continue
 		}
 		m := new(Msg)
 		if err := m.Unpack(mb); err != nil {
-			t.Errorf("Failed to unpack message signed using “%s”: %v", algstr, err)
+			t.Errorf("failed to unpack message signed using “%s”: %v", algstr, err)
 			continue
 		}
 		if len(m.Extra) != 1 {
-			t.Errorf("Missing SIG for message signed using “%s”", algstr)
+			t.Errorf("missing SIG for message signed using “%s”", algstr)
 			continue
 		}
 		var sigrrwire *SIG
@@ -60,7 +60,7 @@ func TestSIG0(t *testing.T) {
 		case *SIG:
 			sigrrwire = rr
 		default:
-			t.Errorf("Expected SIG RR, instead: %v", rr)
+			t.Errorf("expected SIG RR, instead: %v", rr)
 			continue
 		}
 		for _, rr := range []*SIG{sigrr, sigrrwire} {
@@ -69,20 +69,20 @@ func TestSIG0(t *testing.T) {
 				id = "sigrrwire"
 			}
 			if err := rr.Verify(keyrr, mb); err != nil {
-				t.Errorf("Failed to verify “%s” signed SIG(%s): %v", algstr, id, err)
+				t.Errorf("failed to verify “%s” signed SIG(%s): %v", algstr, id, err)
 				continue
 			}
 		}
 		mb[13]++
 		if err := sigrr.Verify(keyrr, mb); err == nil {
-			t.Errorf("Verify succeeded on an altered message using “%s”", algstr)
+			t.Errorf("verify succeeded on an altered message using “%s”", algstr)
 			continue
 		}
 		sigrr.Expiration = 2
 		sigrr.Inception = 1
 		mb, _ = sigrr.Sign(pk.(crypto.Signer), m)
 		if err := sigrr.Verify(keyrr, mb); err == nil {
-			t.Errorf("Verify succeeded on an expired message using “%s”", algstr)
+			t.Errorf("verify succeeded on an expired message using “%s”", algstr)
 			continue
 		}
 	}

+ 0 - 0
Godeps/_workspace/src/github.com/miekg/dns/singleinflight.go → vendor/github.com/miekg/dns/singleinflight.go


+ 1 - 1
Godeps/_workspace/src/github.com/miekg/dns/tlsa.go → vendor/github.com/miekg/dns/tlsa.go

@@ -82,5 +82,5 @@ func TLSAName(name, service, network string) (string, error) {
 	if e != nil {
 		return "", e
 	}
-	return "_" + strconv.Itoa(p) + "_" + network + "." + name, nil
+	return "_" + strconv.Itoa(p) + "._" + network + "." + name, nil
 }

+ 2 - 15
Godeps/_workspace/src/github.com/miekg/dns/tsig.go → vendor/github.com/miekg/dns/tsig.go

@@ -37,10 +37,6 @@ type TSIG struct {
 	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 {
@@ -58,15 +54,6 @@ func (rr *TSIG) String() string {
 	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 {
@@ -125,7 +112,7 @@ func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, s
 
 	t := new(TSIG)
 	var h hash.Hash
-	switch rr.Algorithm {
+	switch strings.ToLower(rr.Algorithm) {
 	case HmacMD5:
 		h = hmac.New(md5.New, []byte(rawsecret))
 	case HmacSHA1:
@@ -191,7 +178,7 @@ func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {
 	}
 
 	var h hash.Hash
-	switch tsig.Algorithm {
+	switch strings.ToLower(tsig.Algorithm) {
 	case HmacMD5:
 		h = hmac.New(md5.New, rawsecret)
 	case HmacSHA1:

+ 37 - 0
vendor/github.com/miekg/dns/tsig_test.go

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

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