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
 .DS_Store
+/geodns
+/REVISION
+/run
+.idea
+/dns/geodns.conf

+ 8 - 6
.travis.yml

@@ -1,20 +1,22 @@
 language: go
 language: go
 go:
 go:
-  - 1.4
-  - 1.5
+  - 1.5.4
+  - 1.6.2
   - tip
   - tip
+
+env:
+  global:
+  - GO15VENDOREXPERIMENT='1'
+
 before_install:
 before_install:
   - sudo apt-get install libgeoip-dev bzr
   - sudo apt-get install libgeoip-dev bzr
+
 install:
 install:
   - mkdir -p $TRAVIS_BUILD_DIR/db
   - 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://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
   - 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 [geodns] >> dns/geodns.conf
   - echo Directory=$TRAVIS_BUILD_DIR/db >> 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 build -v
   - go install
   - go install
 
 

+ 28 - 7
Godeps/Godeps.json

@@ -1,11 +1,8 @@
 {
 {
 	"ImportPath": "github.com/abh/geodns",
 	"ImportPath": "github.com/abh/geodns",
-	"GoVersion": "go1.4.2",
+	"GoVersion": "go1.7",
+	"GodepVersion": "v74",
 	"Deps": [
 	"Deps": [
-		{
-			"ImportPath": "code.google.com/p/gcfg",
-			"Rev": "c2d3050044d05357eaf6c3547249ba57c5e235cb"
-		},
 		{
 		{
 			"ImportPath": "github.com/abh/errorutil",
 			"ImportPath": "github.com/abh/errorutil",
 			"Rev": "f9bd360d00b902548fbb80837aef90dca2c8285e"
 			"Rev": "f9bd360d00b902548fbb80837aef90dca2c8285e"
@@ -16,7 +13,7 @@
 		},
 		},
 		{
 		{
 			"ImportPath": "github.com/miekg/dns",
 			"ImportPath": "github.com/miekg/dns",
-			"Rev": "8395762c3490507cf5a27405fcd0e3d3dc547109"
+			"Rev": "b9171237b0642de1d8e8004f16869970e065f46b"
 		},
 		},
 		{
 		{
 			"ImportPath": "github.com/pborman/uuid",
 			"ImportPath": "github.com/pborman/uuid",
@@ -24,7 +21,7 @@
 		},
 		},
 		{
 		{
 			"ImportPath": "github.com/rcrowley/go-metrics",
 			"ImportPath": "github.com/rcrowley/go-metrics",
-			"Rev": "1ce93efbc8f9c568886b2ef85ce305b2217b3de3"
+			"Rev": "eeba7bd0dd01ace6e690fa833b3f22aaec29af43"
 		},
 		},
 		{
 		{
 			"ImportPath": "github.com/stathat/go",
 			"ImportPath": "github.com/stathat/go",
@@ -42,6 +39,30 @@
 			"ImportPath": "gopkg.in/fsnotify.v1",
 			"ImportPath": "gopkg.in/fsnotify.v1",
 			"Comment": "v1.2.0",
 			"Comment": "v1.2.0",
 			"Rev": "96c060f6a6b7e0d6f75fddd10efeaca3e5d1bcb0"
 			"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
 templates.go: templates/*.html monitor.go
 	go generate
 	go generate
 
 
+test:
+	go test -race $(go list ./... | grep -v /vendor/)
+
 devel:
 devel:
 	go build -tags 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`
 `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.
 The binary can be moved to /usr/local/bin, /opt/geodns/ or wherever you find appropriate.
 
 
 ### Command options
 ### Command options
@@ -136,10 +144,13 @@ with `max_hosts` 2 then .4 will be returned about 4 times more often than .1.
 ## Configuration file
 ## Configuration file
 
 
 The geodns.conf file allows you to specify a specific directory for the GeoIP
 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.
 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
 ## Zone format
 
 

+ 7 - 4
config.go

@@ -7,8 +7,8 @@ import (
 	"sync"
 	"sync"
 	"time"
 	"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 {
 type AppConfig struct {
@@ -25,6 +25,11 @@ type AppConfig struct {
 		User     string
 		User     string
 		Password string
 		Password string
 	}
 	}
+	QueryLog struct {
+		Path    string
+		MaxSize int
+		Keep    int
+	}
 	Nodeping struct {
 	Nodeping struct {
 		Token string
 		Token string
 	}
 	}
@@ -60,8 +65,6 @@ func (conf *AppConfig) GeoIPDirectory() string {
 
 
 func configWatcher(fileName string) {
 func configWatcher(fileName string) {
 
 
-	configReader(fileName)
-
 	watcher, err := fsnotify.NewWatcher()
 	watcher, err := fsnotify.NewWatcher()
 	if err != nil {
 	if err != nil {
 		fmt.Println(err)
 		fmt.Println(err)

+ 11 - 0
countries/countries.go

@@ -251,3 +251,14 @@ var CountryContinent = map[string]string{
 	"zm": "africa",
 	"zm": "africa",
 	"zw": "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"
 	"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
 		return group
 	}
 	}
 
 
 	log.Printf("Did not find a region group for '%s'/'%s'", country, region)
 	log.Printf("Did not find a region group for '%s'/'%s'", country, region)
 	return ""
 	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 containing the GeoIP .dat database files
 ;directory=/usr/local/share/GeoIP/
 ;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]
 [stathat]
 ;; Add an API key to send query counts and other metrics to stathat
 ;; Add an API key to send query counts and other metrics to stathat
 ;apikey=abc123
 ;apikey=abc123

+ 31 - 9
geodns.go

@@ -29,11 +29,12 @@ import (
 	"strings"
 	"strings"
 	"time"
 	"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
 // VERSION is the current version of GeoDNS
-var VERSION string = "2.6.0"
+var VERSION string = "2.7.0"
 var buildTime string
 var buildTime string
 var gitVersion string
 var gitVersion string
 
 
@@ -52,6 +53,7 @@ var timeStarted = time.Now()
 
 
 var (
 var (
 	flagconfig       = flag.String("config", "./dns/", "directory of zone files")
 	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")
 	flagcheckconfig  = flag.Bool("checkconfig", false, "check configuration and exit")
 	flagidentifier   = flag.String("identifier", "", "identifier (hostname, pop name or similar)")
 	flagidentifier   = flag.String("identifier", "", "identifier (hostname, pop name or similar)")
 	flaginter        = flag.String("interface", "*", "set the listener address")
 	flaginter        = flag.String("interface", "*", "set the listener address")
@@ -94,6 +96,8 @@ func main() {
 		os.Exit(0)
 		os.Exit(0)
 	}
 	}
 
 
+	srv := Server{}
+
 	if len(*flagLogFile) > 0 {
 	if len(*flagLogFile) > 0 {
 		logToFileOpen(*flagLogFile)
 		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 {
 	if *flagcheckconfig {
 		dirName := *flagconfig
 		dirName := *flagconfig
@@ -118,8 +128,8 @@ func main() {
 		}
 		}
 
 
 		Zones := make(Zones)
 		Zones := make(Zones)
-		setupPgeodnsZone(Zones)
-		err = zonesReadDir(dirName, Zones)
+		srv.setupPgeodnsZone(Zones)
+		err = srv.zonesReadDir(dirName, Zones)
 		if err != nil {
 		if err != nil {
 			log.Println("Errors reading zones", err)
 			log.Println("Errors reading zones", err)
 			os.Exit(2)
 			os.Exit(2)
@@ -152,11 +162,23 @@ func main() {
 		}()
 		}()
 	}
 	}
 
 
+	// load geodns.conf config
+	configReader(configFileName)
+
+	// load (and re-load) zone data
 	go configWatcher(configFileName)
 	go configWatcher(configFileName)
 
 
 	metrics := NewMetrics()
 	metrics := NewMetrics()
 	go metrics.Updater()
 	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 == "*" {
 	if *flaginter == "*" {
 		addrs, _ := net.InterfaceAddrs()
 		addrs, _ := net.InterfaceAddrs()
 		ips := make([]string, 0)
 		ips := make([]string, 0)
@@ -182,14 +204,14 @@ func main() {
 	go monitor(Zones)
 	go monitor(Zones)
 	go Zones.statHatPoster()
 	go Zones.statHatPoster()
 
 
-	setupRootZone()
-	setupPgeodnsZone(Zones)
+	srv.setupRootZone()
+	srv.setupPgeodnsZone(Zones)
 
 
 	dirName := *flagconfig
 	dirName := *flagconfig
-	go zonesReader(dirName, Zones)
+	go srv.zonesReader(dirName, Zones)
 
 
 	for _, host := range inter {
 	for _, host := range inter {
-		go listenAndServe(host)
+		go srv.listenAndServe(host)
 	}
 	}
 
 
 	terminate := make(chan os.Signal)
 	terminate := make(chan os.Signal)

+ 1 - 1
geoip.go

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

+ 2 - 1
healthtest.go

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

+ 1 - 1
metrics.go

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

+ 2 - 2
monitor.go

@@ -15,8 +15,8 @@ import (
 	"strconv"
 	"strconv"
 	"time"
 	"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
 // Initial status message on websocket

+ 4 - 2
monitor_test.go

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

+ 1 - 1
picker.go

@@ -3,7 +3,7 @@ package main
 import (
 import (
 	"math/rand"
 	"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 {
 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"
 	"strings"
 	"time"
 	"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 {
 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, "."))
 	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
 	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())
 		dns.TypeToString[qtype], req.Id, w.RemoteAddr())
 
 
 	// Global meter
 	// Global meter
@@ -49,6 +63,9 @@ func serve(w dns.ResponseWriter, req *dns.Msg, z *Zone) {
 		realIP = make(net.IP, len(addr.IP))
 		realIP = make(net.IP, len(addr.IP))
 		copy(realIP, addr.IP)
 		copy(realIP, addr.IP)
 	}
 	}
+	if qle != nil {
+		qle.RemoteAddr = realIP.String()
+	}
 
 
 	z.Metrics.ClientStats.Add(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 {
 					if e.Address != nil {
 						edns = e
 						edns = e
 						ip = e.Address
 						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
 	if len(ip) == 0 { // no edns subnet
 		ip = realIP
 		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)
 	targets, netmask, location := z.Options.Targeting.GetTargets(ip, z.HasClosest)
 
 
+	if qle != nil {
+		qle.Targets = targets
+	}
+
 	m := new(dns.Msg)
 	m := new(dns.Msg)
+
+	if qle != nil {
+		defer func() {
+			qle.Rcode = m.Rcode
+			qle.Answers = len(m.Answer)
+		}()
+	}
+
 	m.SetReply(req)
 	m.SetReply(req)
 	if e := m.IsEdns0(); e != nil {
 	if e := m.IsEdns0(); e != nil {
 		m.SetEdns0(4096, e.Do())
 		m.SetEdns0(4096, e.Do())
@@ -112,6 +149,10 @@ func serve(w dns.ResponseWriter, req *dns.Msg, z *Zone) {
 
 
 		firstLabel := (strings.Split(label, "."))[0]
 		firstLabel := (strings.Split(label, "."))[0]
 
 
+		if qle != nil {
+			qle.LabelName = firstLabel
+		}
+
 		if permitDebug && firstLabel == "_status" {
 		if permitDebug && firstLabel == "_status" {
 			if qtype == dns.TypeANY || qtype == dns.TypeTXT {
 			if qtype == dns.TypeANY || qtype == dns.TypeTXT {
 				m.Answer = statusRR(label + "." + z.Origin + ".")
 				m.Answer = statusRR(label + "." + z.Origin + ".")
@@ -164,6 +205,7 @@ func serve(w dns.ResponseWriter, req *dns.Msg, z *Zone) {
 			}
 			}
 
 
 			m.Authoritative = true
 			m.Authoritative = true
+
 			w.WriteMsg(m)
 			w.WriteMsg(m)
 			return
 			return
 		}
 		}
@@ -186,18 +228,24 @@ func serve(w dns.ResponseWriter, req *dns.Msg, z *Zone) {
 		var rrs []dns.RR
 		var rrs []dns.RR
 		for _, record := range servers {
 		for _, record := range servers {
 			rr := dns.Copy(record.RR)
 			rr := dns.Copy(record.RR)
-			rr.Header().Name = req.Question[0].Name
+			rr.Header().Name = qname
 			rrs = append(rrs, rr)
 			rrs = append(rrs, rr)
 		}
 		}
 		m.Answer = rrs
 		m.Answer = rrs
 	}
 	}
 
 
 	if len(m.Answer) == 0 {
 	if len(m.Answer) == 0 {
+		// Return a SOA so the NOERROR answer gets cached
 		m.Ns = append(m.Ns, z.SoaRR())
 		m.Ns = append(m.Ns, z.SoaRR())
 	}
 	}
 
 
 	logPrintln(m)
 	logPrintln(m)
 
 
+	if qle != nil {
+		qle.LabelName = labels.Label
+		qle.Answers = len(m.Answer)
+		qle.Rcode = m.Rcode
+	}
 	err := w.WriteMsg(m)
 	err := w.WriteMsg(m)
 	if err != nil {
 	if err != nil {
 		// if Pack'ing fails the Write fails. Return SERVFAIL.
 		// 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)}}}
 	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
 package main
 
 
 import (
 import (
+	"math/rand"
 	"net"
 	"net"
 	"strings"
 	"strings"
 	"sync"
 	"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 (
 const (
@@ -24,14 +26,19 @@ func (s *ServeSuite) SetUpSuite(c *C) {
 	metrics := NewMetrics()
 	metrics := NewMetrics()
 	go metrics.Updater()
 	go metrics.Updater()
 
 
+	srv := Server{}
+
 	Zones := make(Zones)
 	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
 	// 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) {
 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)
 	r = exchange(c, "one.test.example.com.", dns.TypeA)
 	ip = r.Answer[0].(*dns.A).A
 	ip = r.Answer[0].(*dns.A).A
 	c.Check(ip.String(), Equals, "192.168.1.6")
 	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) {
 func (s *ServeSuite) TestServingMixedCase(c *C) {
@@ -222,12 +237,28 @@ func (s *ServeSuite) TestServeRace(c *C) {
 	wg.Wait()
 	wg.Wait()
 }
 }
 
 
-func (s *ServeSuite) BenchmarkServing(c *C) {
+func (s *ServeSuite) BenchmarkServingCountryDebug(c *C) {
 	for i := 0; i < c.N; i++ {
 	for i := 0; i < c.N; i++ {
 		exchange(c, "_country.foo.pgeodns.", dns.TypeTXT)
 		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 {
 func exchangeSubnet(c *C, name string, dnstype uint16, ip string) *dns.Msg {
 	msg := new(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
 fi
 fi
 
 
-
 CONFIG=dns
 CONFIG=dns
 if [ -e env/CONFIG ]; then
 if [ -e env/CONFIG ]; then
   CONFIG=`head -1 env/CONFIG`
   CONFIG=`head -1 env/CONFIG`
 fi
 fi
 
 
+if [ -e env/ARGS ]; then
+  XARGS=`cat env/ARGS`
+fi
+
 ulimit -n 64000
 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"
 	"strings"
 	"time"
 	"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() {
 func (zs *Zones) statHatPoster() {

+ 1 - 1
targeting_test.go

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

+ 46 - 16
templates.go

@@ -3,6 +3,7 @@ package main
 import (
 import (
 	"bytes"
 	"bytes"
 	"compress/gzip"
 	"compress/gzip"
+	"encoding/base64"
 	"io/ioutil"
 	"io/ioutil"
 	"net/http"
 	"net/http"
 	"os"
 	"os"
@@ -19,14 +20,20 @@ type _escStaticFS struct{}
 
 
 var _escStatic _escStaticFS
 var _escStatic _escStaticFS
 
 
+type _escDirectory struct {
+	fs   http.FileSystem
+	name string
+}
+
 type _escFile struct {
 type _escFile struct {
 	compressed string
 	compressed string
 	size       int64
 	size       int64
+	modtime    int64
 	local      string
 	local      string
 	isDir      bool
 	isDir      bool
 
 
-	data []byte
 	once sync.Once
 	once sync.Once
+	data []byte
 	name string
 	name string
 }
 }
 
 
@@ -50,7 +57,8 @@ func (_escStaticFS) prepare(name string) (*_escFile, error) {
 			return
 			return
 		}
 		}
 		var gr *gzip.Reader
 		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 {
 		if err != nil {
 			return
 			return
 		}
 		}
@@ -70,6 +78,10 @@ func (fs _escStaticFS) Open(name string) (http.File, error) {
 	return f.File()
 	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) {
 func (f *_escFile) File() (http.File, error) {
 	type httpFile struct {
 	type httpFile struct {
 		*bytes.Reader
 		*bytes.Reader
@@ -106,7 +118,7 @@ func (f *_escFile) Mode() os.FileMode {
 }
 }
 
 
 func (f *_escFile) ModTime() time.Time {
 func (f *_escFile) ModTime() time.Time {
-	return time.Time{}
+	return time.Unix(f.modtime, 0)
 }
 }
 
 
 func (f *_escFile) IsDir() bool {
 func (f *_escFile) IsDir() bool {
@@ -126,6 +138,15 @@ func FS(useLocal bool) http.FileSystem {
 	return _escStatic
 	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
 // FSByte returns the named file from the embedded assets. If useLocal is
 // true, the filesystem's contents are instead used.
 // true, the filesystem's contents are instead used.
 func FSByte(useLocal bool, name string) ([]byte, error) {
 func FSByte(useLocal bool, name string) ([]byte, error) {
@@ -134,7 +155,9 @@ func FSByte(useLocal bool, name string) ([]byte, error) {
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
-		return ioutil.ReadAll(f)
+		b, err := ioutil.ReadAll(f)
+		f.Close()
+		return b, err
 	}
 	}
 	f, err := _escStatic.prepare(name)
 	f, err := _escStatic.prepare(name)
 	if err != nil {
 	if err != nil {
@@ -166,18 +189,25 @@ func FSMustString(useLocal bool, name string) string {
 var _escData = map[string]*_escFile{
 var _escData = map[string]*_escFile{
 
 
 	"/templates/status.html": {
 	"/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 (
 import (
 	"fmt"
 	"fmt"
-	"github.com/abh/geodns/Godeps/_workspace/src/github.com/abh/geoip"
+	"github.com/abh/geoip"
 )
 )
 
 
 func main() {
 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"
 	"fmt"
 	"testing"
 	"testing"
 
 
-	. "github.com/abh/geodns/Godeps/_workspace/src/gopkg.in/check.v1"
+	. "gopkg.in/check.v1"
 )
 )
 
 
 // Hook up gocheck into the gotest runner.
 // 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
 language: go
 sudo: false
 sudo: false
 go:
 go:
-  - 1.4
   - 1.5
   - 1.5
+  - 1.6
 script:
 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
 function for it. Server side and client side programming is supported, i.e. you
 can build servers and resolvers with it.
 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
 # 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/fcambus/rrda
 * https://github.com/kenshinx/godns
 * https://github.com/kenshinx/godns
 * https://github.com/skynetservices/skydns
 * https://github.com/skynetservices/skydns
+* https://github.com/hashicorp/consul
 * https://github.com/DevelopersPL/godnsagent
 * https://github.com/DevelopersPL/godnsagent
 * https://github.com/duedil-ltd/discodns
 * https://github.com/duedil-ltd/discodns
 * https://github.com/StalkR/dns-reverse-proxy
 * 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://play.google.com/store/apps/details?id=com.turbobytes.dig
 * https://github.com/fcambus/statzone
 * https://github.com/fcambus/statzone
 * https://github.com/benschw/dns-clb-go
 * 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.
 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;
 * EDNS0, NSID;
 * AXFR/IXFR;
 * AXFR/IXFR;
 * TSIG, SIG(0);
 * TSIG, SIG(0);
+* DNS over TLS: optional encrypted connection between client and server;
 * DNS name compression;
 * DNS name compression;
 * Depends only on the standard library.
 * Depends only on the standard library.
 
 
@@ -123,6 +130,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
 * 6605 - ECDSA
 * 6605 - ECDSA
 * 6725 - IANA Registry Update
 * 6725 - IANA Registry Update
 * 6742 - ILNP DNS
 * 6742 - ILNP DNS
+* 6840 - Clarifications and Implementation Notes for DNS Security
 * 6844 - CAA record
 * 6844 - CAA record
 * 6891 - EDNS0 update
 * 6891 - EDNS0 update
 * 6895 - DNS IANA considerations
 * 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
 * 7314 - DNS (EDNS) EXPIRE Option
 * 7553 - URI record
 * 7553 - URI record
 * xxxx - EDNS0 DNS Update Lease (draft)
 * xxxx - EDNS0 DNS Update Lease (draft)
+* yyyy - DNS over TLS: Initiation and Performance Considerations (draft)
 
 
 ## Loosely based upon
 ## 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 (
 import (
 	"bytes"
 	"bytes"
+	"crypto/tls"
 	"io"
 	"io"
 	"net"
 	"net"
 	"time"
 	"time"
@@ -24,8 +25,9 @@ type Conn struct {
 
 
 // A Client defines parameters for a DNS client.
 // A Client defines parameters for a DNS client.
 type Client struct {
 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
 	UDPSize        uint16            // minimum receive buffer for UDP messages
+	TLSConfig      *tls.Config       // TLS connection configuration
 	DialTimeout    time.Duration     // net.DialTimeout, defaults to 2 seconds
 	DialTimeout    time.Duration     // net.DialTimeout, defaults to 2 seconds
 	ReadTimeout    time.Duration     // net.Conn.SetReadTimeout value for connections, 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
 	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
 // 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
 // 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.
 // 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) {
 func Exchange(m *Msg, a string) (r *Msg, err error) {
 	var co *Conn
 	var co *Conn
 	co, err = DialTimeout("udp", a, dnsTimeout)
 	co, err = DialTimeout("udp", a, dnsTimeout)
@@ -53,8 +48,6 @@ func Exchange(m *Msg, a string) (r *Msg, err error) {
 	}
 	}
 
 
 	defer co.Close()
 	defer co.Close()
-	co.SetReadDeadline(time.Now().Add(dnsTimeout))
-	co.SetWriteDeadline(time.Now().Add(dnsTimeout))
 
 
 	opt := m.IsEdns0()
 	opt := m.IsEdns0()
 	// If EDNS0 is used use that for size.
 	// 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.UDPSize = opt.UDPSize()
 	}
 	}
 
 
+	co.SetWriteDeadline(time.Now().Add(dnsTimeout))
 	if err = co.WriteMsg(m); err != nil {
 	if err = co.WriteMsg(m); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
+
+	co.SetReadDeadline(time.Now().Add(dnsTimeout))
 	r, err = co.ReadMsg()
 	r, err = co.ReadMsg()
 	if err == nil && r.Id != m.Id {
 	if err == nil && r.Id != m.Id {
 		err = ErrId
 		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
 // Exchange does not retry a failed query, nor will it fall back to TCP in
 // case of truncation.
 // 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) {
 func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
 	if !c.SingleInflight {
 	if !c.SingleInflight {
 		return c.exchange(m, a)
 		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) {
 func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
 	var co *Conn
 	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 {
 	} else {
-		co, err = DialTimeout(c.Net, a, c.dialTimeout())
+		co, err = DialTimeout(network, a, c.dialTimeout())
 	}
 	}
+
 	if err != nil {
 	if err != nil {
 		return nil, 0, err
 		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.UDPSize = c.UDPSize
 	}
 	}
 
 
-	co.SetReadDeadline(time.Now().Add(c.readTimeout()))
-	co.SetWriteDeadline(time.Now().Add(c.writeTimeout()))
-
 	co.TsigSecret = c.TsigSecret
 	co.TsigSecret = c.TsigSecret
+	co.SetWriteDeadline(time.Now().Add(c.writeTimeout()))
 	if err = co.WriteMsg(m); err != nil {
 	if err = co.WriteMsg(m); err != nil {
 		return nil, 0, err
 		return nil, 0, err
 	}
 	}
+
+	co.SetReadDeadline(time.Now().Add(c.readTimeout()))
 	r, err = co.ReadMsg()
 	r, err = co.ReadMsg()
 	if err == nil && r.Id != m.Id {
 	if err == nil && r.Id != m.Id {
 		err = ErrId
 		err = ErrId
@@ -196,6 +216,12 @@ func (co *Conn) ReadMsg() (*Msg, error) {
 
 
 	m := new(Msg)
 	m := new(Msg)
 	if err := m.Unpack(p); err != nil {
 	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
 		return nil, err
 	}
 	}
 	if t := m.IsTsig(); t != nil {
 	if t := m.IsTsig(); t != nil {
@@ -218,21 +244,26 @@ func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) {
 		err 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.
 		// First two bytes specify the length of the entire message.
-		l, err := tcpMsgLen(t)
+		l, err := tcpMsgLen(r)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
 		p = make([]byte, l)
 		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 {
 		if co.UDPSize > MinMsgSize {
 			p = make([]byte, co.UDPSize)
 			p = make([]byte, co.UDPSize)
 		} else {
 		} else {
 			p = make([]byte, MinMsgSize)
 			p = make([]byte, MinMsgSize)
 		}
 		}
 		n, err = co.Read(p)
 		n, err = co.Read(p)
+		co.rtt = time.Since(co.t)
 	}
 	}
 
 
 	if err != nil {
 	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.
 // 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}
 	p := []byte{0, 0}
 	n, err := t.Read(p)
 	n, err := t.Read(p)
 	if err != nil {
 	if err != nil {
@@ -268,7 +299,7 @@ func tcpMsgLen(t *net.TCPConn) (int, error) {
 }
 }
 
 
 // tcpRead calls TCPConn.Read enough times to fill allocated buffer.
 // 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)
 	n, err := t.Read(p)
 	if err != nil {
 	if err != nil {
 		return n, err
 		return n, err
@@ -291,27 +322,28 @@ func (co *Conn) Read(p []byte) (n int, err error) {
 	if len(p) < 2 {
 	if len(p) < 2 {
 		return 0, io.ErrShortBuffer
 		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 {
 		if err != nil {
 			return 0, err
 			return 0, err
 		}
 		}
 		if l > len(p) {
 		if l > len(p) {
 			return int(l), io.ErrShortBuffer
 			return int(l), io.ErrShortBuffer
 		}
 		}
-		return tcpRead(t, p[:l])
+		return tcpRead(r, p[:l])
 	}
 	}
 	// UDP connection
 	// UDP connection
 	n, err = co.Conn.Read(p)
 	n, err = co.Conn.Read(p)
 	if err != nil {
 	if err != nil {
 		return n, err
 		return n, err
 	}
 	}
-
-	co.rtt = time.Since(co.t)
 	return n, err
 	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
 // If the message m contains a TSIG record the transaction
 // signature is calculated.
 // signature is calculated.
 func (co *Conn) WriteMsg(m *Msg) (err error) {
 func (co *Conn) WriteMsg(m *Msg) (err error) {
@@ -322,7 +354,7 @@ func (co *Conn) WriteMsg(m *Msg) (err error) {
 			return ErrSecret
 			return ErrSecret
 		}
 		}
 		out, mac, err = TsigGenerate(m, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
 		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
 		co.tsigRequestMAC = mac
 	} else {
 	} else {
 		out, err = m.Pack()
 		out, err = m.Pack()
@@ -339,7 +371,10 @@ func (co *Conn) WriteMsg(m *Msg) (err error) {
 
 
 // Write implements the net.Conn Write method.
 // Write implements the net.Conn Write method.
 func (co *Conn) Write(p []byte) (n int, err error) {
 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)
 		lp := len(p)
 		if lp < 2 {
 		if lp < 2 {
 			return 0, io.ErrShortBuffer
 			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 := make([]byte, 2, lp+2)
 		l[0], l[1] = packUint16(uint16(lp))
 		l[0], l[1] = packUint16(uint16(lp))
 		p = append(l, p...)
 		p = append(l, p...)
-		n, err := io.Copy(t, bytes.NewReader(p))
+		n, err := io.Copy(w, bytes.NewReader(p))
 		return int(n), err
 		return int(n), err
 	}
 	}
 	n, err = co.Conn.(*net.UDPConn).Write(p)
 	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
 	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
 package dns
 
 
 import (
 import (
+	"crypto/tls"
+	"fmt"
+	"net"
 	"strconv"
 	"strconv"
 	"testing"
 	"testing"
 	"time"
 	"time"
@@ -12,7 +15,7 @@ func TestClientSync(t *testing.T) {
 
 
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	if err != nil {
 	if err != nil {
-		t.Fatalf("Unable to run test server: %v", err)
+		t.Fatalf("unable to run test server: %v", err)
 	}
 	}
 	defer s.Shutdown()
 	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) {
 func TestClientSyncBadId(t *testing.T) {
 	HandleFunc("miek.nl.", HelloServerBadId)
 	HandleFunc("miek.nl.", HelloServerBadId)
 	defer HandleRemove("miek.nl.")
 	defer HandleRemove("miek.nl.")
 
 
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	if err != nil {
 	if err != nil {
-		t.Fatalf("Unable to run test server: %v", err)
+		t.Fatalf("unable to run test server: %v", err)
 	}
 	}
 	defer s.Shutdown()
 	defer s.Shutdown()
 
 
@@ -66,7 +106,7 @@ func TestClientEDNS0(t *testing.T) {
 
 
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	if err != nil {
 	if err != nil {
-		t.Fatalf("Unable to run test server: %v", err)
+		t.Fatalf("unable to run test server: %v", err)
 	}
 	}
 	defer s.Shutdown()
 	defer s.Shutdown()
 
 
@@ -113,7 +153,7 @@ func TestClientEDNS0Local(t *testing.T) {
 
 
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	if err != nil {
 	if err != nil {
-		t.Fatalf("Unable to run test server: %s", err)
+		t.Fatalf("unable to run test server: %s", err)
 	}
 	}
 	defer s.Shutdown()
 	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 := new(Msg)
 	m.SetUpdate("t.local.ip6.io.")
 	m.SetUpdate("t.local.ip6.io.")
 	rr, _ := NewRR("t.local.ip6.io. 30 A 127.0.0.1")
 	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")
 	_, _, err := c.Exchange(m, "127.0.0.1:53")
 	if err != nil {
 	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
 	// This uses TCP just to make it slightly different than TestClientSync
 	s, addrstr, err := RunLocalTCPServer("127.0.0.1:0")
 	s, addrstr, err := RunLocalTCPServer("127.0.0.1:0")
 	if err != nil {
 	if err != nil {
-		t.Fatalf("Unable to run test server: %v", err)
+		t.Fatalf("unable to run test server: %v", err)
 	}
 	}
 	defer s.Shutdown()
 	defer s.Shutdown()
 
 
@@ -286,3 +276,146 @@ func TestClientConn(t *testing.T) {
 		t.Errorf("unable to unpack message fully: %v", err)
 		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) {
 func testConfig(t *testing.T, data string) {
 	tempDir, err := ioutil.TempDir("", "")
 	tempDir, err := ioutil.TempDir("", "")
 	if err != nil {
 	if err != nil {
-		t.Fatalf("TempDir: %v", err)
+		t.Fatalf("tempDir: %v", err)
 	}
 	}
 	defer os.RemoveAll(tempDir)
 	defer os.RemoveAll(tempDir)
 
 
 	path := filepath.Join(tempDir, "resolv.conf")
 	path := filepath.Join(tempDir, "resolv.conf")
 	if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil {
 	if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil {
-		t.Fatalf("WriteFile: %v", err)
+		t.Fatalf("writeFile: %v", err)
 	}
 	}
 	cc, err := ClientConfigFromFile(path)
 	cc, err := ClientConfigFromFile(path)
 	if err != nil {
 	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
 	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) {
 func IsDomainName(s string) (labels int, ok bool) {
 	_, labels, err := packDomainName(s, nil, 0, nil, false)
 	_, labels, err := packDomainName(s, nil, 0, nil, false)
 	return labels, err == nil
 	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) {
 func TestNoRdataPack(t *testing.T) {
 	data := make([]byte, 1024)
 	data := make([]byte, 1024)
-	for typ, fn := range typeToRR {
+	for typ, fn := range TypeToRR {
 		r := fn()
 		r := fn()
 		*r.Header() = RR_Header{Name: "miek.nl.", Rrtype: typ, Class: ClassINET, Ttl: 3600}
 		*r.Header() = RR_Header{Name: "miek.nl.", Rrtype: typ, Class: ClassINET, Ttl: 3600}
 		_, err := PackRR(r, data, 0, nil, false)
 		_, 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
 // TODO(miek): fix dns buffer too small errors this throws
 func TestNoRdataUnpack(t *testing.T) {
 func TestNoRdataUnpack(t *testing.T) {
 	data := make([]byte, 1024)
 	data := make([]byte, 1024)
-	for typ, fn := range typeToRR {
+	for typ, fn := range TypeToRR {
 		if typ == TypeSOA || typ == TypeTSIG || typ == TypeWKS {
 		if typ == TypeSOA || typ == TypeTSIG || typ == TypeWKS {
 			// SOA, TSIG will not be seen (like this) in dyn. updates?
 			// SOA, TSIG will not be seen (like this) in dyn. updates?
 			// WKS is an bug, but...deprecated record.
 			// 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 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).
 // the conversion (and re-use the pack/unpack functions).
 type rrsigWireFmt struct {
 type rrsigWireFmt struct {
 	TypeCovered uint16
 	TypeCovered uint16
@@ -248,13 +248,12 @@ func (d *DS) ToCDS() *CDS {
 	return c
 	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 {
 func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
 	if k == nil {
 	if k == nil {
 		return ErrPrivKey
 		return ErrPrivKey
@@ -421,8 +420,8 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
 
 
 	sigbuf := rr.sigBuf()           // Get the binary signature data
 	sigbuf := rr.sigBuf()           // Get the binary signature data
 	if rr.Algorithm == PRIVATEDNS { // PRIVATEOID
 	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]
 	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,
 		//   NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
 		//   HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
 		//   HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
 		//   SRV, DNAME, A6
 		//   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) {
 		switch x := r1.(type) {
 		case *NS:
 		case *NS:
 			x.Ns = strings.ToLower(x.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.Weight = 800
 	srv.Target = "web1.miek.nl."
 	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
 	// With this key
 	key := new(DNSKEY)
 	key := new(DNSKEY)
 	key.Hdr.Rrtype = TypeDNSKEY
 	key.Hdr.Rrtype = TypeDNSKEY
@@ -194,12 +205,12 @@ func TestSignVerify(t *testing.T) {
 	sig.SignerName = key.Hdr.Name
 	sig.SignerName = key.Hdr.Name
 	sig.Algorithm = RSASHA256
 	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
 			continue
 		}
 		}
-		if sig.Verify(key, []RR{r}) != nil {
+		if err := sig.Verify(key, []RR{r}); err != nil {
 			t.Error("failure to validate")
 			t.Error("failure to validate")
 			continue
 			continue
 		}
 		}
@@ -451,7 +462,7 @@ PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
 	}
 	}
 
 
 	if err := sig.Verify(eckey.(*DNSKEY), []RR{a}); err != nil {
 	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(),
 			eckey.(*DNSKEY).String(),
 			a.String(),
 			a.String(),
 			sig.String(),
 			sig.String(),
@@ -500,7 +511,7 @@ func TestSignVerifyECDSA2(t *testing.T) {
 
 
 	err = sig.Verify(key, []RR{srv})
 	err = sig.Verify(key, []RR{srv})
 	if err != nil {
 	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(),
 			key.String(),
 			srv.String(),
 			srv.String(),
 			sig.String(),
 			sig.String(),
@@ -554,7 +565,7 @@ PrivateKey: GU6SnQ/Ou+xC5RumuIUIuJZteXT2z0O/ok1s38Et6mQ=`
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
 	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{
 	ourRRSIG := &RRSIG{
@@ -573,7 +584,7 @@ PrivateKey: GU6SnQ/Ou+xC5RumuIUIuJZteXT2z0O/ok1s38Et6mQ=`
 	}
 	}
 
 
 	if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
 	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
 	// Signatures are randomized
@@ -630,7 +641,7 @@ PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
 	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{
 	ourRRSIG := &RRSIG{
@@ -649,7 +660,7 @@ PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
 	}
 	}
 
 
 	if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
 	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
 	// 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.
 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
 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 := new(dns.Msg)
      m.SetEdns0(4096, true)
      m.SetEdns0(4096, true)
@@ -184,9 +184,9 @@ Basic use pattern validating and replying to a message that has TSIG set.
 	dns.HandleFunc(".", handleRequest)
 	dns.HandleFunc(".", handleRequest)
 
 
 	func handleRequest(w dns.ResponseWriter, r *dns.Msg) {
 	func handleRequest(w dns.ResponseWriter, r *dns.Msg) {
-		m := new(Msg)
+		m := new(dns.Msg)
 		m.SetReply(r)
 		m.SetReply(r)
-		if r.IsTsig() {
+		if r.IsTsig() != nil {
 			if w.TsigStatus() == nil {
 			if w.TsigStatus() == nil {
 				// *Msg r has an TSIG record and it was validated
 				// *Msg r has an TSIG record and it was validated
 				m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
 				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"`
 	Option []EDNS0 `dns:"opt"`
 }
 }
 
 
-// Header implements the RR interface.
-func (rr *OPT) Header() *RR_Header {
-	return &rr.Hdr
-}
-
 func (rr *OPT) String() string {
 func (rr *OPT) String() string {
 	s := "\n;; OPT PSEUDOSECTION:\n; EDNS: version " + strconv.Itoa(int(rr.Version())) + "; "
 	s := "\n;; OPT PSEUDOSECTION:\n; EDNS: version " + strconv.Itoa(int(rr.Version())) + "; "
 	if rr.Do() {
 	if rr.Do() {
@@ -88,10 +83,6 @@ func (rr *OPT) len() int {
 	return l
 	return l
 }
 }
 
 
-func (rr *OPT) copy() RR {
-	return &OPT{*rr.Hdr.copyHeader(), rr.Option}
-}
-
 // return the old value -> delete SetVersion?
 // return the old value -> delete SetVersion?
 
 
 // Version returns the EDNS version used. Only zero is defined.
 // 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 (
 import (
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
-	"github.com/abh/geodns/Godeps/_workspace/src/github.com/miekg/dns"
 	"log"
 	"log"
 	"net"
 	"net"
+
+	"github.com/miekg/dns"
 )
 )
 
 
 // Retrieve the MX records for miek.nl.
 // Retrieve the MX records for miek.nl.
@@ -31,13 +32,11 @@ func ExampleMX() {
 
 
 // Retrieve the DNSKEY records of a zone and convert them
 // Retrieve the DNSKEY records of a zone and convert them
 // to DS records for SHA1, SHA256 and SHA384.
 // to DS records for SHA1, SHA256 and SHA384.
-func ExampleDS(zone string) {
+func ExampleDS() {
 	config, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
 	config, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
 	c := new(dns.Client)
 	c := new(dns.Client)
 	m := new(dns.Msg)
 	m := new(dns.Msg)
-	if zone == "" {
-		zone = "miek.nl"
-	}
+	zone := "miek.nl"
 	m.SetQuestion(dns.Fqdn(zone), dns.TypeDNSKEY)
 	m.SetQuestion(dns.Fqdn(zone), dns.TypeDNSKEY)
 	m.SetEdns0(4096, true)
 	m.SetEdns0(4096, true)
 	r, _, err := c.Exchange(m, config.Servers[0]+":"+config.Port)
 	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 (
 import (
 	"fmt"
 	"fmt"
-	"github.com/abh/geodns/Godeps/_workspace/src/github.com/miekg/dns/idn"
+	"github.com/miekg/dns/idn"
 )
 )
 
 
 func ExampleToPunycode() {
 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"
 	"unicode/utf8"
 	"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
 // Implementation idea from RFC itself and from from IDNA::Punycode created by
@@ -199,7 +199,6 @@ func needFromPunycode(s string) bool {
 			return true
 			return true
 		}
 		}
 	}
 	}
-	panic("dns: not reached")
 }
 }
 
 
 // encode transforms Unicode input bytes (that represent DNS label) into
 // 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.
 // 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"}
+// .www.miek.nl. returns []string{"", "www", "miek", "nl"},
 // The root label (.) returns nil. Note that using
 // The root label (.) returns nil. Note that using
 // strings.Split(s) will work in most cases, but does not handle
 // strings.Split(s) will work in most cases, but does not handle
 // escaped dots (\.) for instance.
 // escaped dots (\.) for instance.
+// s must be a syntactically valid domain name, see IsDomainName.
 func SplitDomainName(s string) (labels []string) {
 func SplitDomainName(s string) (labels []string) {
 	if len(s) == 0 {
 	if len(s) == 0 {
 		return nil
 		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 miek.nl. have two labels in common: miek and nl
 // www.miek.nl. and www.bla.nl. have one label in common: 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) {
 func CompareDomainName(s1, s2 string) (n int) {
 	s1 = Fqdn(s1)
 	s1 = Fqdn(s1)
 	s2 = Fqdn(s2)
 	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.
 // 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) {
 func CountLabel(s string) (labels int) {
 	if s == "." {
 	if s == "." {
 		return
 		return
@@ -102,7 +107,8 @@ func CountLabel(s string) (labels int) {
 
 
 // Split splits a name s into its label indexes.
 // 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}.
 // 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 {
 func Split(s string) []int {
 	if s == "." {
 	if s == "." {
 		return nil
 		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
 package dns
 
 
-import (
-	"testing"
-)
+import "testing"
 
 
 func TestCompareDomainName(t *testing.T) {
 func TestCompareDomainName(t *testing.T) {
 	s1 := "www.miek.nl."
 	s1 := "www.miek.nl."
@@ -61,9 +59,9 @@ func TestSplit(t *testing.T) {
 
 
 func TestSplit2(t *testing.T) {
 func TestSplit2(t *testing.T) {
 	splitter := map[string][]int{
 	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 {
 	for s, i := range splitter {
 		x := Split(s)
 		x := Split(s)
@@ -125,13 +123,14 @@ func TestCountLabel(t *testing.T) {
 
 
 func TestSplitDomainName(t *testing.T) {
 func TestSplitDomainName(t *testing.T) {
 	labels := map[string][]string{
 	labels := map[string][]string{
-		"miek.nl":       []string{"miek", "nl"},
+		"miek.nl":       {"miek", "nl"},
 		".":             nil,
 		".":             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:
 domainLoop:
 	for domain, splits := range labels {
 	for domain, splits := range labels {
@@ -155,13 +154,13 @@ func TestIsDomainName(t *testing.T) {
 		lab int
 		lab int
 	}
 	}
 	names := map[string]*ret{
 	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 {
 	for d, ok := range names {
 		l, k := IsDomainName(d)
 		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"}
 	ErrSoa error = &Error{err: "no SOA"}
 	// ErrTime indicates a timing error in TSIG authentication.
 	// ErrTime indicates a timing error in TSIG authentication.
 	ErrTime error = &Error{err: "bad time"}
 	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
 // 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.
 	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.
 // StringToType is the reverse of TypeToString, needed for string parsing.
 var StringToType = reverseInt16(TypeToString)
 var StringToType = reverseInt16(TypeToString)
 
 
@@ -332,7 +257,7 @@ func packDomainName(s string, msg []byte, off int, compression map[string]int, c
 				roBs = string(bs)
 				roBs = string(bs)
 				bsFresh = true
 				bsFresh = true
 			}
 			}
-			// Dont try to compress '.'
+			// Don't try to compress '.'
 			if compress && roBs[begin:] != "." {
 			if compress && roBs[begin:] != "." {
 				if p, ok := compression[roBs[begin:]]; !ok {
 				if p, ok := compression[roBs[begin:]]; !ok {
 					// Only offsets smaller than this can be used.
 					// 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
 						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
 					break
 				}
 				}
 				s, off, err = UnpackDomainName(msg, off)
 				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)
 	end := off + int(h.Rdlength)
 	// make an rr of that type and re-unpack.
 	// make an rr of that type and re-unpack.
-	mk, known := typeToRR[h.Rrtype]
+	mk, known := TypeToRR[h.Rrtype]
 	if !known {
 	if !known {
 		rr = new(RFC3597)
 		rr = new(RFC3597)
 	} else {
 	} else {
@@ -1474,6 +1399,32 @@ func UnpackRR(msg []byte, off int) (rr RR, off1 int, err error) {
 	return rr, off, err
 	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
 // Reverse a map
 func reverseInt8(m map[uint8]string) map[string]uint8 {
 func reverseInt8(m map[uint8]string) map[string]uint8 {
 	n := make(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.CheckingDisabled = (dh.Bits & _CD) != 0
 	dns.Rcode = int(dh.Bits & 0xF)
 	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
 	var q Question
 	for i := 0; i < int(dh.Qdcount); i++ {
 	for i := 0; i < int(dh.Qdcount); i++ {
 		off1 := off
 		off1 := off
 		off, err = UnpackStruct(&q, msg, off)
 		off, err = UnpackStruct(&q, msg, off)
 		if err != nil {
 		if err != nil {
+			// Even if Truncated is set, we only will set ErrTruncated if we
+			// actually got the questions
 			return err
 			return err
 		}
 		}
 		if off1 == off { // Offset does not increase anymore, dh.Qdcount is a lie!
 		if off1 == off { // Offset does not increase anymore, dh.Qdcount is a lie!
 			dh.Qdcount = uint16(i)
 			dh.Qdcount = uint16(i)
 			break
 			break
 		}
 		}
-
 		dns.Question = append(dns.Question, q)
 		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) {
 	if off != len(msg) {
 		// TODO(miek) make this an error?
 		// TODO(miek) make this an error?
 		// use PackOpt to let people tell how detailed the error reporting should be?
 		// use PackOpt to let people tell how detailed the error reporting should be?
 		// println("dns: extra bytes in dns packet", off, "<", len(msg))
 		// 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.
 // 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 {
 	for _, s := range test {
 		rr, err := NewRR(fmt.Sprintf("example.com. IN TXT %v", s[0]))
 		rr, err := NewRR(fmt.Sprintf("example.com. IN TXT %v", s[0]))
 		if err != nil {
 		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
 			continue
 		}
 		}
 
 
 		txt := sprintTxt(rr.(*TXT).Txt)
 		txt := sprintTxt(rr.(*TXT).Txt)
 		if txt != s[1] {
 		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))
 	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 .
 	zone := `$ORIGIN .
 $TTL 3600       ; 1 hour
 $TTL 3600       ; 1 hour
 name                    IN SOA  a6.nstld.com. hostmaster.nic.name. (
 name                    IN SOA  a6.nstld.com. hostmaster.nic.name. (
@@ -768,7 +768,7 @@ func TestRfc1982(t *testing.T) {
 }
 }
 
 
 func TestEmpty(t *testing.T) {
 func TestEmpty(t *testing.T) {
-	for _ = range ParseZone(strings.NewReader(""), "", "") {
+	for range ParseZone(strings.NewReader(""), "", "") {
 		t.Errorf("should be empty")
 		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
 	// 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"
 	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.", "")
 	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.")
 		t.Skip("skipping test in short mode.")
 	}
 	}
 	algorithms := []algorithm{
 	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 {
 	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 {
 func mkPrivateRR(rrtype uint16) *PrivateRR {
 	// Panics if RR is not an instance of PrivateRR.
 	// Panics if RR is not an instance of PrivateRR.
-	rrfunc, ok := typeToRR[rrtype]
+	rrfunc, ok := TypeToRR[rrtype]
 	if !ok {
 	if !ok {
 		panic(fmt.Sprintf("dns: invalid operation with Private RR type %d", rrtype))
 		panic(fmt.Sprintf("dns: invalid operation with Private RR type %d", rrtype))
 	}
 	}
@@ -43,7 +43,7 @@ func mkPrivateRR(rrtype uint16) *PrivateRR {
 	case *PrivateRR:
 	case *PrivateRR:
 		return rr
 		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.
 // 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) {
 func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata) {
 	rtypestr = strings.ToUpper(rtypestr)
 	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
 	TypeToString[rtype] = rtypestr
 	StringToType[rtypestr] = rtype
 	StringToType[rtypestr] = rtype
 
 
@@ -108,7 +108,7 @@ func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata)
 func PrivateHandleRemove(rtype uint16) {
 func PrivateHandleRemove(rtype uint16) {
 	rtypestr, ok := TypeToString[rtype]
 	rtypestr, ok := TypeToString[rtype]
 	if ok {
 	if ok {
-		delete(typeToRR, rtype)
+		delete(TypeToRR, rtype)
 		delete(TypeToString, rtype)
 		delete(TypeToString, rtype)
 		delete(typeToparserFunc, rtype)
 		delete(typeToparserFunc, rtype)
 		delete(StringToType, rtypestr)
 		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"
 	"strings"
 	"testing"
 	"testing"
 
 
-	"github.com/abh/geodns/Godeps/_workspace/src/github.com/miekg/dns"
+	"github.com/miekg/dns"
 )
 )
 
 
 const TypeISBN uint16 = 0x0F01
 const TypeISBN uint16 = 0x0F01
@@ -90,11 +90,11 @@ func TestPrivateByteSlice(t *testing.T) {
 	}
 	}
 
 
 	if off1 != off {
 	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 {
 	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 {
 	} else {
 		t.Log(rr1.String())
 		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
 	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 {
 func rawSetAnswerLen(msg []byte, i uint16) bool {
 	if len(msg) < 8 {
 	if len(msg) < 8 {
 		return false
 		return false
@@ -30,7 +30,7 @@ func rawSetAnswerLen(msg []byte, i uint16) bool {
 	return true
 	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 {
 func rawSetNsLen(msg []byte, i uint16) bool {
 	if len(msg) < 10 {
 	if len(msg) < 10 {
 		return false
 		return false
@@ -39,7 +39,7 @@ func rawSetNsLen(msg []byte, i uint16) bool {
 	return true
 	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 {
 func rawSetExtraLen(msg []byte, i uint16) bool {
 	if len(msg) < 12 {
 	if len(msg) < 12 {
 		return false
 		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"),
 			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{
 		[...]RR{
 			newRR(t, "miEk.nl. 2000 IN A 127.0.0.1"),
 			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. 1000 IN A 127.0.0.1"),
 			newRR(t, "Miek.nL. 500 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{
 		[...]RR{
 			newRR(t, "miek.nl. IN A 127.0.0.1"),
 			newRR(t, "miek.nl. IN A 127.0.0.1"),
 			newRR(t, "miek.nl. CH 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"),
 			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",
 			"miek.nl.\t3600\tCH\tA\t127.0.0.1",
 		},
 		},
 		[...]RR{
 		[...]RR{
 			newRR(t, "miek.nl. CH 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"),
 			newRR(t, "miek.nl. IN A 127.0.0.1"),
 			newRR(t, "miek.de. 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.nl.\t3600\tIN\tA\t127.0.0.1",
 			"miek.de.\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.de. IN A 127.0.0.1"),
 			newRR(t, "miek.nl. 200 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"),
 			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",
 			"miek.nl.\t200\tIN\tA\t127.0.0.1",
 		},
 		},
 	}
 	}
@@ -57,7 +57,7 @@ func BenchmarkDedup(b *testing.B) {
 	}
 	}
 	m := make(map[string]RR)
 	m := make(map[string]RR)
 	for i := 0; i < b.N; i++ {
 	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 (
 import (
 	"bytes"
 	"bytes"
+	"crypto/tls"
 	"io"
 	"io"
 	"net"
 	"net"
 	"sync"
 	"sync"
@@ -47,7 +48,7 @@ type response struct {
 	tsigRequestMAC string
 	tsigRequestMAC string
 	tsigSecret     map[string]string // the tsig secrets
 	tsigSecret     map[string]string // the tsig secrets
 	udp            *net.UDPConn      // i/o connection if UDP was used
 	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
 	udpSession     *SessionUDP       // oob data to get egress interface right
 	remoteAddr     net.Addr          // address of the client
 	remoteAddr     net.Addr          // address of the client
 	writer         Writer            // writer to output the raw DNS bits
 	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) }
 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.
 // for incoming queries.
 func ListenAndServe(addr string, network string, handler Handler) error {
 func ListenAndServe(addr string, network string, handler Handler) error {
 	server := &Server{Addr: addr, Net: network, Handler: handler}
 	server := &Server{Addr: addr, Net: network, Handler: handler}
 	return server.ListenAndServe()
 	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,
 // ActivateAndServe activates a server with a listener from systemd,
 // l and p should not both be non-nil.
 // l and p should not both be non-nil.
 // If both l and p are not nil only p will be used.
 // If both l and p are not nil only p will be used.
@@ -210,7 +233,7 @@ type Writer interface {
 type Reader interface {
 type Reader interface {
 	// ReadTCP reads a raw message from a TCP connection. Implementations may alter
 	// ReadTCP reads a raw message from a TCP connection. Implementations may alter
 	// connection properties, for example the read-deadline.
 	// 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
 	// ReadUDP reads a raw message from a UDP connection. Implementations may alter
 	// connection properties, for example the read-deadline.
 	// connection properties, for example the read-deadline.
 	ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error)
 	ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error)
@@ -222,7 +245,7 @@ type defaultReader struct {
 	*Server
 	*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)
 	return dr.readTCP(conn, timeout)
 }
 }
 
 
@@ -242,10 +265,12 @@ type DecorateWriter func(Writer) Writer
 type Server struct {
 type Server struct {
 	// Address to listen on, ":dns" if empty.
 	// Address to listen on, ":dns" if empty.
 	Addr string
 	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
 	Net string
 	// TCP Listener to use, this is to aid in systemd's socket activation.
 	// TCP Listener to use, this is to aid in systemd's socket activation.
 	Listener net.Listener
 	Listener net.Listener
+	// TLS connection configuration
+	TLSConfig *tls.Config
 	// UDP "Listener" to use, this is to aid in systemd's socket activation.
 	// UDP "Listener" to use, this is to aid in systemd's socket activation.
 	PacketConn net.PacketConn
 	PacketConn net.PacketConn
 	// Handler to invoke, dns.DefaultServeMux if nil.
 	// Handler to invoke, dns.DefaultServeMux if nil.
@@ -262,7 +287,7 @@ type Server struct {
 	// Secret(s) for Tsig map[<zonename>]<base64 secret>.
 	// Secret(s) for Tsig map[<zonename>]<base64 secret>.
 	TsigSecret map[string]string
 	TsigSecret map[string]string
 	// Unsafe instructs the server to disregard any sanity checks and directly hand the message to
 	// 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
 	Unsafe bool
 	// If NotifyStartedFunc is set it is called once the server has started listening.
 	// If NotifyStartedFunc is set it is called once the server has started listening.
 	NotifyStartedFunc func()
 	NotifyStartedFunc func()
@@ -271,26 +296,21 @@ type Server struct {
 	// DecorateWriter is optional, allows customization of the process that writes raw DNS messages.
 	// DecorateWriter is optional, allows customization of the process that writes raw DNS messages.
 	DecorateWriter DecorateWriter
 	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
 	started bool
 }
 }
 
 
 // ListenAndServe starts a nameserver on the configured address in *Server.
 // ListenAndServe starts a nameserver on the configured address in *Server.
 func (srv *Server) ListenAndServe() error {
 func (srv *Server) ListenAndServe() error {
 	srv.lock.Lock()
 	srv.lock.Lock()
+	defer srv.lock.Unlock()
 	if srv.started {
 	if srv.started {
-		srv.lock.Unlock()
 		return &Error{err: "server already started"}
 		return &Error{err: "server already started"}
 	}
 	}
-	srv.stopUDP, srv.stopTCP = make(chan bool), make(chan bool)
-	srv.started = true
 	addr := srv.Addr
 	addr := srv.Addr
 	if addr == "" {
 	if addr == "" {
 		addr = ":domain"
 		addr = ":domain"
@@ -309,8 +329,29 @@ func (srv *Server) ListenAndServe() error {
 			return e
 			return e
 		}
 		}
 		srv.Listener = l
 		srv.Listener = l
+		srv.started = true
 		srv.lock.Unlock()
 		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":
 	case "udp", "udp4", "udp6":
 		a, e := net.ResolveUDPAddr(srv.Net, addr)
 		a, e := net.ResolveUDPAddr(srv.Net, addr)
 		if e != nil {
 		if e != nil {
@@ -324,10 +365,12 @@ func (srv *Server) ListenAndServe() error {
 			return e
 			return e
 		}
 		}
 		srv.PacketConn = l
 		srv.PacketConn = l
+		srv.started = true
 		srv.lock.Unlock()
 		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"}
 	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.
 // configured in *Server. Its main use is to start a server from systemd.
 func (srv *Server) ActivateAndServe() error {
 func (srv *Server) ActivateAndServe() error {
 	srv.lock.Lock()
 	srv.lock.Lock()
+	defer srv.lock.Unlock()
 	if srv.started {
 	if srv.started {
-		srv.lock.Unlock()
 		return &Error{err: "server already started"}
 		return &Error{err: "server already started"}
 	}
 	}
-	srv.stopUDP, srv.stopTCP = make(chan bool), make(chan bool)
-	srv.started = true
 	pConn := srv.PacketConn
 	pConn := srv.PacketConn
 	l := srv.Listener
 	l := srv.Listener
-	srv.lock.Unlock()
 	if pConn != nil {
 	if pConn != nil {
 		if srv.UDPSize == 0 {
 		if srv.UDPSize == 0 {
 			srv.UDPSize = MinMsgSize
 			srv.UDPSize = MinMsgSize
@@ -352,13 +392,19 @@ func (srv *Server) ActivateAndServe() error {
 			if e := setUDPSocketOptions(t); e != nil {
 			if e := setUDPSocketOptions(t); e != nil {
 				return e
 				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 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"}
 	return &Error{err: "bad listeners"}
 }
 }
@@ -374,36 +420,20 @@ func (srv *Server) Shutdown() error {
 		return &Error{err: "server not started"}
 		return &Error{err: "server not started"}
 	}
 	}
 	srv.started = false
 	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()
 	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 {
 	select {
 	case <-time.After(srv.getReadTimeout()):
 	case <-time.After(srv.getReadTimeout()):
@@ -424,7 +454,7 @@ func (srv *Server) getReadTimeout() time.Duration {
 
 
 // serveTCP starts a TCP listener for the server.
 // serveTCP starts a TCP listener for the server.
 // Each request is handled in a separate goroutine.
 // 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()
 	defer l.Close()
 
 
 	if srv.NotifyStartedFunc != nil {
 	if srv.NotifyStartedFunc != nil {
@@ -443,20 +473,24 @@ func (srv *Server) serveTCP(l *net.TCPListener) error {
 	rtimeout := srv.getReadTimeout()
 	rtimeout := srv.getReadTimeout()
 	// deadline is not used here
 	// deadline is not used here
 	for {
 	for {
-		rw, e := l.AcceptTCP()
+		rw, e := l.Accept()
 		if e != nil {
 		if e != nil {
-			continue
+			if neterr, ok := e.(net.Error); ok && neterr.Temporary() {
+				continue
+			}
+			return e
 		}
 		}
 		m, e := reader.ReadTCP(rw, rtimeout)
 		m, e := reader.ReadTCP(rw, rtimeout)
-		select {
-		case <-srv.stopTCP:
+		srv.lock.RLock()
+		if !srv.started {
+			srv.lock.RUnlock()
 			return nil
 			return nil
-		default:
 		}
 		}
+		srv.lock.RUnlock()
 		if e != nil {
 		if e != nil {
 			continue
 			continue
 		}
 		}
-		srv.wgTCP.Add(1)
+		srv.inFlight.Add(1)
 		go srv.serve(rw.RemoteAddr(), handler, m, nil, nil, rw)
 		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
 	// deadline is not used here
 	for {
 	for {
 		m, s, e := reader.ReadUDP(l, rtimeout)
 		m, s, e := reader.ReadUDP(l, rtimeout)
-		select {
-		case <-srv.stopUDP:
+		srv.lock.RLock()
+		if !srv.started {
+			srv.lock.RUnlock()
 			return nil
 			return nil
-		default:
 		}
 		}
+		srv.lock.RUnlock()
 		if e != nil {
 		if e != nil {
 			continue
 			continue
 		}
 		}
-		srv.wgUDP.Add(1)
+		srv.inFlight.Add(1)
 		go srv.serve(s.RemoteAddr(), handler, m, l, s, nil)
 		go srv.serve(s.RemoteAddr(), handler, m, l, s, nil)
 	}
 	}
 }
 }
 
 
 // Serve a new connection.
 // 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}
 	w := &response{tsigSecret: srv.TsigSecret, udp: u, tcp: t, remoteAddr: a, udpSession: s}
 	if srv.DecorateWriter != nil {
 	if srv.DecorateWriter != nil {
 		w.writer = srv.DecorateWriter(w)
 		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
 	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})
 	reader := Reader(&defaultReader{srv})
 	if srv.DecorateReader != nil {
 	if srv.DecorateReader != nil {
 		reader = srv.DecorateReader(reader)
 		reader = srv.DecorateReader(reader)
@@ -548,6 +576,9 @@ Redo:
 	h.ServeDNS(w, req) // Writes back to the client
 	h.ServeDNS(w, req) // Writes back to the client
 
 
 Exit:
 Exit:
+	if w.tcp == nil {
+		return
+	}
 	// TODO(miek): make this number configurable?
 	// TODO(miek): make this number configurable?
 	if q > maxTCPQueries { // close socket after this many queries
 	if q > maxTCPQueries { // close socket after this many queries
 		w.Close()
 		w.Close()
@@ -574,7 +605,7 @@ Exit:
 	return
 	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))
 	conn.SetReadDeadline(time.Now().Add(timeout))
 	l := make([]byte, 2)
 	l := make([]byte, 2)
 	n, err := conn.Read(l)
 	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
 package dns
 
 
 import (
 import (
+	"crypto/tls"
 	"fmt"
 	"fmt"
+	"io"
 	"net"
 	"net"
 	"runtime"
 	"runtime"
 	"sync"
 	"sync"
 	"testing"
 	"testing"
+	"time"
 )
 )
 
 
 func HelloServer(w ResponseWriter, req *Msg) {
 func HelloServer(w ResponseWriter, req *Msg) {
@@ -37,23 +40,32 @@ func AnotherHelloServer(w ResponseWriter, req *Msg) {
 }
 }
 
 
 func RunLocalUDPServer(laddr string) (*Server, string, error) {
 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)
 	pc, err := net.ListenPacket("udp", laddr)
 	if err != nil {
 	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 := sync.Mutex{}
 	waitLock.Lock()
 	waitLock.Lock()
 	server.NotifyStartedFunc = waitLock.Unlock
 	server.NotifyStartedFunc = waitLock.Unlock
 
 
+	fin := make(chan struct{}, 0)
+
 	go func() {
 	go func() {
 		server.ActivateAndServe()
 		server.ActivateAndServe()
+		close(fin)
 		pc.Close()
 		pc.Close()
 	}()
 	}()
 
 
 	waitLock.Lock()
 	waitLock.Lock()
-	return server, pc.LocalAddr().String(), nil
+	return server, pc.LocalAddr().String(), fin, nil
 }
 }
 
 
 func RunLocalUDPServerUnsafe(laddr string) (*Server, string, error) {
 func RunLocalUDPServerUnsafe(laddr string) (*Server, string, error) {
@@ -61,7 +73,8 @@ func RunLocalUDPServerUnsafe(laddr string) (*Server, string, error) {
 	if err != nil {
 	if err != nil {
 		return nil, "", err
 		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 := sync.Mutex{}
 	waitLock.Lock()
 	waitLock.Lock()
@@ -82,7 +95,28 @@ func RunLocalTCPServer(laddr string) (*Server, string, error) {
 		return nil, "", err
 		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 := sync.Mutex{}
 	waitLock.Lock()
 	waitLock.Lock()
@@ -105,7 +139,7 @@ func TestServing(t *testing.T) {
 
 
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	if err != nil {
 	if err != nil {
-		t.Fatalf("Unable to run test server: %v", err)
+		t.Fatalf("unable to run test server: %v", err)
 	}
 	}
 	defer s.Shutdown()
 	defer s.Shutdown()
 
 
@@ -118,7 +152,7 @@ func TestServing(t *testing.T) {
 	}
 	}
 	txt := r.Extra[0].(*TXT).Txt[0]
 	txt := r.Extra[0].(*TXT).Txt[0]
 	if txt != "Hello world" {
 	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)
 	m.SetQuestion("example.com.", TypeTXT)
@@ -128,7 +162,7 @@ func TestServing(t *testing.T) {
 	}
 	}
 	txt = r.Extra[0].(*TXT).Txt[0]
 	txt = r.Extra[0].(*TXT).Txt[0]
 	if txt != "Hello example" {
 	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.
 	// Test Mixes cased as noticed by Ask.
@@ -139,7 +173,67 @@ func TestServing(t *testing.T) {
 	}
 	}
 	txt = r.Extra[0].(*TXT).Txt[0]
 	txt = r.Extra[0].(*TXT).Txt[0]
 	if txt != "Hello example" {
 	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")
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	if err != nil {
 	if err != nil {
-		b.Fatalf("Unable to run test server: %v", err)
+		b.Fatalf("unable to run test server: %v", err)
 	}
 	}
 	defer s.Shutdown()
 	defer s.Shutdown()
 
 
@@ -173,7 +267,7 @@ func benchmarkServe6(b *testing.B) {
 	a := runtime.GOMAXPROCS(4)
 	a := runtime.GOMAXPROCS(4)
 	s, addrstr, err := RunLocalUDPServer("[::1]:0")
 	s, addrstr, err := RunLocalUDPServer("[::1]:0")
 	if err != nil {
 	if err != nil {
-		b.Fatalf("Unable to run test server: %v", err)
+		b.Fatalf("unable to run test server: %v", err)
 	}
 	}
 	defer s.Shutdown()
 	defer s.Shutdown()
 
 
@@ -204,7 +298,7 @@ func BenchmarkServeCompress(b *testing.B) {
 	a := runtime.GOMAXPROCS(4)
 	a := runtime.GOMAXPROCS(4)
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	if err != nil {
 	if err != nil {
-		b.Fatalf("Unable to run test server: %v", err)
+		b.Fatalf("unable to run test server: %v", err)
 	}
 	}
 	defer s.Shutdown()
 	defer s.Shutdown()
 
 
@@ -305,7 +399,7 @@ func TestServingLargeResponses(t *testing.T) {
 
 
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	if err != nil {
 	if err != nil {
-		t.Fatalf("Unable to run test server: %v", err)
+		t.Fatalf("unable to run test server: %v", err)
 	}
 	}
 	defer s.Shutdown()
 	defer s.Shutdown()
 
 
@@ -345,7 +439,7 @@ func TestServingResponse(t *testing.T) {
 	HandleFunc("miek.nl.", HelloServer)
 	HandleFunc("miek.nl.", HelloServer)
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
 	if err != nil {
 	if err != nil {
-		t.Fatalf("Unable to run test server: %v", err)
+		t.Fatalf("unable to run test server: %v", err)
 	}
 	}
 
 
 	c := new(Client)
 	c := new(Client)
@@ -365,7 +459,7 @@ func TestServingResponse(t *testing.T) {
 	s.Shutdown()
 	s.Shutdown()
 	s, addrstr, err = RunLocalUDPServerUnsafe("127.0.0.1:0")
 	s, addrstr, err = RunLocalUDPServerUnsafe("127.0.0.1:0")
 	if err != nil {
 	if err != nil {
-		t.Fatalf("Unable to run test server: %v", err)
+		t.Fatalf("unable to run test server: %v", err)
 	}
 	}
 	defer s.Shutdown()
 	defer s.Shutdown()
 
 
@@ -379,22 +473,104 @@ func TestServingResponse(t *testing.T) {
 func TestShutdownTCP(t *testing.T) {
 func TestShutdownTCP(t *testing.T) {
 	s, _, err := RunLocalTCPServer("127.0.0.1:0")
 	s, _, err := RunLocalTCPServer("127.0.0.1:0")
 	if err != nil {
 	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()
 	err = s.Shutdown()
 	if err != nil {
 	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) {
 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 {
 	if err != nil {
-		t.Fatalf("Unable to run test server: %v", err)
+		t.Fatalf("unable to run test server: %v", err)
 	}
 	}
 	err = s.Shutdown()
 	err = s.Shutdown()
 	if err != nil {
 	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{
 	server := &Server{
 		PacketConn:     pc,
 		PacketConn:     pc,
 		DecorateWriter: wf,
 		DecorateWriter: wf,
+		ReadTimeout:    time.Hour, WriteTimeout: time.Hour,
 	}
 	}
 
 
 	waitLock := sync.Mutex{}
 	waitLock := sync.Mutex{}
@@ -448,3 +625,55 @@ func ExampleDecorateWriter() {
 	}
 	}
 	// Output: writing raw DNS message of length 56
 	// 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)
 		pk, err := keyrr.Generate(keysize)
 		if err != nil {
 		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
 			continue
 		}
 		}
 		now := uint32(time.Now().Unix())
 		now := uint32(time.Now().Unix())
@@ -43,16 +43,16 @@ func TestSIG0(t *testing.T) {
 		sigrr.SignerName = keyrr.Hdr.Name
 		sigrr.SignerName = keyrr.Hdr.Name
 		mb, err := sigrr.Sign(pk.(crypto.Signer), m)
 		mb, err := sigrr.Sign(pk.(crypto.Signer), m)
 		if err != nil {
 		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
 			continue
 		}
 		}
 		m := new(Msg)
 		m := new(Msg)
 		if err := m.Unpack(mb); err != nil {
 		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
 			continue
 		}
 		}
 		if len(m.Extra) != 1 {
 		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
 			continue
 		}
 		}
 		var sigrrwire *SIG
 		var sigrrwire *SIG
@@ -60,7 +60,7 @@ func TestSIG0(t *testing.T) {
 		case *SIG:
 		case *SIG:
 			sigrrwire = rr
 			sigrrwire = rr
 		default:
 		default:
-			t.Errorf("Expected SIG RR, instead: %v", rr)
+			t.Errorf("expected SIG RR, instead: %v", rr)
 			continue
 			continue
 		}
 		}
 		for _, rr := range []*SIG{sigrr, sigrrwire} {
 		for _, rr := range []*SIG{sigrr, sigrrwire} {
@@ -69,20 +69,20 @@ func TestSIG0(t *testing.T) {
 				id = "sigrrwire"
 				id = "sigrrwire"
 			}
 			}
 			if err := rr.Verify(keyrr, mb); err != nil {
 			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
 				continue
 			}
 			}
 		}
 		}
 		mb[13]++
 		mb[13]++
 		if err := sigrr.Verify(keyrr, mb); err == nil {
 		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
 			continue
 		}
 		}
 		sigrr.Expiration = 2
 		sigrr.Expiration = 2
 		sigrr.Inception = 1
 		sigrr.Inception = 1
 		mb, _ = sigrr.Sign(pk.(crypto.Signer), m)
 		mb, _ = sigrr.Sign(pk.(crypto.Signer), m)
 		if err := sigrr.Verify(keyrr, mb); err == nil {
 		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
 			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 {
 	if e != nil {
 		return "", e
 		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"`
 	OtherData  string `dns:"size-hex"`
 }
 }
 
 
-func (rr *TSIG) Header() *RR_Header {
-	return &rr.Hdr
-}
-
 // TSIG has no official presentation format, but this will suffice.
 // TSIG has no official presentation format, but this will suffice.
 
 
 func (rr *TSIG) String() string {
 func (rr *TSIG) String() string {
@@ -58,15 +54,6 @@ func (rr *TSIG) String() string {
 	return s
 	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.
 // The following values must be put in wireformat, so that the MAC can be calculated.
 // RFC 2845, section 3.4.2. TSIG Variables.
 // RFC 2845, section 3.4.2. TSIG Variables.
 type tsigWireFmt struct {
 type tsigWireFmt struct {
@@ -125,7 +112,7 @@ func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, s
 
 
 	t := new(TSIG)
 	t := new(TSIG)
 	var h hash.Hash
 	var h hash.Hash
-	switch rr.Algorithm {
+	switch strings.ToLower(rr.Algorithm) {
 	case HmacMD5:
 	case HmacMD5:
 		h = hmac.New(md5.New, []byte(rawsecret))
 		h = hmac.New(md5.New, []byte(rawsecret))
 	case HmacSHA1:
 	case HmacSHA1:
@@ -191,7 +178,7 @@ func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {
 	}
 	}
 
 
 	var h hash.Hash
 	var h hash.Hash
-	switch tsig.Algorithm {
+	switch strings.ToLower(tsig.Algorithm) {
 	case HmacMD5:
 	case HmacMD5:
 		h = hmac.New(md5.New, rawsecret)
 		h = hmac.New(md5.New, rawsecret)
 	case HmacSHA1:
 	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